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
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
// forward declarations
|
// forward declarations
|
||||||
|
|
||||||
class input_channel;
|
class mixer_input;
|
||||||
|
|
||||||
// includes
|
// includes
|
||||||
|
|
||||||
@@ -36,15 +36,17 @@ class AudioMixer :
|
|||||||
AudioMixer(BMediaAddOn *addOn);
|
AudioMixer(BMediaAddOn *addOn);
|
||||||
~AudioMixer();
|
~AudioMixer();
|
||||||
|
|
||||||
// Our own (AudioMixer) methods
|
// AudioMixer support
|
||||||
|
|
||||||
bool IsValidDest( media_destination dest);
|
bool IsValidDest( media_destination dest);
|
||||||
bool IsValidSource( media_source source);
|
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();
|
void AllocateBuffers();
|
||||||
|
status_t FillMixBuffer(void *outbuffer, size_t size);
|
||||||
|
|
||||||
// BMediaNode methods
|
// BMediaNode methods
|
||||||
|
|
||||||
@@ -186,8 +188,14 @@ class AudioMixer :
|
|||||||
|
|
||||||
thread_id fThread; // for launching mixthread
|
thread_id fThread; // for launching mixthread
|
||||||
|
|
||||||
media_input fInput; // descriptor of single input (temp)
|
|
||||||
media_output fOutput; // single output (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;
|
BMediaAddOn * fAddOn;
|
||||||
BParameterWeb * fWeb; // local pointer to parameterweb
|
BParameterWeb * fWeb; // local pointer to parameterweb
|
||||||
@@ -200,8 +208,9 @@ class AudioMixer :
|
|||||||
media_format fPrefInputFormat, fPrefOutputFormat;
|
media_format fPrefInputFormat, fPrefOutputFormat;
|
||||||
BBufferGroup * fBufferGroup;
|
BBufferGroup * fBufferGroup;
|
||||||
|
|
||||||
|
BList fMixerInputs;
|
||||||
|
|
||||||
BList fInputChannels;
|
int fNextFreeID;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,452 @@
|
|||||||
|
#include "AudioMixer.h"
|
||||||
|
#include "IOStructures.h"
|
||||||
|
|
||||||
|
#include <media/RealtimeAlloc.h>
|
||||||
|
#include <media/Buffer.h>
|
||||||
|
#include <media/TimeSource.h>
|
||||||
|
#include <media/ParameterWeb.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -9,17 +9,33 @@
|
|||||||
#ifndef _IO_STRUCT_H
|
#ifndef _IO_STRUCT_H
|
||||||
#define _IO_STRUCT_H
|
#define _IO_STRUCT_H
|
||||||
|
|
||||||
struct input_channel
|
class mixer_input
|
||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
mixer_input(media_input &input);
|
||||||
|
~mixer_input();
|
||||||
|
|
||||||
media_input fInput;
|
media_input fInput;
|
||||||
int32 fProducerDataStatus; // status of upstream data
|
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;
|
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
|
size_t fDataSize; // size of ringbuffer
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ UsePrivateHeaders media ;
|
|||||||
Addon mixer.media_addon : media :
|
Addon mixer.media_addon : media :
|
||||||
AudioMixer.cpp
|
AudioMixer.cpp
|
||||||
MixerAddOn.cpp
|
MixerAddOn.cpp
|
||||||
|
FillMixBuffer.cpp
|
||||||
;
|
;
|
||||||
|
|
||||||
LinkSharedOSLibs mixer.media_addon : be media ;
|
LinkSharedOSLibs mixer.media_addon : be media ;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ extern "C" _EXPORT BMediaAddOn* make_media_addon(image_id image) {
|
|||||||
return new AudioMixerAddon(image);
|
return new AudioMixerAddon(image);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------- //
|
// ------------------------------------------------------- //
|
||||||
// ctor/dtor
|
// ctor/dtor
|
||||||
// -------------------------------------------------------- //
|
// -------------------------------------------------------- //
|
||||||
|
|
||||||
@@ -32,6 +32,7 @@ status_t AudioMixerAddon::InitCheck(
|
|||||||
}
|
}
|
||||||
|
|
||||||
int32 AudioMixerAddon::CountFlavors() {
|
int32 AudioMixerAddon::CountFlavors() {
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,27 +42,26 @@ status_t AudioMixerAddon::GetFlavorAt(
|
|||||||
if(n)
|
if(n)
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
|
|
||||||
flavor_info* pInfo = new flavor_info;
|
flavor_info* fInfo = new flavor_info;
|
||||||
pInfo->internal_id = n;
|
fInfo->internal_id = n;
|
||||||
pInfo->name = "AudioMixer";
|
fInfo->name = "AudioMixer";
|
||||||
pInfo->info =
|
fInfo->info =
|
||||||
"AudioMixer media addon\n";
|
"AudioMixer media addon\n";
|
||||||
"By David Shipman, 2002";
|
fInfo->kinds = B_BUFFER_PRODUCER | B_BUFFER_CONSUMER | B_SYSTEM_MIXER | B_CONTROLLABLE;
|
||||||
pInfo->kinds = B_BUFFER_PRODUCER | B_BUFFER_CONSUMER | B_SYSTEM_MIXER | B_CONTROLLABLE;
|
fInfo->flavor_flags = B_FLAVOR_IS_LOCAL;
|
||||||
pInfo->flavor_flags = 0;
|
fInfo->possible_count = 500;
|
||||||
pInfo->possible_count = 0;
|
|
||||||
|
|
||||||
media_format* pFormat = new media_format;
|
media_format* fFormat = new media_format;
|
||||||
pFormat->type = B_MEDIA_RAW_AUDIO;
|
fFormat->type = B_MEDIA_RAW_AUDIO;
|
||||||
pFormat->u.raw_audio = media_raw_audio_format::wildcard;
|
fFormat->u.raw_audio = media_raw_audio_format::wildcard;
|
||||||
|
|
||||||
pInfo->in_format_count = 1;
|
fInfo->in_format_count = 1;
|
||||||
pInfo->in_formats = pFormat;
|
fInfo->in_formats = fFormat;
|
||||||
|
|
||||||
pInfo->out_format_count = 1;
|
fInfo->out_format_count = 1;
|
||||||
pInfo->out_formats = pFormat;
|
fInfo->out_formats = fFormat;
|
||||||
|
|
||||||
*out_info = pInfo;
|
*out_info = fInfo;
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,7 +78,7 @@ status_t AudioMixerAddon::GetConfigurationFor(
|
|||||||
BMessage* into_message) {
|
BMessage* into_message) {
|
||||||
|
|
||||||
// no config yet
|
// no config yet
|
||||||
return B_OK;
|
return B_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
// end
|
// end
|
||||||
@@ -1,39 +1,35 @@
|
|||||||
OpenBeOS Audio Mixer
|
OpenBeOS Audio Mixer
|
||||||
David Shipman, 2002
|
David Shipman, 14/08/2002
|
||||||
|
|
||||||
Overview
|
Overview
|
||||||
|
|
||||||
The node is based on a mediaeventlooper, using the HandleEvent loop to compile
|
The node is based on a mediaeventlooper, using the HandleEvent loop to compile the mixed buffer output.
|
||||||
the mixed buffer output.
|
Each input creates a ringbuffer (fixed size "looped" buffer) for its input - this way buffer contents can be
|
||||||
Each input creates a ringbuffer (fixed size "looped" buffer) for its input - this way
|
placed in the ringbuffer and the buffer recycled immediately.
|
||||||
buffer contents can be placed in the ringbuffer and recycled immediately.
|
|
||||||
|
|
||||||
Inputs are maintained using a list of input_channel structs - inputs are created/
|
Inputs are maintained using a list of mixer_input objects - inputs are created/destroyed dynamically when
|
||||||
destroyed dynamically when producers connect/disconnect.
|
producers connect/disconnect.
|
||||||
|
|
||||||
Done
|
Done
|
||||||
|
|
||||||
- node functions as a replacement for the BeOS R5 mixer.media-addon
|
- 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
|
Tested
|
||||||
|
|
||||||
- Output to emu10k1 (SBLive)
|
- 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
|
To Do (vaguely in order of importance)
|
||||||
|
|
||||||
- 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)
|
|
||||||
|
|
||||||
- fix bugs
|
- fix bugs
|
||||||
- support for remaining formats
|
- format negotation fine-tuning - some nodes still connect with weird formats
|
||||||
- samplerate conversion
|
- rewrite of buffer mixing routines - at the moment its really inefficient, and a total mess
|
||||||
- Interface (BControllable)
|
- complete interface (add mutes, panning)
|
||||||
- multithreading (separate mix thread)
|
- multithreading (separate mix thread)
|
||||||
- tidy up code
|
- mixer save (to save parameter states when a node is disconnected)
|
||||||
|
|
||||||
Notes :
|
Notes :
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user