From be4e010170a900c3c38b00e17f6104a7bdb938ed Mon Sep 17 00:00:00 2001 From: beveloper Date: Mon, 17 Mar 2003 21:58:35 +0000 Subject: [PATCH] The multi_audio media addon, written by Jerome Duval. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@2926 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/add-ons/media/media-add-ons/Jamfile | 1 + .../media/media-add-ons/multi_audio/Jamfile | 9 + .../multi_audio/MultiAudioAddOn.cpp | 218 ++ .../multi_audio/MultiAudioAddOn.h | 76 + .../multi_audio/MultiAudioDevice.cpp | 354 +++ .../multi_audio/MultiAudioDevice.h | 88 + .../multi_audio/MultiAudioNode.cpp | 1975 +++++++++++++++++ .../multi_audio/MultiAudioNode.h | 395 ++++ .../media/media-add-ons/multi_audio/debug.h | 34 + .../media-add-ons/multi_audio/driver_io.h | 47 + .../media-add-ons/multi_audio/multi_audio.h | 699 ++++++ 11 files changed, 3896 insertions(+) create mode 100644 src/add-ons/media/media-add-ons/multi_audio/Jamfile create mode 100644 src/add-ons/media/media-add-ons/multi_audio/MultiAudioAddOn.cpp create mode 100644 src/add-ons/media/media-add-ons/multi_audio/MultiAudioAddOn.h create mode 100644 src/add-ons/media/media-add-ons/multi_audio/MultiAudioDevice.cpp create mode 100644 src/add-ons/media/media-add-ons/multi_audio/MultiAudioDevice.h create mode 100644 src/add-ons/media/media-add-ons/multi_audio/MultiAudioNode.cpp create mode 100644 src/add-ons/media/media-add-ons/multi_audio/MultiAudioNode.h create mode 100644 src/add-ons/media/media-add-ons/multi_audio/debug.h create mode 100644 src/add-ons/media/media-add-ons/multi_audio/driver_io.h create mode 100644 src/add-ons/media/media-add-ons/multi_audio/multi_audio.h diff --git a/src/add-ons/media/media-add-ons/Jamfile b/src/add-ons/media/media-add-ons/Jamfile index 53d34e2629..d03a147f6a 100644 --- a/src/add-ons/media/media-add-ons/Jamfile +++ b/src/add-ons/media/media-add-ons/Jamfile @@ -5,4 +5,5 @@ SubInclude OBOS_TOP src add-ons media media-add-ons reader ; SubInclude OBOS_TOP src add-ons media media-add-ons writer ; SubInclude OBOS_TOP src add-ons media media-add-ons legacy ; SubInclude OBOS_TOP src add-ons media media-add-ons mixer ; +SubInclude OBOS_TOP src add-ons media media-add-ons multi_audio ; diff --git a/src/add-ons/media/media-add-ons/multi_audio/Jamfile b/src/add-ons/media/media-add-ons/multi_audio/Jamfile new file mode 100644 index 0000000000..a768fae1ff --- /dev/null +++ b/src/add-ons/media/media-add-ons/multi_audio/Jamfile @@ -0,0 +1,9 @@ +SubDir OBOS_TOP src add-ons media media-add-ons multi_audio ; + +Addon multi_audio.media_addon : media : + MultiAudioAddOn.cpp + MultiAudioDevice.cpp + MultiAudioNode.cpp +; + +LinkSharedOSLibs multi_audio.media_addon : be media ; diff --git a/src/add-ons/media/media-add-ons/multi_audio/MultiAudioAddOn.cpp b/src/add-ons/media/media-add-ons/multi_audio/MultiAudioAddOn.cpp new file mode 100644 index 0000000000..8588f63d30 --- /dev/null +++ b/src/add-ons/media/media-add-ons/multi_audio/MultiAudioAddOn.cpp @@ -0,0 +1,218 @@ +/* + * multiaudio replacement media addon for BeOS + * + * Copyright (c) 2002, Jerome Duval (jerome.duval@free.fr) + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "MultiAudioNode.h" +#include "MultiAudioAddOn.h" +#include "MultiAudioDevice.h" + +#include +#include +#include + +#include "debug.h" + +// instantiation function +extern "C" _EXPORT BMediaAddOn * make_media_addon(image_id image) { + CALLED(); + return new MultiAudioAddOn(image); +} + +// -------------------------------------------------------- // +// ctor/dtor +// -------------------------------------------------------- // + +MultiAudioAddOn::~MultiAudioAddOn() +{ + CALLED(); + + void *device = NULL; + for ( int32 i = 0; (device = fDevices.ItemAt(i)); i++ ) + delete device; + +} + +MultiAudioAddOn::MultiAudioAddOn(image_id image) : + BMediaAddOn(image), + fDevices() +{ + CALLED(); + fInitCheckStatus = B_NO_INIT; + + if(RecursiveScan("/dev/audio/multi/")!=B_OK) + return; + + fInitCheckStatus = B_OK; +} + +// -------------------------------------------------------- // +// BMediaAddOn impl +// -------------------------------------------------------- // + +status_t MultiAudioAddOn::InitCheck( + const char ** out_failure_text) +{ + CALLED(); + return B_OK; +} + +int32 MultiAudioAddOn::CountFlavors() +{ + CALLED(); + return fDevices.CountItems(); +} + +status_t MultiAudioAddOn::GetFlavorAt( + int32 n, + const flavor_info ** out_info) +{ + CALLED(); + if (out_info == 0) { + fprintf(stderr,"<- B_BAD_VALUE\n"); + return B_BAD_VALUE; // we refuse to crash because you were stupid + } + if (n < 0 || n > fDevices.CountItems() - 1) { + fprintf(stderr,"<- B_BAD_INDEX\n"); + return B_BAD_INDEX; + } + + MultiAudioDevice *device = (MultiAudioDevice *) fDevices.ItemAt(n); + + flavor_info * infos = new flavor_info[1]; + MultiAudioNode::GetFlavor(&infos[0], n); + infos[0].name = device->MD.friendly_name; + (*out_info) = infos; + return B_OK; +} + +BMediaNode * MultiAudioAddOn::InstantiateNodeFor( + const flavor_info * info, + BMessage * config, + status_t * out_error) +{ + CALLED(); + if (out_error == 0) { + fprintf(stderr,"<- NULL\n"); + return 0; // we refuse to crash because you were stupid + } + + MultiAudioDevice *device = (MultiAudioDevice*)fDevices.ItemAt(info->internal_id); + if(device == NULL) { + *out_error = B_ERROR; + return NULL; + } + + MultiAudioNode * node + = new MultiAudioNode(this, + device->MD.friendly_name, + device, + info->internal_id, + config); + if (node == 0) { + *out_error = B_NO_MEMORY; + fprintf(stderr,"<- B_NO_MEMORY\n"); + } else { + *out_error = node->InitCheck(); + } + return node; +} + +status_t +MultiAudioAddOn::GetConfigurationFor(BMediaNode * your_node, BMessage * into_message) +{ + CALLED(); + // currently never called by the media kit. Seems it is not implemented. + if (into_message == 0) { + fprintf(stderr,"<- B_BAD_VALUE\n"); + return B_BAD_VALUE; // we refuse to crash because you were stupid + } + MultiAudioNode * node = dynamic_cast(your_node); + if (node == 0) { + fprintf(stderr,"<- B_BAD_TYPE\n"); + return B_BAD_TYPE; + } + return node->GetConfigurationFor(into_message); +} + + +bool MultiAudioAddOn::WantsAutoStart() +{ + CALLED(); + return false; +} + +status_t MultiAudioAddOn::AutoStart( + int in_count, + BMediaNode ** out_node, + int32 * out_internal_id, + bool * out_has_more) +{ + CALLED(); + return B_OK; +} + +status_t +MultiAudioAddOn::RecursiveScan(char* rootPath, BEntry *rootEntry = NULL) +{ + CALLED(); + + BDirectory root; + if(rootEntry!=NULL) + root.SetTo(rootEntry); + else if(rootPath!=NULL) { + root.SetTo(rootPath); + } else { + PRINT(("Error in MultiAudioAddOn::RecursiveScan null params\n")); + return B_ERROR; + } + + BEntry entry; + + while(root.GetNextEntry(&entry) > B_ERROR) { + + if(entry.IsDirectory()) { + RecursiveScan(rootPath, &entry); + } else { + BPath path; + entry.GetPath(&path); + MultiAudioDevice *device = new MultiAudioDevice(path.Path() + strlen(rootPath), path.Path()); + if(device && device->InitCheck() == B_OK) + fDevices.AddItem(device); + } + } + + return B_OK; +} \ No newline at end of file diff --git a/src/add-ons/media/media-add-ons/multi_audio/MultiAudioAddOn.h b/src/add-ons/media/media-add-ons/multi_audio/MultiAudioAddOn.h new file mode 100644 index 0000000000..09de6ce1d2 --- /dev/null +++ b/src/add-ons/media/media-add-ons/multi_audio/MultiAudioAddOn.h @@ -0,0 +1,76 @@ +/* + * multiaudio replacement media addon for BeOS + * + * Copyright (c) 2002, Jerome Duval (jerome.duval@free.fr) + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _MULTIAUDIOADDON_H +#define _MULTIAUDIOADDON_H + +#include +#include + +class MultiAudioAddOn : + public BMediaAddOn +{ +public: + virtual ~MultiAudioAddOn(void); + explicit MultiAudioAddOn(image_id image); + +/**************************/ +/* begin from BMediaAddOn */ +public: +virtual status_t InitCheck( + const char ** out_failure_text); +virtual int32 CountFlavors(void); +virtual status_t GetFlavorAt( + int32 n, + const flavor_info ** out_info); +virtual BMediaNode * InstantiateNodeFor( + const flavor_info * info, + BMessage * config, + status_t * out_error); +virtual status_t GetConfigurationFor( + BMediaNode * your_node, + BMessage * into_message); +virtual bool WantsAutoStart(void); +virtual status_t AutoStart( + int in_count, + BMediaNode ** out_node, + int32 * out_internal_id, + bool * out_has_more); + +/* end from BMediaAddOn */ +/************************/ + +private: + status_t RecursiveScan(char* path, BEntry *rootEntry = NULL); + + status_t fInitCheckStatus; + BList fDevices; +}; + +extern "C" _EXPORT BMediaAddOn *make_media_addon( image_id you ); + +#endif /* _MULTIAUDIOADDON_H */ diff --git a/src/add-ons/media/media-add-ons/multi_audio/MultiAudioDevice.cpp b/src/add-ons/media/media-add-ons/multi_audio/MultiAudioDevice.cpp new file mode 100644 index 0000000000..51bd93c03a --- /dev/null +++ b/src/add-ons/media/media-add-ons/multi_audio/MultiAudioDevice.cpp @@ -0,0 +1,354 @@ +/* + * multiaudio replacement media addon for BeOS + * + * Copyright (c) 2002, Jerome Duval (jerome.duval@free.fr) + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "MultiAudioDevice.h" +#include "debug.h" +#include "driver_io.h" +#include +#include + +float SAMPLE_RATES[] = { + 8000.0, 11025.0, 12000.0, 16000.0, 22050.0, 24000.0, 32000.0, 44100.0, + 48000.0, 64000.0, 88200.0, 96000.0, 176400.0, 192000.0, 384000.0, 1536000.0 + }; + + +float MultiAudioDevice::convert_multiaudio_rate_to_media_rate(uint32 rate) +{ + uint8 count = 0; + uint8 size = sizeof(SAMPLE_RATES)/sizeof(SAMPLE_RATES[0]); + while(count < size) { + if(rate & 1) + return SAMPLE_RATES[count]; + count++; + rate >>= 1; + } + return 0.0; +} + +uint32 MultiAudioDevice::convert_media_rate_to_multiaudio_rate(float rate) +{ + uint8 count = 0; + uint size = sizeof(SAMPLE_RATES)/sizeof(SAMPLE_RATES[0]); + while(count < size) { + if(rate <= SAMPLE_RATES[count]) + return (0x1 << count); + count++; + } + return 0; +} + +uint32 MultiAudioDevice::convert_multiaudio_format_to_media_format(uint32 fmt) +{ + switch(fmt) { + case B_FMT_FLOAT: + return media_raw_audio_format::B_AUDIO_FLOAT; + case B_FMT_18BIT: + case B_FMT_20BIT: + case B_FMT_24BIT: + case B_FMT_32BIT: + return media_raw_audio_format::B_AUDIO_INT; + case B_FMT_16BIT: + return media_raw_audio_format::B_AUDIO_SHORT; + case B_FMT_8BIT_S: + return media_raw_audio_format::B_AUDIO_CHAR; + case B_FMT_8BIT_U: + return media_raw_audio_format::B_AUDIO_UCHAR; + default: + return 0; + } +} + +uint32 MultiAudioDevice::convert_media_format_to_multiaudio_format(uint32 fmt) +{ + switch(fmt) { + case media_raw_audio_format::B_AUDIO_FLOAT: + return B_FMT_FLOAT; + case media_raw_audio_format::B_AUDIO_INT: + return B_FMT_32BIT; + case media_raw_audio_format::B_AUDIO_SHORT: + return B_FMT_16BIT; + case media_raw_audio_format::B_AUDIO_CHAR: + return B_FMT_8BIT_S; + case media_raw_audio_format::B_AUDIO_UCHAR: + return B_FMT_8BIT_U; + default: + return 0; + } +} + +uint32 MultiAudioDevice::select_multiaudio_rate(uint32 rate) +{ + //highest rate + uint32 crate = B_SR_1536000; + while(crate != 0) { + if(rate & crate) + return crate; + crate >>= 1; + } + return 0; +} + +uint32 MultiAudioDevice::select_multiaudio_format(uint32 fmt) +{ + //highest format + if(fmt & B_FMT_FLOAT) { + return B_FMT_FLOAT; + } else if(fmt & B_FMT_32BIT) { + return B_FMT_32BIT; + } else if(fmt & B_FMT_24BIT) { + return B_FMT_24BIT; + } else if(fmt & B_FMT_20BIT) { + return B_FMT_20BIT; + } else if(fmt & B_FMT_18BIT) { + return B_FMT_18BIT; + } else if(fmt & B_FMT_16BIT) { + return B_FMT_16BIT; + } else if(fmt & B_FMT_8BIT_S) { + return B_FMT_8BIT_S; + } else if(fmt & B_FMT_8BIT_U) { + return B_FMT_8BIT_U; + } else + return 0; +} + + + +MultiAudioDevice::~MultiAudioDevice() +{ + CALLED(); + if ( fd != 0 ) { + close( fd ); + } +} + +MultiAudioDevice::MultiAudioDevice(const char* name, const char* path) +{ + CALLED(); + fInitCheckStatus = B_NO_INIT; + + strcpy(fDevice_name, name); + strcpy(fDevice_path, path); + + PRINT(("name : %s, path : %s\n", fDevice_name, fDevice_path)); + + if(InitDriver()!=B_OK) + return; + + fInitCheckStatus = B_OK; +} + + +status_t MultiAudioDevice::InitCheck(void) const +{ + CALLED(); + return fInitCheckStatus; +} + + +status_t MultiAudioDevice::InitDriver() +{ + multi_channel_enable MCE; + uint32 mce_enable_bits; + int rval, i, num_outputs, num_inputs, num_channels; + + CALLED(); + + //open the device driver for output + fd = open( fDevice_path, O_WRONLY ); + + if ( fd == 0 ) { + return B_ERROR; + } + + // + // Get description + // + MD.info_size = sizeof(MD); + MD.request_channel_count = MAX_CHANNELS; + MD.channels = MCI; + rval = DRIVER_GET_DESCRIPTION(&MD,0); + if (B_OK != rval) + { + fprintf(stderr, "Failed on B_MULTI_GET_DESCRIPTION\n"); + return B_ERROR; + } + + printf("Friendly name:\t%s\nVendor:\t\t%s\n", + MD.friendly_name,MD.vendor_info); + printf("%ld outputs\t%ld inputs\n%ld out busses\t%ld in busses\n", + MD.output_channel_count,MD.input_channel_count, + MD.output_bus_channel_count,MD.input_bus_channel_count); + printf("\nChannels\n" + "ID\tKind\tDesig\tConnectors\n"); + + for (i = 0 ; i < (MD.output_channel_count + MD.input_channel_count); i++) + { + printf("%ld\t%d\t0x%lx\t0x%lx\n",MD.channels[i].channel_id, + MD.channels[i].kind, + MD.channels[i].designations, + MD.channels[i].connectors); + } + printf("\n"); + + printf("Output rates\t\t0x%lx\n",MD.output_rates); + printf("Input rates\t\t0x%lx\n",MD.input_rates); + printf("Max CVSR\t\t%.0f\n",MD.max_cvsr_rate); + printf("Min CVSR\t\t%.0f\n",MD.min_cvsr_rate); + printf("Output formats\t\t0x%lx\n",MD.output_formats); + printf("Input formats\t\t0x%lx\n",MD.input_formats); + printf("Lock sources\t\t0x%lx\n",MD.lock_sources); + printf("Timecode sources\t0x%lx\n",MD.timecode_sources); + printf("Interface flags\t\t0x%lx\n",MD.interface_flags); + printf("Control panel string:\t\t%s\n",MD.control_panel); + printf("\n"); + + num_outputs = MD.output_channel_count; + num_inputs = MD.input_channel_count; + num_channels = num_outputs + num_inputs; + + // Get and set enabled channels + MCE.info_size = sizeof(MCE); + MCE.enable_bits = (uchar *) &mce_enable_bits; + rval = DRIVER_GET_ENABLED_CHANNELS(&MCE, sizeof(MCE)); + if (B_OK != rval) + { + fprintf(stderr, "Failed on B_MULTI_GET_ENABLED_CHANNELS len is 0x%lx\n",sizeof(MCE)); + return B_ERROR; + } + + mce_enable_bits = (1 << num_channels) - 1; + MCE.lock_source = B_MULTI_LOCK_INTERNAL; + rval = DRIVER_SET_ENABLED_CHANNELS(&MCE, 0); + if (B_OK != rval) + { + fprintf(stderr, "Failed on B_MULTI_SET_ENABLED_CHANNELS 0x%p 0x%x\n", MCE.enable_bits, *(MCE.enable_bits)); + return B_ERROR; + } + + // + // Set the sample rate + // + MFI.info_size = sizeof(MFI); + MFI.output.rate = select_multiaudio_rate(MD.output_rates); //B_SR_48000; + MFI.output.cvsr = 0; + MFI.output.format = select_multiaudio_format(MD.output_formats); //B_FMT_16BIT; + MFI.input.rate = MFI.output.rate; + MFI.input.cvsr = MFI.output.cvsr; + MFI.input.format = MFI.output.format; + rval = DRIVER_SET_GLOBAL_FORMAT(&MFI, 0); + if (B_OK != rval) + { + fprintf(stderr, "Failed on B_MULTI_SET_GLOBAL_FORMAT\n"); + return B_ERROR; + } + + // + // Get the buffers + // + play_buffer_desc[0] = play_buffer_list0; + play_buffer_desc[1] = play_buffer_list1; + record_buffer_desc[0] = record_buffer_list0; + record_buffer_desc[1] = record_buffer_list1; + MBL.info_size = sizeof(MBL); + MBL.request_playback_buffer_size = 0; //use the default...... + MBL.request_playback_buffers = NB_BUFFERS; + MBL.request_playback_channels = num_outputs; + MBL.playback_buffers = (buffer_desc **) play_buffer_desc; + MBL.request_record_buffer_size = 0; //use the default...... + MBL.request_record_buffers = NB_BUFFERS; + MBL.request_record_channels = num_inputs; + MBL.record_buffers = (buffer_desc **) record_buffer_desc; + rval = DRIVER_GET_BUFFERS(&MBL, 0); + + if (B_OK != rval) + { + fprintf(stderr, "Failed on B_MULTI_GET_BUFFERS\n"); + return B_ERROR; + } + + for (i = 0; i < MBL.return_playback_buffers; i++) + { + for (int j=0; j < MBL.return_playback_channels; j++) + { + //fPlay[i][j] = (int32 *) MBL.playback_buffers[i][j].base; + PRINT(("MBL.playback_buffers[%d][%d].base: %p\n", + i,j,MBL.playback_buffers[i][j].base)); + PRINT(("MBL.playback_buffers[%d][%d].stride: %i\n", + i,j,MBL.playback_buffers[i][j].stride)); + } + //memset(MBL.playback_buffers[i][0].base, 0, MBL.return_playback_buffer_size * 4 /*samps to bytes*/); + //PRINT(("Play buffers[%d] size : %li zeroed\n",i, MBL.return_playback_buffer_size * 4)); + } + + for (i = 0; i < MBL.return_record_buffers; i++) + { + for (int j=0; j < MBL.return_record_channels; j++) + { + /*fRecord[i][j] = (int32 *) MBL.record_buffers[i][j].base; + PRINT(("Record buffers[%d][%d]: %p\n",i,j,fRecord[i][j]));*/ + PRINT(("MBL.record_buffers[%d][%d].base: %p\n", + i,j,MBL.record_buffers[i][j].base)); + PRINT(("MBL.record_buffers[%d][%d].stride: %i\n", + i,j,MBL.record_buffers[i][j].stride)); + } + //memset(MBL.record_buffers[i][0].base, 0, MBL.return_record_buffer_size * 4 /*samps to bytes*/); + //PRINT(("Record buffers[%d] size : %li zeroed\n",i, MBL.return_record_buffer_size * 4)); + } + + MMCI.info_size = sizeof(MMCI); + MMCI.control_count = MAX_CONTROLS; + MMCI.controls = MMC; + + rval = DRIVER_LIST_MIX_CONTROLS(&MMCI, 0); + + if (B_OK != rval) + { + fprintf(stderr, "Failed on DRIVER_LIST_MIX_CONTROLS\n"); + return B_ERROR; + } + + return B_OK; +} + +int +MultiAudioDevice::DoBufferExchange(multi_buffer_info *MBI) +{ + return DRIVER_BUFFER_EXCHANGE(MBI, 0); +} + +int +MultiAudioDevice::DoSetMix(multi_mix_value_info *MMVI) +{ + return DRIVER_SET_MIX(MMVI, 0); +} + +int +MultiAudioDevice::DoGetMix(multi_mix_value_info *MMVI) +{ + return DRIVER_GET_MIX(MMVI, 0); +} diff --git a/src/add-ons/media/media-add-ons/multi_audio/MultiAudioDevice.h b/src/add-ons/media/media-add-ons/multi_audio/MultiAudioDevice.h new file mode 100644 index 0000000000..7ee89849cb --- /dev/null +++ b/src/add-ons/media/media-add-ons/multi_audio/MultiAudioDevice.h @@ -0,0 +1,88 @@ +/* + * multiaudio replacement media addon for BeOS + * + * Copyright (c) 2002, Jerome Duval (jerome.duval@free.fr) + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _MULTIAUDIODEVICE_H +#define _MULTIAUDIODEVICE_H + +#include "multi_audio.h" + +#define MAX_CONTROLS 128 +#define MAX_CHANNELS 32 +#define NB_BUFFERS 2 + +class MultiAudioDevice +{ +protected: + virtual ~MultiAudioDevice(void); +public: + + explicit MultiAudioDevice(const char *name, const char* path); + + virtual status_t InitCheck(void) const; + + static float convert_multiaudio_rate_to_media_rate(uint32 rate); + static uint32 convert_media_rate_to_multiaudio_rate(float rate); + static uint32 convert_multiaudio_format_to_media_format(uint32 fmt); + static uint32 convert_media_format_to_multiaudio_format(uint32 fmt); + static uint32 select_multiaudio_rate(uint32 rate); + static uint32 select_multiaudio_format(uint32 fmt); + + int DoBufferExchange(multi_buffer_info *MBI); + int DoSetMix(multi_mix_value_info *MMVI); + int DoGetMix(multi_mix_value_info *MMVI); + +private: + status_t InitDriver(); + + int fd; //file descriptor for hw driver + char fDevice_name[32]; + char fDevice_path[32]; + +public: + multi_description MD; + multi_channel_info MCI[MAX_CHANNELS]; + multi_format_info MFI; + multi_buffer_list MBL; + + multi_mix_control_info MMCI; + multi_mix_control MMC[MAX_CONTROLS]; + + /*int32 *fPlay[NB_BUFFERS][MAX_CHANNELS]; + int32 *fRecord[NB_BUFFERS][MAX_CHANNELS];*/ + + buffer_desc play_buffer_list0[MAX_CHANNELS]; + buffer_desc play_buffer_list1[MAX_CHANNELS]; + buffer_desc *play_buffer_desc[NB_BUFFERS]; + buffer_desc record_buffer_list0[MAX_CHANNELS]; + buffer_desc record_buffer_list1[MAX_CHANNELS]; + buffer_desc *record_buffer_desc[NB_BUFFERS]; + +private: + status_t fInitCheckStatus; +}; + +#endif \ No newline at end of file diff --git a/src/add-ons/media/media-add-ons/multi_audio/MultiAudioNode.cpp b/src/add-ons/media/media-add-ons/multi_audio/MultiAudioNode.cpp new file mode 100644 index 0000000000..6f21d9ec7a --- /dev/null +++ b/src/add-ons/media/media-add-ons/multi_audio/MultiAudioNode.cpp @@ -0,0 +1,1975 @@ +/* + * multiaudio replacement media addon for BeOS + * + * Copyright (c) 2002, Jerome Duval (jerome.duval@free.fr) + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "MultiAudioNode.h" +#include "driver_io.h" +#include "debug.h" + +#include + +const char * multi_string[] = +{ + "NAME IS ATTACHED", + "Output", "Input", "Setup", "Tone Control", "Extended Setup", "Enhanced Setup", "Master", + "Beep", "Phone", "Mic", "Line", "CD", "Video", "Aux", "Wave", "Gain", "Level", "Volume", + "Mute", "Enable", "Stereo Mix", "Mono Mix", "Output Stereo Mix", "Output Mono Mix", "Output Bass", + "Output Treble", "Output 3D Center", "Output 3D Depth" +}; + +node_input::node_input(media_input &input, media_format format) +{ + CALLED(); + fInput = input; + fPreferredFormat = format; + fBufferCycle = 1; + fBuffer = NULL; +} + +node_input::~node_input() +{ + CALLED(); +} + +node_output::node_output(media_output &output, media_format format) + : fBufferGroup(NULL), + fOutputEnabled(true) +{ + CALLED(); + fOutput = output; + fPreferredFormat = format; + fBufferCycle = 1; +} + +node_output::~node_output() +{ + CALLED(); +} + + +// -------------------------------------------------------- // +// ctor/dtor +// -------------------------------------------------------- // + +MultiAudioNode::~MultiAudioNode(void) +{ + CALLED(); + StopThread(); + BMediaEventLooper::Quit(); + + fWeb = NULL; +} + +MultiAudioNode::MultiAudioNode(BMediaAddOn *addon, char* name, MultiAudioDevice *device, + int32 internal_id, BMessage * config) + : BMediaNode(name), + BBufferConsumer(B_MEDIA_RAW_AUDIO), + BBufferProducer(B_MEDIA_RAW_AUDIO), + BTimeSource(), + BMediaEventLooper(), + fThread(-1), + fDevice(device), + fTimeSourceStarted(false), + fWeb(NULL) +{ + CALLED(); + fInitCheckStatus = B_NO_INIT; + + if(!device) + return; + + fAddOn = addon; + fId = internal_id; + + AddNodeKind( B_PHYSICAL_OUTPUT ); + AddNodeKind( B_PHYSICAL_INPUT ); + + // don't overwrite available space, and be sure to terminate + //strncpy(input.name,"MultiAudioNode Input",B_MEDIA_NAME_LENGTH-1); + //input.name[B_MEDIA_NAME_LENGTH-1] = '\0'; + // initialize the input + //input.node = media_node::null; // until registration + //input.source = media_source::null; + //input.destination = media_destination::null; // until registration + //GetFormat(&input.format); + + // initialize our preferred format object + fPreferredFormat.type = B_MEDIA_RAW_AUDIO; + fPreferredFormat.u.raw_audio.format = MultiAudioDevice::convert_multiaudio_format_to_media_format(fDevice->MFI.output.format); + fPreferredFormat.u.raw_audio.channel_count = 2; + fPreferredFormat.u.raw_audio.frame_rate = MultiAudioDevice::convert_multiaudio_rate_to_media_rate(fDevice->MFI.input.rate); // measured in Hertz + fPreferredFormat.u.raw_audio.byte_order = B_MEDIA_HOST_ENDIAN; + + // we'll use the consumer's preferred buffer size, if any + fPreferredFormat.u.raw_audio.buffer_size = fDevice->MBL.return_record_buffer_size + * (fPreferredFormat.u.raw_audio.format & media_raw_audio_format::B_AUDIO_SIZE_MASK) + * fPreferredFormat.u.raw_audio.channel_count; + + // we're not connected yet + //fOutput.destination = media_destination::null; + //fOutput.format = fPreferredFormat; + + config->PrintToStream(); + + fInitCheckStatus = B_OK; +} + +status_t MultiAudioNode::InitCheck(void) const +{ + CALLED(); + return fInitCheckStatus; +} + + +// -------------------------------------------------------- // +// implementation of BMediaNode +// -------------------------------------------------------- // + +BMediaAddOn * MultiAudioNode::AddOn( + int32 * internal_id) const +{ + CALLED(); + // BeBook says this only gets called if we were in an add-on. + if (fAddOn != 0) { + // If we get a null pointer then we just won't write. + if (internal_id != 0) { + *internal_id = fId; + } + } + return fAddOn; +} + +void MultiAudioNode::Preroll(void) +{ + CALLED(); + // XXX:Performance opportunity + BMediaNode::Preroll(); +} + +status_t MultiAudioNode::HandleMessage( + int32 message, + const void * data, + size_t size) +{ + CALLED(); + return B_ERROR; +} + +void MultiAudioNode::NodeRegistered(void) +{ + CALLED(); + + if (fInitCheckStatus != B_OK) { + ReportError(B_NODE_IN_DISTRESS); + return; + } + + SetPriority(B_REAL_TIME_PRIORITY); + + Run(); + + node_input *currentInput = NULL; + int32 currentId = 0; + + for (int32 i = 0; i < fDevice->MD.output_channel_count; i++) + { + if( (currentInput == NULL) + || (fDevice->MD.channels[i].designations & B_CHANNEL_MONO_BUS) + || (fDevice->MD.channels[currentId].designations & B_CHANNEL_STEREO_BUS + && ( fDevice->MD.channels[i].designations & B_CHANNEL_LEFT || + !(fDevice->MD.channels[i].designations & B_CHANNEL_STEREO_BUS))) + || (fDevice->MD.channels[currentId].designations & B_CHANNEL_SURROUND_BUS + && ( fDevice->MD.channels[i].designations & B_CHANNEL_LEFT || + !(fDevice->MD.channels[i].designations & B_CHANNEL_SURROUND_BUS))) + ) { + PRINT(("NodeRegistered() : creating an input for %i\n", i)); + printf("%ld\t%d\t0x%lx\t0x%lx\n",fDevice->MD.channels[i].channel_id, + fDevice->MD.channels[i].kind, + fDevice->MD.channels[i].designations, + fDevice->MD.channels[i].connectors); + + media_input *input = new media_input; + + input->format = fPreferredFormat; + input->destination.port = ControlPort(); + input->destination.id = fInputs.CountItems(); + input->node = Node(); + sprintf(input->name, "output %ld", input->destination.id); + + currentInput = new node_input(*input, fPreferredFormat); + currentInput->fPreferredFormat.u.raw_audio.channel_count = 1; + currentInput->fInput.format = currentInput->fPreferredFormat; + + currentInput->fChannelId = fDevice->MD.channels[i].channel_id; + fInputs.AddItem(currentInput); + + currentId = i; + + } else { + PRINT(("NodeRegistered() : adding a channel\n")); + currentInput->fPreferredFormat.u.raw_audio.channel_count++; + currentInput->fInput.format = currentInput->fPreferredFormat; + } + currentInput->fInput.format.u.raw_audio.format = media_raw_audio_format::wildcard.format; + } + + /*input.destination.port = ControlPort(); + input.destination.id = 0; + input.node = Node(); + ::strcpy(input.name, "Channels 1/2");*/ + + node_output *currentOutput = NULL; + currentId = 0; + + for (int32 i = fDevice->MD.output_channel_count; + i < (fDevice->MD.output_channel_count + fDevice->MD.input_channel_count); i++) + { + if( (currentOutput == NULL) + || (fDevice->MD.channels[i].designations & B_CHANNEL_MONO_BUS) + || (fDevice->MD.channels[currentId].designations & B_CHANNEL_STEREO_BUS + && ( fDevice->MD.channels[i].designations & B_CHANNEL_LEFT || + !(fDevice->MD.channels[i].designations & B_CHANNEL_STEREO_BUS))) + || (fDevice->MD.channels[currentId].designations & B_CHANNEL_SURROUND_BUS + && ( fDevice->MD.channels[i].designations & B_CHANNEL_LEFT || + !(fDevice->MD.channels[i].designations & B_CHANNEL_SURROUND_BUS))) + ) { + PRINT(("NodeRegistered() : creating an output for %i\n", i)); + printf("%ld\t%d\t0x%lx\t0x%lx\n",fDevice->MD.channels[i].channel_id, + fDevice->MD.channels[i].kind, + fDevice->MD.channels[i].designations, + fDevice->MD.channels[i].connectors); + + media_output *output = new media_output; + + output->format = fPreferredFormat; + output->destination = media_destination::null; + output->source.port = ControlPort(); + output->source.id = fOutputs.CountItems(); + output->node = Node(); + sprintf(output->name, "input %ld", output->source.id); + + currentOutput = new node_output(*output, fPreferredFormat); + currentOutput->fPreferredFormat.u.raw_audio.channel_count = 1; + currentOutput->fOutput.format = currentOutput->fPreferredFormat; + currentOutput->fChannelId = fDevice->MD.channels[i].channel_id; + fOutputs.AddItem(currentOutput); + + currentId = i; + + } else { + PRINT(("NodeRegistered() : adding a channel\n")); + currentOutput->fPreferredFormat.u.raw_audio.channel_count++; + currentOutput->fOutput.format = currentOutput->fPreferredFormat; + } + } + + // Set up our parameter web + fWeb = MakeParameterWeb(); + SetParameterWeb(fWeb); + +} + +status_t MultiAudioNode::RequestCompleted(const media_request_info &info) +{ + CALLED(); + return B_OK; +} + +void MultiAudioNode::SetTimeSource(BTimeSource *timeSource) +{ + CALLED(); +} + +// -------------------------------------------------------- // +// implemention of BBufferConsumer +// -------------------------------------------------------- // + +// Check to make sure the format is okay, then remove +// any wildcards corresponding to our requirements. +status_t MultiAudioNode::AcceptFormat( + const media_destination & dest, + media_format * format) +{ + CALLED(); + + node_input *channel = FindInput(dest); + + if(channel==NULL) { + fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION"); + return B_MEDIA_BAD_DESTINATION; // we only have one input so that better be it + } + + if (format == 0) { + fprintf(stderr,"<- B_BAD_VALUE\n"); + return B_BAD_VALUE; // no crashing + } +/* media_format * myFormat = GetFormat(); + fprintf(stderr,"proposed format: "); + print_media_format(format); + fprintf(stderr,"\n"); + fprintf(stderr,"my format: "); + print_media_format(myFormat); + fprintf(stderr,"\n");*/ + // Be's format_is_compatible doesn't work. +// if (!format_is_compatible(*format,*myFormat)) { + + if ( format->type != B_MEDIA_RAW_AUDIO ) { + fprintf(stderr,"<- B_MEDIA_BAD_FORMAT\n"); + return B_MEDIA_BAD_FORMAT; + } + + channel->fFormat = channel->fPreferredFormat; + + /*if(format->u.raw_audio.format == media_raw_audio_format::B_AUDIO_FLOAT + && channel->fPreferredFormat.u.raw_audio.format == media_raw_audio_format::B_AUDIO_SHORT) + format->u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT; + else*/ + format->u.raw_audio.format = channel->fPreferredFormat.u.raw_audio.format; //MultiAudioDevice::convert_multiaudio_format_to_media_format(fDevice->MFI.output.format); //media_raw_audio_format::B_AUDIO_SHORT; + + format->u.raw_audio.frame_rate = channel->fPreferredFormat.u.raw_audio.frame_rate;//MultiAudioDevice::convert_multiaudio_rate_to_media_rate(fDevice->MFI.output.rate); //48000.0; + format->u.raw_audio.channel_count = channel->fPreferredFormat.u.raw_audio.channel_count; + format->u.raw_audio.byte_order = B_MEDIA_HOST_ENDIAN; + format->u.raw_audio.buffer_size = fDevice->MBL.return_playback_buffer_size + * (format->u.raw_audio.format & media_raw_audio_format::B_AUDIO_SIZE_MASK) + * format->u.raw_audio.channel_count; + + + /*media_format myFormat; + GetFormat(&myFormat); + if (!format_is_acceptible(*format,myFormat)) { + fprintf(stderr,"<- B_MEDIA_BAD_FORMAT\n"); + return B_MEDIA_BAD_FORMAT; + }*/ + //AddRequirements(format); + return B_OK; +} + +status_t MultiAudioNode::GetNextInput( + int32 * cookie, + media_input * out_input) +{ + CALLED(); + // let's not crash even if they are stupid + if (out_input == 0) { + // no place to write! + fprintf(stderr,"<- B_BAD_VALUE\n"); + return B_BAD_VALUE; + } + + if ((*cookie < fInputs.CountItems()) && (*cookie >= 0)) { + node_input *channel = (node_input *)fInputs.ItemAt(*cookie); + *out_input = channel->fInput; + *cookie += 1; + PRINT(("input.format : %u\n", channel->fInput.format.u.raw_audio.format)); + return B_OK; + } else + return B_BAD_INDEX; +} + +void MultiAudioNode::DisposeInputCookie( + int32 cookie) +{ + CALLED(); + // nothing to do since our cookies are just integers +} + +void MultiAudioNode::BufferReceived( + BBuffer * buffer) +{ + //CALLED(); + switch (buffer->Header()->type) { + /*case B_MEDIA_PARAMETERS: + { + status_t status = ApplyParameterData(buffer->Data(),buffer->SizeUsed()); + if (status != B_OK) { + fprintf(stderr,"ApplyParameterData in MultiAudioNode::BufferReceived failed\n"); + } + buffer->Recycle(); + } + break;*/ + case B_MEDIA_RAW_AUDIO: + if (buffer->Flags() & BBuffer::B_SMALL_BUFFER) { + fprintf(stderr,"NOT IMPLEMENTED: B_SMALL_BUFFER in MultiAudioNode::BufferReceived\n"); + // XXX: implement this part + buffer->Recycle(); + } else { + media_timed_event event(buffer->Header()->start_time, BTimedEventQueue::B_HANDLE_BUFFER, + buffer, BTimedEventQueue::B_RECYCLE_BUFFER); + status_t status = EventQueue()->AddEvent(event); + if (status != B_OK) { + fprintf(stderr,"EventQueue()->AddEvent(event) in MultiAudioNode::BufferReceived failed\n"); + buffer->Recycle(); + } + } + break; + default: + fprintf(stderr,"unexpected buffer type in MultiAudioNode::BufferReceived\n"); + buffer->Recycle(); + break; + } +} + +void MultiAudioNode::ProducerDataStatus( + const media_destination & for_whom, + int32 status, + bigtime_t at_performance_time) +{ + //CALLED(); + + node_input *channel = FindInput(for_whom); + + if(channel==NULL) { + fprintf(stderr,"invalid destination received in MultiAudioNode::ProducerDataStatus\n"); + return; + } + + media_timed_event event(at_performance_time, BTimedEventQueue::B_DATA_STATUS, + &channel->fInput, BTimedEventQueue::B_NO_CLEANUP, status, 0, NULL); + EventQueue()->AddEvent(event); +} + +status_t MultiAudioNode::GetLatencyFor( + const media_destination & for_whom, + bigtime_t * out_latency, + media_node_id * out_timesource) +{ + CALLED(); + if ((out_latency == 0) || (out_timesource == 0)) { + fprintf(stderr,"<- B_BAD_VALUE\n"); + return B_BAD_VALUE; + } + + node_input *channel = FindInput(for_whom); + + if(channel==NULL) { + fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n"); + return B_MEDIA_BAD_DESTINATION; + } + *out_latency = EventLatency(); + *out_timesource = TimeSource()->ID(); + return B_OK; +} + +status_t MultiAudioNode::Connected( + const media_source & producer, /* here's a good place to request buffer group usage */ + const media_destination & where, + const media_format & with_format, + media_input * out_input) +{ + CALLED(); + if (out_input == 0) { + fprintf(stderr,"<- B_BAD_VALUE\n"); + return B_BAD_VALUE; // no crashing + } + + node_input *channel = FindInput(where); + + if(channel==NULL) { + fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n"); + return B_MEDIA_BAD_DESTINATION; + } + + // compute the latency or just guess + // use one buffer length latency + /*fInternalLatency = with_format.u.raw_audio.buffer_size * 10000 / 2 + / ( (with_format.u.raw_audio.format & media_raw_audio_format::B_AUDIO_SIZE_MASK) + * with_format.u.raw_audio.channel_count) + / ((int32)(with_format.u.raw_audio.frame_rate / 100));*/ + /*char* buffer1 = (char*)malloc(with_format.u.raw_audio.buffer_size); + char* buffer2 = (char*)malloc(with_format.u.raw_audio.buffer_size); + bigtime_t latency_start = TimeSource()->RealTime(); + memcpy(buffer2, buffer1, with_format.u.raw_audio.buffer_size); + bigtime_t latency_end = TimeSource()->RealTime(); + fInternalLatency = latency_end - latency_start; + */ + fInternalLatency = 50LL; + + PRINT((" internal latency = %lld\n",fInternalLatency)); + + SetEventLatency(fInternalLatency); + + // record the agreed upon values + channel->fInput.source = producer; + channel->fInput.format = with_format; + *out_input = channel->fInput; + return B_OK; +} + +void MultiAudioNode::Disconnected( + const media_source & producer, + const media_destination & where) +{ + CALLED(); + node_input *channel = FindInput(where); + + if(channel==NULL) { + fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n"); + return; + } + if (channel->fInput.source != producer) { + fprintf(stderr,"<- B_MEDIA_BAD_SOURCE\n"); + return; + } + /*if ( RunState() == B_STARTED ) { + StopThread(); + }*/ + + channel->fInput.source = media_source::null; + channel->fInput.format = channel->fPreferredFormat; + FillWithZeros(*channel); + //GetFormat(&channel->fInput.format); +} + + /* The notification comes from the upstream producer, so he's already cool with */ + /* the format; you should not ask him about it in here. */ +status_t MultiAudioNode::FormatChanged( + const media_source & producer, + const media_destination & consumer, + int32 change_tag, + const media_format & format) +{ + CALLED(); + node_input *channel = FindInput(consumer); + + if(channel==NULL) { + fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n"); + return B_MEDIA_BAD_DESTINATION; + } + if (channel->fInput.source != producer) { + return B_MEDIA_BAD_SOURCE; + } + + return B_ERROR; +} + + /* Given a performance time of some previous buffer, retrieve the remembered tag */ + /* of the closest (previous or exact) performance time. Set *out_flags to 0; the */ + /* idea being that flags can be added later, and the understood flags returned in */ + /* *out_flags. */ +status_t MultiAudioNode::SeekTagRequested( + const media_destination & destination, + bigtime_t in_target_time, + uint32 in_flags, + media_seek_tag * out_seek_tag, + bigtime_t * out_tagged_time, + uint32 * out_flags) +{ + CALLED(); + return BBufferConsumer::SeekTagRequested(destination,in_target_time,in_flags, + out_seek_tag,out_tagged_time,out_flags); +} + +// -------------------------------------------------------- // +// implementation for BBufferProducer +// -------------------------------------------------------- // + +status_t +MultiAudioNode::FormatSuggestionRequested(media_type type, int32 /*quality*/, media_format* format) +{ + // FormatSuggestionRequested() is not necessarily part of the format negotiation + // process; it's simply an interrogation -- the caller wants to see what the node's + // preferred data format is, given a suggestion by the caller. + CALLED(); + + if (!format) + { + fprintf(stderr, "\tERROR - NULL format pointer passed in!\n"); + return B_BAD_VALUE; + } + + // this is the format we'll be returning (our preferred format) + *format = fPreferredFormat; + + // a wildcard type is okay; we can specialize it + if (type == B_MEDIA_UNKNOWN_TYPE) type = B_MEDIA_RAW_AUDIO; + + // we only support raw audio + if (type != B_MEDIA_RAW_AUDIO) return B_MEDIA_BAD_FORMAT; + else return B_OK; +} + +status_t +MultiAudioNode::FormatProposal(const media_source& output, media_format* format) +{ + // FormatProposal() is the first stage in the BMediaRoster::Connect() process. We hand + // out a suggested format, with wildcards for any variations we support. + CALLED(); + node_output *channel = FindOutput(output); + + // is this a proposal for our select output? + if (channel == NULL) + { + fprintf(stderr, "MultiAudioNode::FormatProposal returning B_MEDIA_BAD_SOURCE\n"); + return B_MEDIA_BAD_SOURCE; + } + + // we only support floating-point raw audio, so we always return that, but we + // supply an error code depending on whether we found the proposal acceptable. + media_type requestedType = format->type; + *format = channel->fPreferredFormat; + if ((requestedType != B_MEDIA_UNKNOWN_TYPE) && (requestedType != B_MEDIA_RAW_AUDIO)) + { + fprintf(stderr, "MultiAudioNode::FormatProposal returning B_MEDIA_BAD_FORMAT\n"); + return B_MEDIA_BAD_FORMAT; + } + else return B_OK; // raw audio or wildcard type, either is okay by us +} + +status_t +MultiAudioNode::FormatChangeRequested(const media_source& source, const media_destination& destination, media_format* io_format, int32* _deprecated_) +{ + CALLED(); + + // we don't support any other formats, so we just reject any format changes. + return B_ERROR; +} + +status_t +MultiAudioNode::GetNextOutput(int32* cookie, media_output* out_output) +{ + CALLED(); + + if ((*cookie < fOutputs.CountItems()) && (*cookie >= 0)) { + node_output *channel = (node_output *)fOutputs.ItemAt(*cookie); + *out_output = channel->fOutput; + *cookie += 1; + return B_OK; + } else + return B_BAD_INDEX; +} + +status_t +MultiAudioNode::DisposeOutputCookie(int32 cookie) +{ + CALLED(); + // do nothing because we don't use the cookie for anything special + return B_OK; +} + +status_t +MultiAudioNode::SetBufferGroup(const media_source& for_source, BBufferGroup* newGroup) +{ + CALLED(); + + node_output *channel = FindOutput(for_source); + + // is this our output? + if (channel == NULL) + { + fprintf(stderr, "MultiAudioNode::SetBufferGroup returning B_MEDIA_BAD_SOURCE\n"); + return B_MEDIA_BAD_SOURCE; + } + + // Are we being passed the buffer group we're already using? + if (newGroup == channel->fBufferGroup) return B_OK; + + // Ahh, someone wants us to use a different buffer group. At this point we delete + // the one we are using and use the specified one instead. If the specified group is + // NULL, we need to recreate one ourselves, and use *that*. Note that if we're + // caching a BBuffer that we requested earlier, we have to Recycle() that buffer + // *before* deleting the buffer group, otherwise we'll deadlock waiting for that + // buffer to be recycled! + delete channel->fBufferGroup; // waits for all buffers to recycle + if (newGroup != NULL) + { + // we were given a valid group; just use that one from now on + channel->fBufferGroup = newGroup; + } + else + { + // we were passed a NULL group pointer; that means we construct + // our own buffer group to use from now on + size_t size = channel->fOutput.format.u.raw_audio.buffer_size; + int32 count = int32(fLatency / BufferDuration() + 1 + 1); + channel->fBufferGroup = new BBufferGroup(size, count); + } + + return B_OK; +} + +status_t +MultiAudioNode::PrepareToConnect(const media_source& what, const media_destination& where, media_format* format, media_source* out_source, char* out_name) +{ + // PrepareToConnect() is the second stage of format negotiations that happens + // inside BMediaRoster::Connect(). At this point, the consumer's AcceptFormat() + // method has been called, and that node has potentially changed the proposed + // format. It may also have left wildcards in the format. PrepareToConnect() + // *must* fully specialize the format before returning! + CALLED(); + + node_output *channel = FindOutput(what); + + // is this our output? + if (channel == NULL) + { + fprintf(stderr, "MultiAudioNode::PrepareToConnect returning B_MEDIA_BAD_SOURCE\n"); + return B_MEDIA_BAD_SOURCE; + } + + // are we already connected? + if (channel->fOutput.destination != media_destination::null) + return B_MEDIA_ALREADY_CONNECTED; + + // the format may not yet be fully specialized (the consumer might have + // passed back some wildcards). Finish specializing it now, and return an + // error if we don't support the requested format. + if (format->type != B_MEDIA_RAW_AUDIO) + { + fprintf(stderr, "\tnon-raw-audio format?!\n"); + return B_MEDIA_BAD_FORMAT; + } + else if (format->u.raw_audio.format != media_raw_audio_format::B_AUDIO_SHORT) + { + fprintf(stderr, "\tnon-short-audio format?!\n"); + return B_MEDIA_BAD_FORMAT; + } + + // !!! validate all other fields except for buffer_size here, because the consumer might have + // supplied different values from AcceptFormat()? + + // check the buffer size, which may still be wildcarded + if (format->u.raw_audio.buffer_size == media_raw_audio_format::wildcard.buffer_size) + { + format->u.raw_audio.buffer_size = 2048; // pick something comfortable to suggest + fprintf(stderr, "\tno buffer size provided, suggesting %lu\n", format->u.raw_audio.buffer_size); + } + else + { + fprintf(stderr, "\tconsumer suggested buffer_size %lu\n", format->u.raw_audio.buffer_size); + } + + // Now reserve the connection, and return information about it + channel->fOutput.destination = where; + channel->fOutput.format = *format; + *out_source = channel->fOutput.source; + strncpy(out_name, channel->fOutput.name, B_MEDIA_NAME_LENGTH); + return B_OK; +} + +void +MultiAudioNode::Connect(status_t error, const media_source& source, const media_destination& destination, const media_format& format, char* io_name) +{ + CALLED(); + + node_output *channel = FindOutput(source); + + // is this our output? + if (channel == NULL) + { + fprintf(stderr, "MultiAudioNode::Connect returning (cause : B_MEDIA_BAD_SOURCE)\n"); + return; + } + + // If something earlier failed, Connect() might still be called, but with a non-zero + // error code. When that happens we simply unreserve the connection and do + // nothing else. + if (error) + { + channel->fOutput.destination = media_destination::null; + channel->fOutput.format = channel->fPreferredFormat; + return; + } + + // Okay, the connection has been confirmed. Record the destination and format + // that we agreed on, and report our connection name again. + channel->fOutput.destination = destination; + channel->fOutput.format = format; + strncpy(io_name, channel->fOutput.name, B_MEDIA_NAME_LENGTH); + + // Now that we're connected, we can determine our downstream latency. + // Do so, then make sure we get our events early enough. + media_node_id id; + FindLatencyFor(channel->fOutput.destination, &fLatency, &id); + fprintf(stderr, "\tdownstream latency = %Ld\n", fLatency); + + fInternalLatency = 50LL; + fprintf(stderr, "\tbuffer-filling took %Ld usec on this machine\n", fInternalLatency); + //SetEventLatency(fLatency + fInternalLatency); + + // reset our buffer duration, etc. to avoid later calculations + bigtime_t duration = channel->fOutput.format.u.raw_audio.buffer_size * 10000 + / ( (channel->fOutput.format.u.raw_audio.format & media_raw_audio_format::B_AUDIO_SIZE_MASK) + * channel->fOutput.format.u.raw_audio.channel_count) + / ((int32)(channel->fOutput.format.u.raw_audio.frame_rate / 100)); + //bigtime_t duration = bigtime_t(1000000) * samplesPerBuffer / bigtime_t(fOutput.format.u.raw_audio.frame_rate); + SetBufferDuration(duration); + + // Set up the buffer group for our connection, as long as nobody handed us a + // buffer group (via SetBufferGroup()) prior to this. That can happen, for example, + // if the consumer calls SetOutputBuffersFor() on us from within its Connected() + // method. + if (!channel->fBufferGroup) + AllocateBuffers(*channel); +} + +void +MultiAudioNode::Disconnect(const media_source& what, const media_destination& where) +{ + CALLED(); + + node_output *channel = FindOutput(what); + + // is this our output? + if (channel == NULL) + { + fprintf(stderr, "MultiAudioNode::Disconnect() returning (cause : B_MEDIA_BAD_SOURCE)\n"); + return; + } + + // Make sure that our connection is the one being disconnected + if ((where == channel->fOutput.destination) && (what == channel->fOutput.source)) + { + channel->fOutput.destination = media_destination::null; + channel->fOutput.format = channel->fPreferredFormat; + delete channel->fBufferGroup; + channel->fBufferGroup = NULL; + } + else + { + fprintf(stderr, "\tDisconnect() called with wrong source/destination (%ld/%ld), ours is (%ld/%ld)\n", + what.id, where.id, channel->fOutput.source.id, channel->fOutput.destination.id); + } +} + +void +MultiAudioNode::LateNoticeReceived(const media_source& what, bigtime_t how_much, bigtime_t performance_time) +{ + CALLED(); + + node_output *channel = FindOutput(what); + + // is this our output? + if (channel == NULL) + { + return; + } + + // If we're late, we need to catch up. Respond in a manner appropriate to our + // current run mode. + if (RunMode() == B_RECORDING) + { + // A hardware capture node can't adjust; it simply emits buffers at + // appropriate points. We (partially) simulate this by not adjusting + // our behavior upon receiving late notices -- after all, the hardware + // can't choose to capture "sooner".... + } + else if (RunMode() == B_INCREASE_LATENCY) + { + // We're late, and our run mode dictates that we try to produce buffers + // earlier in order to catch up. This argues that the downstream nodes are + // not properly reporting their latency, but there's not much we can do about + // that at the moment, so we try to start producing buffers earlier to + // compensate. + fInternalLatency += how_much; + SetEventLatency(fLatency + fInternalLatency); + + fprintf(stderr, "\tincreasing latency to %Ld\n", fLatency + fInternalLatency); + } + else + { + // The other run modes dictate various strategies for sacrificing data quality + // in the interests of timely data delivery. The way *we* do this is to skip + // a buffer, which catches us up in time by one buffer duration. + /*size_t nSamples = fOutput.format.u.raw_audio.buffer_size / sizeof(float); + mSamplesSent += nSamples;*/ + + fprintf(stderr, "\tskipping a buffer to try to catch up\n"); + } +} + +void +MultiAudioNode::EnableOutput(const media_source& what, bool enabled, int32* _deprecated_) +{ + CALLED(); + + // If I had more than one output, I'd have to walk my list of output records to see + // which one matched the given source, and then enable/disable that one. But this + // node only has one output, so I just make sure the given source matches, then set + // the enable state accordingly. + node_output *channel = FindOutput(what); + + if (channel != NULL) + { + channel->fOutputEnabled = enabled; + } +} + +void +MultiAudioNode::AdditionalBufferRequested(const media_source& source, media_buffer_id prev_buffer, bigtime_t prev_time, const media_seek_tag* prev_tag) +{ + CALLED(); + // we don't support offline mode + return; +} + +// -------------------------------------------------------- // +// implementation for BMediaEventLooper +// -------------------------------------------------------- // + +void MultiAudioNode::HandleEvent( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false) +{ + //CALLED(); + switch (event->type) { + case BTimedEventQueue::B_START: + HandleStart(event,lateness,realTimeEvent); + break; + case BTimedEventQueue::B_SEEK: + HandleSeek(event,lateness,realTimeEvent); + break; + case BTimedEventQueue::B_WARP: + HandleWarp(event,lateness,realTimeEvent); + break; + case BTimedEventQueue::B_STOP: + HandleStop(event,lateness,realTimeEvent); + break; + case BTimedEventQueue::B_HANDLE_BUFFER: + if (RunState() == BMediaEventLooper::B_STARTED) { + HandleBuffer(event,lateness,realTimeEvent); + } + break; + case BTimedEventQueue::B_DATA_STATUS: + HandleDataStatus(event,lateness,realTimeEvent); + break; + case BTimedEventQueue::B_PARAMETER: + HandleParameter(event,lateness,realTimeEvent); + break; + default: + fprintf(stderr," unknown event type: %li\n",event->type); + break; + } +} + +// protected: + +// how should we handle late buffers? drop them? +// notify the producer? +status_t MultiAudioNode::HandleBuffer( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false) +{ + //CALLED(); + BBuffer * buffer = const_cast((BBuffer*)event->pointer); + if (buffer == 0) { + fprintf(stderr,"<- B_BAD_VALUE\n"); + return B_BAD_VALUE; + } + + node_input *channel = FindInput(buffer->Header()->destination); + //PRINT(("buffer->Header()->destination : %i\n", buffer->Header()->destination)); + + if(channel==NULL) { + fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n"); + return B_MEDIA_BAD_DESTINATION; + } + + /*if (buffer->Header()->destination != input.destination.id) { + fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n"); + return B_MEDIA_BAD_DESTINATION; + }*/ + + media_header* hdr = buffer->Header(); + bigtime_t now = TimeSource()->Now(); + bigtime_t perf_time = hdr->start_time; + + // the how_early calculate here doesn't include scheduling latency because + // we've already been scheduled to handle the buffer + bigtime_t how_early = perf_time - EventLatency() - now; + + // if the buffer is late, we ignore it and report the fact to the producer + // who sent it to us + if ((RunMode() != B_OFFLINE) && // lateness doesn't matter in offline mode... + (RunMode() != B_RECORDING) && // ...or in recording mode + (how_early < 0LL)) + { + //mLateBuffers++; + NotifyLateProducer(channel->fInput.source, -how_early, perf_time); + fprintf(stderr," <- LATE BUFFER : %lli\n", how_early); + buffer->Recycle(); + } else { + //WriteBuffer(buffer, *channel); + if(channel->fBuffer != NULL) { + PRINT(("MultiAudioNode::HandleBuffer snoozing recycling channelId : %i, how_early:%lli\n", channel->fChannelId, how_early)); + //channel->fBuffer->Recycle(); + snooze(100); + if(channel->fBuffer != NULL) + buffer->Recycle(); + else + channel->fBuffer = buffer; + } else { + //PRINT(("MultiAudioNode::HandleBuffer writing channelId : %i, how_early:%lli\n", channel->fChannelId, how_early)); + channel->fBuffer = buffer; + } + } + return B_OK; +} + +status_t MultiAudioNode::HandleDataStatus( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false) +{ + //CALLED(); + PRINT(("MultiAudioNode::HandleDataStatus status:%li, lateness:%li\n", event->data, lateness)); + switch(event->data) { + case B_DATA_NOT_AVAILABLE: + break; + case B_DATA_AVAILABLE: + break; + case B_PRODUCER_STOPPED: + break; + default: + break; + } + return B_OK; +} + +status_t MultiAudioNode::HandleStart( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false) +{ + CALLED(); + if (RunState() != B_STARTED) { + + } + return B_OK; +} + +status_t MultiAudioNode::HandleSeek( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false) +{ + CALLED(); + PRINT(("MultiAudioNode::HandleSeek(t=%lld,d=%li,bd=%lld)\n",event->event_time,event->data,event->bigdata)); + return B_OK; +} + +status_t MultiAudioNode::HandleWarp( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false) +{ + CALLED(); + return B_OK; +} + +status_t MultiAudioNode::HandleStop( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false) +{ + CALLED(); + // flush the queue so downstreamers don't get any more + EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER); + + //StopThread(); + return B_OK; +} + +status_t MultiAudioNode::HandleParameter( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false) +{ + CALLED(); + return B_OK; +} + +// -------------------------------------------------------- // +// implemention of BTimeSource +// -------------------------------------------------------- // + +void +MultiAudioNode::SetRunMode(run_mode mode) +{ + CALLED(); + PRINT(("MultiAudioNode::SetRunMode mode:%i\n", mode)); + //BTimeSource::SetRunMode(mode); +} + +status_t +MultiAudioNode::TimeSourceOp(const time_source_op_info &op, void *_reserved) +{ + CALLED(); + switch(op.op) { + case B_TIMESOURCE_START: + PRINT(("TimeSourceOp op B_TIMESOURCE_START\n")); + if (RunState() != BMediaEventLooper::B_STARTED) { + fTimeSourceStarted = true; + StartThread(); + + media_timed_event startEvent(0, BTimedEventQueue::B_START); + EventQueue()->AddEvent(startEvent); + } + break; + case B_TIMESOURCE_STOP: + PRINT(("TimeSourceOp op B_TIMESOURCE_STOP\n")); + if (RunState() == BMediaEventLooper::B_STARTED) { + media_timed_event stopEvent(0, BTimedEventQueue::B_STOP); + EventQueue()->AddEvent(stopEvent); + fTimeSourceStarted = false; + StopThread(); + PublishTime(0, 0, 0); + } + break; + case B_TIMESOURCE_STOP_IMMEDIATELY: + PRINT(("TimeSourceOp op B_TIMESOURCE_STOP_IMMEDIATELY\n")); + if (RunState() == BMediaEventLooper::B_STARTED) { + media_timed_event stopEvent(0, BTimedEventQueue::B_STOP); + EventQueue()->AddEvent(stopEvent); + fTimeSourceStarted = false; + StopThread(); + PublishTime(0, 0, 0); + } + break; + case B_TIMESOURCE_SEEK: + PRINT(("TimeSourceOp op B_TIMESOURCE_SEEK\n")); + BroadcastTimeWarp(op.real_time, op.performance_time); + break; + default: + break; + } + return B_OK; +} + +// -------------------------------------------------------- // +// implemention of BControllable +// -------------------------------------------------------- // + +status_t +MultiAudioNode::GetParameterValue(int32 id, bigtime_t* last_change, void* value, size_t* ioSize) +{ + CALLED(); + + PRINT(("id : %i\n", id)); + BParameter *parameter = NULL; + for(int32 i=0; iCountParameters(); i++) { + parameter = fWeb->ParameterAt(i); + if(parameter->ID() == id) + break; + } + + if(!parameter) { + // Hmmm, we were asked for a parameter that we don't actually + // support. Report an error back to the caller. + PRINT(("\terror - asked for illegal parameter %ld\n", id)); + return B_ERROR; + } + + multi_mix_value_info MMVI; + multi_mix_value MMV[2]; + int rval; + MMVI.values = MMV; + id = id - 100; + MMVI.item_count = 0; + + if (*ioSize < sizeof(float)) + return B_ERROR; + + if(parameter->Type() == BParameter::B_CONTINUOUS_PARAMETER) { + MMVI.item_count = 1; + MMV[0].id = id; + + if(parameter->CountChannels() == 2) { + if (*ioSize < 2*sizeof(float)) + return B_ERROR; + MMVI.item_count = 2; + MMV[1].id = id + 1; + } + + } else if(parameter->Type() == BParameter::B_DISCRETE_PARAMETER) { + MMVI.item_count = 1; + MMV[0].id = id; + } + + if(MMVI.item_count > 0) { + rval = fDevice->DoGetMix(&MMVI); + + if (B_OK != rval) { + fprintf(stderr, "Failed on DRIVER_GET_MIX\n"); + } else { + + if(parameter->Type() == BParameter::B_CONTINUOUS_PARAMETER) { + ((float*)value)[0] = MMV[0].gain; + *ioSize = sizeof(float); + + if(parameter->CountChannels() == 2) { + ((float*)value)[1] = MMV[1].gain; + *ioSize = 2*sizeof(float); + } + + for(uint32 i=0; i < (*ioSize/sizeof(float)); i++) { + PRINT(("B_CONTINUOUS_PARAMETER value[%i] : %f\n", i, ((float*)value)[i])); + } + } else if(parameter->Type() == BParameter::B_DISCRETE_PARAMETER) { + + BDiscreteParameter *dparameter = (BDiscreteParameter*) parameter; + if(dparameter->CountItems()<=2) { + ((int32*)value)[0] = (MMV[0].enable) ? 1 : 0; + } else { + ((int32*)value)[0] = MMV[0].mux; + } + *ioSize = sizeof(int32); + + for(uint32 i=0; i < (*ioSize/sizeof(int32)); i++) { + PRINT(("B_DISCRETE_PARAMETER value[%i] : %i\n", i, ((int32*)value)[i])); + } + } + + } + } + return B_OK; +} + +void +MultiAudioNode::SetParameterValue(int32 id, bigtime_t performance_time, const void* value, size_t size) +{ + CALLED(); + PRINT(("id : %i, performance_time : %lld, size : %i\n", id, performance_time, size)); + BParameter *parameter = NULL; + for(int32 i=0; iCountParameters(); i++) { + parameter = fWeb->ParameterAt(i); + if(parameter->ID() == id) + break; + } + if(parameter) { + multi_mix_value_info MMVI; + multi_mix_value MMV[2]; + int rval; + MMVI.values = MMV; + id = id - 100; + MMVI.item_count = 0; + + if(parameter->Type() == BParameter::B_CONTINUOUS_PARAMETER) { + for(uint32 i=0; i < (size/sizeof(float)); i++) { + PRINT(("B_CONTINUOUS_PARAMETER value[%i] : %f\n", i, ((float*)value)[i])); + } + MMVI.item_count = 1; + MMV[0].id = id; + MMV[0].gain = ((float*)value)[0]; + + if(parameter->CountChannels() == 2) { + MMVI.item_count = 2; + MMV[1].id = id + 1; + MMV[1].gain = ((float*)value)[1]; + } + + } else if(parameter->Type() == BParameter::B_DISCRETE_PARAMETER) { + for(uint32 i=0; i < (size/sizeof(int32)); i++) { + PRINT(("B_DISCRETE_PARAMETER value[%i] : %i\n", i, ((int32*)value)[i])); + } + BDiscreteParameter *dparameter = (BDiscreteParameter*) parameter; + + if(dparameter->CountItems()<=2) { + MMVI.item_count = 1; + MMV[0].id = id; + MMV[0].enable = (((int32*)value)[0] == 1) ? true : false; + } else { + MMVI.item_count = 1; + MMV[0].id = id; + MMV[0].mux = ((uint32*)value)[0]; + } + } + + if(MMVI.item_count > 0) { + rval = fDevice->DoSetMix(&MMVI); + + if (B_OK != rval) + { + fprintf(stderr, "Failed on DRIVER_SET_MIX\n"); + } + } + } + +} + +BParameterWeb* +MultiAudioNode::MakeParameterWeb() +{ + CALLED(); + BParameterWeb* web = new BParameterWeb; + + PRINT(("MMCI.control_count : %i\n", fDevice->MMCI.control_count)); + multi_mix_control *MMC = fDevice->MMCI.controls; + + for(int i=0; iMMCI.control_count; i++) { + if(MMC[i].flags & B_MULTI_MIX_GROUP && MMC[i].parent == 0) { + PRINT(("NEW_GROUP\n")); + int32 nb = 0; + const char* childName; + if(MMC[i].string != S_null) + childName = multi_string[MMC[i].string]; + else + childName = MMC[i].name; + BParameterGroup *child = web->MakeGroup(childName); + ProcessGroup(child, i, nb); + } + } + + return web; +} + +void +MultiAudioNode::ProcessGroup(BParameterGroup *group, int32 index, int32 &nbParameters) { + CALLED(); + multi_mix_control *parent = &fDevice->MMCI.controls[index]; + multi_mix_control *MMC = fDevice->MMCI.controls; + for(int32 i=0; iMMCI.control_count; i++) { + if(MMC[i].parent != parent->id) + continue; + + const char* childName; + if(MMC[i].string != S_null) + childName = multi_string[MMC[i].string]; + else + childName = MMC[i].name; + + if(MMC[i].flags & B_MULTI_MIX_GROUP) { + PRINT(("NEW_GROUP\n")); + int32 nb = 1; + BParameterGroup *child = group->MakeGroup(childName); + child->MakeNullParameter(MMC[i].id, B_MEDIA_RAW_AUDIO, childName, B_WEB_BUFFER_OUTPUT); + ProcessGroup(child, i, nb); + } else if(MMC[i].flags & B_MULTI_MIX_MUX) { + PRINT(("NEW_MUX\n")); + BDiscreteParameter *parameter = + group->MakeDiscreteParameter(100 + MMC[i].id, B_MEDIA_RAW_AUDIO, childName, B_INPUT_MUX); + if(nbParameters>0) { + (group->ParameterAt(nbParameters - 1))->AddOutput(group->ParameterAt(nbParameters)); + nbParameters++; + } + ProcessMux(parameter, i); + } else if(MMC[i].flags & B_MULTI_MIX_GAIN) { + PRINT(("NEW_GAIN\n")); + group->MakeContinuousParameter(100 + MMC[i].id, B_MEDIA_RAW_AUDIO, "", B_MASTER_GAIN, + "dB", MMC[i].gain.min_gain, MMC[i].gain.max_gain, MMC[i].gain.granularity); + + if(i+1 MMCI.control_count && MMC[i+1].master == MMC[i].id && MMC[i+1].flags & B_MULTI_MIX_GAIN) { + group->ParameterAt(nbParameters)->SetChannelCount( + group->ParameterAt(nbParameters)->CountChannels() + 1); + i++; + } + + (group->ParameterAt(nbParameters - 1))->AddOutput(group->ParameterAt(nbParameters)); + nbParameters++; + } else if(MMC[i].flags & B_MULTI_MIX_ENABLE) { + PRINT(("NEW_ENABLE\n")); + if(MMC[i].string == S_MUTE) + group->MakeDiscreteParameter(100 + MMC[i].id, B_MEDIA_RAW_AUDIO, childName, B_MUTE); + else + group->MakeDiscreteParameter(100 + MMC[i].id, B_MEDIA_RAW_AUDIO, childName, B_ENABLE); + if(nbParameters>0) { + (group->ParameterAt(nbParameters - 1))->AddOutput(group->ParameterAt(nbParameters)); + nbParameters++; + } + } + } +} + +void +MultiAudioNode::ProcessMux(BDiscreteParameter *parameter, int32 index) { + CALLED(); + multi_mix_control *parent = &fDevice->MMCI.controls[index]; + multi_mix_control *MMC = fDevice->MMCI.controls; + int32 itemIndex = 0; + for(int32 i=0; iMMCI.control_count; i++) { + if(MMC[i].parent != parent->id) + continue; + + const char* childName; + if(MMC[i].string != S_null) + childName = multi_string[MMC[i].string]; + else + childName = MMC[i].name; + + if(MMC[i].flags & B_MULTI_MIX_MUX_VALUE) { + PRINT(("NEW_MUX_VALUE\n")); + parameter->AddItem(itemIndex, childName); + itemIndex++; + } + } +} + +// -------------------------------------------------------- // +// MultiAudioNode specific functions +// -------------------------------------------------------- // + +/*void +MultiAudioNode::WriteBuffer( BBuffer *buffer, node_input &input ) +{*/ + //CALLED(); + /* + //wait for free buffer + acquire_sem( fBuffer_free ); + //PRINT(("MultiAudioNode::WriteBuffer: buffer free (fBufferCycle:%li)\n", fBufferCycle)); + + //prepare buffer + memcpy( fDevice->fPlay[input.fBufferCycle][input.fChannelId], buffer->Data(), buffer->SizeUsed() ); + //PRINT(("MultiAudioNode::WriteBuffer: output : %p\n", fPlay[fBufferCycle][0])); + //PRINT(("MultiAudioNode::WriteBuffer: buffer prepared\n")); */ + + /*if(input.fBuffer != NULL) { + PRINT(("MultiAudioNode::WriteBuffer recycling channelId : %i\n", input.fChannelId)); + //input.fBuffer->Recycle(); + } else + input.fBuffer = buffer; +}*/ + +int32 +MultiAudioNode::RunThread() +{ + CALLED(); + multi_buffer_info MBI;//, oldMBI; + MBI.info_size = sizeof(MBI); + MBI._reserved_0 = 0; + MBI._reserved_1 = 2; + + while ( 1 ) { + //acquire buffer if any + if ( acquire_sem_etc( fBuffer_free, 1, B_RELATIVE_TIMEOUT, 0 ) == B_BAD_SEM_ID ) { + return B_OK; + } + + //oldMBI = MBI; + + //send buffer + fDevice->DoBufferExchange(&MBI); + + //PRINT(("MultiAudioNode::RunThread: buffer exchanged\n")); + //PRINT(("MultiAudioNode::RunThread: played_real_time : %i\n", MBI.played_real_time)); + //PRINT(("MultiAudioNode::RunThread: played_frames_count : %i\n", MBI.played_frames_count)); + //PRINT(("MultiAudioNode::RunThread: buffer_cycle : %i\n", MBI.playback_buffer_cycle)); + + node_input *input = NULL; + + for(int32 i=0; ifChannelId) + && (input->fOldMBI.playback_buffer_cycle != MBI.playback_buffer_cycle + || fDevice->MBL.return_playback_buffers == 1) + && (input->fInput.source != media_source::null + || input->fChannelId == 0)) { + + //PRINT(("playback_buffer_cycle ok input : %i\n", i)); + + input->fBufferCycle = (MBI.playback_buffer_cycle - 1 + + fDevice->MBL.return_playback_buffers) % fDevice->MBL.return_playback_buffers; + + // update the timesource + if(input->fChannelId==0) { + //PRINT(("updating timesource\n")); + UpdateTimeSource(MBI, input->fOldMBI, *input); + } + + input->fOldMBI = MBI; + + if(input->fBuffer!=NULL) { + /*memcpy( fDevice->MBL.playback_buffers[input->fBufferCycle][input->fChannelId].base, + input->fBuffer->Data(), input->fBuffer->SizeUsed() );*/ + FillNextBuffer(*input, input->fBuffer); + input->fBuffer->Recycle(); + input->fBuffer = NULL; + } else { + // put zeros in current buffer + WriteZeros(*input, input->fBufferCycle); + //PRINT(("MultiAudioNode::Runthread WriteZeros\n")); + } + + //mark buffer free + release_sem( fBuffer_free ); + } else { + //PRINT(("playback_buffer_cycle non ok input : %i\n", i)); + } + } + + node_output *output = NULL; + for(int32 i=0; ifOutput.destination != media_destination::null)) { + if((MBI._reserved_1 == output->fChannelId) + && (output->fOldMBI.record_buffer_cycle != MBI.record_buffer_cycle + || fDevice->MBL.return_record_buffers == 1)) { + //PRINT(("record_buffer_cycle ok\n")); + + // Get the next buffer of data + BBuffer* buffer = FillNextBuffer(MBI, *output); + if (buffer) + { + // send the buffer downstream if and only if output is enabled + status_t err = B_ERROR; + if (output->fOutputEnabled) + err = SendBuffer(buffer, output->fOutput.destination); + if (err) { + buffer->Recycle(); + } else { + // track how much media we've delivered so far + size_t nSamples = output->fOutput.format.u.raw_audio.buffer_size / sizeof(int16); + output->fSamplesSent += nSamples; + } + } + + output->fOldMBI = MBI; + } else { + //PRINT(("record_buffer_cycle non ok\n")); + } + } + } + + } + + return B_OK; +} + +void +MultiAudioNode::WriteZeros(node_input &input, uint32 bufferCycle) +{ + //CALLED(); + /*int32 samples = input.fInput.format.u.raw_audio.buffer_size; + if(input.fInput.format.u.raw_audio.format == media_raw_audio_format::B_AUDIO_UCHAR) { + uint8 *sample = (uint8*)fDevice->MBL.playback_buffers[input.fBufferCycle][input.fChannelId].base; + for(int32 i = samples-1; i>=0; i--) + *sample++ = 128; + + } else { + int32 *sample = (int32*)fDevice->MBL.playback_buffers[input.fBufferCycle][input.fChannelId].base; + for(int32 i = (samples / 4)-1; i>=0; i--) + *sample++ = 0;*/ + + size_t stride = fDevice->MBL.playback_buffers[bufferCycle][input.fChannelId].stride; + uint32 channelCount = input.fFormat.u.raw_audio.channel_count; + + switch(input.fFormat.u.raw_audio.format) { + case media_raw_audio_format::B_AUDIO_FLOAT: + for(uint32 channel = 0; channel < channelCount; channel++) { + char *sample_dest = fDevice->MBL.playback_buffers[bufferCycle][input.fChannelId + channel].base; + for(uint32 i=fDevice->MBL.return_playback_buffer_size; i>0; i--) { + *(float*)sample_dest = 0; + sample_dest += stride; + } + } + break; + case media_raw_audio_format::B_AUDIO_INT: + for(uint32 channel = 0; channel < channelCount; channel++) { + char *sample_dest = fDevice->MBL.playback_buffers[bufferCycle][input.fChannelId + channel].base; + for(uint32 i=fDevice->MBL.return_playback_buffer_size; i>0; i--) { + *(int32*)sample_dest = 0; + sample_dest += stride; + } + } + break; + case media_raw_audio_format::B_AUDIO_SHORT:{ + for(uint32 channel = 0; channel < channelCount; channel++) { + char *sample_dest = fDevice->MBL.playback_buffers[bufferCycle][input.fChannelId + channel].base; + for(uint32 i=fDevice->MBL.return_playback_buffer_size; i>0; i--) { + *(int16*)sample_dest = 0; + sample_dest += stride; + } + } + break; + } + default: + fprintf(stderr, "ERROR in WriteZeros format not handled\n"); + } + +} + +void +MultiAudioNode::FillWithZeros(node_input &input) +{ + CALLED(); + for(int32 i=0; iMBL.return_playback_buffers; i++) + WriteZeros(input, i); +} + +void +MultiAudioNode::FillNextBuffer(node_input &input, BBuffer* buffer) +{ + switch(input.fInput.format.u.raw_audio.format) { + case media_raw_audio_format::B_AUDIO_FLOAT: + + switch(input.fFormat.u.raw_audio.format) { + case media_raw_audio_format::B_AUDIO_FLOAT: + break; + case media_raw_audio_format::B_AUDIO_INT: + break; + case media_raw_audio_format::B_AUDIO_SHORT: + if(input.fInput.format.u.raw_audio.channel_count == 2) { + int16 *sample_dest1 = (int16*)fDevice->MBL.playback_buffers[input.fBufferCycle][input.fChannelId].base; + int16 *sample_dest2 = (int16*)fDevice->MBL.playback_buffers[input.fBufferCycle][input.fChannelId + 1].base; + float *sample_src = (float*)buffer->Data(); + if(fDevice->MBL.playback_buffers[input.fBufferCycle][input.fChannelId].stride == sizeof(int16) + && fDevice->MBL.playback_buffers[input.fBufferCycle][input.fChannelId + 1].stride == sizeof(int16)) { + //PRINT(("FillNextBuffer : 2 channels strides 2\n")); + for(uint32 i=fDevice->MBL.return_playback_buffer_size; i>0; i--) { + *sample_dest1++ = int16(32767 * *sample_src++); + *sample_dest2++ = int16(32767 * *sample_src++); + } + } else if(fDevice->MBL.playback_buffers[input.fBufferCycle][input.fChannelId].stride == 2 * sizeof(int16) + && fDevice->MBL.playback_buffers[input.fBufferCycle][input.fChannelId + 1].stride == 2 * sizeof(int16) + && sample_dest1 + 1 == sample_dest2) { + //PRINT(("FillNextBuffer : 2 channels strides 4\n")); + for(uint32 i=2*fDevice->MBL.return_playback_buffer_size; i>0; i--) + *sample_dest1++ = int16(32767 * *sample_src++); + } else { + //PRINT(("FillNextBuffer : 2 channels strides != 2\n")); + size_t stride1 = fDevice->MBL.playback_buffers[input.fBufferCycle][input.fChannelId].stride / sizeof(int16); + size_t stride2 = fDevice->MBL.playback_buffers[input.fBufferCycle][input.fChannelId + 1].stride / sizeof(int16); + for(uint32 i=fDevice->MBL.return_playback_buffer_size; i>0; i--) { + *sample_dest1 = int16(32767 * *sample_src++); + *sample_dest2 = int16(32767 * *sample_src++); + sample_dest1 += stride1; + sample_dest2 += stride2; + } + } + } else { + size_t frame_size = input.fInput.format.u.raw_audio.channel_count * sizeof(int16); + size_t stride = fDevice->MBL.playback_buffers[input.fBufferCycle][input.fChannelId].stride; + //PRINT(("stride : %i, frame_size : %i, return_playback_buffer_size : %i\n", stride, frame_size, fDevice->MBL.return_playback_buffer_size)); + for(uint32 channel = 0; channel < input.fInput.format.u.raw_audio.channel_count; channel++) { + char *sample_dest = fDevice->MBL.playback_buffers[input.fBufferCycle][input.fChannelId + channel].base; + //char *sample_src = (char*)buffer->Data() + (input.fInput.format.u.raw_audio.channel_count - 1 - channel) * sizeof(int16); + char *sample_src = (char*)buffer->Data() + channel * sizeof(int16); + for(uint32 i=fDevice->MBL.return_playback_buffer_size; i>0; i--) { + *(int16*)sample_dest = int16(32767 * *(float*)sample_src); + sample_dest += stride; + sample_src += frame_size; + } + } + } + break; + default: + break; + } + + break; + case media_raw_audio_format::B_AUDIO_INT: + + break; + case media_raw_audio_format::B_AUDIO_SHORT: + + switch(input.fFormat.u.raw_audio.format) { + case media_raw_audio_format::B_AUDIO_FLOAT: + break; + case media_raw_audio_format::B_AUDIO_INT: + break; + case media_raw_audio_format::B_AUDIO_SHORT: + if(input.fInput.format.u.raw_audio.channel_count == 2) { + int16 *sample_dest1 = (int16*)fDevice->MBL.playback_buffers[input.fBufferCycle][input.fChannelId].base; + int16 *sample_dest2 = (int16*)fDevice->MBL.playback_buffers[input.fBufferCycle][input.fChannelId + 1].base; + int16 *sample_src = (int16*)buffer->Data(); + if(fDevice->MBL.playback_buffers[input.fBufferCycle][input.fChannelId].stride == sizeof(int16) + && fDevice->MBL.playback_buffers[input.fBufferCycle][input.fChannelId + 1].stride == sizeof(int16)) { + //PRINT(("FillNextBuffer : 2 channels strides 2\n")); + for(uint32 i=fDevice->MBL.return_playback_buffer_size; i>0; i--) { + *sample_dest1++ = *sample_src++; + *sample_dest2++ = *sample_src++; + } + } else if(fDevice->MBL.playback_buffers[input.fBufferCycle][input.fChannelId].stride == 2 * sizeof(int16) + && fDevice->MBL.playback_buffers[input.fBufferCycle][input.fChannelId + 1].stride == 2 * sizeof(int16) + && sample_dest1 + 1 == sample_dest2) { + //PRINT(("FillNextBuffer : 2 channels strides 4\n")); + memcpy(sample_dest1, sample_src, fDevice->MBL.return_playback_buffer_size * 2 * sizeof(int16)); + } else { + //PRINT(("FillNextBuffer : 2 channels strides != 2\n")); + size_t stride1 = fDevice->MBL.playback_buffers[input.fBufferCycle][input.fChannelId].stride / 2; + size_t stride2 = fDevice->MBL.playback_buffers[input.fBufferCycle][input.fChannelId + 1].stride / 2; + for(uint32 i=fDevice->MBL.return_playback_buffer_size; i>0; i--) { + *sample_dest1 = *sample_src++; + *sample_dest2 = *sample_src++; + sample_dest1 += stride1; + sample_dest2 += stride2; + } + } + } else { + size_t frame_size = input.fInput.format.u.raw_audio.channel_count * sizeof(int16); + size_t stride = fDevice->MBL.playback_buffers[input.fBufferCycle][input.fChannelId].stride; + //PRINT(("stride : %i, frame_size : %i, return_playback_buffer_size : %i\n", stride, frame_size, fDevice->MBL.return_playback_buffer_size)); + for(uint32 channel = 0; channel < input.fInput.format.u.raw_audio.channel_count; channel++) { + char *sample_dest = fDevice->MBL.playback_buffers[input.fBufferCycle][input.fChannelId + channel].base; + //char *sample_src = (char*)buffer->Data() + (input.fInput.format.u.raw_audio.channel_count - 1 - channel) * sizeof(int16); + char *sample_src = (char*)buffer->Data() + channel * sizeof(int16); + for(uint32 i=fDevice->MBL.return_playback_buffer_size; i>0; i--) { + *(int16*)sample_dest = *(int16*)sample_src; + sample_dest += stride; + sample_src += frame_size; + } + } + } + break; + default: + break; + } + + break; + case media_raw_audio_format::B_AUDIO_UCHAR: + default: + break; + } +} + +status_t +MultiAudioNode::StartThread() +{ + CALLED(); + //allocate buffer free semaphore + fBuffer_free = create_sem( NB_BUFFERS - 1, "multi_audio out buffer free" ); + + if ( fBuffer_free < B_OK ) { + return B_ERROR; + } + + fThread = spawn_thread( _run_thread_, "multi_audio audio output", B_REAL_TIME_PRIORITY, this ); + + if ( fThread < B_OK ) { + delete_sem( fBuffer_free ); + return B_ERROR; + } + + resume_thread( fThread ); + + return B_OK; +} + +status_t +MultiAudioNode::StopThread() +{ + CALLED(); + delete_sem( fBuffer_free ); + wait_for_thread( fThread, &fThread ); + return B_OK; +} + +void +MultiAudioNode::AllocateBuffers(node_output &channel) +{ + CALLED(); + + // allocate enough buffers to span our downstream latency, plus one + size_t size = channel.fOutput.format.u.raw_audio.buffer_size; + int32 count = int32(fLatency / BufferDuration() + 1 + 1); + + PRINT(("\tlatency = %Ld, buffer duration = %Ld\n", fLatency, BufferDuration())); + PRINT(("\tcreating group of %ld buffers, size = %lu\n", count, size)); + channel.fBufferGroup = new BBufferGroup(size, count); +} + +void +MultiAudioNode::UpdateTimeSource(multi_buffer_info &MBI, multi_buffer_info &oldMBI, node_input &input) +{ + //CALLED(); + if(fTimeSourceStarted) { + bigtime_t perf_time = (bigtime_t)(MBI.played_frames_count / + input.fInput.format.u.raw_audio.frame_rate * 1000000); + bigtime_t real_time = MBI.played_real_time; + float drift = ((MBI.played_frames_count - oldMBI.played_frames_count) + / input.fInput.format.u.raw_audio.frame_rate * 1000000) + / (MBI.played_real_time - oldMBI.played_real_time); + + PublishTime(perf_time, real_time, drift); + //PRINT(("UpdateTimeSource() perf_time : %lli, real_time : %lli, drift : %f\n", perf_time, real_time, drift)); + } +} + +BBuffer* +MultiAudioNode::FillNextBuffer(multi_buffer_info &MBI, node_output &channel) +{ + //CALLED(); + // get a buffer from our buffer group + //PRINT(("buffer size : %i, buffer duration : %i\n", fOutput.format.u.raw_audio.buffer_size, BufferDuration())); + //PRINT(("MBI.record_buffer_cycle : %i\n", MBI.record_buffer_cycle)); + //PRINT(("MBI.recorded_real_time : %i\n", MBI.recorded_real_time)); + //PRINT(("MBI.recorded_frames_count : %i\n", MBI.recorded_frames_count)); + if(!channel.fBufferGroup) + return NULL; + + BBuffer* buffer = channel.fBufferGroup->RequestBuffer(channel.fOutput.format.u.raw_audio.buffer_size, BufferDuration()); + + // if we fail to get a buffer (for example, if the request times out), we skip this + // buffer and go on to the next, to avoid locking up the control thread + if (!buffer) + return NULL; + + if(fDevice==NULL) + PRINT(("fDevice NULL\n")); + if(buffer->Header()==NULL) + PRINT(("buffer->Header() NULL\n")); + if(TimeSource()==NULL) + PRINT(("TimeSource() NULL\n")); + + // now fill it with data, continuing where the last buffer left off + memcpy( buffer->Data(), + fDevice->MBL.record_buffers[MBI.record_buffer_cycle][channel.fChannelId - fDevice->MD.output_channel_count].base, + channel.fOutput.format.u.raw_audio.buffer_size ); + + // fill in the buffer header + media_header* hdr = buffer->Header(); + hdr->type = B_MEDIA_RAW_AUDIO; + hdr->size_used = channel.fOutput.format.u.raw_audio.buffer_size; + hdr->time_source = TimeSource()->ID(); + hdr->start_time = PerformanceTimeFor(MBI.recorded_real_time); + + return buffer; +} + +status_t +MultiAudioNode::GetConfigurationFor(BMessage * into_message) +{ + CALLED(); + into_message->AddString("name", Name()); + return B_OK; +} + +node_output* +MultiAudioNode::FindOutput(media_source source) +{ + node_output *channel = NULL; + + for(int32 i=0; ifOutput.source) + break; + } + + if (source != channel->fOutput.source) + return NULL; + else + return channel; + +} + +node_input* +MultiAudioNode::FindInput(media_destination dest) +{ + node_input *channel = NULL; + + for(int32 i=0; ifInput.destination) + break; + } + + if (dest != channel->fInput.destination) + return NULL; + else + return channel; + +} + +node_input* +MultiAudioNode::FindInput(int32 destinationId) +{ + node_input *channel = NULL; + + for(int32 i=0; ifInput.destination.id) + break; + } + + if (destinationId != channel->fInput.destination.id) + return NULL; + else + return channel; + +} + +// static: + +int32 +MultiAudioNode::_run_thread_( void *data ) +{ + CALLED(); + return static_cast( data )->RunThread(); +} + +void MultiAudioNode::GetFlavor(flavor_info * outInfo, int32 id) +{ + CALLED(); + if (outInfo == 0) { + return; + } + + outInfo->flavor_flags = 0; + outInfo->possible_count = 1; // one flavor at a time + outInfo->in_format_count = 0; // no inputs + outInfo->in_formats = 0; + outInfo->out_format_count = 0; // no outputs + outInfo->out_formats = 0; + outInfo->internal_id = id; + + outInfo->name = "MultiAudioNode Node"; + outInfo->info = "The MultiAudioNode node outputs to multi_audio drivers."; + outInfo->kinds = B_BUFFER_CONSUMER | B_BUFFER_PRODUCER | B_TIME_SOURCE + | B_PHYSICAL_OUTPUT | B_PHYSICAL_INPUT | B_CONTROLLABLE; + outInfo->in_format_count = 1; // 1 input + media_format * informats = new media_format[outInfo->in_format_count]; + GetFormat(&informats[0]); + outInfo->in_formats = informats; + + outInfo->out_format_count = 1; // 1 output + media_format * outformats = new media_format[outInfo->out_format_count]; + GetFormat(&outformats[0]); + outInfo->out_formats = outformats; +} + +void MultiAudioNode::GetFormat(media_format * outFormat) +{ + CALLED(); + if (outFormat == 0) { + return; + } + outFormat->type = B_MEDIA_RAW_AUDIO; + outFormat->require_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS; + outFormat->deny_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS; + outFormat->u.raw_audio = media_raw_audio_format::wildcard; +} diff --git a/src/add-ons/media/media-add-ons/multi_audio/MultiAudioNode.h b/src/add-ons/media/media-add-ons/multi_audio/MultiAudioNode.h new file mode 100644 index 0000000000..1f6cb158e2 --- /dev/null +++ b/src/add-ons/media/media-add-ons/multi_audio/MultiAudioNode.h @@ -0,0 +1,395 @@ +/* + * multiaudio replacement media addon for BeOS + * + * Copyright (c) 2002, Jerome Duval (jerome.duval@free.fr) + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _MULTIAUDIONODE_H +#define _MULTIAUDIONODE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "multi_audio.h" +#include "MultiAudioDevice.h" + +/*bool format_is_acceptible( + const media_format & producer_format, + const media_format & consumer_format);*/ + + +class node_input +{ + public: + node_input(media_input &input, media_format format); + ~node_input(); + + int32 fChannelId; + media_input fInput; + media_format fPreferredFormat; + media_format fFormat; + uint32 fBufferCycle; + multi_buffer_info fOldMBI; + BBuffer *fBuffer; +}; + +class node_output +{ + public: + node_output(media_output &output, media_format format); + ~node_output(); + + int32 fChannelId; + media_output fOutput; + media_format fPreferredFormat; + media_format fFormat; + + BBufferGroup *fBufferGroup; + bool fOutputEnabled; + uint64 fSamplesSent; + volatile uint32 fBufferCycle; + multi_buffer_info fOldMBI; +}; + +class MultiAudioNode : + public BBufferConsumer, + public BBufferProducer, + public BTimeSource, + public BMediaEventLooper, + public BControllable +{ +protected: +virtual ~MultiAudioNode(void); + +public: + +explicit MultiAudioNode(BMediaAddOn *addon, char* name, MultiAudioDevice *device, + int32 internal_id, BMessage * config); + +virtual status_t InitCheck(void) const; + +/*************************/ +/* begin from BMediaNode */ +public: +virtual BMediaAddOn* AddOn( + int32 * internal_id) const; /* Who instantiated you -- or NULL for app class */ + +protected: + /* These don't return errors; instead, they use the global error condition reporter. */ + /* A node is required to have a queue of at least one pending command (plus TimeWarp) */ + /* and is recommended to allow for at least one pending command of each type. */ + /* Allowing an arbitrary number of outstanding commands might be nice, but apps */ + /* cannot depend on that happening. */ +virtual void Preroll(void); + +public: +virtual status_t HandleMessage( + int32 message, + const void * data, + size_t size); + +protected: +virtual void NodeRegistered(void); /* reserved 2 */ +virtual status_t RequestCompleted(const media_request_info &info); +virtual void SetTimeSource(BTimeSource *timeSource); + +/* end from BMediaNode */ +/***********************/ + +/******************************/ +/* begin from BBufferConsumer */ + +//included from BMediaAddOn +//virtual status_t HandleMessage( +// int32 message, +// const void * data, +// size_t size); + + /* Someone, probably the producer, is asking you about this format. Give */ + /* your honest opinion, possibly modifying *format. Do not ask upstream */ + /* producer about the format, since he's synchronously waiting for your */ + /* reply. */ +virtual status_t AcceptFormat( + const media_destination & dest, + media_format * format); +virtual status_t GetNextInput( + int32 * cookie, + media_input * out_input); +virtual void DisposeInputCookie( + int32 cookie); +virtual void BufferReceived( + BBuffer * buffer); +virtual void ProducerDataStatus( + const media_destination & for_whom, + int32 status, + bigtime_t at_performance_time); +virtual status_t GetLatencyFor( + const media_destination & for_whom, + bigtime_t * out_latency, + media_node_id * out_timesource); +virtual status_t Connected( + const media_source & producer, /* here's a good place to request buffer group usage */ + const media_destination & where, + const media_format & with_format, + media_input * out_input); +virtual void Disconnected( + const media_source & producer, + const media_destination & where); + /* The notification comes from the upstream producer, so he's already cool with */ + /* the format; you should not ask him about it in here. */ +virtual status_t FormatChanged( + const media_source & producer, + const media_destination & consumer, + int32 change_tag, + const media_format & format); + + /* Given a performance time of some previous buffer, retrieve the remembered tag */ + /* of the closest (previous or exact) performance time. Set *out_flags to 0; the */ + /* idea being that flags can be added later, and the understood flags returned in */ + /* *out_flags. */ +virtual status_t SeekTagRequested( + const media_destination & destination, + bigtime_t in_target_time, + uint32 in_flags, + media_seek_tag * out_seek_tag, + bigtime_t * out_tagged_time, + uint32 * out_flags); + +/* end from BBufferConsumer */ +/****************************/ + +/******************************/ +/* begin from BBufferProducer */ + + virtual status_t FormatSuggestionRequested( media_type type, + int32 quality, + media_format* format); + + virtual status_t FormatProposal( const media_source& output, + media_format* format); + + virtual status_t FormatChangeRequested( const media_source& source, + const media_destination& destination, + media_format* io_format, + int32* _deprecated_); + virtual status_t GetNextOutput( int32* cookie, + media_output* out_output); + virtual status_t DisposeOutputCookie( int32 cookie); + + virtual status_t SetBufferGroup( const media_source& for_source, + BBufferGroup* group); + + virtual status_t PrepareToConnect( const media_source& what, + const media_destination& where, + media_format* format, + media_source* out_source, + char* out_name); + + virtual void Connect( status_t error, + const media_source& source, + const media_destination& destination, + const media_format& format, + char* io_name); + + virtual void Disconnect( const media_source& what, + const media_destination& where); + + virtual void LateNoticeReceived( const media_source& what, + bigtime_t how_much, + bigtime_t performance_time); + + virtual void EnableOutput( const media_source & what, + bool enabled, + int32* _deprecated_); + virtual void AdditionalBufferRequested( const media_source& source, + media_buffer_id prev_buffer, + bigtime_t prev_time, + const media_seek_tag* prev_tag); + +/* end from BBufferProducer */ +/****************************/ + +/*****************/ +/* BControllable */ +/*****************/ + +/********************************/ +/* start from BMediaEventLooper */ + + protected: + /* you must override to handle your events! */ + /* you should not call HandleEvent directly */ + virtual void HandleEvent( const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false); + +/* end from BMediaEventLooper */ +/******************************/ + +/********************************/ +/* start from BTimeSource */ + protected: + virtual void SetRunMode( run_mode mode); + virtual status_t TimeSourceOp( const time_source_op_info &op, + void *_reserved); + +/* end from BTimeSource */ +/******************************/ + +/********************************/ +/* start from BControllable */ + protected: + virtual status_t GetParameterValue( int32 id, + bigtime_t* last_change, + void* value, + size_t* ioSize); + virtual void SetParameterValue( int32 id, + bigtime_t when, + const void* value, + size_t size); + virtual BParameterWeb* MakeParameterWeb(); + +/* end from BControllable */ +/******************************/ + +protected: + +virtual status_t HandleStart( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false); +virtual status_t HandleSeek( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false); +virtual status_t HandleWarp( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false); +virtual status_t HandleStop( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false); +virtual status_t HandleBuffer( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false); +virtual status_t HandleDataStatus( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false); +virtual status_t HandleParameter( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false); + +public: + +static void GetFlavor(flavor_info * outInfo, int32 id); +static void GetFormat(media_format * outFormat); + +status_t GetConfigurationFor(BMessage * into_message); + + +private: + + MultiAudioNode( /* private unimplemented */ + const MultiAudioNode & clone); + MultiAudioNode & operator=( + const MultiAudioNode & clone); + + //void WriteBuffer( BBuffer *buffer, node_input &input ); + void WriteZeros(node_input &input, uint32 bufferCycle); + void FillWithZeros(node_input &input); + void FillNextBuffer(node_input &channel, BBuffer* buffer); + + static int32 _run_thread_( void *data ); + int32 RunThread(); + status_t StartThread(); + status_t StopThread(); + + + void AllocateBuffers(node_output &channel); + BBuffer* FillNextBuffer( multi_buffer_info &MBI, + node_output &channel); + void UpdateTimeSource(multi_buffer_info &MBI, + multi_buffer_info &oldMBI, + node_input &input); + + node_output* FindOutput(media_source source); + node_input* FindInput(media_destination dest); + node_input* FindInput(int32 destinationId); + + void ProcessGroup(BParameterGroup *group, int32 index, int32 &nbParameters); + void ProcessMux(BDiscreteParameter *parameter, int32 index); + + status_t fInitCheckStatus; + + BMediaAddOn *fAddOn; + int32 fId; + + //media_input input; + BList fInputs; + + bigtime_t fLatency; + BList fOutputs; + media_format fPreferredFormat; + + bigtime_t fInternalLatency; + // this is computed from the real (negotiated) chunk size and bit rate, + // not the defaults that are in the parameters + bigtime_t fBufferPeriod; + + + //volatile uint32 fBufferCycle; + sem_id fBuffer_free; + + + thread_id fThread; + + MultiAudioDevice *fDevice; + + //int fd; //file descriptor for hw driver + //char device_name[32]; + //multi_description MD; + //multi_format_info MFI; + //multi_buffer_list MBL; + + //multi_mix_control_info MMCI; + //multi_mix_control MMC[MAX_CONTROLS]; + + bool fTimeSourceStarted; + + BParameterWeb *fWeb; +}; + +#endif /* _MULTIAUDIONODE_H */ diff --git a/src/add-ons/media/media-add-ons/multi_audio/debug.h b/src/add-ons/media/media-add-ons/multi_audio/debug.h new file mode 100644 index 0000000000..835882f2e8 --- /dev/null +++ b/src/add-ons/media/media-add-ons/multi_audio/debug.h @@ -0,0 +1,34 @@ + +#include + +#ifndef NDEBUG + + #ifndef DEBUG + #define DEBUG 2 + #endif + + #if DEBUG >= 1 + #define UNIMPLEMENTED() printf("UNIMPLEMENTED %s\n",__PRETTY_FUNCTION__) + #else + #define UNIMPLEMENTED() ((void)0) + #endif + + #if DEBUG >= 2 + #define BROKEN() printf("BROKEN %s\n",__PRETTY_FUNCTION__) + #else + #define BROKEN() ((void)0) + #endif + + #if DEBUG >= 3 + #define CALLED() printf("CALLED %s\n",__PRETTY_FUNCTION__) + #else + #define CALLED() ((void)0) + #endif + +#else + + #define UNIMPLEMENTED() ((void)0) + #define BROKEN() ((void)0) + #define CALLED() ((void)0) + +#endif diff --git a/src/add-ons/media/media-add-ons/multi_audio/driver_io.h b/src/add-ons/media/media-add-ons/multi_audio/driver_io.h new file mode 100644 index 0000000000..b1cebfce30 --- /dev/null +++ b/src/add-ons/media/media-add-ons/multi_audio/driver_io.h @@ -0,0 +1,47 @@ +/* + * multiaudio replacement media addon for BeOS + * + * Copyright (c) 2002, Jerome Duval (jerome.duval@free.fr) + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _DRIVER_IO_H +#define _DRIVER_IO_H + +#include +#include +#include +#include "multi_audio.h" + +#define DRIVER_GET_DESCRIPTION(x...) ioctl( fd, B_MULTI_GET_DESCRIPTION, x ) +#define DRIVER_GET_ENABLED_CHANNELS(x...) ioctl( fd, B_MULTI_GET_ENABLED_CHANNELS, x ) +#define DRIVER_SET_ENABLED_CHANNELS(x...) ioctl( fd, B_MULTI_SET_ENABLED_CHANNELS, x ) +#define DRIVER_SET_GLOBAL_FORMAT(x...) ioctl( fd, B_MULTI_SET_GLOBAL_FORMAT, x ) +#define DRIVER_GET_BUFFERS(x...) ioctl( fd, B_MULTI_GET_BUFFERS, x ) +#define DRIVER_BUFFER_EXCHANGE(x...) ioctl( fd, B_MULTI_BUFFER_EXCHANGE, x ) + +#define DRIVER_LIST_MIX_CONTROLS(x...) ioctl( fd, B_MULTI_LIST_MIX_CONTROLS, x ) +#define DRIVER_SET_MIX(x...) ioctl( fd, B_MULTI_SET_MIX, x ) +#define DRIVER_GET_MIX(x...) ioctl( fd, B_MULTI_GET_MIX, x ) + +#endif diff --git a/src/add-ons/media/media-add-ons/multi_audio/multi_audio.h b/src/add-ons/media/media-add-ons/multi_audio/multi_audio.h new file mode 100644 index 0000000000..5964973d61 --- /dev/null +++ b/src/add-ons/media/media-add-ons/multi_audio/multi_audio.h @@ -0,0 +1,699 @@ +/* multi_audio.h */ + +/* Interface description for drivers implementing studio-level audio I/O with */ +/* possible auxillary functions (transport, time code, etc). */ +/* Copyright © 1998-1999 Be Incorporated. All rights reserved. */ + +/* This is the first release candidate for the API. Unless we hear feedback that */ +/* forces a change before the end of August, we will try to stay binary compatible */ +/* with this interface. */ +/* Send feedback to trinity@be.com [1999-07-02] */ + +#if !defined(_MULTI_AUDIO_H) +#define _MULTI_AUDIO_H + +#include +#include + +#define B_MULTI_DRIVER_BASE (B_AUDIO_DRIVER_BASE+20) + +enum { /* open() modes */ + /* O_RDONLY is 0, O_WRONLY is 1, O_RDWR is 2 */ + B_MULTI_CONTROL = 3 +}; + +/* ioctl codes */ +enum { + /* multi_description */ + B_MULTI_GET_DESCRIPTION = B_MULTI_DRIVER_BASE, + /* multi_event_handling */ + B_MULTI_GET_EVENT_INFO, + B_MULTI_SET_EVENT_INFO, + B_MULTI_GET_EVENT, + /* multi_channel_enable */ + B_MULTI_GET_ENABLED_CHANNELS, + B_MULTI_SET_ENABLED_CHANNELS, + /* multi_format_info */ + B_MULTI_GET_GLOBAL_FORMAT, + B_MULTI_SET_GLOBAL_FORMAT, /* always sets for all channels, always implemented */ + /* multi_channel_formats */ + B_MULTI_GET_CHANNEL_FORMATS, + B_MULTI_SET_CHANNEL_FORMATS, /* only implemented if possible */ + /* multi_mix_value_info */ + B_MULTI_GET_MIX, + B_MULTI_SET_MIX, + /* multi_mix_channel_info */ + B_MULTI_LIST_MIX_CHANNELS, + /* multi_mix_control_info */ + B_MULTI_LIST_MIX_CONTROLS, + /* multi_mix_connection_info */ + B_MULTI_LIST_MIX_CONNECTIONS, + /* multi_buffer_list */ + B_MULTI_GET_BUFFERS, /* Fill out the struct for the first time; doesn't start anything. */ + B_MULTI_SET_BUFFERS, /* Set what buffers to use, if the driver supports soft buffers. */ + /* bigtime_t */ + B_MULTI_SET_START_TIME, /* When to actually start */ + /* multi_buffer_info */ + B_MULTI_BUFFER_EXCHANGE, /* stop and go are derived from this being called */ + B_MULTI_BUFFER_FORCE_STOP, /* force stop of playback */ + /* extension protocol */ + B_MULTI_LIST_EXTENSIONS, /* get a list of supported extensions */ + B_MULTI_GET_EXTENSION, /* get the value of an extension (or return error if not supported) */ + B_MULTI_SET_EXTENSION, /* set the value of an extension */ + /* multi_mode_list */ + B_MULTI_LIST_MODES, /* get a list of possible modes (multi_mode_list * arg) */ + B_MULTI_GET_MODE, /* get the current mode (int32 * arg) */ + B_MULTI_SET_MODE /* set a new mode (int32 * arg) */ +}; + +/* sample rate values */ +/* various fixed sample rates we support (for hard-sync clocked values) */ +#define B_SR_8000 0x1 +#define B_SR_11025 0x2 +#define B_SR_12000 0x4 +#define B_SR_16000 0x8 +#define B_SR_22050 0x10 +#define B_SR_24000 0x20 +#define B_SR_32000 0x40 +#define B_SR_44100 0x80 +#define B_SR_48000 0x100 +#define B_SR_64000 0x200 +#define B_SR_88200 0x400 +#define B_SR_96000 0x800 +#define B_SR_176400 0x1000 +#define B_SR_192000 0x2000 +#define B_SR_384000 0x4000 +#define B_SR_1536000 0x10000 +/* continuously variable sample rate (typically board-generated) */ +#define B_SR_CVSR 0x10000000UL +/* sample rate parameter global to all channels (input and output rates respectively) */ +#define B_SR_IS_GLOBAL 0x80000000UL +/* output sample rate locked to input sample rate (output_rates only; the common case!) */ +#define B_SR_SAME_AS_INPUT 0x40000000UL + + +/* format values */ +/* signed char */ +#define B_FMT_8BIT_S 0x01 +/* unsigned char -- this is special case */ +#define B_FMT_8BIT_U 0x02 +/* traditional 16 bit signed format (host endian) */ +#define B_FMT_16BIT 0x10 +/* left-adjusted in 32 bit signed word */ +#define B_FMT_18BIT 0x20 +#define B_FMT_20BIT 0x40 +#define B_FMT_24BIT 0x100 +#define B_FMT_32BIT 0x1000 +/* 32-bit floating point, -1.0 to 1.0 */ +#define B_FMT_FLOAT 0x20000 +/* 64-bit floating point, -1.0 to 1.0 */ +#define B_FMT_DOUBLE 0x40000 +/* 80-bit floating point, -1.0 to 1.0 */ +#define B_FMT_EXTENDED 0x80000 +/* bit stream */ +#define B_FMT_BITSTREAM 0x1000000 +/* format parameter global to all channels (input and output formats respectively) */ +#define B_FMT_IS_GLOBAL 0x80000000UL +/* output format locked to input format (output_formats) */ +#define B_FMT_SAME_AS_INPUT 0x40000000UL + +/* possible sample lock sources */ +#define B_MULTI_LOCK_INPUT_CHANNEL 0x0 /* lock_source_data is channel id */ +#define B_MULTI_LOCK_INTERNAL 0x1 +#define B_MULTI_LOCK_WORDCLOCK 0x2 +#define B_MULTI_LOCK_SUPERCLOCK 0x4 +#define B_MULTI_LOCK_LIGHTPIPE 0x8 +#define B_MULTI_LOCK_VIDEO 0x10 /* or blackburst */ +#define B_MULTI_LOCK_FIRST_CARD 0x20 /* if you have more than one card */ +#define B_MULTI_LOCK_MTC 0x40 +#define B_MULTI_LOCK_SPDIF 0x80 + +/* possible timecode sources */ +#define B_MULTI_TIMECODE_MTC 0x1 +#define B_MULTI_TIMECODE_VTC 0x2 +#define B_MULTI_TIMECODE_SMPTE 0x4 +#define B_MULTI_TIMECODE_SUPERCLOCK 0x8 +#define B_MULTI_TIMECODE_FIREWIRE 0x10 + +/* interface_flags values */ +/* Available functions on this device. */ +#define B_MULTI_INTERFACE_PLAYBACK 0x1 +#define B_MULTI_INTERFACE_RECORD 0x2 +#define B_MULTI_INTERFACE_TRANSPORT 0x4 +#define B_MULTI_INTERFACE_TIMECODE 0x8 +/* "Soft" buffers means you can change the pointer values and the driver will still be happy. */ +#define B_MULTI_INTERFACE_SOFT_PLAY_BUFFERS 0x10000 +#define B_MULTI_INTERFACE_SOFT_REC_BUFFERS 0x20000 +/* Whether the data stream is interrupted when changing channel enables. */ +#define B_MULTI_INTERFACE_CLICKS_WHEN_ENABLING_OUTPUTS 0x40000 +#define B_MULTI_INTERFACE_CLICKS_WHEN_ENABLING_INPUTS 0x80000 + +#define B_CURRENT_INTERFACE_VERSION 0x4502 +#define B_MINIMUM_INTERFACE_VERSION 0x4502 + +typedef struct multi_description multi_description; +typedef struct multi_channel_info multi_channel_info; +struct multi_description { + + size_t info_size; /* sizeof(multi_description) */ + uint32 interface_version; /* current version of interface that's implemented */ + uint32 interface_minimum; /* minimum version required to understand driver */ + + char friendly_name[32]; /* name displayed to user (C string) */ + char vendor_info[32]; /* name used internally by vendor (C string) */ + + int32 output_channel_count; + int32 input_channel_count; + int32 output_bus_channel_count; + int32 input_bus_channel_count; + int32 aux_bus_channel_count; + + int32 request_channel_count; /* how many channel_infos are there */ + multi_channel_info * + channels; + + uint32 output_rates; + uint32 input_rates; + float min_cvsr_rate; + float max_cvsr_rate; + uint32 output_formats; + uint32 input_formats; + uint32 lock_sources; + uint32 timecode_sources; + + uint32 interface_flags; + bigtime_t start_latency; /* how much in advance driver needs SET_START_TIME */ + + uint32 _reserved_[11]; + + char control_panel[64]; /* MIME type of control panel application */ +}; + +#if !defined(_MEDIA_DEFS_H) /* enum in MediaDefs.h */ +/* designation values */ +/* mono channels have no designation */ +#define B_CHANNEL_LEFT 0x1 +#define B_CHANNEL_RIGHT 0x2 +#define B_CHANNEL_CENTER 0x4 /* 5.1+ or fake surround */ +#define B_CHANNEL_SUB 0x8 /* 5.1+ */ +#define B_CHANNEL_REARLEFT 0x10 /* quad surround or 5.1+ */ +#define B_CHANNEL_REARRIGHT 0x20 /* quad surround or 5.1+ */ +#define B_CHANNEL_FRONT_LEFT_CENTER 0x40 +#define B_CHANNEL_FRONT_RIGHT_CENTER 0x80 +#define B_CHANNEL_BACK_CENTER 0x100 /* 6.1 or fake surround */ +#define B_CHANNEL_SIDE_LEFT 0x200 +#define B_CHANNEL_SIDE_RIGHT 0x400 +#define B_CHANNEL_TOP_CENTER 0x800 +#define B_CHANNEL_TOP_FRONT_LEFT 0x1000 +#define B_CHANNEL_TOP_FRONT_CENTER 0x2000 +#define B_CHANNEL_TOP_FRONT_RIGHT 0x4000 +#define B_CHANNEL_TOP_BACK_LEFT 0x8000 +#define B_CHANNEL_TOP_BACK_CENTER 0x10000 +#define B_CHANNEL_TOP_BACK_RIGHT 0x20000 +#endif + +#define B_CHANNEL_MONO_BUS 0x4000000 +#define B_CHANNEL_STEREO_BUS 0x2000000 /* + left/right */ +#define B_CHANNEL_SURROUND_BUS 0x1000000 /* multichannel */ + +/* If you have interactions where some inputs can not be used when some */ +/* outputs are used, mark both inputs and outputs with this flag. */ +#define B_CHANNEL_INTERACTION 0x80000000UL +/* If input channel #n is simplexed with output channel #n, they should both */ +/* have this flag set (different from the previous flag, which is more vague). */ +#define B_CHANNEL_SIMPLEX 0x40000000UL + +/* connector values */ +/* analog connectors */ +#define B_CHANNEL_RCA 0x1 +#define B_CHANNEL_XLR 0x2 +#define B_CHANNEL_TRS 0x4 +#define B_CHANNEL_QUARTER_INCH_MONO 0x8 +#define B_CHANNEL_MINI_JACK_STEREO 0x10 +#define B_CHANNEL_QUARTER_INCH_STEREO 0x20 +#define B_CHANNEL_ANALOG_HEADER 0x100 /* internal on card */ +#define B_CHANNEL_SNAKE 0x200 /* or D-sub */ +/* digital connectors (stereo) */ +#define B_CHANNEL_OPTICAL_SPDIF 0x1000 +#define B_CHANNEL_COAX_SPDIF 0x2000 +#define B_CHANNEL_COAX_EBU 0x4000 +#define B_CHANNEL_XLR_EBU 0x8000 +#define B_CHANNEL_TRS_EBU 0x10000 +#define B_CHANNEL_SPDIF_HEADER 0x20000 /* internal on card */ +/* multi-channel digital connectors */ +#define B_CHANNEL_LIGHTPIPE 0x100000 +#define B_CHANNEL_TDIF 0x200000 +#define B_CHANNEL_FIREWIRE 0x400000 +#define B_CHANNEL_USB 0x800000 +/* If you have multiple output connectors, only one of which can */ +/* be active at a time. */ +#define B_CHANNEL_EXCLUSIVE_SELECTION 0x80000000UL + + +typedef enum { + B_MULTI_NO_CHANNEL_KIND, + B_MULTI_OUTPUT_CHANNEL = 0x1, + B_MULTI_INPUT_CHANNEL = 0x2, + B_MULTI_OUTPUT_BUS = 0x4, + B_MULTI_INPUT_BUS = 0x8, + B_MULTI_AUX_BUS = 0x10 +} channel_kind; + +struct multi_channel_info { + int32 channel_id; + channel_kind kind; + uint32 designations; + uint32 connectors; + uint32 _reserved_[4]; +}; + + +/* Constants */ +#define B_MULTI_EVENT_MINMAX 16 +/* Event flags/masks */ +#define B_MULTI_EVENT_TRANSPORT 0x40000000UL +#define B_MULTI_EVENT_HAS_TIMECODE 0x80000000UL + +/* possible transport events */ +#define B_MULTI_EVENT_NONE 0x00000000UL +#define B_MULTI_EVENT_START 0x40010000UL +#define B_MULTI_EVENT_LOCATION 0x40020000UL /* location when shuttling or locating */ +#define B_MULTI_EVENT_SHUTTLING 0x40040000UL +#define B_MULTI_EVENT_STOP 0x40080000UL +#define B_MULTI_EVENT_RECORD 0x40100000UL +#define B_MULTI_EVENT_PAUSE 0x40200000UL +#define B_MULTI_EVENT_RUNNING 0x40400000UL /* location when running */ +/* possible device events */ +enum { + B_MULTI_EVENT_STARTED = 0x1, + B_MULTI_EVENT_STOPPED = 0x2, + B_MULTI_EVENT_CHANNEL_FORMAT_CHANGED= 0x4, + B_MULTI_EVENT_BUFFER_OVERRUN = 0x8, + B_MULTI_EVENT_SIGNAL_LOST = 0x10, + B_MULTI_EVENT_SIGNAL_DETECTED = 0x20, + B_MULTI_EVENT_CLOCK_LOST = 0x40, + B_MULTI_EVENT_CLOCK_DETECTED = 0x80, + B_MULTI_EVENT_NEW_MODE = 0x100, + B_MULTI_EVENT_CONTROL_CHANGED = 0x200 +}; + +typedef struct multi_get_event_info multi_get_event_info; +struct multi_get_event_info { + size_t info_size; /* sizeof(multi_get_event_info) */ + uint32 supported_mask; /* what events h/w supports */ + uint32 current_mask; /* current driver value */ + uint32 queue_size; /* current queue size */ + uint32 event_count; /* number of events currently in queue*/ + uint32 _reserved[3]; +}; + +typedef struct multi_set_event_info multi_set_event_info; +struct multi_set_event_info { + size_t info_size; /* sizeof(multi_set_event_info) */ + uint32 in_mask; /* what events to wait for */ + int32 semaphore; /* semaphore app will wait on */ + uint32 queue_size; /* minimum number of events to save */ + uint32 _reserved[4]; +}; + +typedef struct multi_get_event multi_get_event; +struct multi_get_event { + size_t info_size; /* sizeof(multi_get_event) */ + uint32 event; + bigtime_t timestamp; /* real time at which event was received */ + int32 count; /* used for configuration events */ + union { + int32 channels[100]; + uint32 clocks; + int32 mode; + int32 controls[100]; + struct { /* transport event */ + float out_rate; /* what rate it's now playing at */ + int32 out_hours; /* location at the time given */ + int32 out_minutes; + int32 out_seconds; + int32 out_frames; + }transport; + char _reserved_[400]; + #if defined(__cplusplus) + }; + #else + } u; + #endif + uint32 _reserved_1[10]; +}; + +typedef struct multi_channel_enable multi_channel_enable; +struct multi_channel_enable { + size_t info_size; /* sizeof(multi_channel_enable) */ + /* this must have bytes for all channels (see multi_description) */ + /* channel 0 is lowest bit of first byte */ + uchar * enable_bits; + + uint32 lock_source; + int32 lock_data; + uint32 timecode_source; + uint32 * connectors; /* which connector(s) is/are active, per channel */ +}; + +#include + +#if defined(__cplusplus) + inline void B_SET_CHANNEL(void * bits, int channel, bool value) + { + ASSERT(channel>=0); + (((uchar *)(bits))[((channel)&0x7fff)>>3] = + (((uchar *)(bits))[((channel)&0x7fff)>>3] & ~(1<<((channel)&0x7))) | + ((value) ? (1<<((channel)&0x7)) : 0)); + } + inline bool B_TEST_CHANNEL(const void * bits, int channel) + { + return ((((uchar *)(bits))[((channel)&0x7fff)>>3] >> ((channel)&0x7)) & 1); + } +#else + #define B_SET_CHANNEL(bits, channel, value) \ + ASSERT(channel>=0); \ + (((uchar *)(bits))[((channel)&0x7fff)>>3] = \ + (((uchar *)(bits))[((channel)&0x7fff)>>3] & ~(1<<((channel)&0x7))) | \ + ((value) ? (1<<((channel)&0x7)) : 0)) + #define B_TEST_CHANNEL(bits, channel) \ + ((((uchar *)(bits))[((channel)&0x7fff)>>3] >> ((channel)&0x7)) & 1) +#endif + +typedef struct multi_channel_formats multi_channel_formats; +typedef struct multi_format_info multi_format_info; +typedef struct _multi_format _multi_format; + +struct _multi_format { + uint32 rate; + float cvsr; + uint32 format; + uint32 _reserved_[3]; +}; +enum { /* timecode kinds */ + B_MULTI_NO_TIMECODE, + B_MULTI_TIMECODE_30, /* MIDI */ + B_MULTI_TIMECODE_30_DROP_2, /* NTSC */ + B_MULTI_TIMECODE_30_DROP_4, /* Brazil */ + B_MULTI_TIMECODE_25, /* PAL */ + B_MULTI_TIMECODE_24 /* Film */ +}; +struct multi_format_info { + size_t info_size; /* sizeof(multi_format_info) */ + bigtime_t output_latency; + bigtime_t input_latency; + int32 timecode_kind; + uint32 _reserved_[7]; + _multi_format input; + _multi_format output; +}; +struct multi_channel_formats { + size_t info_size; /* sizeof(multi_channel_formats) */ + int32 request_channel_count; + int32 request_first_channel; + int32 returned_channel_count; + int32 timecode_kind; + int32 _reserved_[4]; + _multi_format * + channels; + bigtime_t * latencies; /* DMA/hardware latencies; client calculates for buffers */ +}; + + +typedef struct multi_mix_value multi_mix_value; +struct multi_mix_value { + int32 id; + union { + float gain; + uint32 mux; /* bitmask of mux points */ + bool enable; + uint32 _reserved_[2]; +#if defined(__cplusplus) + }; +#else + } u; +#endif + int32 ramp; + uint32 _reserved_2[2]; +}; + +typedef struct multi_mix_value_info multi_mix_value_info; +struct multi_mix_value_info { + size_t info_size; /* sizeof(multi_mix_value_info) */ + int32 item_count; + multi_mix_value * + values; + int32 at_frame; /* time at which to start the change */ +}; + +// only one of these should be set +#define B_MULTI_MIX_JUNCTION 0x1 +#define B_MULTI_MIX_GAIN 0x2 +#define B_MULTI_MIX_MUX 0x4 +#define B_MULTI_MIX_ENABLE 0x8 +#define B_MULTI_MIX_GROUP 0x10 +#define B_MULTI_MIX_KIND_MASK 0xffff + +#define B_MULTI_MIX_MUX_VALUE 0x0104 +// any combination of these can be set +#define B_MULTI_MIX_RAMP 0x10000 + +enum strind_id { + S_null = 0, S_OUTPUT, S_INPUT, S_SETUP, S_TONE_CONTROL, S_EXTENDED_SETUP, + S_ENHANDED_SETUP, S_MASTER, S_BEEP, S_PHONE, S_MIC, S_LINE, S_CD, S_VIDEO, + S_AUX, S_WAVE, S_GAIN, S_LEVEL, S_VOLUME, S_MUTE, S_ENABLE, S_STEREO_MIX, + S_MONO_MIX, S_OUTPUT_STEREO_MIX, S_OUTPUT_MONO_MIX, S_OUTPUT_BASS, + S_OUTPUT_TREBLE, S_OUTPUT_3D_CENTER, S_OUTPUT_3D_DEPTH, + S_USERID = 1000000 +}; + +typedef struct multi_mix_control multi_mix_control; +struct multi_mix_control { + int32 id; /* unique for device -- not same id as any channel/bus ! */ + uint32 flags; /* including kind */ + int32 master; /* or 0 if it's not slaved */ + union { + struct { + float min_gain; /* dB */ + float max_gain; /* dB */ + float granularity; /* dB */ + } gain; + struct { + uint32 _reserved; + } mux; + struct { + uint32 _reserved; + } enable; + uint32 _reserved[12]; +#if defined(__cplusplus) + }; +#else + } u; +#endif + enum strind_id string; /* string id (S_null : use name) */ + int32 parent; /* parent id */ + char name[48]; +}; + +typedef struct multi_mix_channel_info multi_mix_channel_info; +struct multi_mix_channel_info { + size_t info_size; /* sizeof(multi_mix_channel_info) */ + int32 channel_count; + int32 * channels; /* allocated by caller, lists requested channels */ + int32 max_count; /* in: control ids per channel */ + int32 actual_count; /* out: actual max # controls for any individual requested channel */ + int32 ** controls; +}; + +typedef struct multi_mix_control_info multi_mix_control_info; +struct multi_mix_control_info { + size_t info_size; /* sizeof(multi_mix_control_info) */ + int32 control_count; /* in: number of controls */ + multi_mix_control * + controls; /* allocated by caller, returns control description for each */ +}; + +typedef struct multi_mix_connection multi_mix_connection; +struct multi_mix_connection { + int32 from; + int32 to; + uint32 _reserved_[2]; +}; + +typedef struct multi_mix_connection_info multi_mix_connection_info; +struct multi_mix_connection_info { + size_t info_size; + int32 max_count; /* in: available space */ + int32 actual_count; /* out: actual count */ + multi_mix_connection * + connections; /* allocated by caller, returns connections */ +}; + + +/* possible flags values for what is available (in and out) */ +#define B_MULTI_BUFFER_PLAYBACK 0x1 +#define B_MULTI_BUFFER_RECORD 0x2 +#define B_MULTI_BUFFER_METERING 0x4 +#define B_MULTI_BUFFER_TIMECODE 0x40000 + +typedef struct multi_buffer_list multi_buffer_list; +typedef struct buffer_desc buffer_desc; +/* This struct is used to query the driver about what buffers it will use, */ +/* and to tell it what buffers to use if it supports soft buffers. */ +struct multi_buffer_list { + + size_t info_size; /* sizeof(multi_buffer_list) */ + uint32 flags; + + int32 request_playback_buffers; + int32 request_playback_channels; + uint32 request_playback_buffer_size; /* frames per buffer */ + int32 return_playback_buffers; /* playback_buffers[b][] */ + int32 return_playback_channels; /* playback_buffers[][c] */ + uint32 return_playback_buffer_size; /* frames */ + buffer_desc ** playback_buffers; + void * _reserved_1; + + int32 request_record_buffers; + int32 request_record_channels; + uint32 request_record_buffer_size; /* frames per buffer */ + int32 return_record_buffers; + int32 return_record_channels; + uint32 return_record_buffer_size; /* frames */ + buffer_desc ** record_buffers; + void * _reserved_2; + +}; + +struct buffer_desc { + char * base; /* pointer to first sample for channel for buffer */ + size_t stride; /* offset to next sample */ + uint32 _reserved_[2]; +}; + + +/* This struct is used when actually queuing data to be played, and/or */ +/* receiving data from a recorder. */ +typedef struct multi_buffer_info multi_buffer_info; +struct multi_buffer_info { + + size_t info_size; /* sizeof(multi_buffer_info) */ + uint32 flags; + + bigtime_t played_real_time; + bigtime_t played_frames_count; + int32 _reserved_0; + int32 playback_buffer_cycle; + + bigtime_t recorded_real_time; + bigtime_t recorded_frames_count; + int32 _reserved_1; + int32 record_buffer_cycle; + + int32 meter_channel_count; + char * meters_peak; /* in the same format as the data; allocated by caller */ + char * meters_average; /* in the same format as the data; allocated by caller */ + + /* timecode sent and received at buffer swap */ + int32 hours; + int32 minutes; + int32 seconds; + int32 tc_frames; /* for timecode frames as opposed to sample frames */ + int32 at_frame_delta; /* how far into buffer (or before buffer for negative) */ + +}; + + +typedef struct multi_mode_info multi_mode_info; +typedef struct multi_mode_list multi_mode_list; + +struct multi_mode_list { + size_t info_size; /* sizeof(multi_mode_list) */ + int32 in_request_count; + int32 out_actual_count; + int32 out_current_mode; + multi_mode_info * + io_modes; +}; + +struct multi_mode_info { + int32 mode_id; + uint32 flags; + char mode_name[64]; + int32 input_channel_count; + int32 output_channel_count; + float best_frame_rate_in; + float best_frame_rate_out; + uint32 sample_formats_in; + uint32 sample_formats_out; + char _reserved[160]; +}; + + +/* This extension protocol can grow however much you want. */ +/* Good extensions should be put into this header; really */ +/* good extensions should become part of the regular API. */ +/* For developer-developed extensions, use all lowercase */ +/* and digits (no upper case). If we then bless a third- */ +/* party extension, we can just upper-case the selector. */ + +typedef struct multi_extension_list multi_extension_list; +typedef struct multi_extension_info multi_extension_info; +struct multi_extension_info { + uint32 code; + uint32 flags; + char name[24]; +}; + +#define B_MULTI_MAX_EXTENSION_COUNT 31 +struct multi_extension_list { /* MULTI_LIST_EXTENSIONS */ + size_t info_size; /* sizeof(multi_extension_list) */ + uint32 max_count; + int32 actual_count; /* return # of actual extensions */ + multi_extension_info * + extensions; /* allocated by caller */ +}; + +typedef struct multi_extension_cmd multi_extension_cmd; +struct multi_extension_cmd { /* MULTI_GET_EXTENSION and MULTI_SET_EXTENSION */ + size_t info_size; /* sizeof(multi_extension_cmd) */ + uint32 code; + uint32 _reserved_1; + void * in_data; + size_t in_size; + void * out_data; + size_t out_size; +}; + +enum { + B_MULTI_EX_CLOCK_GENERATION = 'CLGE', + B_MULTI_EX_DIGITAL_FORMAT = 'DIFO', + B_MULTI_EX_OUTPUT_NOMINAL = 'OUNO', + B_MULTI_EX_INPUT_NOMINAL = 'INNO' +}; + +typedef struct multi_ex_clock_generation multi_ex_clock_generation; +struct multi_ex_clock_generation { + int32 channel; /* if specific, or -1 for all */ + uint32 clock; /* WORDCLOCK or SUPERCLOCK, typically */ +}; + +typedef struct multi_ex_digital_format multi_ex_digital_format; +struct multi_ex_digital_format { + int32 channel; /* if specific, or -1 for all */ + uint32 format; /* B_CHANNEL_*_SPDIF or B_CHANNEL_*_EBU */ +}; + +enum { + B_MULTI_NOMINAL_MINUS_10 = 1, + B_MULTI_NOMINAL_PLUS_4 +}; + +typedef struct multi_ex_nominal_level multi_ex_nominal_level; +struct multi_ex_nominal_level { + int32 channel; /* if specific, or -1 for all */ + int32 level; +}; + +#endif /* _MULTI_AUDIO_H */ +