diff --git a/src/add-ons/media/media-add-ons/multi_audio/Jamfile b/src/add-ons/media/media-add-ons/multi_audio/Jamfile index 34fbed64e1..983616e3f0 100644 --- a/src/add-ons/media/media-add-ons/multi_audio/Jamfile +++ b/src/add-ons/media/media-add-ons/multi_audio/Jamfile @@ -2,7 +2,7 @@ SubDir HAIKU_TOP src add-ons media media-add-ons multi_audio ; SetSubDirSupportedPlatformsBeOSCompatible ; -UsePrivateHeaders media ; +UsePrivateHeaders media shared ; if ! $(TARGET_PLATFORM_HAIKU_COMPATIBLE) { SubDirC++Flags -fmultiple-symbol-spaces ; 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 index 35a019230c..c45b594216 100644 --- a/src/add-ons/media/media-add-ons/multi_audio/MultiAudioDevice.cpp +++ b/src/add-ons/media/media-add-ons/multi_audio/MultiAudioDevice.cpp @@ -45,6 +45,75 @@ MultiAudioDevice::InitCheck() const } +status_t +MultiAudioDevice::BufferExchange(multi_buffer_info *info) +{ + return buffer_exchange(fDevice, info); +} + + +status_t +MultiAudioDevice::SetMix(multi_mix_value_info *info) +{ + return set_mix(fDevice, info); +} + + +status_t +MultiAudioDevice::GetMix(multi_mix_value_info *info) +{ + return get_mix(fDevice, info); +} + + +status_t +MultiAudioDevice::SetInputFrameRate(uint32 multiAudioRate) +{ + if ((fDescription.input_rates & multiAudioRate) == 0) + return B_BAD_VALUE; + + if (fFormatInfo.input.rate == multiAudioRate) + return B_OK; + + uint32 oldRate = fFormatInfo.input.rate; + fFormatInfo.input.rate = multiAudioRate; + + status_t status = set_global_format(fDevice, &fFormatInfo); + if (status != B_OK) { + fprintf(stderr, "Failed on B_MULTI_SET_GLOBAL_FORMAT: %s\n", + strerror(status)); + fFormatInfo.input.rate = oldRate; + return status; + } + + return _GetBuffers(); +} + + +status_t +MultiAudioDevice::SetOutputFrameRate(uint32 multiAudioRate) +{ + if ((fDescription.output_rates & multiAudioRate) == 0) + return B_BAD_VALUE; + + if (fFormatInfo.output.rate == multiAudioRate) + return B_OK; + + uint32 oldRate = fFormatInfo.output.rate; + fFormatInfo.output.rate = multiAudioRate; + + status_t status = set_global_format(fDevice, &fFormatInfo); + if (status != B_OK) { + fprintf(stderr, "Failed on B_MULTI_SET_GLOBAL_FORMAT: %s\n", + strerror(status)); + fFormatInfo.output.rate = oldRate; + return status; + } + + return _GetBuffers(); +} + + status_t MultiAudioDevice::_InitDriver() { @@ -154,7 +223,29 @@ MultiAudioDevice::_InitDriver() } // Get the buffers + status = _GetBuffers(); + if (status != B_OK) + return status; + + fMixControlInfo.info_size = sizeof(fMixControlInfo); + fMixControlInfo.control_count = MAX_CONTROLS; + fMixControlInfo.controls = fMixControl; + + status = list_mix_controls(fDevice, &fMixControlInfo); + if (status != B_OK) { + fprintf(stderr, "Failed on DRIVER_LIST_MIX_CONTROLS: %s\n", + strerror(status)); + return status; + } + + return B_OK; +} + + +status_t +MultiAudioDevice::_GetBuffers() +{ for (uint32 i = 0; i < MAX_BUFFERS; i++) { fPlayBuffers[i] = &fPlayBufferList[i * MAX_CHANNELS]; fRecordBuffers[i] = &fRecordBufferList[i * MAX_CHANNELS]; @@ -163,15 +254,15 @@ MultiAudioDevice::_InitDriver() fBufferList.request_playback_buffer_size = 0; // use the default... fBufferList.request_playback_buffers = MAX_BUFFERS; - fBufferList.request_playback_channels = num_outputs; + fBufferList.request_playback_channels = fDescription.output_channel_count; fBufferList.playback_buffers = (buffer_desc **) fPlayBuffers; fBufferList.request_record_buffer_size = 0; // use the default... fBufferList.request_record_buffers = MAX_BUFFERS; - fBufferList.request_record_channels = num_inputs; + fBufferList.request_record_channels = fDescription.input_channel_count; fBufferList.record_buffers = /*(buffer_desc **)*/ fRecordBuffers; - status = get_buffers(fDevice, &fBufferList); + status_t status = get_buffers(fDevice, &fBufferList); if (status != B_OK) { fprintf(stderr, "Failed on B_MULTI_GET_BUFFERS: %s\n", strerror(status)); @@ -196,37 +287,5 @@ MultiAudioDevice::_InitDriver() } } - fMixControlInfo.info_size = sizeof(fMixControlInfo); - fMixControlInfo.control_count = MAX_CONTROLS; - fMixControlInfo.controls = fMixControl; - - status = list_mix_controls(fDevice, &fMixControlInfo); - if (status != B_OK) { - fprintf(stderr, "Failed on DRIVER_LIST_MIX_CONTROLS: %s\n", - strerror(status)); - return status; - } - return B_OK; } - - -status_t -MultiAudioDevice::BufferExchange(multi_buffer_info *info) -{ - return buffer_exchange(fDevice, info); -} - - -status_t -MultiAudioDevice::SetMix(multi_mix_value_info *info) -{ - return set_mix(fDevice, info); -} - - -status_t -MultiAudioDevice::GetMix(multi_mix_value_info *info) -{ - return get_mix(fDevice, info); -} 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 index 842b017bda..cdda452ef9 100644 --- a/src/add-ons/media/media-add-ons/multi_audio/MultiAudioDevice.h +++ b/src/add-ons/media/media-add-ons/multi_audio/MultiAudioDevice.h @@ -31,9 +31,14 @@ public: status_t SetMix(multi_mix_value_info* mixValueInfo); status_t GetMix(multi_mix_value_info* mixValueInfo); + status_t SetInputFrameRate(uint32 multiAudioRate); + status_t SetOutputFrameRate(uint32 multiAudioRate); + private: status_t _InitDriver(); + status_t _GetBuffers(); +private: status_t fInitStatus; int fDevice; char fPath[B_PATH_NAME_LENGTH]; 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 index a8bd61e251..2a820d27fd 100644 --- a/src/add-ons/media/media-add-ons/multi_audio/MultiAudioNode.cpp +++ b/src/add-ons/media/media-add-ons/multi_audio/MultiAudioNode.cpp @@ -5,14 +5,19 @@ //! Multi-audio replacement media addon for BeOS + #include "MultiAudioNode.h" #include #include +#include #include #include #include +#include + +#include #include "MultiAudioUtility.h" #ifdef DEBUG @@ -20,6 +25,11 @@ #endif #include "debug.h" + +#define PARAMETER_ID_INPUT_FREQUENCY 1 +#define PARAMETER_ID_OUTPUT_FREQUENCY 2 + + class node_input { public: node_input(media_input& input, media_format format); @@ -52,6 +62,37 @@ public: }; +struct OutputFrameRateChangeCookie : public BReferenceable { + float oldFrameRate; +}; + + +struct sample_rate_info { + uint32 multiAudioRate; + const char* name; +}; + +static const sample_rate_info kSampleRateInfos[] = { + {B_SR_8000, "8000"}, + {B_SR_11025, "11025"}, + {B_SR_12000, "12000"}, + {B_SR_16000, "16000"}, + {B_SR_22050, "22050"}, + {B_SR_24000, "24000"}, + {B_SR_32000, "32000"}, + {B_SR_44100, "44100"}, + {B_SR_48000, "48000"}, + {B_SR_64000, "64000"}, + {B_SR_88200, "88200"}, + {B_SR_96000, "96000"}, + {B_SR_176400, "176400"}, + {B_SR_192000, "192000"}, + {B_SR_384000, "384000"}, + {B_SR_1536000, "1536000"}, + {} +}; + + const char* kMultiControlString[] = { "NAME IS ATTACHED", "Output", "Input", "Setup", "Tone Control", "Extended Setup", "Enhanced Setup", "Master", @@ -61,6 +102,9 @@ const char* kMultiControlString[] = { }; +// #pragma mark - + + node_input::node_input(media_input& input, media_format format) { CALLED(); @@ -105,6 +149,7 @@ MultiAudioNode::MultiAudioNode(BMediaAddOn* addon, const char* name, MultiAudioDevice* device, int32 internalID, BMessage* config) : BMediaNode(name), BBufferConsumer(B_MEDIA_RAW_AUDIO), BBufferProducer(B_MEDIA_RAW_AUDIO), + fBufferLock("multi audio buffers"), fThread(-1), fDevice(device), fTimeSourceStarted(false), @@ -181,6 +226,52 @@ MultiAudioNode::InitCheck() const } +void +MultiAudioNode::GetFlavor(flavor_info* info, int32 id) +{ + CALLED(); + if (info == NULL) + return; + + info->flavor_flags = 0; + info->possible_count = 1; // one flavor at a time + info->in_format_count = 0; // no inputs + info->in_formats = 0; + info->out_format_count = 0; // no outputs + info->out_formats = 0; + info->internal_id = id; + + info->name = (char*)"MultiAudioNode Node"; + info->info = (char*)"The MultiAudioNode node outputs to multi_audio " + "drivers."; + info->kinds = B_BUFFER_CONSUMER | B_BUFFER_PRODUCER | B_TIME_SOURCE + | B_PHYSICAL_OUTPUT | B_PHYSICAL_INPUT | B_CONTROLLABLE; + info->in_format_count = 1; // 1 input + media_format* inFormats = new media_format[info->in_format_count]; + GetFormat(&inFormats[0]); + info->in_formats = inFormats; + + info->out_format_count = 1; // 1 output + media_format* outFormats = new media_format[info->out_format_count]; + GetFormat(&outFormats[0]); + info->out_formats = outFormats; +} + + +void +MultiAudioNode::GetFormat(media_format* format) +{ + CALLED(); + if (format == NULL) + return; + + format->type = B_MEDIA_RAW_AUDIO; + format->require_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS; + format->deny_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS; + format->u.raw_audio = media_raw_audio_format::wildcard; +} + + // -------------------------------------------------------- // // implementation of BMediaNode // -------------------------------------------------------- // @@ -344,6 +435,25 @@ status_t MultiAudioNode::RequestCompleted(const media_request_info& info) { CALLED(); + + if (info.what != media_request_info::B_REQUEST_FORMAT_CHANGE) + return B_OK; + + OutputFrameRateChangeCookie* cookie + = (OutputFrameRateChangeCookie*)info.user_data; + if (cookie == NULL) + return B_OK; + + BReference cookieReference(cookie, true); + + // if the request failed, we reset the frame rate + if (info.status != B_OK) { + _SetNodeInputFrameRate(cookie->oldFrameRate); + + // TODO: If we have multiple connections, we should request to change + // the format back! + } + return B_OK; } @@ -529,15 +639,7 @@ MultiAudioNode::Connected(const media_source& producer, return B_MEDIA_BAD_DESTINATION; } - // 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)); - - PRINT((" internal latency = %lld\n",fInternalLatency)); - - SetEventLatency(fInternalLatency); + _UpdateInternalLatency(with_format); // record the agreed upon values channel->fInput.source = producer; @@ -563,6 +665,8 @@ MultiAudioNode::Disconnected(const media_source& producer, channel->fInput.source = media_source::null; channel->fInput.format = channel->fPreferredFormat; + + BAutolock locker(fBufferLock); _FillWithZeros(*channel); //GetFormat(&channel->fInput.format); } @@ -1201,6 +1305,20 @@ MultiAudioNode::GetParameterValue(int32 id, bigtime_t* lastChange, void* value, return B_ERROR; } + if (id == PARAMETER_ID_INPUT_FREQUENCY + || id == PARAMETER_ID_OUTPUT_FREQUENCY) { + const multi_format_info& info = fDevice->FormatInfo(); + uint32 rate = id == PARAMETER_ID_INPUT_FREQUENCY + ? info.input.rate : info.output.rate; + + if (*size < sizeof(rate)) + return B_ERROR; + + memcpy(value, &rate, sizeof(rate)); + *size = sizeof(rate); + return B_OK; + } + multi_mix_value_info info; multi_mix_value values[2]; info.values = values; @@ -1279,6 +1397,58 @@ MultiAudioNode::SetParameterValue(int32 id, bigtime_t performanceTime, if (parameter == NULL) return; + if (id == PARAMETER_ID_INPUT_FREQUENCY) { + // TODO: Support! + return; + } + + if (id == PARAMETER_ID_OUTPUT_FREQUENCY) { + uint32 rate; + if (size < sizeof(rate)) + return; + memcpy(&rate, value, sizeof(rate)); + + // create a cookie RequestCompleted() can get the old frame rate from, + // if anything goes wrong + OutputFrameRateChangeCookie* cookie + = new(std::nothrow) OutputFrameRateChangeCookie; + if (cookie == NULL) + return; + + cookie->oldFrameRate = fOutputPreferredFormat.u.raw_audio.frame_rate; + BReference cookieReference(cookie, true); + + // NOTE: What we should do is call RequestFormatChange() for all + // connections and change the device's format in RequestCompleted(). + // Unfortunately we need the new buffer size first, which we only get + // from the device after changing the format. So we do that now and + // reset it in RequestCompleted(), if something went wrong. This causes + // the buffers we receive until then to be played incorrectly leading + // to unpleasant noise. + float frameRate = MultiAudio::convert_to_sample_rate(rate); + if (_SetNodeInputFrameRate(frameRate) != B_OK) + return; + + for (int32 i = 0; i < fInputs.CountItems(); i++) { + node_input* channel = (node_input*)fInputs.ItemAt(i); + if (channel->fInput.source == media_source::null) + continue; + + media_format newFormat = channel->fInput.format; + newFormat.u.raw_audio.frame_rate = frameRate; + newFormat.u.raw_audio.buffer_size + = fOutputPreferredFormat.u.raw_audio.buffer_size; + + int32 changeTag = 0; + status_t error = RequestFormatChange(channel->fInput.source, + channel->fInput.destination, newFormat, NULL, &changeTag); + if (error == B_OK) + cookie->AcquireReference(); + } + + return; + } + multi_mix_value_info info; multi_mix_value values[2]; info.values = values; @@ -1333,6 +1503,15 @@ MultiAudioNode::MakeParameterWeb() PRINT(("MixControlInfo().control_count : %li\n", fDevice->MixControlInfo().control_count)); + BParameterGroup* generalGroup = web->MakeGroup("General"); + + const multi_description& description = fDevice->Description(); +// _CreateFrequencyParameterGroup(generalGroup, "Input", +// PARAMETER_ID_INPUT_FREQUENCY, description.input_rates); + // TODO: Enable when implemented correctly in SetParameterValue()! + _CreateFrequencyParameterGroup(generalGroup, "Output", + PARAMETER_ID_OUTPUT_FREQUENCY, description.output_rates); + multi_mix_control* controls = fDevice->MixControlInfo().controls; for (int i = 0; i < fDevice->MixControlInfo().control_count; i++) { @@ -1453,6 +1632,25 @@ MultiAudioNode::_ProcessMux(BDiscreteParameter* parameter, int32 index) } +void +MultiAudioNode::_CreateFrequencyParameterGroup(BParameterGroup* parentGroup, + const char* name, int32 parameterID, uint32 rateMask) +{ + BParameterGroup* group = parentGroup->MakeGroup(name); + BDiscreteParameter* frequencyParam = group->MakeDiscreteParameter( + parameterID, B_MEDIA_NO_TYPE, BString(name) << " Frequency:", + B_GENERIC); + + for (int32 i = 0; kSampleRateInfos[i].name != NULL; i++) { + const sample_rate_info& info = kSampleRateInfos[i]; + if ((rateMask & info.multiAudioRate) != 0) { + frequencyParam->AddItem(info.multiAudioRate, + BString(info.name) << " kHz"); + } + } +} + + // #pragma mark - MultiAudioNode specific functions @@ -1467,12 +1665,18 @@ MultiAudioNode::_RunThread() bufferInfo.playback_buffer_cycle = 0; bufferInfo.record_buffer_cycle = 0; + // reset the info for the performance time computation + fResetPerformanceTimeBase = true; + while (true) { // TODO: why this semaphore?? if (acquire_sem_etc(fBufferFreeSem, 1, B_RELATIVE_TIMEOUT, 0) == B_BAD_SEM_ID) return B_OK; + BAutolock locker(fBufferLock); + // make sure the buffers don't change while we're playing with them + // send buffer fDevice->BufferExchange(&bufferInfo); @@ -1924,11 +2128,23 @@ MultiAudioNode::_UpdateTimeSource(multi_buffer_info& info, if (!fTimeSourceStarted || oldInfo.played_real_time == 0) return; - bigtime_t performanceTime = (bigtime_t)(info.played_frames_count / - input.fInput.format.u.raw_audio.frame_rate * 1000000LL); + if (fResetPerformanceTimeBase) { + fPerformanceTimeBase = info.played_real_time; + fPerformanceTimeBaseFrames = info.played_frames_count; + fResetPerformanceTimeBase = false; + return; + } + + double usecsPerFrame = 1000000 / input.fInput.format.u.raw_audio.frame_rate; + uint64 frameCount = info.played_frames_count + - fPerformanceTimeBaseFrames; + uint64 oldFrameCount = oldInfo.played_frames_count + - fPerformanceTimeBaseFrames; + + bigtime_t performanceTime = (bigtime_t)(frameCount * usecsPerFrame) + + fPerformanceTimeBase; bigtime_t realTime = info.played_real_time; - float drift = ((info.played_frames_count - oldInfo.played_frames_count) - / input.fInput.format.u.raw_audio.frame_rate * 1000000LL) + float drift = ((frameCount - oldFrameCount) * usecsPerFrame) / (info.played_real_time - oldInfo.played_real_time); PublishTime(performanceTime, realTime, drift); @@ -2092,47 +2308,69 @@ MultiAudioNode::_run_thread_(void* data) } -void -MultiAudioNode::GetFlavor(flavor_info* info, int32 id) +status_t +MultiAudioNode::_SetNodeInputFrameRate(float frameRate) { - CALLED(); - if (info == NULL) - return; + // check whether the frame rate is supported + uint32 multiAudioRate = MultiAudio::convert_from_sample_rate(frameRate); + if ((fDevice->Description().output_rates & multiAudioRate) == 0) + return B_BAD_VALUE; - info->flavor_flags = 0; - info->possible_count = 1; // one flavor at a time - info->in_format_count = 0; // no inputs - info->in_formats = 0; - info->out_format_count = 0; // no outputs - info->out_formats = 0; - info->internal_id = id; + BAutolock locker(fBufferLock); - info->name = "MultiAudioNode Node"; - info->info = "The MultiAudioNode node outputs to multi_audio drivers."; - info->kinds = B_BUFFER_CONSUMER | B_BUFFER_PRODUCER | B_TIME_SOURCE - | B_PHYSICAL_OUTPUT | B_PHYSICAL_INPUT | B_CONTROLLABLE; - info->in_format_count = 1; // 1 input - media_format* inFormats = new media_format[info->in_format_count]; - GetFormat(&inFormats[0]); - info->in_formats = inFormats; + // already set? + if (fDevice->FormatInfo().output.rate == multiAudioRate) + return B_OK; - info->out_format_count = 1; // 1 output - media_format* outFormats = new media_format[info->out_format_count]; - GetFormat(&outFormats[0]); - info->out_formats = outFormats; + // set the frame rate on the device + status_t error = fDevice->SetOutputFrameRate(multiAudioRate); + if (error != B_OK) + return error; + + // it went fine -- update all formats + fOutputPreferredFormat.u.raw_audio.frame_rate = frameRate; + fOutputPreferredFormat.u.raw_audio.buffer_size + = fDevice->BufferList().return_playback_buffer_size + * (fOutputPreferredFormat.u.raw_audio.format + & media_raw_audio_format::B_AUDIO_SIZE_MASK) + * fOutputPreferredFormat.u.raw_audio.channel_count; + + for (int32 i = 0; node_input* channel = (node_input*)fInputs.ItemAt(i); + i++) { + channel->fPreferredFormat.u.raw_audio.frame_rate = frameRate; + channel->fPreferredFormat.u.raw_audio.buffer_size + = fOutputPreferredFormat.u.raw_audio.buffer_size; + + channel->fFormat.u.raw_audio.frame_rate = frameRate; + channel->fFormat.u.raw_audio.buffer_size + = fOutputPreferredFormat.u.raw_audio.buffer_size; + + channel->fInput.format.u.raw_audio.frame_rate = frameRate; + channel->fInput.format.u.raw_audio.buffer_size + = fOutputPreferredFormat.u.raw_audio.buffer_size; + } + + // make sure the time base is reset + fResetPerformanceTimeBase = true; + + // update internal latency + _UpdateInternalLatency(fOutputPreferredFormat); + + return B_OK; } void -MultiAudioNode::GetFormat(media_format* format) +MultiAudioNode::_UpdateInternalLatency(const media_format& format) { - CALLED(); - if (format == NULL) - return; + // use one buffer length latency + fInternalLatency = format.u.raw_audio.buffer_size * 10000 / 2 + / ((format.u.raw_audio.format + & media_raw_audio_format::B_AUDIO_SIZE_MASK) + * format.u.raw_audio.channel_count) + / ((int32)(format.u.raw_audio.frame_rate / 100)); - format->type = B_MEDIA_RAW_AUDIO; - format->require_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS; - format->deny_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS; - format->u.raw_audio = media_raw_audio_format::wildcard; + PRINT((" internal latency = %lld\n",fInternalLatency)); + + SetEventLatency(fInternalLatency); } - 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 index 0186bffbdf..2a8b145d44 100644 --- a/src/add-ons/media/media-add-ons/multi_audio/MultiAudioNode.h +++ b/src/add-ons/media/media-add-ons/multi_audio/MultiAudioNode.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -188,6 +189,12 @@ private: int32& numParameters); void _ProcessMux(BDiscreteParameter* parameter, int32 index); + void _CreateFrequencyParameterGroup( + BParameterGroup* parentGroup, const char* name, + int32 parameterID, uint32 rateMask); + + status_t _SetNodeInputFrameRate(float frameRate); + void _UpdateInternalLatency(const media_format& format); private: status_t fInitStatus; @@ -195,7 +202,12 @@ private: BMediaAddOn* fAddOn; int32 fId; + BLocker fBufferLock; + BList fInputs; + bool fResetPerformanceTimeBase; + bigtime_t fPerformanceTimeBase; + uint64 fPerformanceTimeBaseFrames; bigtime_t fLatency; BList fOutputs;