Internal API rewritten, BMediaNode and derived classes functionality implemented.
git-svn-id: file:///srv/svn/repos/haiku/trunk/current@3525 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
8636b80151
commit
7ee2c8049c
@ -1,10 +1,8 @@
|
|||||||
// AudioMixer.cpp
|
/* AudioMixer
|
||||||
/*
|
*
|
||||||
|
* First implementation by David Shipman, 2002
|
||||||
By David Shipman, 2002
|
* Rewritten by Marcus Overhagen, 2003
|
||||||
|
*/
|
||||||
*/
|
|
||||||
|
|
||||||
#include <RealtimeAlloc.h>
|
#include <RealtimeAlloc.h>
|
||||||
#include <Buffer.h>
|
#include <Buffer.h>
|
||||||
#include <TimeSource.h>
|
#include <TimeSource.h>
|
||||||
@ -27,13 +25,11 @@ AudioMixer::AudioMixer(BMediaAddOn *addOn)
|
|||||||
BControllable(),
|
BControllable(),
|
||||||
BMediaEventLooper(),
|
BMediaEventLooper(),
|
||||||
fAddOn(addOn),
|
fAddOn(addOn),
|
||||||
fWeb(NULL),
|
fCore(new MixerCore),
|
||||||
fLatency(1),
|
fWeb(0),
|
||||||
|
fBufferGroup(0),
|
||||||
|
fDownstreamLatency(1),
|
||||||
fInternalLatency(1),
|
fInternalLatency(1),
|
||||||
fStartTime(0),
|
|
||||||
fFramesSent(0),
|
|
||||||
fOutputEnabled(true),
|
|
||||||
fBufferGroup(NULL),
|
|
||||||
fDisableStop(false)
|
fDisableStop(false)
|
||||||
{
|
{
|
||||||
BMediaNode::AddNodeKind(B_SYSTEM_MIXER);
|
BMediaNode::AddNodeKind(B_SYSTEM_MIXER);
|
||||||
@ -52,13 +48,13 @@ AudioMixer::AudioMixer(BMediaAddOn *addOn)
|
|||||||
|
|
||||||
AudioMixer::~AudioMixer()
|
AudioMixer::~AudioMixer()
|
||||||
{
|
{
|
||||||
|
|
||||||
BMediaEventLooper::Quit();
|
BMediaEventLooper::Quit();
|
||||||
SetParameterWeb(NULL);
|
SetParameterWeb(NULL);
|
||||||
fWeb = NULL;
|
|
||||||
|
|
||||||
// any other cleanup goes here
|
|
||||||
|
|
||||||
|
delete fCore;
|
||||||
|
delete fBufferGroup;
|
||||||
|
|
||||||
|
DEBUG_ONLY(fCore = 0; fBufferGroup = 0; fWeb = 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -84,13 +80,8 @@ void AudioMixer::Stop(bigtime_t performance_time, bool immediate)
|
|||||||
BMediaAddOn *
|
BMediaAddOn *
|
||||||
AudioMixer::AddOn(int32 *internal_id) const
|
AudioMixer::AddOn(int32 *internal_id) const
|
||||||
{
|
{
|
||||||
if(fAddOn)
|
*internal_id = 0;
|
||||||
{
|
return fAddOn;
|
||||||
*internal_id = 0;
|
|
||||||
return fAddOn;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -99,14 +90,14 @@ AudioMixer::AddOn(int32 *internal_id) const
|
|||||||
|
|
||||||
status_t
|
status_t
|
||||||
AudioMixer::GetParameterValue(int32 id, bigtime_t *last_change,
|
AudioMixer::GetParameterValue(int32 id, bigtime_t *last_change,
|
||||||
void *value, size_t *ioSize)
|
void *value, size_t *ioSize)
|
||||||
{
|
{
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
AudioMixer::SetParameterValue(int32 id, bigtime_t when,
|
AudioMixer::SetParameterValue(int32 id, bigtime_t when,
|
||||||
const void *value, size_t ioSize)
|
const void *value, size_t size)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,11 +106,10 @@ AudioMixer::SetParameterValue(int32 id, bigtime_t when,
|
|||||||
//
|
//
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
AudioMixer::HandleMessage( int32 message, const void *data, size_t size)
|
AudioMixer::HandleMessage(int32 message, const void *data, size_t size)
|
||||||
{
|
{
|
||||||
// since we're using a mediaeventlooper, there shouldn't be any messages
|
// since we're using a mediaeventlooper, there shouldn't be any messages
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
@ -281,8 +271,6 @@ AudioMixer::Connected(const media_source &producer, const media_destination &whe
|
|||||||
if (where.id != 0 || where.port != ControlPort())
|
if (where.id != 0 || where.port != ControlPort())
|
||||||
return B_MEDIA_BAD_DESTINATION;
|
return B_MEDIA_BAD_DESTINATION;
|
||||||
|
|
||||||
media_input input;
|
|
||||||
|
|
||||||
fCore->Lock();
|
fCore->Lock();
|
||||||
|
|
||||||
// we assign a new id (!= 0) to the newly created input
|
// we assign a new id (!= 0) to the newly created input
|
||||||
@ -327,14 +315,23 @@ AudioMixer::Disconnected(const media_source &producer, const media_destination &
|
|||||||
}
|
}
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
AudioMixer::FormatChanged( const media_source &producer, const media_destination &consumer,
|
AudioMixer::FormatChanged(const media_source &producer, const media_destination &consumer,
|
||||||
int32 change_tag, const media_format &format)
|
int32 change_tag, const media_format &format)
|
||||||
{
|
{
|
||||||
|
// at some point in the future (indicated by change_tag and RequestCompleted()),
|
||||||
|
// we will receive buffers in a different format
|
||||||
|
|
||||||
|
if (consumer.port != ControlPort() || consumer.id == 0)
|
||||||
|
return B_MEDIA_BAD_DESTINATION;
|
||||||
|
|
||||||
|
// XXX we should not apply the format change at this point
|
||||||
|
|
||||||
// XXX tell core about format change
|
// tell core about format change
|
||||||
void InputFormatChanged(int32 inputID, const media_format *format);
|
fCore->Lock();
|
||||||
printf("Format changed\n");
|
fCore->InputFormatChanged(consumer.id, &format);
|
||||||
return B_ERROR;
|
fCore->Unlock();
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -375,11 +372,6 @@ AudioMixer::FormatProposal(const media_source &output, media_format *ioFormat)
|
|||||||
// we require a raw audio format
|
// we require a raw audio format
|
||||||
if (ioFormat->type != B_MEDIA_RAW_AUDIO)
|
if (ioFormat->type != B_MEDIA_RAW_AUDIO)
|
||||||
return B_MEDIA_BAD_FORMAT;
|
return B_MEDIA_BAD_FORMAT;
|
||||||
|
|
||||||
// XXX tell core about format change
|
|
||||||
void OutputFormatChanged(const media_format *format);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
@ -387,7 +379,7 @@ AudioMixer::FormatChangeRequested(const media_source &source, const media_destin
|
|||||||
media_format *io_format, int32 *_deprecated_)
|
media_format *io_format, int32 *_deprecated_)
|
||||||
{
|
{
|
||||||
// the downstream consumer node (soundcard) requested that we produce
|
// the downstream consumer node (soundcard) requested that we produce
|
||||||
// another format, we need to check if the format is accecptable and
|
// another format, we need to check if the format is acceptable and
|
||||||
// remove any wildcards before returning OK.
|
// remove any wildcards before returning OK.
|
||||||
|
|
||||||
fCore->Lock();
|
fCore->Lock();
|
||||||
@ -411,6 +403,13 @@ AudioMixer::FormatChangeRequested(const media_source &source, const media_destin
|
|||||||
|
|
||||||
/* remove wildcards */
|
/* remove wildcards */
|
||||||
io_format->SpecializeTo(&fDefaultFormat);
|
io_format->SpecializeTo(&fDefaultFormat);
|
||||||
|
|
||||||
|
// apply format change
|
||||||
|
fCore->Lock();
|
||||||
|
fCore->OutputFormatChanged(io_format);
|
||||||
|
fCore->Unlock();
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
fCore->Unlock();
|
fCore->Unlock();
|
||||||
@ -423,11 +422,8 @@ AudioMixer::GetNextOutput(int32 *cookie, media_output *out_output)
|
|||||||
if (*cookie != 0)
|
if (*cookie != 0)
|
||||||
return B_BAD_INDEX;
|
return B_BAD_INDEX;
|
||||||
|
|
||||||
MixerOutput *output;
|
|
||||||
status_t rv;
|
|
||||||
|
|
||||||
fCore->Lock();
|
fCore->Lock();
|
||||||
output = fCore->Output();
|
MixerOutput *output = fCore->Output();
|
||||||
if (output) {
|
if (output) {
|
||||||
*out_output = output->MediaOutput();
|
*out_output = output->MediaOutput();
|
||||||
} else {
|
} else {
|
||||||
@ -441,7 +437,7 @@ AudioMixer::GetNextOutput(int32 *cookie, media_output *out_output)
|
|||||||
fCore->Unlock();
|
fCore->Unlock();
|
||||||
|
|
||||||
*cookie += 1;
|
*cookie += 1;
|
||||||
return B_OK
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
@ -454,32 +450,21 @@ AudioMixer::DisposeOutputCookie(int32 cookie)
|
|||||||
status_t
|
status_t
|
||||||
AudioMixer::SetBufferGroup(const media_source &for_source, BBufferGroup *newGroup)
|
AudioMixer::SetBufferGroup(const media_source &for_source, BBufferGroup *newGroup)
|
||||||
{
|
{
|
||||||
|
// the downstream consumer (soundcard) node asks us to use another
|
||||||
if (! IsValidSource(for_source))
|
// BBufferGroup (might be NULL). We only have one output (id 0)
|
||||||
|
if (for_source.port != ControlPort() || for_source.id != 0)
|
||||||
return B_MEDIA_BAD_SOURCE;
|
return B_MEDIA_BAD_SOURCE;
|
||||||
|
|
||||||
if (newGroup == fBufferGroup) // we're already using this buffergroup
|
if (newGroup == fBufferGroup) // we're already using this buffergroup
|
||||||
return B_OK;
|
return B_OK;
|
||||||
|
|
||||||
// Ahh, someone wants us to use a different buffer group. At this point we delete
|
fCore->Lock();
|
||||||
// the one we are using and use the specified one instead. If the specified group is
|
if (!newGroup)
|
||||||
// NULL, we need to recreate one ourselves, and use *that*. Note that if we're
|
newGroup = CreateBufferGroup();
|
||||||
// caching a BBuffer that we requested earlier, we have to Recycle() that buffer
|
fCore->SetOutputBufferGroup(newGroup);
|
||||||
// *before* deleting the buffer group, otherwise we'll deadlock waiting for that
|
delete fBufferGroup;
|
||||||
// buffer to be recycled!
|
fBufferGroup = newGroup;
|
||||||
delete fBufferGroup; // waits for all buffers to recycle
|
fCore->Unlock();
|
||||||
if (newGroup != NULL)
|
|
||||||
{
|
|
||||||
// we were given a valid group; just use that one from now on
|
|
||||||
fBufferGroup = newGroup;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// we were passed a NULL group pointer; that means we construct
|
|
||||||
// our own buffer group to use from now on
|
|
||||||
AllocateBuffers();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
@ -497,146 +482,109 @@ AudioMixer::GetLatency(bigtime_t *out_latency)
|
|||||||
|
|
||||||
status_t
|
status_t
|
||||||
AudioMixer::PrepareToConnect(const media_source &what, const media_destination &where,
|
AudioMixer::PrepareToConnect(const media_source &what, const media_destination &where,
|
||||||
media_format *format, media_source *out_source, char *out_name)
|
media_format *format, media_source *out_source, char *out_name)
|
||||||
{
|
{
|
||||||
// PrepareToConnect() is the second stage of format negotiations that happens
|
// PrepareToConnect() is the second stage of format negotiations that happens
|
||||||
// inside BMediaRoster::Connect(). At this point, the consumer's AcceptFormat()
|
// inside BMediaRoster::Connect(). At this point, the consumer's AcceptFormat()
|
||||||
// method has been called, and that node has potentially changed the proposed
|
// method has been called, and that node has potentially changed the proposed
|
||||||
// format. It may also have left wildcards in the format. PrepareToConnect()
|
// format. It may also have left wildcards in the format. PrepareToConnect()
|
||||||
// *must* fully specialize the format before returning!
|
// *must* fully specialize the format before returning!
|
||||||
|
// we also create the new output connection and return it in out_source.
|
||||||
|
|
||||||
/*
|
// is the source valid?
|
||||||
|
if (what.port != ControlPort() || what.id != 0)
|
||||||
he PrepareToConnect() hook is called before a new connection between the source whichSource and the destination whichDestination is established, in
|
|
||||||
order to give your producer one last chance to specialize any wildcards that remain in the format (although by this point there shouldn't be any, you should
|
|
||||||
check anyway).
|
|
||||||
Your implementation should, additionally, return in outSource the source to be used for the connection, and should fill the outName buffer with the name the
|
|
||||||
connection will be given; the consumer will see this in the outInput->name argument specified to BBufferConsumer::Connected(). If your node doesn't
|
|
||||||
care what the name is, you can leave the outName untouched.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// trying to connect something that isn't our source?
|
|
||||||
if (! IsValidSource(what))
|
|
||||||
return B_MEDIA_BAD_SOURCE;
|
return B_MEDIA_BAD_SOURCE;
|
||||||
|
|
||||||
|
// is the format acceptable?
|
||||||
|
if (format->type != B_MEDIA_RAW_AUDIO && format->type != B_MEDIA_UNKNOWN_TYPE)
|
||||||
|
return B_MEDIA_BAD_FORMAT;
|
||||||
|
|
||||||
|
fCore->Lock();
|
||||||
|
|
||||||
// are we already connected?
|
// are we already connected?
|
||||||
if (fOutput.destination != media_destination::null)
|
if (fCore->Output() != 0) {
|
||||||
|
fCore->Unlock();
|
||||||
return B_MEDIA_ALREADY_CONNECTED;
|
return B_MEDIA_ALREADY_CONNECTED;
|
||||||
|
}
|
||||||
|
|
||||||
// the format may not yet be fully specialized (the consumer might have
|
/* set source and suggest a name */
|
||||||
// passed back some wildcards). Finish specializing it now, and return an
|
*out_source = what;
|
||||||
// error if we don't support the requested format.
|
strcpy(out_name, "Mixer Output");
|
||||||
if (format->type != B_MEDIA_RAW_AUDIO)
|
|
||||||
return B_MEDIA_BAD_FORMAT;
|
/* remove wildcards */
|
||||||
|
format->SpecializeTo(&fDefaultFormat);
|
||||||
// CHANGE_THIS - we're messing around with formats, need to clean up
|
|
||||||
// still need to check u.raw_audio.format
|
/* add output to core */
|
||||||
|
media_output output;
|
||||||
|
output.node = Node();
|
||||||
|
output.source = *out_source;
|
||||||
|
output.destination = where;
|
||||||
|
output.format = *format;
|
||||||
|
strcpy(output.name, out_name);
|
||||||
|
|
||||||
if (format->u.raw_audio.format == media_raw_audio_format::wildcard.format)
|
fCore->EnableOutput(false);
|
||||||
format->u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT;
|
fCore->AddOutput(output);
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
// all fields MUST be validated here
|
fCore->Unlock();
|
||||||
// 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 = 44100;
|
|
||||||
|
|
||||||
if(format->u.raw_audio.channel_count == media_raw_audio_format::wildcard.channel_count)
|
|
||||||
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 = B_MEDIA_HOST_ENDIAN;
|
|
||||||
|
|
||||||
if (format->u.raw_audio.buffer_size == media_raw_audio_format::wildcard.buffer_size)
|
|
||||||
format->u.raw_audio.buffer_size = 1024; // pick something comfortable to suggest
|
|
||||||
|
|
||||||
|
|
||||||
// Now reserve the connection, and return information about it
|
|
||||||
fOutput.destination = where;
|
|
||||||
fOutput.format = *format;
|
|
||||||
*out_source = fOutput.source;
|
|
||||||
strncpy(out_name, fOutput.name, B_MEDIA_NAME_LENGTH); // strncpy?
|
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
AudioMixer::Connect( status_t error, const media_source &source, const media_destination &dest,
|
AudioMixer::Connect(status_t error, const media_source &source, const media_destination &dest,
|
||||||
const media_format &format, char *io_name)
|
const media_format &format, char *io_name)
|
||||||
{
|
{
|
||||||
printf("AudioMixer::Connect\n");
|
printf("AudioMixer::Connect\n");
|
||||||
|
|
||||||
// we need to check which output dest refers to - we only have one for now
|
if (error != B_OK) {
|
||||||
|
// if an error occured, remove output from core
|
||||||
if (error)
|
printf("AudioMixer::Connect failed, removing connction\n");
|
||||||
{
|
fCore->Lock();
|
||||||
fOutput.destination = media_destination::null;
|
fCore->RemoveOutput();
|
||||||
fOutput.format = fPrefOutputFormat;
|
fCore->Unlock();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// connection is confirmed, record information and send output name
|
|
||||||
|
|
||||||
fOutput.destination = dest;
|
// if the connection has no name, we set it now
|
||||||
fOutput.format = format;
|
if (strlen(io_name) == 0)
|
||||||
strncpy(io_name, fOutput.name, B_MEDIA_NAME_LENGTH);
|
strcpy(io_name, "Mixer Output");
|
||||||
|
|
||||||
// Now that we're connected, we can determine our downstream latency.
|
// Now that we're connected, we can determine our downstream latency.
|
||||||
// Do so, then make sure we get our events early enough.
|
|
||||||
media_node_id id;
|
media_node_id id;
|
||||||
FindLatencyFor(fOutput.destination, &fLatency, &id);
|
FindLatencyFor(dest, &fDownstreamLatency, &id);
|
||||||
printf("Downstream Latency is %Ld usecs\n", fLatency);
|
printf("AudioMixer: Downstream Latency is %Ld usecs\n", fDownstreamLatency);
|
||||||
|
|
||||||
// we need at least the length of a full output buffer's latency (I think?)
|
// SetDuration of one buffer
|
||||||
|
SetBufferDuration((1000000 * (format.u.raw_audio.buffer_size / ((format.u.raw_audio.format & media_raw_audio_format::B_AUDIO_SIZE_MASK) * format.u.raw_audio.channel_count))) / format.u.raw_audio.frame_rate);
|
||||||
|
|
||||||
size_t sample_size = fOutput.format.u.raw_audio.format & media_raw_audio_format::B_AUDIO_SIZE_MASK;
|
printf("AudioMixer: buffer duration is %Ld usecs\n", BufferDuration());
|
||||||
|
|
||||||
size_t framesPerBuffer = (fOutput.format.u.raw_audio.buffer_size / sample_size) / fOutput.format.u.raw_audio.channel_count;
|
// Our internal latency is at least the length of a full output buffer
|
||||||
|
// XXX we use two for now
|
||||||
|
fInternalLatency = 2 * BufferDuration();
|
||||||
|
printf("AudioMixer: Internal latency is %Ld usecs\n", fInternalLatency);
|
||||||
|
|
||||||
//fInternalLatency = (framesPerBuffer / fOutput.format.u.raw_audio.frame_rate); // test * 1000000
|
SetEventLatency(fDownstreamLatency + fInternalLatency);
|
||||||
|
|
||||||
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("Internal latency is %Ld usecs\n", fInternalLatency);
|
|
||||||
|
|
||||||
// use a higher internal latency to be able to process buffers that arrive late
|
|
||||||
// XXX does this make sense?
|
|
||||||
if (fInternalLatency < 5000)
|
|
||||||
fInternalLatency = 5000;
|
|
||||||
|
|
||||||
delete mouse;
|
|
||||||
|
|
||||||
// might need to tweak the latency
|
|
||||||
SetEventLatency(fLatency + fInternalLatency);
|
|
||||||
|
|
||||||
printf("AudioMixer: SendLatencyChange %Ld\n", EventLatency());
|
printf("AudioMixer: SendLatencyChange %Ld\n", EventLatency());
|
||||||
SendLatencyChange(source, dest, EventLatency());
|
SendLatencyChange(source, dest, EventLatency());
|
||||||
|
|
||||||
// calculate buffer duration and set it
|
|
||||||
if (fOutput.format.u.raw_audio.frame_rate == 0) {
|
|
||||||
// XXX must be adjusted later when the format is known
|
|
||||||
SetBufferDuration((framesPerBuffer * 1000000LL) / 44100);
|
|
||||||
} else {
|
|
||||||
SetBufferDuration((framesPerBuffer * 1000000LL) / fOutput.format.u.raw_audio.frame_rate);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up the buffer group for our connection, as long as nobody handed us a
|
// Set up the buffer group for our connection, as long as nobody handed us a
|
||||||
// buffer group (via SetBufferGroup()) prior to this. That can happen, for example,
|
// buffer group (via SetBufferGroup()) prior to this. That can happen, for example,
|
||||||
// if the consumer calls SetOutputBuffersFor() on us from within its Connected()
|
// if the consumer calls SetOutputBuffersFor() on us from within its Connected()
|
||||||
// method.
|
// method.
|
||||||
if (!fBufferGroup)
|
if (!fBufferGroup)
|
||||||
AllocateBuffers();
|
fBufferGroup = CreateBufferGroup();
|
||||||
|
|
||||||
|
ASSERT(fCore->Output() != 0);
|
||||||
|
ASSERT(fCore->Output()->MediaOutput().format == format);
|
||||||
|
|
||||||
|
fCore->Lock();
|
||||||
|
fCore->EnableOutput(true);
|
||||||
|
fCore->SetTimeSource(TimeSource()->ID());
|
||||||
|
fCore->SetOutputBufferGroup(fBufferGroup);
|
||||||
|
fCore->Unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -645,15 +593,28 @@ AudioMixer::Disconnect(const media_source &what, const media_destination &where)
|
|||||||
{
|
{
|
||||||
|
|
||||||
printf("AudioMixer::Disconnect\n");
|
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
|
fCore->Lock();
|
||||||
{
|
|
||||||
fOutput.destination = media_destination::null;
|
|
||||||
fOutput.format = fPrefOutputFormat;
|
|
||||||
delete fBufferGroup;
|
|
||||||
fBufferGroup = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Make sure that our connection is the one being disconnected
|
||||||
|
MixerOutput * output = fCore->Output();
|
||||||
|
if (!output || output->MediaOutput().node != Node() || output->MediaOutput().source != what || output->MediaOutput().destination != where) {
|
||||||
|
FATAL("AudioMixer::Disconnect can't disconnect (wrong connection)\n");
|
||||||
|
fCore->Unlock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// force a stop
|
||||||
|
fCore->Stop();
|
||||||
|
|
||||||
|
fCore->RemoveOutput();
|
||||||
|
|
||||||
|
// destroy buffer group
|
||||||
|
delete fBufferGroup;
|
||||||
|
fBufferGroup = 0;
|
||||||
|
fCore->SetOutputBufferGroup(0);
|
||||||
|
|
||||||
|
fCore->Unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -664,7 +625,8 @@ AudioMixer::LateNoticeReceived(const media_source &what, bigtime_t how_much, big
|
|||||||
// is the only runmode in which we can do anything about this
|
// is the only runmode in which we can do anything about this
|
||||||
|
|
||||||
printf("AudioMixer::LateNoticeReceived, %Ld too late at %Ld\n", how_much, performance_time);
|
printf("AudioMixer::LateNoticeReceived, %Ld too late at %Ld\n", how_much, performance_time);
|
||||||
|
|
||||||
|
/*
|
||||||
if (what == fOutput.source) {
|
if (what == fOutput.source) {
|
||||||
if (RunMode() == B_INCREASE_LATENCY) {
|
if (RunMode() == B_INCREASE_LATENCY) {
|
||||||
fInternalLatency += how_much;
|
fInternalLatency += how_much;
|
||||||
@ -673,26 +635,26 @@ AudioMixer::LateNoticeReceived(const media_source &what, bigtime_t how_much, big
|
|||||||
fInternalLatency = 50000;
|
fInternalLatency = 50000;
|
||||||
|
|
||||||
printf("AudioMixer: increasing internal latency to %Ld usec\n", fInternalLatency);
|
printf("AudioMixer: increasing internal latency to %Ld usec\n", fInternalLatency);
|
||||||
SetEventLatency(fLatency + fInternalLatency);
|
SetEventLatency(fDownstreamLatency + fInternalLatency);
|
||||||
|
|
||||||
// printf("AudioMixer: SendLatencyChange %Ld (2)\n", EventLatency());
|
// printf("AudioMixer: SendLatencyChange %Ld (2)\n", EventLatency());
|
||||||
// SendLatencyChange(source, dest, EventLatency());
|
// SendLatencyChange(source, dest, EventLatency());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
AudioMixer::EnableOutput(const media_source &what, bool enabled, int32 *_deprecated_)
|
AudioMixer::EnableOutput(const media_source &what, bool enabled, int32 *_deprecated_)
|
||||||
{
|
{
|
||||||
|
// we only have one output
|
||||||
// right now we've only got one output... check this against the supplied
|
if (what.id != 0 || what.port != ControlPort())
|
||||||
// media_source and set its state accordingly...
|
return;
|
||||||
|
|
||||||
if (what == fOutput.source)
|
fCore->Lock();
|
||||||
{
|
fCore->EnableOutput(enabled);
|
||||||
fOutputEnabled = enabled;
|
fCore->Unlock();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -703,33 +665,19 @@ AudioMixer::EnableOutput(const media_source &what, bool enabled, int32 *_depreca
|
|||||||
void
|
void
|
||||||
AudioMixer::NodeRegistered()
|
AudioMixer::NodeRegistered()
|
||||||
{
|
{
|
||||||
|
|
||||||
Run();
|
Run();
|
||||||
|
SetPriority(8);
|
||||||
fOutput.node = Node();
|
// SetPriority(120);
|
||||||
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
AudioMixer::SetTimeSource(BTimeSource * time_source)
|
||||||
|
{
|
||||||
|
printf("AudioMixer::SetTimeSource: timesource is now %ld\n", time_source->ID());
|
||||||
|
fCore->Lock();
|
||||||
|
fCore->SetTimeSource(time_source->ID());
|
||||||
|
fCore->Unlock();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
AudioMixer::HandleEvent( const media_timed_event *event, bigtime_t lateness, bool realTimeEvent)
|
AudioMixer::HandleEvent( const media_timed_event *event, bigtime_t lateness, bool realTimeEvent)
|
||||||
@ -744,142 +692,52 @@ AudioMixer::HandleEvent( const media_timed_event *event, bigtime_t lateness, boo
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case SEND_NEW_BUFFER_EVENT:
|
case BTimedEventQueue::B_START:
|
||||||
{
|
{
|
||||||
// if the output is connected and enabled, send a bufffer
|
if (RunState() != B_STARTED) {
|
||||||
if (fOutputEnabled && fOutput.destination != media_destination::null)
|
fCore->Lock();
|
||||||
SendNewBuffer(event->event_time);
|
fCore->Start(event->event_time);
|
||||||
|
fCore->Unlock();
|
||||||
// if this is the first buffer, mark with the start time
|
|
||||||
// we need this to calculate the other buffer times
|
|
||||||
if (fStartTime == 0) {
|
|
||||||
fStartTime = event->event_time;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// count frames that have been played
|
|
||||||
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));
|
|
||||||
|
|
||||||
fFramesSent += framesperbuffer;
|
|
||||||
|
|
||||||
// calculate the start time for the next event and add the event
|
|
||||||
bigtime_t nextevent = bigtime_t(fStartTime + double(fFramesSent / fOutput.format.u.raw_audio.frame_rate) * 1000000.0);
|
|
||||||
media_timed_event nextBufferEvent(nextevent, SEND_NEW_BUFFER_EVENT);
|
|
||||||
EventQueue()->AddEvent(nextBufferEvent);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case BTimedEventQueue::B_START:
|
|
||||||
|
|
||||||
if (RunState() != B_STARTED)
|
|
||||||
{
|
|
||||||
// We want to start sending buffers now, so we set up the buffer-sending bookkeeping
|
|
||||||
// and fire off the first "produce a buffer" event.
|
|
||||||
fStartTime = 0;
|
|
||||||
fFramesSent = 0;
|
|
||||||
|
|
||||||
//fThread = spawn_thread(_mix_thread_, "audio mixer thread", B_REAL_TIME_PRIORITY, this);
|
|
||||||
|
|
||||||
media_timed_event firstBufferEvent(event->event_time, SEND_NEW_BUFFER_EVENT);
|
|
||||||
|
|
||||||
// Alternatively, we could call HandleEvent() directly with this event, to avoid a trip through
|
|
||||||
// the event queue, like this:
|
|
||||||
//
|
|
||||||
// this->HandleEvent(&firstBufferEvent, 0, false);
|
|
||||||
//
|
|
||||||
EventQueue()->AddEvent(firstBufferEvent);
|
|
||||||
|
|
||||||
// fStartTime = event->event_time;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BTimedEventQueue::B_STOP:
|
case BTimedEventQueue::B_STOP:
|
||||||
{
|
{
|
||||||
// stopped - don't process any more buffers, flush all buffers from eventqueue
|
// stopped - don't process any more buffers, flush all buffers from eventqueue
|
||||||
|
|
||||||
EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER);
|
EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER);
|
||||||
EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true, SEND_NEW_BUFFER_EVENT);
|
fCore->Lock();
|
||||||
|
fCore->Stop();
|
||||||
|
fCore->Unlock();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case BTimedEventQueue::B_DATA_STATUS:
|
case BTimedEventQueue::B_DATA_STATUS:
|
||||||
{
|
{
|
||||||
|
|
||||||
printf("DataStatus message\n");
|
printf("DataStatus message\n");
|
||||||
|
|
||||||
mixer_input *mixerInput;
|
|
||||||
|
|
||||||
int inputcount = fMixerInputs.CountItems();
|
|
||||||
|
|
||||||
for (int i = 0; i < inputcount; i++)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// AudioMixer methods
|
// AudioMixer methods
|
||||||
//
|
//
|
||||||
|
|
||||||
void
|
BBufferGroup *
|
||||||
AudioMixer::AllocateBuffers()
|
AudioMixer::CreateBufferGroup()
|
||||||
{
|
{
|
||||||
|
// allocate enough buffers to span our downstream latency (plus one for rounding up), plus one extra
|
||||||
// allocate enough buffers to span our downstream latency, plus one
|
int32 count = int32(fDownstreamLatency / BufferDuration()) + 2;
|
||||||
size_t size = fOutput.format.u.raw_audio.buffer_size;
|
|
||||||
int32 count = int32((fLatency / (BufferDuration() + 1)) + 1);
|
|
||||||
|
|
||||||
if (count < 3) {
|
fCore->Lock();
|
||||||
printf("AudioMixer: calculated only %ld buffers, that's not enough\n", count);
|
uint32 size = fCore->OutputBufferSize();
|
||||||
count = 3;
|
fCore->Unlock();
|
||||||
}
|
|
||||||
|
|
||||||
printf("AudioMixer: allocating %ld buffers\n", count);
|
|
||||||
fBufferGroup = new BBufferGroup(size, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
// use this later for separate threads
|
|
||||||
|
|
||||||
int32
|
|
||||||
AudioMixer::_mix_thread_(void *data)
|
|
||||||
{
|
|
||||||
return ((AudioMixer *)data)->MixThread();
|
|
||||||
}
|
|
||||||
|
|
||||||
int32
|
|
||||||
AudioMixer::MixThread()
|
|
||||||
{
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
snooze(500000);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
printf("AudioMixer: allocating %ld buffers of %ld bytes each\n", count, size);
|
||||||
|
return new BBufferGroup(size, count);
|
||||||
}
|
}
|
||||||
|
@ -46,19 +46,19 @@ class AudioMixer :
|
|||||||
BParameterWeb * BuildParameterWeb(); // used to create the initial 'master' web
|
BParameterWeb * BuildParameterWeb(); // used to create the initial 'master' web
|
||||||
void MakeWebForInput(char *name, media_format format);
|
void MakeWebForInput(char *name, media_format format);
|
||||||
|
|
||||||
void AllocateBuffers();
|
|
||||||
status_t FillMixBuffer(void *outbuffer, size_t size);
|
|
||||||
|
|
||||||
void SendNewBuffer(bigtime_t event_time);
|
|
||||||
void HandleInputBuffer(BBuffer *buffer, bigtime_t lateness);
|
void HandleInputBuffer(BBuffer *buffer, bigtime_t lateness);
|
||||||
|
|
||||||
|
BBufferGroup * CreateBufferGroup();
|
||||||
|
|
||||||
// BMediaNode methods
|
// BMediaNode methods
|
||||||
|
|
||||||
BMediaAddOn * AddOn(int32*) const;
|
BMediaAddOn * AddOn(int32*) const;
|
||||||
// void SetRunMode(run_mode);
|
// void SetRunMode(run_mode);
|
||||||
// void Preroll();
|
// void Preroll();
|
||||||
// void SetTimeSource(BTimeSource* time_source);
|
// status_t RequestCompleted(const media_request_info & info);
|
||||||
// status_t RequestCompleted(const media_request_info & info);
|
void NodeRegistered();
|
||||||
|
void Stop(bigtime_t performance_time, bool immediate);
|
||||||
|
void SetTimeSource(BTimeSource * time_source);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
@ -176,43 +176,20 @@ class AudioMixer :
|
|||||||
|
|
||||||
// BMediaEventLooper methods
|
// BMediaEventLooper methods
|
||||||
|
|
||||||
virtual void NodeRegistered();
|
|
||||||
|
|
||||||
virtual void Stop(bigtime_t performance_time,
|
|
||||||
bool immediate);
|
|
||||||
|
|
||||||
void HandleEvent( const media_timed_event *event,
|
void HandleEvent( const media_timed_event *event,
|
||||||
bigtime_t lateness,
|
bigtime_t lateness,
|
||||||
bool realTimeEvent = false);
|
bool realTimeEvent = false);
|
||||||
|
|
||||||
// handle mixing in separate thread
|
|
||||||
// not implemented (yet)
|
|
||||||
|
|
||||||
static int32 _mix_thread_(void *data);
|
|
||||||
int32 MixThread();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
BMediaAddOn *fAddOn;
|
||||||
|
MixerCore *fCore;
|
||||||
|
BParameterWeb *fWeb; // local pointer to parameterweb
|
||||||
BMediaAddOn * fAddOn;
|
BBufferGroup *fBufferGroup;
|
||||||
BParameterWeb * fWeb; // local pointer to parameterweb
|
bigtime_t fDownstreamLatency;
|
||||||
|
bigtime_t fInternalLatency;
|
||||||
bigtime_t fLatency, fInternalLatency; // latency (downstream and internal)
|
bool fDisableStop;
|
||||||
bigtime_t fStartTime; // time node started
|
media_format fDefaultFormat;
|
||||||
uint64 fFramesSent; // audio frames sent
|
|
||||||
bool fOutputEnabled;
|
|
||||||
|
|
||||||
BBufferGroup * fBufferGroup;
|
|
||||||
|
|
||||||
BList fMixerInputs;
|
|
||||||
|
|
||||||
|
|
||||||
bool fDisableStop;
|
|
||||||
|
|
||||||
MixerCore *fCore;
|
|
||||||
media_format fDefaultFormat;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,403 +0,0 @@
|
|||||||
#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) {
|
|
||||||
printf("#### FillMixBuffer(): 1.Should split from B_AUDIO_FLOAT!\n");
|
|
||||||
} else if (mix) {
|
|
||||||
printf("#### FillMixBuffer(): 1.Should mix from B_AUDIO_FLOAT!\n");
|
|
||||||
} 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) {
|
|
||||||
printf("#### FillMixBuffer(): 2.Should split from B_AUDIO_SHORT!\n");
|
|
||||||
} else if (mix) {
|
|
||||||
printf("#### FillMixBuffer(): 2.Should mix from B_AUDIO_SHORT!\n");
|
|
||||||
} 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
|
|
||||||
} else
|
|
||||||
printf("#### sample rate does not match - don't do anything\n");
|
|
||||||
} // 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();
|
|
||||||
if (mixerInputs == 0)
|
|
||||||
printf("#### mixer null\n");
|
|
||||||
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) {
|
|
||||||
printf("#### FillMixBuffer(): 3.Should mix from B_AUDIO_FLOAT!\n");
|
|
||||||
} 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 = int((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) {
|
|
||||||
printf("#### FillMixBuffer(): 4.Should split from B_AUDIO_SHORT!\n");
|
|
||||||
} else if (mix) {
|
|
||||||
printf("#### FillMixBuffer(): 4.Should mix from B_AUDIO_SHORT!\n");
|
|
||||||
} else {
|
|
||||||
// printf("#### FillMixBuffer(): 4.Should copy from B_AUDIO_SHORT!\n");
|
|
||||||
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 = int(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) {
|
|
||||||
printf("#### FillMixBuffer(): 5.Should mix from B_AUDIO_INT!\n");
|
|
||||||
} 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
printf("#### sample rate does not match - don't do anything\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 = int((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;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
printf("##### oh no, unknown conversion %ld #####\n", fOutput.format.u.raw_audio.format);
|
|
||||||
}
|
|
||||||
|
|
||||||
return B_OK;
|
|
||||||
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
// IOStructures.h
|
|
||||||
/*
|
|
||||||
|
|
||||||
Structures to represent IO channels in the AudioMixer node
|
|
||||||
By David Shipman, 2002
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _IO_STRUCT_H
|
|
||||||
#define _IO_STRUCT_H
|
|
||||||
|
|
||||||
class mixer_input
|
|
||||||
{
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
mixer_input(media_input &input);
|
|
||||||
~mixer_input();
|
|
||||||
|
|
||||||
media_input fInput;
|
|
||||||
int32 fProducerDataStatus; // status of upstream data
|
|
||||||
bool enabled; // there is a buffer ready for mixing
|
|
||||||
char * fData;
|
|
||||||
|
|
||||||
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
|
|
@ -4,7 +4,6 @@ UsePrivateHeaders media ;
|
|||||||
|
|
||||||
Addon mixer.media_addon : media :
|
Addon mixer.media_addon : media :
|
||||||
AudioMixer.cpp
|
AudioMixer.cpp
|
||||||
FillMixBuffer.cpp
|
|
||||||
MixerAddOn.cpp
|
MixerAddOn.cpp
|
||||||
MixerCore.cpp
|
MixerCore.cpp
|
||||||
MixerInput.cpp
|
MixerInput.cpp
|
||||||
|
@ -33,32 +33,94 @@ MixerCore::AddOutput(const media_output &output)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
MixerCore::RemoveInput(const media_input &input)
|
MixerCore::RemoveInput(int32 inputID)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
MixerCore::RemoveOutput(const media_output &output)
|
MixerCore::RemoveOutput()
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
int32
|
||||||
MixerCore::OutputBufferLengthChanged(bigtime_t length)
|
MixerCore::CreateInputID()
|
||||||
{
|
{
|
||||||
Lock();
|
return 1;
|
||||||
|
|
||||||
Unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
MixerInput *
|
MixerInput *
|
||||||
MixerCore::Input(int i)
|
MixerCore::Input(int i)
|
||||||
{
|
{
|
||||||
return (MixerInput *)fInputs->ItemAt(i);
|
return (MixerInput *)fInputs->ItemAt(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MixerOutput *
|
||||||
|
MixerCore::Output()
|
||||||
|
{
|
||||||
|
return fOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MixerCore::BufferReceived(BBuffer *buffer, bigtime_t lateness)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MixerCore::InputFormatChanged(int32 inputID, const media_format *format)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MixerCore::OutputFormatChanged(const media_format *format)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MixerCore::SetOutputBufferGroup(BBufferGroup *group)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MixerCore::SetTimeSource(media_node_id id)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MixerCore::EnableOutput(bool enabled)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MixerCore::Start(bigtime_t time)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MixerCore::Stop()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32
|
||||||
|
MixerCore::OutputBufferSize()
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
MixerCore::IsStarted()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MixerCore::OutputBufferLengthChanged(bigtime_t length)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
void BufferReceived(BBuffer *buffer, bigtime_t lateness);
|
void BufferReceived(BBuffer *buffer, bigtime_t lateness);
|
||||||
|
|
||||||
@ -79,4 +141,23 @@ MixerCore::Input(int i)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// use this later for separate threads
|
||||||
|
|
||||||
|
int32
|
||||||
|
AudioMixer::_mix_thread_(void *data)
|
||||||
|
{
|
||||||
|
return ((AudioMixer *)data)->MixThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
int32
|
||||||
|
AudioMixer::MixThread()
|
||||||
|
{
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
snooze(500000);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
*/
|
*/
|
@ -23,7 +23,7 @@ public:
|
|||||||
|
|
||||||
MixerInput *Input(int index); // index = 0 to count-1, NOT inputID
|
MixerInput *Input(int index); // index = 0 to count-1, NOT inputID
|
||||||
MixerOutput *Output();
|
MixerOutput *Output();
|
||||||
|
|
||||||
void Lock();
|
void Lock();
|
||||||
void Unlock();
|
void Unlock();
|
||||||
|
|
||||||
@ -32,8 +32,22 @@ public:
|
|||||||
void InputFormatChanged(int32 inputID, const media_format *format);
|
void InputFormatChanged(int32 inputID, const media_format *format);
|
||||||
void OutputFormatChanged(const media_format *format);
|
void OutputFormatChanged(const media_format *format);
|
||||||
|
|
||||||
|
void SetOutputBufferGroup(BBufferGroup *group);
|
||||||
|
void SetTimeSource(media_node_id id);
|
||||||
|
void EnableOutput(bool enabled);
|
||||||
|
void Start(bigtime_t time);
|
||||||
|
void Stop();
|
||||||
|
|
||||||
|
uint32 OutputBufferSize();
|
||||||
|
bool IsStarted();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void OutputBufferLengthChanged(bigtime_t length);
|
void OutputBufferLengthChanged(bigtime_t length);
|
||||||
|
// handle mixing in separate thread
|
||||||
|
// not implemented (yet)
|
||||||
|
|
||||||
|
static int32 _mix_thread_(void *data);
|
||||||
|
int32 MixThread();
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -16,3 +16,9 @@ void
|
|||||||
MixerInput::BufferReceived(BBuffer *buffer)
|
MixerInput::BufferReceived(BBuffer *buffer)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
media_input &
|
||||||
|
MixerInput::MediaInput()
|
||||||
|
{
|
||||||
|
return fInput;
|
||||||
|
}
|
||||||
|
@ -15,6 +15,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
MixerCore *fCore;
|
MixerCore *fCore;
|
||||||
|
media_input fInput;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -12,4 +12,8 @@ MixerOutput::~MixerOutput()
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
media_output MediaOutput();
|
media_output &
|
||||||
|
MixerOutput::MediaOutput()
|
||||||
|
{
|
||||||
|
return fOutput;
|
||||||
|
}
|
||||||
|
@ -9,10 +9,11 @@ public:
|
|||||||
MixerOutput(MixerCore *core);
|
MixerOutput(MixerCore *core);
|
||||||
~MixerOutput();
|
~MixerOutput();
|
||||||
|
|
||||||
media_output MediaOutput();
|
media_output & MediaOutput();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MixerCore *fCore;
|
MixerCore *fCore;
|
||||||
|
media_output fOutput;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
----------------------
|
|
||||||
Be Sample Code License
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
Copyright 1991-1999, Be Incorporated.
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions
|
|
||||||
are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions, and the following disclaimer.
|
|
||||||
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions, and the following disclaimer in the
|
|
||||||
documentation and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
3. The name of the author may not be used to endorse or promote products
|
|
||||||
derived from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
|
|
||||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
||||||
OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
||||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
|
||||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
||||||
AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
|
||||||
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -1,37 +0,0 @@
|
|||||||
OpenBeOS Audio Mixer
|
|
||||||
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 the buffer recycled immediately.
|
|
||||||
|
|
||||||
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 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, file-readers, SoundPlay, music software - almost all work well
|
|
||||||
|
|
||||||
To Do (vaguely in order of importance)
|
|
||||||
|
|
||||||
- fix bugs
|
|
||||||
- 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)
|
|
||||||
- mixer save (to save parameter states when a node is disconnected)
|
|
||||||
|
|
||||||
Notes :
|
|
||||||
|
|
||||||
Parts of this program are based on code under the Be Sample Code License.
|
|
||||||
This is included in the archive, in accordance with the license.
|
|
Loading…
Reference in New Issue
Block a user