From ea83877f0b04404ca20720d89179c4531e678a2d Mon Sep 17 00:00:00 2001 From: beveloper Date: Sun, 18 Aug 2002 12:43:06 +0000 Subject: [PATCH] David Shipman provides this update to the audio mixer add-on git-svn-id: file:///srv/svn/repos/haiku/trunk/current@808 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../media/media-add-ons/mixer/AudioMixer.cpp | 1071 ++++++++++------- .../media/media-add-ons/mixer/AudioMixer.h | 23 +- .../media-add-ons/mixer/FillMixBuffer.cpp | 452 +++++++ .../media/media-add-ons/mixer/IOStructures.h | 26 +- src/add-ons/media/media-add-ons/mixer/Jamfile | 1 + .../media/media-add-ons/mixer/MixerAddOn.cpp | 36 +- .../media/media-add-ons/mixer/readme.txt | 34 +- 7 files changed, 1151 insertions(+), 492 deletions(-) create mode 100644 src/add-ons/media/media-add-ons/mixer/FillMixBuffer.cpp diff --git a/src/add-ons/media/media-add-ons/mixer/AudioMixer.cpp b/src/add-ons/media/media-add-ons/mixer/AudioMixer.cpp index 831f92c321..c2bfc91c3d 100644 --- a/src/add-ons/media/media-add-ons/mixer/AudioMixer.cpp +++ b/src/add-ons/media/media-add-ons/mixer/AudioMixer.cpp @@ -8,11 +8,63 @@ #include "AudioMixer.h" #include "IOStructures.h" +#include #include #include +#include #include #include #include +#include + +#define MALLOC_DEBUG = 1; + +// + +mixer_input::mixer_input(media_input &input) +{ + + fInput = input; + + fEventOffset = 0; + fDataSize = 0; + + enabled = false; + + int channelCount = fInput.format.u.raw_audio.channel_count; + + fGainDisplay = new float[channelCount]; + fGainScale = new float[channelCount]; + + for (int i = 0; i < channelCount; i++) + { + fGainScale[i] = 1.0; + fGainDisplay[i] = 0.0; + } + + fMuteValue = 0; + fPanValue = 0; + + fGainDisplayLastChange = 0; + fMuteValueLastChange = 0; + fPanValueLastChange = 0; + +} + +mixer_input::~mixer_input() +{ + if (fGainDisplay) + delete fGainDisplay; + if (fGainScale) + delete fGainScale; + + if (fData) + rtm_free(fData); + + printf("mixer_input deleted\n"); + +} + // AudioMixer support classes @@ -54,10 +106,117 @@ AudioMixer::IsValidSource( media_source source) } +char * +StringForFormat(const media_format format) +{ + + char *name = new char[B_MEDIA_NAME_LENGTH]; + + sprintf(name, "%g kHz ", format.u.raw_audio.frame_rate / 1000.0); + + switch (format.u.raw_audio.format) + { + case media_raw_audio_format::B_AUDIO_FLOAT: + + strncat(name, "float", 5); + break; + + case media_raw_audio_format::B_AUDIO_SHORT: + + strncat(name, "16bit", 5); + break; + + } + + return name; + +} + +/* +// Interface +*/ + +BParameterWeb * +AudioMixer::BuildParameterWeb() +{ + + BParameterWeb *web = new BParameterWeb(); + + BParameterGroup *top = web->MakeGroup(Name()); // top level group + + BParameterGroup *masterGroup = top->MakeGroup("Master"); // 'Master' group split + BParameterGroup *channelsGroup = top->MakeGroup("Channels"); // 'Channels' group split + + BParameterGroup *headerGroup = masterGroup->MakeGroup("Master"); // The 'header group' for this split... + + headerGroup->MakeNullParameter(100, B_MEDIA_RAW_AUDIO, "Master Gain", B_WEB_BUFFER_OUTPUT); + headerGroup->MakeNullParameter(100, B_MEDIA_RAW_AUDIO, "44.1kHz 16bit", B_GENERIC); // set our initial display like R5's mixer... + headerGroup->MakeDiscreteParameter(2, B_MEDIA_RAW_AUDIO, "Mute", B_MUTE); + headerGroup->MakeContinuousParameter(3, B_MEDIA_RAW_AUDIO, "Gain", B_MASTER_GAIN, "dB", -60.0, 18.0, 1.0)->SetChannelCount(2); + headerGroup->MakeNullParameter(100, B_MEDIA_RAW_AUDIO, "To Output", B_WEB_BUFFER_OUTPUT); + + (headerGroup->ParameterAt(0))->AddOutput(headerGroup->ParameterAt(1)); + (headerGroup->ParameterAt(1))->AddOutput(headerGroup->ParameterAt(2)); + (headerGroup->ParameterAt(2))->AddOutput(headerGroup->ParameterAt(3)); + (headerGroup->ParameterAt(3))->AddOutput(headerGroup->ParameterAt(4)); + + int inputsCount = fMixerInputs.CountItems(); + + for (int which_input = 1; which_input < inputsCount; which_input ++) + { + + mixer_input *mixerInput = (mixer_input *)fMixerInputs.ItemAt(which_input); + + char *groupNumLabel = new char[4]; + + sprintf(groupNumLabel, "%d", which_input); + + BParameterGroup *group = channelsGroup->MakeGroup(groupNumLabel); + + int baseID = (which_input) * 65536; + + char *formatString = StringForFormat(mixerInput->fInput.format); + + group->MakeNullParameter(baseID + 100, B_MEDIA_RAW_AUDIO, mixerInput->fInput.name, B_WEB_BUFFER_OUTPUT); // name of attached producer + group->MakeNullParameter(baseID + 300, B_MEDIA_RAW_AUDIO, formatString, B_GENERIC); // display information about format + group->MakeDiscreteParameter(baseID + 3, B_MEDIA_RAW_AUDIO, "Mute", B_MUTE); + group->MakeContinuousParameter(baseID + 4, B_MEDIA_RAW_AUDIO, "Gain", B_GAIN, "dB", -60.0, 18.0, 1.0) + ->SetChannelCount(mixerInput->fInput.format.u.raw_audio.channel_count); + + bool panning = false; + + if (fOutput.destination != media_destination::null) + { + if (mixerInput->fInput.format.u.raw_audio.channel_count < fOutput.format.u.raw_audio.channel_count) + { + + group->MakeContinuousParameter(baseID + 1, B_MEDIA_RAW_AUDIO, "Pan", B_BALANCE, "L R", -1.0, 1.0, 0.02); + (group->ParameterAt(0))->AddOutput(group->ParameterAt(1)); + (group->ParameterAt(1))->AddOutput(group->ParameterAt(4)); + (group->ParameterAt(4))->AddOutput(group->ParameterAt(2)); + (group->ParameterAt(2))->AddOutput(group->ParameterAt(3)); + + panning = true; + + } + } + + if (!panning) + { + (group->ParameterAt(0))->AddOutput(group->ParameterAt(1)); + (group->ParameterAt(1))->AddOutput(group->ParameterAt(2)); + (group->ParameterAt(2))->AddOutput(group->ParameterAt(3)); + } + + } + + return web; +} + // 'structors... AudioMixer::AudioMixer(BMediaAddOn *addOn) - : BMediaNode("OBOS Mixer"), + : BMediaNode("Audio Mixer"), BBufferConsumer(B_MEDIA_RAW_AUDIO), BBufferProducer(B_MEDIA_RAW_AUDIO), BControllable(), @@ -69,7 +228,8 @@ AudioMixer::AudioMixer(BMediaAddOn *addOn) fStartTime(0), fNextEventTime(0), fFramesSent(0), fOutputEnabled(true), - fBufferGroup(NULL) + fBufferGroup(NULL), + fNextFreeID(1) { @@ -81,7 +241,7 @@ AudioMixer::AudioMixer(BMediaAddOn *addOn) fPrefOutputFormat.type = B_MEDIA_RAW_AUDIO; fPrefOutputFormat.u.raw_audio.format = media_raw_audio_format::wildcard.format; // B_AUDIO_FLOAT - fPrefOutputFormat.u.raw_audio.frame_rate = 44100; //media_raw_audio_format::wildcard.frame_rate; + fPrefOutputFormat.u.raw_audio.frame_rate = media_raw_audio_format::wildcard.frame_rate; fPrefOutputFormat.u.raw_audio.byte_order = (B_HOST_IS_BENDIAN) ? B_MEDIA_BIG_ENDIAN : B_MEDIA_LITTLE_ENDIAN; fPrefOutputFormat.u.raw_audio.buffer_size = media_raw_audio_format::wildcard.buffer_size; //2048 @@ -92,10 +252,10 @@ AudioMixer::AudioMixer(BMediaAddOn *addOn) fPrefInputFormat.type = B_MEDIA_RAW_AUDIO; fPrefInputFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT; - fPrefInputFormat.u.raw_audio.frame_rate = media_raw_audio_format::wildcard.frame_rate;//44100; // + fPrefInputFormat.u.raw_audio.frame_rate = 96000; //wildcard? fPrefInputFormat.u.raw_audio.byte_order = (B_HOST_IS_BENDIAN) ? B_MEDIA_BIG_ENDIAN : B_MEDIA_LITTLE_ENDIAN; - fPrefInputFormat.u.raw_audio.buffer_size = media_raw_audio_format::wildcard.buffer_size;//1024; // + fPrefInputFormat.u.raw_audio.buffer_size = 1024; fPrefInputFormat.u.raw_audio.channel_count = 2; //media_raw_audio_format::wildcard.channel_count; //fInput.source = media_source::null; @@ -109,23 +269,17 @@ AudioMixer::AudioMixer(BMediaAddOn *addOn) strcpy(fOutput.name, "Audio Mix"); - input_channel *freechannel = new input_channel; + fMasterGainScale = new float[2]; + fMasterGainDisplay = new float[2]; - freechannel->DataAvailable = false; + fMasterGainDisplay[0] = 0.0; + fMasterGainDisplay[1] = 0.0; - media_input freeinput; + fMasterGainScale[0] = 1.0; + fMasterGainScale[1] = 1.0; - freeinput.format = fPrefInputFormat; - freeinput.source = media_source::null; - freeinput.destination.port = ControlPort(); - freeinput.destination.id = 0; + fMasterGainDisplayLastChange = TimeSource()->Now(); - strcpy(freeinput.name, "Free Input"); - - freechannel->fInput = freeinput; - - fInputChannels.AddItem(freechannel); - } AudioMixer::~AudioMixer() @@ -160,27 +314,159 @@ AudioMixer::AddOn(int32 *internal_id) const // status_t -AudioMixer::GetParameterValue( int32 id, bigtime_t* last_change, - void* value, size_t* ioSize) +AudioMixer::GetParameterValue(int32 id, bigtime_t *last_change, + void *value, size_t *ioSize) { - switch (id) + int idGroup = int(id/65536.0); + + if (idGroup == 0) + { + if (id == 3) + { + float *valueOut = (float *)value; + valueOut[0] = fMasterGainDisplay[0]; + valueOut[1] = fMasterGainDisplay[1]; + + *last_change = fMasterGainDisplayLastChange; + *ioSize = 8; + + return B_OK; + } + else if (id == 2) + { + + int *valueOut = (int *)value; + *valueOut = fMasterMuteValue; + + *last_change = fMasterMuteValueLastChange; + *ioSize = 4; + + return B_OK; + } + } + else { - default: + mixer_input *mixerInput = (mixer_input *)fMixerInputs.ItemAt(idGroup); - return B_ERROR; + int idBase = idGroup * 65536; + + if (id == idBase + 4) + { + float *valueOut = (float *)value; + valueOut[0] = mixerInput->fGainDisplay[0]; + valueOut[1] = mixerInput->fGainDisplay[1]; + + *last_change = mixerInput->fGainDisplayLastChange; + *ioSize = 8; + + return B_OK; + } + else if (id == idBase + 3) + { + int *valueOut = (int *)value; + *valueOut = mixerInput->fMuteValue; + + *last_change = mixerInput->fMuteValueLastChange; + *ioSize = 4; + + return B_OK; + } - } - - return B_OK; + } + + return B_ERROR; } void -AudioMixer::SetParameterValue( int32 id, bigtime_t performance_time, - const void* value, size_t size) +AudioMixer::SetParameterValue(int32 id, bigtime_t when, + const void *value, size_t ioSize) { + + int idGroup = int(id/65536.0); + + if (idGroup == 0) + { + if (id == 3) + { + + float *inValue = (float *)value; + if (ioSize == 4) { + fMasterGainDisplay[0] = inValue[0]; + fMasterGainDisplay[1] = inValue[0]; + } + else if (ioSize == 8) { + fMasterGainDisplay[0] = inValue[0]; + fMasterGainDisplay[1] = inValue[1]; + } + + fMasterGainDisplayLastChange = when; + + BroadcastNewParameterValue(fMasterGainDisplayLastChange, + id, fMasterGainDisplay, ioSize); + + fMasterGainScale[0] = pow(2, (fMasterGainDisplay[0] / 6)); + fMasterGainScale[1] = pow(2, (fMasterGainDisplay[1] / 6)); + + } + else if (id == 2) + { + + int *inValue = (int *)value; + + fMasterMuteValue = *inValue; + + fMasterMuteValueLastChange = when; + + BroadcastNewParameterValue(fMasterMuteValueLastChange, + id, &fMasterMuteValue, ioSize); + + } + } + else + { + + mixer_input *mixerInput = (mixer_input *)fMixerInputs.ItemAt(idGroup); + + int idBase = idGroup * 65536; + + if (id == idBase + 4) + { + + float *inValue = (float *)value; + if (ioSize == 4) { + mixerInput->fGainDisplay[0] = inValue[0]; + mixerInput->fGainDisplay[1] = inValue[0]; + } + else if (ioSize == 8) { + mixerInput->fGainDisplay[0] = inValue[0]; + mixerInput->fGainDisplay[1] = inValue[1]; + } + + mixerInput->fGainDisplayLastChange = when; + + BroadcastNewParameterValue(mixerInput->fGainDisplayLastChange, + id, mixerInput->fGainDisplay, ioSize); + + mixerInput->fGainScale[0] = pow(2, (mixerInput->fGainDisplay[0] / 6)); + mixerInput->fGainScale[1] = pow(2, (mixerInput->fGainDisplay[1] / 6)); + + } + else if (id == idBase + 3) + { + int *inValue = (int *)value; + + mixerInput->fMuteValue = *inValue; + + mixerInput->fMuteValueLastChange = when; + + BroadcastNewParameterValue(mixerInput->fMuteValueLastChange, + id, &mixerInput->fMuteValue, ioSize); + } + + } } @@ -194,6 +480,8 @@ AudioMixer::HandleMessage( int32 message, const void *data, size_t size) // since we're using a mediaeventlooper, there shouldn't be any messages + printf("Received message to BBufferConsumer - this should not be!\n"); + return B_ERROR; } @@ -205,14 +493,14 @@ AudioMixer::AcceptFormat(const media_destination &dest, media_format *format) // we accept any raw audio if (IsValidDest(dest)) - { - if (format->type == B_MEDIA_RAW_AUDIO) - return B_OK; - else + { + if ((format->type != B_MEDIA_UNKNOWN_TYPE) && (format->type != B_MEDIA_RAW_AUDIO)) return B_MEDIA_BAD_FORMAT; + else + return B_OK; } else - return B_MEDIA_BAD_DESTINATION; + return B_MEDIA_BAD_DESTINATION; } @@ -220,9 +508,9 @@ status_t AudioMixer::GetNextInput(int32 *cookie, media_input *out_input) { - if (*cookie < fInputChannels.CountItems() && *cookie >= 0) + if ((*cookie < fMixerInputs.CountItems()) && (*cookie >= 0)) { - input_channel *channel = (input_channel *)fInputChannels.ItemAt(*cookie); + mixer_input *channel = (mixer_input *)fMixerInputs.ItemAt(*cookie); *out_input = channel->fInput; *cookie += 1; return B_OK; @@ -246,101 +534,80 @@ AudioMixer::BufferReceived(BBuffer *buffer) if (buffer) { - if (buffer->Header()->type == B_MEDIA_PARAMETERS) - { - ApplyParameterData(buffer->Data(), buffer->SizeUsed()); - buffer->Recycle(); - } - else - { - - media_header *hdr = buffer->Header(); - - // check input - - input_channel *channel; - - int inputcount = fInputChannels.CountItems(); - - for (int i = 0; i < inputcount; i++) + if (buffer->Header()->type == B_MEDIA_PARAMETERS) { - channel = (input_channel *)fInputChannels.ItemAt(i); - if (channel->fInput.destination.id == hdr->destination) - { - - i = inputcount; - - bigtime_t now = TimeSource()->Now(); - bigtime_t perf_time = hdr->start_time; - bigtime_t how_early = perf_time - fLatency - now; - - if ((RunMode() != B_OFFLINE) && - (RunMode() != B_RECORDING) && - (how_early < 0)) - { - - NotifyLateProducer(channel->fInput.source, -how_early, perf_time); - - } - else - { - - size_t sample_size; - - switch (channel->fInput.format.u.raw_audio.format) - { - - case (media_raw_audio_format::B_AUDIO_FLOAT): - case (media_raw_audio_format::B_AUDIO_INT): - - sample_size = 4; - break; - - case (media_raw_audio_format::B_AUDIO_SHORT): - - sample_size = 2; - break; - - case (media_raw_audio_format::B_AUDIO_CHAR): - case (media_raw_audio_format::B_AUDIO_UCHAR): - - sample_size = 1; - break; - - } - - // calculate total byte offset for writing to the ringbuffer - // this takes account of the Event offset, as well as the buffer's start time - - size_t total_offset = int(channel->fEventOffset + ((((perf_time - fNextEventTime) / 1000000) * - channel->fInput.format.u.raw_audio.frame_rate) * - sample_size * channel->fInput.format.u.raw_audio.channel_count)) % int(channel->fDataSize); - - char *indata = (char *)buffer->Data(); - - if (buffer->SizeUsed() > (channel->fDataSize - total_offset)) - { - memcpy(channel->fData + total_offset, indata, channel->fDataSize - total_offset); - memcpy(channel->fData, indata + (channel->fDataSize - total_offset), buffer->SizeUsed() - (channel->fDataSize - total_offset)); - } - else - memcpy(channel->fData + total_offset, indata, buffer->SizeUsed()); - - } - + printf("Control Buffer Received\n"); + ApplyParameterData(buffer->Data(), buffer->SizeUsed()); } - - buffer->Recycle(); - - if ((B_OFFLINE == RunMode()) && (B_DATA_AVAILABLE == channel->fProducerDataStatus)) + else { - RequestAdditionalBuffer(channel->fInput.source, buffer); - } + media_header *hdr = buffer->Header(); + + // check input + + mixer_input *channel; + + int inputcount = fMixerInputs.CountItems(); + + for (int i = 0; i < inputcount; i++) + { + + channel = (mixer_input *)fMixerInputs.ItemAt(i); + + if (channel->fInput.destination.id == hdr->destination) + { + + i = inputcount; + + bigtime_t now = TimeSource()->Now(); + bigtime_t perf_time = hdr->start_time; + bigtime_t how_early = perf_time - fLatency - now; + + if ((RunMode() != B_OFFLINE) && + (RunMode() != B_RECORDING) && + (how_early < 0)) + { + printf("Received buffer %d usecs late from %s\n", -how_early, channel->fInput.name); + NotifyLateProducer(channel->fInput.source, -how_early, perf_time); + } + else + { + + size_t sample_size = channel->fInput.format.u.raw_audio.format & media_raw_audio_format::B_AUDIO_SIZE_MASK; + + // calculate total byte offset for writing to the ringbuffer + // this takes account of the Event offset, as well as the buffer's start time + + size_t total_offset = int(channel->fEventOffset + ((((perf_time - fNextEventTime) / 1000000) * + channel->fInput.format.u.raw_audio.frame_rate) * + sample_size * channel->fInput.format.u.raw_audio.channel_count)) % int(channel->fDataSize); + + char *indata = (char *)buffer->Data(); + + if (buffer->SizeUsed() > (channel->fDataSize - total_offset)) + { + memcpy(channel->fData + total_offset, indata, channel->fDataSize - total_offset); + memcpy(channel->fData, indata + (channel->fDataSize - total_offset), buffer->SizeUsed() - (channel->fDataSize - total_offset)); + } + else + memcpy(channel->fData + total_offset, indata, buffer->SizeUsed()); + + } + + } + + if ((B_OFFLINE == RunMode()) && (B_DATA_AVAILABLE == channel->fProducerDataStatus)) + { + RequestAdditionalBuffer(channel->fInput.source, buffer); + } + } } + buffer->Recycle(); + } } @@ -352,8 +619,12 @@ AudioMixer::ProducerDataStatus( const media_destination &for_whom, if (IsValidDest(for_whom)) { media_timed_event event(at_performance_time, BTimedEventQueue::B_DATA_STATUS, - (void *)&for_whom, BTimedEventQueue::B_NO_CLEANUP, status, 0, NULL); + (void *)(&for_whom), BTimedEventQueue::B_NO_CLEANUP, status, 0, NULL); EventQueue()->AddEvent(event); + + // FIX_THIS + // the for_whom destination is not being sent correctly - verify in HandleEvent loop + } } @@ -365,8 +636,11 @@ AudioMixer::GetLatencyFor( const media_destination &for_whom, bigtime_t *out_lat if (! IsValidDest(for_whom)) return B_MEDIA_BAD_DESTINATION; + + // return our event latency - this includes our internal + downstream + // latency, but _not_ the scheduling latency - *out_latency = EventLatency() + SchedulingLatency(); + *out_latency = EventLatency(); *out_timesource = TimeSource()->ID(); return B_OK; @@ -380,32 +654,90 @@ AudioMixer::Connected( const media_source &producer, const media_destination &wh if (! IsValidDest(where)) return B_MEDIA_BAD_DESTINATION; - input_channel *channel = new input_channel; - - media_input freeinput; + char *name = out_input->name; + mixer_input *mixerInput; - freeinput.format = with_format; - freeinput.source = producer; - freeinput.destination.port = ControlPort(); - freeinput.destination.id = fInputChannels.CountItems(); + // We use fNextFreeID to keep track of the next available input + // 1-4 are our initial "free" inputs - strcpy(freeinput.name, "Used Input"); // should be the name of the upstream producer - - channel->fInput = freeinput; + if (fNextFreeID < 5) + { - fInputChannels.AddItem(channel); + mixerInput = (mixer_input *)fMixerInputs.ItemAt(fNextFreeID); - // update connection info + mixerInput->fInput.format = with_format; + mixerInput->fInput.source = producer; - *out_input = channel->fInput; + strcpy(mixerInput->fInput.name, name); - channel->fData = (char *)malloc(131072); // CHANGE_THIS - we only need to allocate space for as many - channel->fDataSize = 131072; // input buffers as would fill an output - plus one + } + else + { - memset(channel->fData, 0, channel->fDataSize); + media_input *newInput = new media_input; - channel->DataAvailable = true; + newInput->format = with_format; + newInput->source = producer; + newInput->destination.port = ControlPort(); + newInput->destination.id = fNextFreeID; + newInput->node = Node(); + + strcpy(newInput->name, name); + + mixerInput = new mixer_input(*newInput); + + fMixerInputs.AddItem(mixerInput, fNextFreeID); + + } + // Recalculate the next available input + + bool idSet = 0; + int testID = fNextFreeID; + int inputsCount = fMixerInputs.CountItems(); + + for (int testID = fNextFreeID; testID < inputsCount; testID ++) + { + + mixer_input *testInput = (mixer_input *)fMixerInputs.ItemAt(testID); + + if (testID < 5) + { + if (testInput->fInput.source == media_source::null) { + fNextFreeID = testID; + idSet = 1; + testID = inputsCount; + } + } + else + { + if (testInput->fInput.destination.id > testID) + { + fNextFreeID = testID; + idSet = 1; + testID = inputsCount; + } + + } + + } + if (!idSet) + fNextFreeID = inputsCount; + + // Set up the ringbuffer in which to write incoming data + + mixerInput->fData = (char *)rtm_alloc(NULL, 65536); // CHANGE_THIS - we only need to allocate space for as many + mixerInput->fDataSize = 65536; // input buffers as would fill an output - plus one + + memset(mixerInput->fData, 0, mixerInput->fDataSize); + + mixerInput->enabled = true; + + *out_input = mixerInput->fInput; + + fWeb = BuildParameterWeb(); + SetParameterWeb(fWeb); + return B_OK; } @@ -414,33 +746,75 @@ void AudioMixer::Disconnected( const media_source &producer, const media_destination &where) { - input_channel *channel; + // One of our inputs has been disconnected + // If its one of our initial "Free" inputs, we need to set it back to its original state + // Otherwise, we can delete the input - // disconnected - remove the input from our list + mixer_input *mixerInput; - int inputcount = fInputChannels.CountItems(); + int inputcount = fMixerInputs.CountItems(); - for (int i = 0; i < inputcount; i++) + for (int i = 1; i < inputcount; i++) { - channel = (input_channel *)fInputChannels.ItemAt(i); - if (channel->fInput.destination == where) - { + mixerInput = (mixer_input *)fMixerInputs.ItemAt(i); + if (mixerInput->fInput.destination == where) + { + if (mixerInput->fInput.source != media_source::null) // input already disconnected? + { + if ((mixerInput->fInput.destination.id > 0) && (mixerInput->fInput.destination.id < 5)) + { + mixerInput->fInput.source = media_source::null; + strcpy(mixerInput->fInput.name, "Free"); + mixerInput->fInput.format = fPrefInputFormat; + + rtm_free(mixerInput->fData); + + mixerInput->fData = NULL; + + mixerInput->fEventOffset = 0; + mixerInput->fDataSize = 0; + + delete mixerInput->fGainDisplay, mixerInput->fGainScale; + + mixerInput->fMuteValue = 0; + mixerInput->fPanValue = 0.0; + + mixerInput->enabled = false; + + int channelCount = mixerInput->fInput.format.u.raw_audio.channel_count; + + mixerInput->fGainDisplay = new float[channelCount]; + mixerInput->fGainScale = new float[channelCount]; + + for (int i = 0; i < channelCount; i++) + { + mixerInput->fGainScale[i] = 1.0; + mixerInput->fGainDisplay[i] = 0.0; + } + + } + else if (mixerInput->fInput.destination.id > 4) + { + + delete mixerInput; + fMixerInputs.RemoveItem(mixerInput); + + } + } + + fWeb = BuildParameterWeb(); + SetParameterWeb(fWeb); + + if (mixerInput->fInput.destination.id < fNextFreeID) + fNextFreeID = mixerInput->fInput.destination.id; i = inputcount; - - // channel->DataAvailable = false; - // memset(channel->fData, 0, channel->fDataSize); - - delete channel->fData; - - delete channel; - - fInputChannels.RemoveItem(channel); } + else printf("Disconnection requested for input with no source\n"); - } - + } + } status_t @@ -452,6 +826,7 @@ AudioMixer::FormatChanged( const media_source &producer, const media_destination // and change our ringbuffer size if needs be // Not supported (yet) + printf("Format changed\n"); return B_ERROR; } @@ -602,7 +977,13 @@ AudioMixer::PrepareToConnect(const media_source &what, const media_destination & if (format->type != B_MEDIA_RAW_AUDIO) return B_MEDIA_BAD_FORMAT; - else if ((format->u.raw_audio.format != media_raw_audio_format::B_AUDIO_FLOAT) && // currently support floating point + // CHANGE_THIS - we're messing around with formats, need to clean up + // still need to check u.raw_audio.format + + if (format->u.raw_audio.format == media_raw_audio_format::wildcard.format) + format->u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT; + + if ((format->u.raw_audio.format != media_raw_audio_format::B_AUDIO_FLOAT) && // currently support floating point (format->u.raw_audio.format != media_raw_audio_format::B_AUDIO_SHORT)) // and 16bit int audio return B_MEDIA_BAD_FORMAT; @@ -610,11 +991,10 @@ AudioMixer::PrepareToConnect(const media_source &what, const media_destination & // or else the consumer is prone to divide-by-zero errors if(format->u.raw_audio.frame_rate == media_raw_audio_format::wildcard.frame_rate) - format->u.raw_audio.frame_rate = fPrefOutputFormat.u.raw_audio.frame_rate; + format->u.raw_audio.frame_rate = 44100; if(format->u.raw_audio.channel_count == media_raw_audio_format::wildcard.channel_count) - format->u.raw_audio.channel_count = fPrefOutputFormat.u.raw_audio.channel_count; -// format->u.raw_audio.channel_count = 2; + format->u.raw_audio.channel_count = 2; if(format->u.raw_audio.byte_order == media_raw_audio_format::wildcard.byte_order) format->u.raw_audio.byte_order = fPrefOutputFormat.u.raw_audio.byte_order; @@ -632,16 +1012,13 @@ AudioMixer::PrepareToConnect(const media_source &what, const media_destination & return B_OK; } + void AudioMixer::Connect( status_t error, const media_source &source, const media_destination &dest, const media_format &format, char *io_name) { - //FPRINTF(stderr, "AudioMixer::Connect\n"); + printf("AudioMixer::Connect\n"); - // 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. - // we need to check which output dest refers to - we only have one for now if (error) @@ -664,33 +1041,23 @@ AudioMixer::Connect( status_t error, const media_source &source, const media_des // we need at least the length of a full output buffer's latency (I think?) - size_t sample_size; - - switch (fOutput.format.u.raw_audio.format) - { - - case (media_raw_audio_format::B_AUDIO_FLOAT): - case (media_raw_audio_format::B_AUDIO_INT): - - sample_size = 4; - break; - - case (media_raw_audio_format::B_AUDIO_SHORT): - - sample_size = 2; - break; - - case (media_raw_audio_format::B_AUDIO_CHAR): - case (media_raw_audio_format::B_AUDIO_UCHAR): - - sample_size = 1; - break; - - } + size_t sample_size = fOutput.format.u.raw_audio.format & media_raw_audio_format::B_AUDIO_SIZE_MASK; size_t framesPerBuffer = (fOutput.format.u.raw_audio.buffer_size / sample_size) / fOutput.format.u.raw_audio.channel_count; - fInternalLatency = (framesPerBuffer / fOutput.format.u.raw_audio.frame_rate) * 1000000.0; + //fInternalLatency = (framesPerBuffer / fOutput.format.u.raw_audio.frame_rate); // test * 1000000 + + void *mouse = malloc(fOutput.format.u.raw_audio.buffer_size); + + bigtime_t latency_start = TimeSource()->RealTime(); + FillMixBuffer(mouse, fOutput.format.u.raw_audio.buffer_size); + bigtime_t latency_end = TimeSource()->RealTime(); + + fInternalLatency = latency_end - latency_start; + printf("Latency set at %d usecs\n", fInternalLatency); + + delete mouse; + // might need to tweak the latency @@ -698,10 +1065,10 @@ AudioMixer::Connect( status_t error, const media_source &source, const media_des // reset our buffer duration, etc. to avoid later calculations // crashes w/ divide-by-zero when connecting to a variety of nodes... - if (fOutput.format.u.raw_audio.frame_rate == media_raw_audio_format::wildcard.frame_rate) - { - fOutput.format.u.raw_audio.frame_rate = 44100; - } +// if (fOutput.format.u.raw_audio.frame_rate == media_raw_audio_format::wildcard.frame_rate) +// { +// fOutput.format.u.raw_audio.frame_rate = 44100; +// } bigtime_t duration = bigtime_t(1000000) * framesPerBuffer / bigtime_t(fOutput.format.u.raw_audio.frame_rate); SetBufferDuration(duration); @@ -713,10 +1080,12 @@ AudioMixer::Connect( status_t error, const media_source &source, const media_des if (!fBufferGroup) AllocateBuffers(); } + void AudioMixer::Disconnect(const media_source &what, const media_destination &where) { + printf("AudioMixer::Disconnect\n"); // Make sure that our connection is the one being disconnected if ((where == fOutput.destination) && (what == fOutput.source)) // change later for multisource outputs { @@ -728,54 +1097,33 @@ AudioMixer::Disconnect(const media_source &what, const media_destination &where) } + void AudioMixer::LateNoticeReceived(const media_source &what, bigtime_t how_much, bigtime_t performance_time) { - // If we're late, we need to catch up. Respond in a manner appropriate to our - // current run mode. + // We've produced some late buffers... Increase Latency + // is the only runmode in which we can do anything about this + if (what == fOutput.source) - { - if (RunMode() == B_RECORDING) + if (RunMode() == B_INCREASE_LATENCY) { - // 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); - } - 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. - // this should be the "drop data" mode - decrease precision could repeat the buffer ? - - //size_t nSamples = fOutput.format.u.raw_audio.buffer_size / sizeof(float); - //fFramesSent += (nSamples / 2); + + printf("Late notice received. Buffer was %d usecs late\n", how_much); - } - } } + void AudioMixer::EnableOutput(const media_source &what, bool enabled, int32 *_deprecated_) { - // 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. + // right now we've only got one output... check this against the supplied + // media_source and set its state accordingly... + if (what == fOutput.source) { fOutputEnabled = enabled; @@ -794,14 +1142,35 @@ AudioMixer::NodeRegistered() Run(); fOutput.node = Node(); - fInput.node = Node(); + + + for (int i = 0; i < 5; i++) + { + + media_input *newInput = new media_input; + + newInput->format = fPrefInputFormat; + newInput->source = media_source::null; + newInput->destination.port = ControlPort(); + newInput->destination.id = i; + newInput->node = Node(); + strcpy(newInput->name, "Free"); // + + mixer_input *mixerInput = new mixer_input(*newInput); + + fMixerInputs.AddItem(mixerInput); + + } SetPriority(120); + fWeb = BuildParameterWeb(); + SetParameterWeb(fWeb); } + void AudioMixer::HandleEvent( const media_timed_event *event, bigtime_t lateness, bool realTimeEvent = false) @@ -821,184 +1190,8 @@ AudioMixer::HandleEvent( const media_timed_event *event, bigtime_t lateness, if (outbuffer) { - - int32 outChannels = fOutput.format.u.raw_audio.channel_count; - - // we have an output buffer - now it needs to be filled! - - switch (fOutput.format.u.raw_audio.format) - { - case media_raw_audio_format::B_AUDIO_FLOAT: - { - float *outdata = (float*)outbuffer->Data(); - - memset(outdata, 0, sizeof(outdata)); - - int numsamples = int(fOutput.format.u.raw_audio.buffer_size / sizeof(float)); - - for (int c = 0; c < fInputChannels.CountItems(); c++) - { - input_channel *channel = (input_channel *)fInputChannels.ItemAt(c); - - if (channel->DataAvailable == true) // formerly DataAvailable - { - int32 inChannels = channel->fInput.format.u.raw_audio.channel_count; - bool split = outChannels > inChannels; - bool mix = outChannels < inChannels; - - - if (fOutput.format.u.raw_audio.frame_rate == channel->fInput.format.u.raw_audio.frame_rate) - { - switch(channel->fInput.format.u.raw_audio.format) - { - - case media_raw_audio_format::B_AUDIO_FLOAT: - - if (split) - { } - else if (mix) - { - for (int s = 0; s < numsamples; s++) - { - // outdata[s] = (float *)(channel->fData)[s] - } - } - else - { - for (int s = 0; s < numsamples; s++) - { - float *indata = (float *)channel->fData; - int os = ((channel->fEventOffset / 4) + s) % int(channel->fDataSize / 4); - //outdata[s] = indata[ int(((channel->fEventOffset) * 0.25) + s) % int(channel->fDataSize / 4)]; - outdata[s] = float(indata[os]); - // fHost->PostMessage(int32(32767 * indata[os])); - //*(float *)channel->fData[channel->fEventOffset/4) + s) % channel->fDataSize]; - } - - channel->fEventOffset = (channel->fEventOffset + fOutput.format.u.raw_audio.buffer_size) % - channel->fDataSize; - - } - - break; - - case media_raw_audio_format::B_AUDIO_SHORT: - - if (split) - { } - else if (mix) - { - - } - else - { - for (int s = 0; s < numsamples; s++) - { - int16 *indata = (int16 *)channel->fData; - int os = ((channel->fEventOffset / 2) + s) % int(channel->fDataSize / 2); - //outdata[s] = indata[ int(((channel->fEventOffset) * 0.25) + s) % int(channel->fDataSize / 4)]; - outdata[s] = float(indata[os] / 32767.0); - //*(float *)channel->fData[channel->fEventOffset/4) + s) % channel->fDataSize]; - } - - channel->fEventOffset = (channel->fEventOffset + fOutput.format.u.raw_audio.buffer_size / 2) % - channel->fDataSize; - - } - - break; - - } // end formatswitch - - } // end same-samplerate condition - - } // data - - } // chan-loop - } // cur-case - - case media_raw_audio_format::B_AUDIO_SHORT: - - int16 *outdata = (int16*)outbuffer->Data(); - int numsamples = int(fOutput.format.u.raw_audio.buffer_size / sizeof(int16)); - - memset(outdata, 0, outbuffer->SizeAvailable()); - - for (int c = 0; c < fInputChannels.CountItems(); c++) - { - input_channel *channel = (input_channel *)fInputChannels.ItemAt(c); - - if (channel->DataAvailable == true) // only use if there are buffers waiting - { // CHANGE_THIS - we still get looped audio when there are no buffers - int32 inChannels = channel->fInput.format.u.raw_audio.channel_count; - bool split = outChannels > inChannels; - bool mix = outChannels < inChannels; - - if (fOutput.format.u.raw_audio.frame_rate == channel->fInput.format.u.raw_audio.frame_rate) - { - switch(channel->fInput.format.u.raw_audio.format) - { - - case media_raw_audio_format::B_AUDIO_FLOAT: - - if (split) - { } - else if (mix) - { - for (int s = 0; s < numsamples; s++) - { - // outdata[s] = (float *)(channel->fData)[s] - } - } - else - { - for (int s = 0; s < numsamples; s++) - { - float *indata = (float *)channel->fData; - int os = ((channel->fEventOffset / 4) + s) % int(channel->fDataSize / 4); - outdata[s] = outdata[s] + int16(32767 * indata[os]); - } - - channel->fEventOffset = (channel->fEventOffset + fOutput.format.u.raw_audio.buffer_size * 2) % - channel->fDataSize; - - } - - break; - - case media_raw_audio_format::B_AUDIO_SHORT: - - if (split) - { } - else if (mix) - { - - } - else - { - for (int s = 0; s < numsamples; s++) - { - int16 *indata = (int16 *)channel->fData; - int os = ((channel->fEventOffset / 2) + s) % int(channel->fDataSize / 2); - outdata[s] = outdata[s] + indata[os]; - } - - channel->fEventOffset = (channel->fEventOffset + (fOutput.format.u.raw_audio.buffer_size / 2)) % - channel->fDataSize; - - } - - break; - - } - } - - } - - } - } - // for each input, check the format, then mix into output buffer + FillMixBuffer(outbuffer->Data(), outbuffer->SizeAvailable()); media_header *outheader = outbuffer->Header(); outheader->type = B_MEDIA_RAW_AUDIO; @@ -1016,7 +1209,7 @@ AudioMixer::HandleEvent( const media_timed_event *event, bigtime_t lateness, bigtime_t stamp; if (RunMode() == B_RECORDING) stamp = event->event_time; // this is actually the same as the other modes, since we're using - else // a timedevent queue and adding events at + else // a timedevent queue and adding events at fNextEventTime { // we're in a live performance mode @@ -1033,35 +1226,19 @@ AudioMixer::HandleEvent( const media_timed_event *event, bigtime_t lateness, status_t err = SendBuffer(outbuffer, fOutput.destination); if(err != B_OK) + { outbuffer->Recycle(); + printf("Could not send buffer to output : %s\n", fOutput.name); + } } + + else + printf("Failed to allocate a buffer\n"); // even if we didn't get a buffer allocated, we still need to send the next event - size_t sample_size; - - switch (fOutput.format.u.raw_audio.format) - { - - case (media_raw_audio_format::B_AUDIO_FLOAT): - case (media_raw_audio_format::B_AUDIO_INT): - - sample_size = 4; - break; - - case (media_raw_audio_format::B_AUDIO_SHORT): - - sample_size = 2; - break; - - case (media_raw_audio_format::B_AUDIO_CHAR): - case (media_raw_audio_format::B_AUDIO_UCHAR): - - sample_size = 1; - break; - - } + size_t sample_size = fOutput.format.u.raw_audio.format & media_raw_audio_format::B_AUDIO_SIZE_MASK; int framesperbuffer = (fOutput.format.u.raw_audio.buffer_size / (sample_size * fOutput.format.u.raw_audio.channel_count)); @@ -1073,9 +1250,7 @@ AudioMixer::HandleEvent( const media_timed_event *event, bigtime_t lateness, media_timed_event nextBufferEvent(fNextEventTime, BTimedEventQueue::B_HANDLE_BUFFER); EventQueue()->AddEvent(nextBufferEvent); - - - + } @@ -1119,22 +1294,32 @@ AudioMixer::HandleEvent( const media_timed_event *event, bigtime_t lateness, case BTimedEventQueue::B_DATA_STATUS: { + + printf("DataStatus message\n"); - input_channel *channel; + mixer_input *mixerInput; - int inputcount = fInputChannels.CountItems(); + int inputcount = fMixerInputs.CountItems(); for (int i = 0; i < inputcount; i++) { - channel = (input_channel *)fInputChannels.ItemAt(i); - if (channel->fInput.destination == (media_destination &)event->pointer) + mixerInput = (mixer_input *)fMixerInputs.ItemAt(i); + if (mixerInput->fInput.destination == (media_destination &)event->pointer) { + + printf("Valid DatasStatus destination\n"); + + mixerInput->fProducerDataStatus = event->data; + + if (mixerInput->fProducerDataStatus == B_DATA_AVAILABLE) + printf("B_DATA_AVAILABLE\n"); + else if (mixerInput->fProducerDataStatus == B_DATA_NOT_AVAILABLE) + printf("B_DATA_NOT_AVAILABLE\n"); + else if (mixerInput->fProducerDataStatus == B_PRODUCER_STOPPED) + printf("B_PRODUCER_STOPPED\n"); + i = inputcount; - channel->fProducerDataStatus = event->data; - if (channel->fProducerDataStatus == B_DATA_AVAILABLE) - channel->DataAvailable = true; - else - channel->DataAvailable = false; + } } break; @@ -1158,7 +1343,7 @@ AudioMixer::AllocateBuffers() // allocate enough buffers to span our downstream latency, plus one size_t size = fOutput.format.u.raw_audio.buffer_size; - int32 count = int32(fLatency / BufferDuration() + 1 + 1); + int32 count = int32((fLatency / (BufferDuration() + 1)) + 1); fBufferGroup = new BBufferGroup(size, count); diff --git a/src/add-ons/media/media-add-ons/mixer/AudioMixer.h b/src/add-ons/media/media-add-ons/mixer/AudioMixer.h index d8e266c1ca..6b57f642b1 100644 --- a/src/add-ons/media/media-add-ons/mixer/AudioMixer.h +++ b/src/add-ons/media/media-add-ons/mixer/AudioMixer.h @@ -10,7 +10,7 @@ // forward declarations -class input_channel; +class mixer_input; // includes @@ -36,15 +36,17 @@ class AudioMixer : AudioMixer(BMediaAddOn *addOn); ~AudioMixer(); - // Our own (AudioMixer) methods + // AudioMixer support bool IsValidDest( media_destination dest); bool IsValidSource( media_source source); + - status_t ResolveDest(int32 dest, input_channel *outchannel); - status_t ResolveDest(media_destination dest, input_channel *outchannel); + BParameterWeb * BuildParameterWeb(); // used to create the initial 'master' web + void MakeWebForInput(char *name, media_format format); void AllocateBuffers(); + status_t FillMixBuffer(void *outbuffer, size_t size); // BMediaNode methods @@ -185,9 +187,15 @@ class AudioMixer : private: thread_id fThread; // for launching mixthread - - media_input fInput; // descriptor of single input (temp) + media_output fOutput; // single output (temp) + float * fMasterGainScale; // array used for scaling output - _not_ for display! + float * fMasterGainDisplay; // array of volume values for display purposes + bigtime_t fMasterGainDisplayLastChange; + + int fMasterMuteValue; + bigtime_t fMasterMuteValueLastChange; + BMediaAddOn * fAddOn; BParameterWeb * fWeb; // local pointer to parameterweb @@ -200,8 +208,9 @@ class AudioMixer : media_format fPrefInputFormat, fPrefOutputFormat; BBufferGroup * fBufferGroup; + BList fMixerInputs; - BList fInputChannels; + int fNextFreeID; }; diff --git a/src/add-ons/media/media-add-ons/mixer/FillMixBuffer.cpp b/src/add-ons/media/media-add-ons/mixer/FillMixBuffer.cpp new file mode 100644 index 0000000000..3b521e5385 --- /dev/null +++ b/src/add-ons/media/media-add-ons/mixer/FillMixBuffer.cpp @@ -0,0 +1,452 @@ +#include "AudioMixer.h" +#include "IOStructures.h" + +#include +#include +#include +#include +#include +#include +#include + +status_t +AudioMixer::FillMixBuffer(void *outbuffer, size_t ioSize) +{ + + int32 outChannels = fOutput.format.u.raw_audio.channel_count; + + // we have an output buffer - now it needs to be filled! + + switch (fOutput.format.u.raw_audio.format) + { + case media_raw_audio_format::B_AUDIO_FLOAT: + { + + float *outdata = (float*)outbuffer; + + memset(outdata, 0, ioSize); // CHANGE_THIS + + int sampleCount = int(fOutput.format.u.raw_audio.buffer_size / sizeof(float)); + + for (int s = 0; s < sampleCount; s++) + { + outdata[s] = 0.0; // CHANGE_THIS + } + + int mixerInputCount = fMixerInputs.CountItems(); + + for (int c = 0; c < mixerInputCount; c++) + { + mixer_input *channel = (mixer_input *)fMixerInputs.ItemAt(c); + + if (channel->enabled == true) // still broken... FIX_THIS + { + int32 inChannels = channel->fInput.format.u.raw_audio.channel_count; + bool split = outChannels > inChannels; + bool mix = outChannels < inChannels; + + if (fOutput.format.u.raw_audio.frame_rate == channel->fInput.format.u.raw_audio.frame_rate) + { + switch(channel->fInput.format.u.raw_audio.format) + { + case media_raw_audio_format::B_AUDIO_FLOAT: + { + + float *indata = (float *)channel->fData; + + if (split) + { } // ADD_THIS + else if (mix) + { } // ADD_THIS + else + { + int baseOffset = channel->fEventOffset / 4; + int maxOffset = int(channel->fDataSize / 4); + + int offsetWrap = maxOffset - baseOffset; + + int inputSample = baseOffset; + + for (int s = 0; s < sampleCount; s++) + { + + outdata[s] = indata[inputSample]; + indata[inputSample] = 0; + + if (s == offsetWrap) + inputSample = 0; + else + inputSample ++; + + } + + channel->fEventOffset = (channel->fEventOffset + fOutput.format.u.raw_audio.buffer_size) % + channel->fDataSize; + + } + + break; + } + + case media_raw_audio_format::B_AUDIO_SHORT: + { + + int16 *indata = (int16 *)channel->fData; + + if (split) + { } + else if (mix) + { } + else + { + + int baseOffset = channel->fEventOffset / 2; + int maxOffset = int(channel->fDataSize / 2); + + int offsetWrap = maxOffset - baseOffset; + + int inputSample = baseOffset; + + for (int s = 0; s < sampleCount; s++) + { + + outdata[s] = outdata[s] + (indata[inputSample] / 32768.0); // CHANGE_THIS indata[inputSample] = 0; + + if (s == offsetWrap) + inputSample = 0; + else + inputSample ++; + + } + + channel->fEventOffset = (channel->fEventOffset + fOutput.format.u.raw_audio.buffer_size / 2) % + channel->fDataSize; + + } + + break; + } + + } // input format + + } // samplerate + + } // data available + + } // channel loop + + /* + // The buffer is done - we still need to scale for the MainVolume... + // then check to see if anything is clipping, adjust if needed + */ + + for (int frameStart = 0; frameStart < sampleCount; frameStart += outChannels) + { + for (int channel = 0; channel < outChannels; channel ++) + { + int sample = frameStart + channel; + outdata[sample] = outdata[sample] * fMasterGainScale[channel]; + + //if (outdata[sample] > 1.0) + // outdata[sample] = 1.0; + // else if (outdata[sample] < -1.0) + // outdata[sample] = -1.0; + + } + } + + break; + + } // cur-case + + case media_raw_audio_format::B_AUDIO_SHORT: + { + + int16 *outdata = (int16*)outbuffer; + int sampleCount = int(fOutput.format.u.raw_audio.buffer_size / sizeof(int16)); + + int *clipoffset = new int[sampleCount]; // keep a running tally of +/- clipping so we don't get rollaround distortion + // we only need this for int/char audio types - not float + + memset(outdata, 0, ioSize); + memset(clipoffset, 0, sizeof(int) * sampleCount); + + // for (int s = 0; s < sampleCount; s++) + // { + // clipoffset[s] = 0; + // } + + int mixerInputs = fMixerInputs.CountItems(); + + for (int c = 0; c < mixerInputs; c++) + { + mixer_input *channel = (mixer_input *)fMixerInputs.ItemAt(c); + + if (channel->enabled == true) // only use if there are buffers waiting (seems to be broken atm...) + { + + // if (channel->fProducerDataStatus == B_DATA_AVAILABLE) + // printf("B_DATA_AVAILABLE\n"); + // else if (channel->fProducerDataStatus == B_DATA_NOT_AVAILABLE) + // printf("B_DATA_NOT_AVAILABLE\n"); + // else if (channel->fProducerDataStatus == B_PRODUCER_STOPPED) + // printf("B_PRODUCER_STOPPED\n"); + + int32 inChannels = channel->fInput.format.u.raw_audio.channel_count; + bool split = outChannels > inChannels; + bool mix = outChannels < inChannels; + + if (fOutput.format.u.raw_audio.frame_rate == channel->fInput.format.u.raw_audio.frame_rate) + { + switch(channel->fInput.format.u.raw_audio.format) + { + + case media_raw_audio_format::B_AUDIO_FLOAT: + { + + float *indata = (float *)channel->fData; + + if (split) + { + + int baseOffset = channel->fEventOffset / 4; + int maxOffset = int(channel->fDataSize / 4); + + int offsetWrap = maxOffset - baseOffset; + + int inputSample = baseOffset; + + for (int s = 0; s < sampleCount; s += 2) + { + + if ((outdata[s] + int16(32767 * indata[inputSample])) > 32767) + clipoffset[s] = clipoffset[s] + 1; + else if ((outdata[s] + int16(32767 * indata[inputSample])) < -32768) + clipoffset[s] = clipoffset[s] - 1; + + if ((outdata[s + 1] + int16(32767 * indata[inputSample])) > 32767) + clipoffset[s + 1] = clipoffset[s + 1] + 1; + else if ((outdata[s] + int16(32767 * indata[inputSample])) < -32768) + clipoffset[s + 1] = clipoffset[s + 1] - 1; + + outdata[s] = outdata[s] + int16(32767 * indata[inputSample]); // CHANGE_THIS mixing + outdata[s + 1] = outdata[s]; + + indata[inputSample] = 0; + + if (s == offsetWrap) + inputSample = 0; + else + inputSample ++; + } + + } + else if (mix) + {} + else + { + int baseOffset = channel->fEventOffset / 4; + int maxOffset = int(channel->fDataSize / 4); + + int offsetWrap = maxOffset - baseOffset; + + int inputSample = baseOffset; + + for (int frameStart = 0; frameStart < sampleCount; frameStart += outChannels) + { + for (int chan = 0; chan < outChannels; chan ++) + { + int sample = frameStart + chan; + + int inputValue = (32767 * indata[inputSample] * channel->fGainScale[chan]) + outdata[sample]; + + if (inputValue > 32767) + clipoffset[sample] = clipoffset[sample] + 1; + else if (inputValue < -32768) + clipoffset[sample] = clipoffset[sample] - 1; + + outdata[sample] = inputValue; // CHANGE_THIS + indata[inputSample] = 0; + + if (sample == offsetWrap) + inputSample = 0; + else + inputSample ++; + + } + + } + + channel->fEventOffset += (fOutput.format.u.raw_audio.buffer_size * 2); + if (channel->fEventOffset >= channel->fDataSize) + channel->fEventOffset -= channel->fDataSize; + + } + + break; + + } + + case media_raw_audio_format::B_AUDIO_SHORT: + { + + int16 *indata = (int16 *)channel->fData; + + if (split) + { } + else if (mix) + { } + else + { + int baseOffset = channel->fEventOffset / 2; + int maxOffset = int(channel->fDataSize / 2); + + int offsetWrap = maxOffset - baseOffset; + + int inputSample = baseOffset; + + for (int frameStart = 0; frameStart < sampleCount; frameStart += outChannels) + { + for (int chan = 0; chan < outChannels; chan ++) + { + int sample = frameStart + chan; + + int clipTest = (outdata[sample] + (indata[inputSample] * channel->fGainScale[chan])); + + if (clipTest > 32767) + clipoffset[sample] = clipoffset[sample] + 1; + else if (clipTest < -32768) + clipoffset[sample] = clipoffset[sample] - 1; + + outdata[sample] = int16(outdata[sample] + (indata[inputSample] * channel->fGainScale[chan])); + indata[inputSample] = 0; + + if (sample == offsetWrap) + inputSample = 0; + else + inputSample ++; + + } + } + + channel->fEventOffset = (channel->fEventOffset + fOutput.format.u.raw_audio.buffer_size); + if (channel->fEventOffset >= channel->fDataSize) + channel->fEventOffset -= channel->fDataSize; + + } + + break; + } + + case media_raw_audio_format::B_AUDIO_INT: + { + + int32 *indata = (int32 *)channel->fData; + + if (split) + { + + int baseOffset = channel->fEventOffset / 4; + int maxOffset = int(channel->fDataSize / 4); + + int offsetWrap = maxOffset - baseOffset; + + int inputSample = baseOffset; + + for (int s = 0; s < sampleCount; s += 2) + { + + if ((outdata[s] + (indata[inputSample] / 65536)) > 32767) + clipoffset[s] = clipoffset[s] + 1; + else if ((outdata[s] + (indata[inputSample] / 65536)) < -32768) + clipoffset[s] = clipoffset[s] - 1; + + if ((outdata[s + 1] + (indata[inputSample] / 65536)) > 32767) + clipoffset[s + 1] = clipoffset[s + 1] + 1; + else if ((outdata[s + 1] + (indata[inputSample] / 65536)) < -32768) + clipoffset[s + 1] = clipoffset[s + 1] - 1; + + outdata[s] = int16(outdata[s] + (indata[inputSample] / 65535)); // CHANGE_THIS mixing + outdata[s + 1] = outdata[s]; + + indata[inputSample] = 0; + + if (s == offsetWrap) + inputSample = 0; + else + inputSample ++; + } + + } + else if (mix) + { } + else + { + + int baseOffset = channel->fEventOffset / 4; + int maxOffset = int(channel->fDataSize / 4); + + int offsetWrap = maxOffset - baseOffset; + + int inputSample = baseOffset; + + for (int s = 0; s < sampleCount; s++) + { + + if ((outdata[s] + (indata[inputSample] / 65536)) > 32767) + clipoffset[s] = clipoffset[s] + 1; + else if ((outdata[s] + (indata[inputSample] / 65536)) < -32768) + clipoffset[s] = clipoffset[s] - 1; + + outdata[s] = int16(outdata[s] + (indata[inputSample] / 65536)); // CHANGE_THIS mixing + indata[inputSample] = 0; + + if (s == offsetWrap) + inputSample = 0; + else + inputSample ++; + + } + + } + + channel->fEventOffset = (channel->fEventOffset + (fOutput.format.u.raw_audio.buffer_size * 2)) % + channel->fDataSize; + + break; + } + + } + } + + } + + } + + // use our clipoffset to determine correct limits + for (int frameStart = 0; frameStart < sampleCount; frameStart += outChannels) + { + for (int channel = 0; channel < outChannels; channel ++) + { + int sample = frameStart + channel; + + int scaledSample = (outdata[sample] + (65536 * clipoffset[sample])) * fMasterGainScale[channel]; + + if (scaledSample < -32768) + outdata[sample] = -32768; + else if (scaledSample > 32767) + outdata[sample] = 32767; + else + outdata[sample] = scaledSample; //(int16) + } + } + + delete clipoffset; + + } + + } + + return B_OK; + +} \ No newline at end of file diff --git a/src/add-ons/media/media-add-ons/mixer/IOStructures.h b/src/add-ons/media/media-add-ons/mixer/IOStructures.h index 5d54ca71f5..5514d536e2 100644 --- a/src/add-ons/media/media-add-ons/mixer/IOStructures.h +++ b/src/add-ons/media/media-add-ons/mixer/IOStructures.h @@ -9,19 +9,35 @@ #ifndef _IO_STRUCT_H #define _IO_STRUCT_H -struct input_channel +class mixer_input { public: + + mixer_input(media_input &input); + ~mixer_input(); media_input fInput; int32 fProducerDataStatus; // status of upstream data - bool DataAvailable; // there is a buffer ready for mixing + bool enabled; // there is a buffer ready for mixing char * fData; - - size_t fEventOffset; // offset (in bytes) + + float * fGainScale; // multiplier for gain - computed at paramchange + float * fGainDisplay; // the value that will be displayed for gain + bigtime_t fGainDisplayLastChange; + + + int fMuteValue; // the value of the 'mute' control + bigtime_t fMuteValueLastChange; + + float fPanValue; // value of 'pan' control (only if mono) + bigtime_t fPanValueLastChange; + + + size_t fEventOffset; // offset (in bytes) of the start of the next + // _output_ buffer - use this for keeping in sync size_t fDataSize; // size of ringbuffer }; -#endif; +#endif; \ No newline at end of file diff --git a/src/add-ons/media/media-add-ons/mixer/Jamfile b/src/add-ons/media/media-add-ons/mixer/Jamfile index 18b1c0cdf7..498d7cd318 100644 --- a/src/add-ons/media/media-add-ons/mixer/Jamfile +++ b/src/add-ons/media/media-add-ons/mixer/Jamfile @@ -5,6 +5,7 @@ UsePrivateHeaders media ; Addon mixer.media_addon : media : AudioMixer.cpp MixerAddOn.cpp + FillMixBuffer.cpp ; LinkSharedOSLibs mixer.media_addon : be media ; diff --git a/src/add-ons/media/media-add-ons/mixer/MixerAddOn.cpp b/src/add-ons/media/media-add-ons/mixer/MixerAddOn.cpp index dfa22e5517..0f526da90d 100644 --- a/src/add-ons/media/media-add-ons/mixer/MixerAddOn.cpp +++ b/src/add-ons/media/media-add-ons/mixer/MixerAddOn.cpp @@ -14,7 +14,7 @@ extern "C" _EXPORT BMediaAddOn* make_media_addon(image_id image) { return new AudioMixerAddon(image); } -// -------------------------------------------------------- // +// ------------------------------------------------------- // // ctor/dtor // -------------------------------------------------------- // @@ -32,6 +32,7 @@ status_t AudioMixerAddon::InitCheck( } int32 AudioMixerAddon::CountFlavors() { + return 1; } @@ -41,27 +42,26 @@ status_t AudioMixerAddon::GetFlavorAt( if(n) return B_ERROR; - flavor_info* pInfo = new flavor_info; - pInfo->internal_id = n; - pInfo->name = "AudioMixer"; - pInfo->info = + flavor_info* fInfo = new flavor_info; + fInfo->internal_id = n; + fInfo->name = "AudioMixer"; + fInfo->info = "AudioMixer media addon\n"; - "By David Shipman, 2002"; - pInfo->kinds = B_BUFFER_PRODUCER | B_BUFFER_CONSUMER | B_SYSTEM_MIXER | B_CONTROLLABLE; - pInfo->flavor_flags = 0; - pInfo->possible_count = 0; + fInfo->kinds = B_BUFFER_PRODUCER | B_BUFFER_CONSUMER | B_SYSTEM_MIXER | B_CONTROLLABLE; + fInfo->flavor_flags = B_FLAVOR_IS_LOCAL; + fInfo->possible_count = 500; - media_format* pFormat = new media_format; - pFormat->type = B_MEDIA_RAW_AUDIO; - pFormat->u.raw_audio = media_raw_audio_format::wildcard; + media_format* fFormat = new media_format; + fFormat->type = B_MEDIA_RAW_AUDIO; + fFormat->u.raw_audio = media_raw_audio_format::wildcard; - pInfo->in_format_count = 1; - pInfo->in_formats = pFormat; + fInfo->in_format_count = 1; + fInfo->in_formats = fFormat; - pInfo->out_format_count = 1; - pInfo->out_formats = pFormat; + fInfo->out_format_count = 1; + fInfo->out_formats = fFormat; - *out_info = pInfo; + *out_info = fInfo; return B_OK; } @@ -78,7 +78,7 @@ status_t AudioMixerAddon::GetConfigurationFor( BMessage* into_message) { // no config yet - return B_OK; + return B_ERROR; } // end \ No newline at end of file diff --git a/src/add-ons/media/media-add-ons/mixer/readme.txt b/src/add-ons/media/media-add-ons/mixer/readme.txt index c89f3979b6..1981cf525c 100644 --- a/src/add-ons/media/media-add-ons/mixer/readme.txt +++ b/src/add-ons/media/media-add-ons/mixer/readme.txt @@ -1,39 +1,35 @@ OpenBeOS Audio Mixer -David Shipman, 2002 +David Shipman, 14/08/2002 Overview -The node is based on a mediaeventlooper, using the HandleEvent loop to compile -the mixed buffer output. -Each input creates a ringbuffer (fixed size "looped" buffer) for its input - this way -buffer contents can be placed in the ringbuffer and recycled immediately. +The node is based on a mediaeventlooper, using the HandleEvent loop to compile the mixed buffer output. +Each input creates a ringbuffer (fixed size "looped" buffer) for its input - this way buffer contents can be +placed in the ringbuffer and the buffer recycled immediately. -Inputs are maintained using a list of input_channel structs - inputs are created/ -destroyed dynamically when producers connect/disconnect. +Inputs are maintained using a list of mixer_input objects - inputs are created/destroyed dynamically when +producers connect/disconnect. Done - node functions as a replacement for the BeOS R5 mixer.media-addon -- IO/conversion between stereo B_AUDIO_FLOAT and B_AUDIO_SHORT streams +- IO/conversion between the major audio types (FLOAT, SHORT) +- interface mostly done (all gain controls operational) +- mixing with different gain levels is functional Tested - Output to emu10k1 (SBLive) -- Input from CL-Amp and emu10k1-in +- Input from CL-Amp, file-readers, SoundPlay, music software - almost all work well -Bugs - -- Soundplay, extractor nodes, and a variety of others connect, but sound really bad! -- When input stops, the current ringbuffer contents continue to be played - -To Do (lots) (vaguely in order of importance) +To Do (vaguely in order of importance) - fix bugs -- support for remaining formats -- samplerate conversion -- Interface (BControllable) +- format negotation fine-tuning - some nodes still connect with weird formats +- rewrite of buffer mixing routines - at the moment its really inefficient, and a total mess +- complete interface (add mutes, panning) - multithreading (separate mix thread) -- tidy up code +- mixer save (to save parameter states when a node is disconnected) Notes :