many fixes to improve timing
git-svn-id: file:///srv/svn/repos/haiku/trunk/current@3386 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
67833f26bc
commit
9bedd42cd1
@ -21,6 +21,8 @@
|
||||
|
||||
//
|
||||
|
||||
#define SEND_NEW_BUFFER_EVENT (BTimedEventQueue::B_USER_EVENT + 1)
|
||||
|
||||
mixer_input::mixer_input(media_input &input)
|
||||
{
|
||||
|
||||
@ -225,7 +227,7 @@ AudioMixer::AudioMixer(BMediaAddOn *addOn)
|
||||
fWeb(NULL),
|
||||
fLatency(1),
|
||||
fInternalLatency(1),
|
||||
fStartTime(0), fNextEventTime(0),
|
||||
fStartTime(0),
|
||||
fFramesSent(0),
|
||||
fOutputEnabled(true),
|
||||
fBufferGroup(NULL),
|
||||
@ -236,27 +238,21 @@ AudioMixer::AudioMixer(BMediaAddOn *addOn)
|
||||
|
||||
BMediaNode::AddNodeKind(B_SYSTEM_MIXER);
|
||||
|
||||
// set up the preferred output format
|
||||
|
||||
// set up the preferred output format (although we will accept any format)
|
||||
memset(&fPrefOutputFormat, 0, sizeof(fPrefOutputFormat)); // set everything to wildcard first
|
||||
fPrefOutputFormat.type = B_MEDIA_RAW_AUDIO;
|
||||
fPrefOutputFormat.u.raw_audio.byte_order = B_MEDIA_HOST_ENDIAN;
|
||||
fPrefOutputFormat.u.raw_audio.channel_count = 2;
|
||||
|
||||
fPrefOutputFormat.u.raw_audio.format = media_raw_audio_format::wildcard.format; // B_AUDIO_FLOAT
|
||||
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
|
||||
fPrefOutputFormat.u.raw_audio.channel_count = 2; //media_raw_audio_format::wildcard.channel_count;
|
||||
|
||||
// set up the preferred input format
|
||||
|
||||
// set up the preferred input format (although we will accept any format)
|
||||
memset(&fPrefInputFormat, 0, sizeof(fPrefInputFormat)); // set everything to wildcard first
|
||||
fPrefInputFormat.type = B_MEDIA_RAW_AUDIO;
|
||||
|
||||
fPrefInputFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT;
|
||||
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.byte_order = B_MEDIA_HOST_ENDIAN;
|
||||
|
||||
fPrefInputFormat.u.raw_audio.buffer_size = 1024;
|
||||
fPrefInputFormat.u.raw_audio.channel_count = 2; //media_raw_audio_format::wildcard.channel_count;
|
||||
fPrefInputFormat.u.raw_audio.channel_count = 2;
|
||||
|
||||
//fInput.source = media_source::null;
|
||||
|
||||
@ -489,19 +485,16 @@ AudioMixer::HandleMessage( int32 message, const void *data, size_t size)
|
||||
status_t
|
||||
AudioMixer::AcceptFormat(const media_destination &dest, media_format *format)
|
||||
{
|
||||
// check that the specified format is reasonable for the specified destination, and
|
||||
// fill in any wildcard fields for which our BBufferConsumer has specific requirements.
|
||||
|
||||
// we accept any raw audio
|
||||
|
||||
if (IsValidDest(dest))
|
||||
{
|
||||
if ((format->type != B_MEDIA_UNKNOWN_TYPE) && (format->type != B_MEDIA_RAW_AUDIO))
|
||||
return B_MEDIA_BAD_FORMAT;
|
||||
else
|
||||
return B_OK;
|
||||
}
|
||||
else
|
||||
if (!IsValidDest(dest))
|
||||
return B_MEDIA_BAD_DESTINATION;
|
||||
|
||||
|
||||
if ((format->type != B_MEDIA_UNKNOWN_TYPE) && (format->type != B_MEDIA_RAW_AUDIO))
|
||||
return B_MEDIA_BAD_FORMAT;
|
||||
else
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
status_t
|
||||
@ -523,94 +516,116 @@ AudioMixer::GetNextInput(int32 *cookie, media_input *out_input)
|
||||
void
|
||||
AudioMixer::DisposeInputCookie(int32 cookie)
|
||||
{
|
||||
|
||||
// nothing yet
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
AudioMixer::BufferReceived(BBuffer *buffer)
|
||||
{
|
||||
|
||||
if (buffer)
|
||||
{
|
||||
if (buffer->Header()->type == B_MEDIA_PARAMETERS)
|
||||
{
|
||||
printf("Control Buffer Received\n");
|
||||
ApplyParameterData(buffer->Data(), buffer->SizeUsed());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (buffer->Header()->type == B_MEDIA_PARAMETERS) {
|
||||
printf("Control Buffer Received\n");
|
||||
ApplyParameterData(buffer->Data(), buffer->SizeUsed());
|
||||
buffer->Recycle();
|
||||
return;
|
||||
}
|
||||
|
||||
buffer->Recycle();
|
||||
// to receive the buffer at the right time,
|
||||
// push it through the event looper
|
||||
media_timed_event event(buffer->Header()->start_time,
|
||||
BTimedEventQueue::B_HANDLE_BUFFER,
|
||||
buffer,
|
||||
BTimedEventQueue::B_RECYCLE_BUFFER);
|
||||
EventQueue()->AddEvent(event);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioMixer::HandleInputBuffer(BBuffer *buffer)
|
||||
{
|
||||
media_header *hdr = buffer->Header();
|
||||
|
||||
// check input
|
||||
int inputcount = fMixerInputs.CountItems();
|
||||
|
||||
for (int i = 0; i < inputcount; i++) {
|
||||
|
||||
mixer_input *channel = (mixer_input *)fMixerInputs.ItemAt(i);
|
||||
|
||||
if (channel->fInput.destination.id != hdr->destination)
|
||||
continue;
|
||||
|
||||
bigtime_t now = TimeSource()->Now();
|
||||
bigtime_t perf_time = hdr->start_time;
|
||||
bigtime_t how_late = now - perf_time;
|
||||
bigtime_t event_latency = EventLatency();
|
||||
|
||||
if (how_late > (event_latency + 2000)) {
|
||||
how_late -= event_latency;
|
||||
printf("Received buffer %Ld usecs late from %s\n", how_late, channel->fInput.name);
|
||||
if (RunMode() != B_OFFLINE && RunMode() != B_RECORDING) {
|
||||
printf("sending notify\n");
|
||||
NotifyLateProducer(channel->fInput.source, max_c(500, how_late), perf_time);
|
||||
} else if (RunMode() == B_DROP_DATA) {
|
||||
printf("dropping buffer\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
*/
|
||||
size_t total_offset = int(channel->fEventOffset + channel->fInput.format.u.raw_audio.buffer_size) % 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);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioMixer::SendNewBuffer(bigtime_t event_time)
|
||||
{
|
||||
|
||||
BBuffer *outbuffer = fBufferGroup->RequestBuffer(fOutput.format.u.raw_audio.buffer_size, BufferDuration());
|
||||
if (!outbuffer) {
|
||||
printf("Could not allocate buffer\n");
|
||||
return;
|
||||
}
|
||||
|
||||
FillMixBuffer(outbuffer->Data(), outbuffer->SizeAvailable());
|
||||
|
||||
media_header *outheader = outbuffer->Header();
|
||||
outheader->type = B_MEDIA_RAW_AUDIO;
|
||||
outheader->size_used = fOutput.format.u.raw_audio.buffer_size;
|
||||
outheader->time_source = TimeSource()->ID();
|
||||
outheader->start_time = event_time;
|
||||
|
||||
if (B_OK != SendBuffer(outbuffer, fOutput.destination)) {
|
||||
printf("Could not send buffer to output : %s\n", fOutput.name);
|
||||
outbuffer->Recycle();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioMixer::ProducerDataStatus( const media_destination &for_whom,
|
||||
int32 status, bigtime_t at_performance_time)
|
||||
@ -651,8 +666,14 @@ status_t
|
||||
AudioMixer::Connected( const media_source &producer, const media_destination &where,
|
||||
const media_format &with_format, media_input *out_input)
|
||||
{
|
||||
if (! IsValidDest(where))
|
||||
if (!IsValidDest(where))
|
||||
return B_MEDIA_BAD_DESTINATION;
|
||||
|
||||
// We need to make sure that the outInput's name field contains a valid name,
|
||||
// the name given the connection by the producer may still be an empty string.
|
||||
|
||||
// If we want the producer to use a specific BBufferGroup, we now need to call
|
||||
// BMediaRoster::SetOutputBuffersFor() here to set the producer's buffer group
|
||||
|
||||
char *name = out_input->name;
|
||||
mixer_input *mixerInput;
|
||||
@ -997,7 +1018,7 @@ AudioMixer::PrepareToConnect(const media_source &what, const media_destination &
|
||||
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;
|
||||
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
|
||||
@ -1038,6 +1059,7 @@ AudioMixer::Connect( status_t error, const media_source &source, const media_des
|
||||
// Do so, then make sure we get our events early enough.
|
||||
media_node_id id;
|
||||
FindLatencyFor(fOutput.destination, &fLatency, &id);
|
||||
printf("Downstream Latency is %Ld usecs\n", fLatency);
|
||||
|
||||
// we need at least the length of a full output buffer's latency (I think?)
|
||||
|
||||
@ -1054,30 +1076,27 @@ AudioMixer::Connect( status_t error, const media_source &source, const media_des
|
||||
bigtime_t latency_end = TimeSource()->RealTime();
|
||||
|
||||
fInternalLatency = latency_end - latency_start;
|
||||
printf("Latency set at %d usecs\n", fInternalLatency);
|
||||
printf("Internal latency is %Ld usecs\n", fInternalLatency);
|
||||
|
||||
delete mouse;
|
||||
|
||||
|
||||
// might need to tweak the latency
|
||||
|
||||
SetEventLatency(fLatency + fInternalLatency);
|
||||
|
||||
// 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;
|
||||
// }
|
||||
|
||||
bigtime_t duration = bigtime_t(1000000) * framesPerBuffer / bigtime_t(fOutput.format.u.raw_audio.frame_rate);
|
||||
SetBufferDuration(duration);
|
||||
// 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
|
||||
// buffer group (via SetBufferGroup()) prior to this. That can happen, for example,
|
||||
// if the consumer calls SetOutputBuffersFor() on us from within its Connected()
|
||||
// method.
|
||||
if (!fBufferGroup) AllocateBuffers();
|
||||
if (!fBufferGroup)
|
||||
AllocateBuffers();
|
||||
}
|
||||
|
||||
|
||||
@ -1101,19 +1120,19 @@ 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)
|
||||
{
|
||||
|
||||
// We've produced some late buffers... Increase Latency
|
||||
// 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);
|
||||
|
||||
if (what == fOutput.source)
|
||||
if (RunMode() == B_INCREASE_LATENCY)
|
||||
{
|
||||
if (what == fOutput.source) {
|
||||
if (RunMode() == B_INCREASE_LATENCY) {
|
||||
fInternalLatency += how_much;
|
||||
|
||||
printf("AudioMixer: increasing internal latency to %Ld usec\n", fInternalLatency);
|
||||
SetEventLatency(fLatency + fInternalLatency);
|
||||
}
|
||||
|
||||
printf("Late notice received. Buffer was %d usecs late\n", how_much);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1172,91 +1191,42 @@ AudioMixer::NodeRegistered()
|
||||
|
||||
|
||||
void
|
||||
AudioMixer::HandleEvent( const media_timed_event *event, bigtime_t lateness,
|
||||
bool realTimeEvent = false)
|
||||
AudioMixer::HandleEvent( const media_timed_event *event, bigtime_t lateness, bool realTimeEvent)
|
||||
{
|
||||
|
||||
switch (event->type)
|
||||
{
|
||||
|
||||
case BTimedEventQueue::B_HANDLE_BUFFER:
|
||||
{
|
||||
HandleInputBuffer((BBuffer *)event->pointer);
|
||||
((BBuffer *)event->pointer)->Recycle();
|
||||
break;
|
||||
}
|
||||
|
||||
// check output
|
||||
if (fOutput.destination != media_destination::null && fOutputEnabled) // this is in the wrong order too
|
||||
{
|
||||
|
||||
BBuffer *outbuffer = fBufferGroup->RequestBuffer(fOutput.format.u.raw_audio.buffer_size, BufferDuration());
|
||||
|
||||
if (outbuffer)
|
||||
{
|
||||
case SEND_NEW_BUFFER_EVENT:
|
||||
{
|
||||
|
||||
FillMixBuffer(outbuffer->Data(), outbuffer->SizeAvailable());
|
||||
|
||||
media_header *outheader = outbuffer->Header();
|
||||
outheader->type = B_MEDIA_RAW_AUDIO;
|
||||
outheader->size_used = fOutput.format.u.raw_audio.buffer_size;
|
||||
outheader->time_source = TimeSource()->ID();
|
||||
|
||||
// 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;
|
||||
fNextEventTime = fStartTime;
|
||||
}
|
||||
|
||||
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 fNextEventTime
|
||||
{
|
||||
|
||||
// we're in a live performance mode
|
||||
// use the start time we calculate at the end of the the mix loop
|
||||
// this time is based on the offset of all media produced so far,
|
||||
// plus fStartTime, which is the recorded time of our first event
|
||||
|
||||
stamp = fNextEventTime;
|
||||
|
||||
}
|
||||
|
||||
outheader->start_time = stamp;
|
||||
|
||||
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 = 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
|
||||
|
||||
fNextEventTime = bigtime_t(fStartTime + double(fFramesSent / fOutput.format.u.raw_audio.frame_rate) * 1000000.0);
|
||||
|
||||
media_timed_event nextBufferEvent(fNextEventTime, BTimedEventQueue::B_HANDLE_BUFFER);
|
||||
EventQueue()->AddEvent(nextBufferEvent);
|
||||
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
if (fOutputEnabled && fOutput.destination != media_destination::null)
|
||||
SendNewBuffer(event->event_time);
|
||||
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
|
||||
case BTimedEventQueue::B_START:
|
||||
|
||||
@ -1269,7 +1239,7 @@ AudioMixer::HandleEvent( const media_timed_event *event, bigtime_t lateness,
|
||||
|
||||
//fThread = spawn_thread(_mix_thread_, "audio mixer thread", B_REAL_TIME_PRIORITY, this);
|
||||
|
||||
media_timed_event firstBufferEvent(event->event_time, BTimedEventQueue::B_HANDLE_BUFFER);
|
||||
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:
|
||||
@ -1289,6 +1259,7 @@ AudioMixer::HandleEvent( const media_timed_event *event, bigtime_t lateness,
|
||||
// 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, SEND_NEW_BUFFER_EVENT);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1344,9 +1315,14 @@ 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);
|
||||
|
||||
fBufferGroup = new BBufferGroup(size, count);
|
||||
|
||||
if (count < 2) {
|
||||
printf("AudioMixer: calculated only %ld buffers, that's not enough\n", count);
|
||||
count = 2;
|
||||
}
|
||||
|
||||
printf("AudioMixer: allocating %ld buffers\n", count);
|
||||
fBufferGroup = new BBufferGroup(size, count);
|
||||
}
|
||||
|
||||
// use this later for separate threads
|
||||
|
@ -47,6 +47,9 @@ class AudioMixer :
|
||||
|
||||
void AllocateBuffers();
|
||||
status_t FillMixBuffer(void *outbuffer, size_t size);
|
||||
|
||||
void SendNewBuffer(bigtime_t event_time);
|
||||
void HandleInputBuffer(BBuffer *buffer);
|
||||
|
||||
// BMediaNode methods
|
||||
|
||||
@ -200,8 +203,8 @@ class AudioMixer :
|
||||
BMediaAddOn * fAddOn;
|
||||
BParameterWeb * fWeb; // local pointer to parameterweb
|
||||
|
||||
bigtime_t fLatency, fInternalLatency; // latency (total and internal)
|
||||
bigtime_t fStartTime, fNextEventTime; // time node started, time of next (output) event
|
||||
bigtime_t fLatency, fInternalLatency; // latency (downstream and internal)
|
||||
bigtime_t fStartTime; // time node started
|
||||
uint64 fFramesSent; // audio frames sent
|
||||
bool fOutputEnabled;
|
||||
|
||||
|
@ -130,6 +130,7 @@ MultiAudioNode::MultiAudioNode(BMediaAddOn *addon, char* name, MultiAudioDevice
|
||||
AddNodeKind( B_PHYSICAL_INPUT );
|
||||
|
||||
// initialize our preferred format object
|
||||
memset(&fPreferredFormat, 0, sizeof(fPreferredFormat)); // set everything to wildcard first
|
||||
fPreferredFormat.type = B_MEDIA_RAW_AUDIO;
|
||||
fPreferredFormat.u.raw_audio.format = MultiAudioDevice::convert_multiaudio_format_to_media_format(fDevice->MFI.output.format);
|
||||
fPreferredFormat.u.raw_audio.channel_count = 2;
|
||||
|
@ -176,9 +176,9 @@ media_multistream_format media_multistream_format::wildcard = {0};
|
||||
*************************************************************/
|
||||
|
||||
bool
|
||||
media_format::Matches(const media_format *otherFormat) const
|
||||
media_format::Matches(const media_format *other) const
|
||||
{
|
||||
return format_is_compatible(*this, *otherFormat);
|
||||
return type == other->type; // XXX fixthis
|
||||
}
|
||||
|
||||
|
||||
@ -412,10 +412,7 @@ bool operator==(const media_format & a, const media_format & b)
|
||||
/* return true if a and b are compatible (accounting for wildcards) */
|
||||
bool format_is_compatible(const media_format & a, const media_format & b) /* a is the format you want to feed to something accepting b */
|
||||
{
|
||||
UNIMPLEMENTED();
|
||||
if (a.type == b.type)
|
||||
return true;
|
||||
return false;
|
||||
return a.Matches(&b);
|
||||
}
|
||||
|
||||
bool string_for_format(const media_format & f, char * buf, size_t size)
|
||||
|
@ -33,6 +33,7 @@ static char __copyright[] = "Copyright (c) 2002, 2003 Marcus Overhagen <Marcus@O
|
||||
#include <MediaEventLooper.h>
|
||||
#include <TimeSource.h>
|
||||
#include <scheduler.h>
|
||||
#include <Buffer.h>
|
||||
#include "debug.h"
|
||||
|
||||
// XXX The bebook says that the latency is always calculated in realtime
|
||||
@ -177,6 +178,7 @@ BMediaEventLooper::SetRunMode(run_mode mode)
|
||||
if(fControlThread > 0) {
|
||||
set_thread_priority(fControlThread, fCurrentPriority);
|
||||
fSchedulingLatency = estimate_max_scheduling_latency(fControlThread);
|
||||
printf("BMediaEventLooper: SchedulingLatency is %Ld\n", fSchedulingLatency);
|
||||
}
|
||||
}
|
||||
|
||||
@ -221,7 +223,6 @@ BMediaEventLooper::ControlLoop()
|
||||
// BMediaEventLooper compensates your performance time by adding the event latency
|
||||
// (see SetEventLatency()) and the scheduling latency (or, for real-time events,
|
||||
// only the scheduling latency).
|
||||
// latency = fOut.downstream_latency + fOut.processing_latency + fSchedulingLatency;
|
||||
// XXX well, fix this later
|
||||
latency = fEventLatency + fSchedulingLatency;
|
||||
|
||||
@ -261,10 +262,10 @@ BMediaEventLooper::ControlLoop()
|
||||
if (err == B_OK) {
|
||||
bigtime_t lateness;
|
||||
if (is_realtime)
|
||||
lateness = TimeSource()->RealTime() - event.event_time;
|
||||
lateness = TimeSource()->RealTime() - event.event_time - fEventLatency;
|
||||
else
|
||||
lateness = TimeSource()->Now() - event.event_time;
|
||||
DispatchEvent(&event,lateness,is_realtime);
|
||||
lateness = TimeSource()->Now() - event.event_time - fEventLatency;
|
||||
DispatchEvent(&event, lateness, is_realtime);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -356,6 +357,7 @@ BMediaEventLooper::SetPriority(int32 priority)
|
||||
if(fControlThread > 0) {
|
||||
set_thread_priority(fControlThread, fCurrentPriority);
|
||||
fSchedulingLatency = estimate_max_scheduling_latency(fControlThread);
|
||||
printf("BMediaEventLooper: SchedulingLatency is %Ld\n", fSchedulingLatency);
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
@ -419,6 +421,7 @@ BMediaEventLooper::Run()
|
||||
|
||||
// get latency information
|
||||
fSchedulingLatency = estimate_max_scheduling_latency(fControlThread);
|
||||
printf("BMediaEventLooper: SchedulingLatency is %Ld\n", fSchedulingLatency);
|
||||
}
|
||||
|
||||
|
||||
@ -471,7 +474,8 @@ BMediaEventLooper::DispatchEvent(const media_timed_event *event,
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
_DispatchCleanUp(event);
|
||||
}
|
||||
|
||||
/*************************************************************
|
||||
|
@ -2109,6 +2109,8 @@ BMediaRosterEx::GetDormantFlavorInfo(media_addon_id addonid,
|
||||
dormant_flavor_info * out_flavor)
|
||||
{
|
||||
CALLED();
|
||||
if (out_flavor == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
xfer_server_get_dormant_flavor_info msg;
|
||||
xfer_server_get_dormant_flavor_info_reply *reply;
|
||||
@ -2300,8 +2302,27 @@ BMediaRoster::GetFormatFor(const media_output & output,
|
||||
media_format * io_format,
|
||||
uint32 flags)
|
||||
{
|
||||
UNIMPLEMENTED();
|
||||
return B_ERROR;
|
||||
CALLED();
|
||||
if (io_format == NULL)
|
||||
return B_BAD_VALUE;
|
||||
if ((output.node.kind & B_BUFFER_PRODUCER) == 0)
|
||||
return B_MEDIA_BAD_NODE;
|
||||
if (IS_INVALID_SOURCE(output.source))
|
||||
return B_MEDIA_BAD_SOURCE;
|
||||
|
||||
producer_format_suggestion_requested_request request;
|
||||
producer_format_suggestion_requested_reply reply;
|
||||
status_t rv;
|
||||
|
||||
request.type = B_MEDIA_UNKNOWN_TYPE;
|
||||
request.quality = 0; // XXX what should this be?
|
||||
|
||||
rv = QueryPort(output.source.port, PRODUCER_FORMAT_SUGGESTION_REQUESTED, &request, sizeof(request), &reply, sizeof(reply));
|
||||
if (rv != B_OK)
|
||||
return rv;
|
||||
|
||||
*io_format = reply.format;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
@ -2310,8 +2331,27 @@ BMediaRoster::GetFormatFor(const media_input & input,
|
||||
media_format * io_format,
|
||||
uint32 flags)
|
||||
{
|
||||
UNIMPLEMENTED();
|
||||
return B_ERROR;
|
||||
CALLED();
|
||||
if (io_format == NULL)
|
||||
return B_BAD_VALUE;
|
||||
if ((input.node.kind & B_BUFFER_CONSUMER) == 0)
|
||||
return B_MEDIA_BAD_NODE;
|
||||
if (IS_INVALID_DESTINATION(input.destination))
|
||||
return B_MEDIA_BAD_DESTINATION;
|
||||
|
||||
consumer_accept_format_request request;
|
||||
consumer_accept_format_reply reply;
|
||||
status_t rv;
|
||||
|
||||
request.dest = input.destination;
|
||||
memset(&request.format, 0, sizeof(request.format)); // wildcard
|
||||
|
||||
rv = QueryPort(input.destination.port, CONSUMER_ACCEPT_FORMAT, &request, sizeof(request), &reply, sizeof(reply));
|
||||
if (rv != B_OK)
|
||||
return rv;
|
||||
|
||||
*io_format = reply.format;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
@ -2321,6 +2361,14 @@ BMediaRoster::GetFormatFor(const media_node & node,
|
||||
float quality)
|
||||
{
|
||||
UNIMPLEMENTED();
|
||||
if (io_format == NULL)
|
||||
return B_BAD_VALUE;
|
||||
if (IS_INVALID_NODE(node))
|
||||
return B_MEDIA_BAD_NODE;
|
||||
if ((node.kind & (B_BUFFER_CONSUMER | B_BUFFER_PRODUCER)) == 0)
|
||||
return B_MEDIA_BAD_NODE;
|
||||
|
||||
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
@ -269,6 +269,7 @@ _shared_buffer_list::RecycleBuffer(BBuffer *buffer)
|
||||
reclaimed_count++;
|
||||
if (info[i].reclaimed) {
|
||||
FATAL("_shared_buffer_list: Error, BBuffer %p, id = %ld already reclaimed\n", buffer, id);
|
||||
DEBUG_ONLY(debugger("buffer already reclaimed"));
|
||||
continue;
|
||||
}
|
||||
info[i].reclaimed = true;
|
||||
|
@ -31,7 +31,8 @@ _SoundPlayNode::_SoundPlayNode(const char *name, const media_multi_audio_format
|
||||
mInitCheckStatus(B_OK),
|
||||
mOutputEnabled(true),
|
||||
mBufferGroup(NULL),
|
||||
mFramesSent(0)
|
||||
mFramesSent(0),
|
||||
mTooEarlyCount(0)
|
||||
{
|
||||
CALLED();
|
||||
mPreferredFormat.type = B_MEDIA_RAW_AUDIO;
|
||||
@ -369,7 +370,7 @@ _SoundPlayNode::Connect(status_t error, const media_source& source, const media_
|
||||
FindLatencyFor(mOutput.destination, &mLatency, &id);
|
||||
fprintf(stderr, "\tdownstream latency = %Ld\n", mLatency);
|
||||
|
||||
mInternalLatency = 10LL;
|
||||
mInternalLatency = 5000LL;
|
||||
fprintf(stderr, "\tbuffer-filling took %Ld usec on this machine\n", mInternalLatency);
|
||||
SetEventLatency(mLatency + mInternalLatency);
|
||||
|
||||
@ -421,6 +422,8 @@ _SoundPlayNode::LateNoticeReceived(const media_source& what, bigtime_t how_much,
|
||||
{
|
||||
CALLED();
|
||||
|
||||
printf("_SoundPlayNode::LateNoticeReceived, %Ld too late at %Ld\n", how_much, performance_time);
|
||||
|
||||
// is this our output?
|
||||
if (what != mOutput.source)
|
||||
{
|
||||
@ -430,6 +433,7 @@ _SoundPlayNode::LateNoticeReceived(const media_source& what, bigtime_t how_much,
|
||||
|
||||
// If we're late, we need to catch up. Respond in a manner appropriate to our
|
||||
// current run mode.
|
||||
/*
|
||||
if (RunMode() == B_RECORDING)
|
||||
{
|
||||
// A hardware capture node can't adjust; it simply emits buffers at
|
||||
@ -438,13 +442,16 @@ _SoundPlayNode::LateNoticeReceived(const media_source& what, bigtime_t how_much,
|
||||
// can't choose to capture "sooner"....
|
||||
}
|
||||
else if (RunMode() == B_INCREASE_LATENCY)
|
||||
*/
|
||||
if (RunMode() != B_DROP_DATA)
|
||||
{
|
||||
// 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.
|
||||
mInternalLatency += how_much;
|
||||
// mInternalLatency += how_much;
|
||||
mLatency += 1000;
|
||||
SetEventLatency(mLatency + mInternalLatency);
|
||||
|
||||
fprintf(stderr, "\tincreasing latency to %Ld\n", mLatency + mInternalLatency);
|
||||
@ -552,39 +559,70 @@ status_t
|
||||
_SoundPlayNode::HandleBuffer(
|
||||
const media_timed_event *event,
|
||||
bigtime_t lateness,
|
||||
bool realTimeEvent = false)
|
||||
bool realTimeEvent)
|
||||
{
|
||||
CALLED();
|
||||
|
||||
// make sure we're both started *and* connected before delivering a buffer
|
||||
if ((RunState() == BMediaEventLooper::B_STARTED) && (mOutput.destination != media_destination::null))
|
||||
{
|
||||
if ((RunState() != BMediaEventLooper::B_STARTED) || (mOutput.destination == media_destination::null))
|
||||
return B_OK;
|
||||
|
||||
// The event->event_time is the time at which the buffer we are preparing here should
|
||||
// arrive at it's destination. The MediaEventLooper should have scheduled us early enough
|
||||
// (based on EventLatency() and the SchedulingLatency()) to make this possible.
|
||||
|
||||
bigtime_t scheduling_latency = SchedulingLatency();
|
||||
|
||||
if (lateness > 0) {
|
||||
printf("_SoundPlayNode::HandleBuffer, event sheduled too late, lateness is %Ld\n", lateness);
|
||||
mInternalLatency += 1000;
|
||||
SetEventLatency(mLatency + mInternalLatency);
|
||||
}
|
||||
|
||||
// skip buffer creation if output not enabled
|
||||
if (mOutputEnabled) {
|
||||
|
||||
// Get the next buffer of data
|
||||
BBuffer* buffer = FillNextBuffer(event->event_time);
|
||||
if (buffer)
|
||||
{
|
||||
|
||||
if (buffer) {
|
||||
|
||||
// If we are ready way too early, decrase internal latency
|
||||
bigtime_t how_early = event->event_time - TimeSource()->Now() - mLatency;
|
||||
if (how_early > (3 * scheduling_latency)) {
|
||||
|
||||
printf("_SoundPlayNode::HandleBuffer, event scheduled too early, how_early is %Ld\n", how_early);
|
||||
|
||||
if (mTooEarlyCount++ == 5) {
|
||||
mInternalLatency -= how_early;
|
||||
if (mInternalLatency < 500)
|
||||
mInternalLatency = 500;
|
||||
printf("_SoundPlayNode::HandleBuffer setting internal latency to %Ld\n", mInternalLatency);
|
||||
SetEventLatency(mLatency + mInternalLatency);
|
||||
mTooEarlyCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// send the buffer downstream if and only if output is enabled
|
||||
status_t err = B_ERROR;
|
||||
if (mOutputEnabled) err = SendBuffer(buffer, mOutput.destination);
|
||||
if (err)
|
||||
{
|
||||
// we need to recycle the buffer ourselves if output is disabled or
|
||||
if (B_OK != SendBuffer(buffer, mOutput.destination)) {
|
||||
// we need to recycle the buffer
|
||||
// if the call to SendBuffer() fails
|
||||
buffer->Recycle();
|
||||
}
|
||||
}
|
||||
|
||||
// track how much media we've delivered so far
|
||||
size_t nFrames = mOutput.format.u.raw_audio.buffer_size
|
||||
/ (mOutput.format.u.raw_audio.format & media_raw_audio_format::B_AUDIO_SIZE_MASK)
|
||||
/ mOutput.format.u.raw_audio.channel_count;
|
||||
mFramesSent += nFrames;
|
||||
|
||||
// The buffer is on its way; now schedule the next one to go
|
||||
bigtime_t nextEvent = mStartTime + bigtime_t(double(mFramesSent) / double(mOutput.format.u.raw_audio.frame_rate) * 1000000.0);
|
||||
media_timed_event nextBufferEvent(nextEvent, BTimedEventQueue::B_HANDLE_BUFFER);
|
||||
EventQueue()->AddEvent(nextBufferEvent);
|
||||
}
|
||||
|
||||
// track how much media we've delivered so far
|
||||
size_t nFrames = mOutput.format.u.raw_audio.buffer_size
|
||||
/ (mOutput.format.u.raw_audio.format & media_raw_audio_format::B_AUDIO_SIZE_MASK)
|
||||
/ mOutput.format.u.raw_audio.channel_count;
|
||||
mFramesSent += nFrames;
|
||||
|
||||
// The buffer is on its way; now schedule the next one to go
|
||||
// nextEvent is the time at which the buffer should arrive at it's destination
|
||||
bigtime_t nextEvent = mStartTime + bigtime_t(double(mFramesSent) / double(mOutput.format.u.raw_audio.frame_rate) * 1000000.0);
|
||||
media_timed_event nextBufferEvent(nextEvent, BTimedEventQueue::B_HANDLE_BUFFER);
|
||||
EventQueue()->AddEvent(nextBufferEvent);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
@ -690,6 +728,10 @@ _SoundPlayNode::AllocateBuffers()
|
||||
|
||||
DPRINTF("\tlatency = %Ld, buffer duration = %Ld\n", mLatency, BufferDuration());
|
||||
DPRINTF("\tcreating group of %ld buffers, size = %lu\n", count, size);
|
||||
|
||||
if (count < 2)
|
||||
count == 2;
|
||||
|
||||
mBufferGroup = new BBufferGroup(size, count);
|
||||
}
|
||||
|
||||
@ -719,26 +761,8 @@ _SoundPlayNode::FillNextBuffer(bigtime_t event_time)
|
||||
hdr->type = B_MEDIA_RAW_AUDIO;
|
||||
hdr->size_used = mOutput.format.u.raw_audio.buffer_size;
|
||||
hdr->time_source = TimeSource()->ID();
|
||||
hdr->start_time = event_time;
|
||||
|
||||
bigtime_t stamp;
|
||||
if (RunMode() == B_RECORDING)
|
||||
{
|
||||
// In B_RECORDING mode, we stamp with the capture time. We're not
|
||||
// really a hardware capture node, but we simulate it by using the (precalculated)
|
||||
// time at which this buffer "should" have been created.
|
||||
stamp = event_time;
|
||||
}
|
||||
else
|
||||
{
|
||||
// okay, we're in one of the "live" performance run modes. in these modes, we
|
||||
// stamp the buffer with the time at which the buffer should be rendered to the
|
||||
// output, not with the capture time. mStartTime is the cached value of the
|
||||
// first buffer's performance time; we calculate this buffer's performance time as
|
||||
// an offset from that time, based on the amount of media we've created so far.
|
||||
// Recalculating every buffer like this avoids accumulation of error.
|
||||
stamp = mStartTime + bigtime_t(double(mFramesSent) / double(mOutput.format.u.raw_audio.frame_rate) * 1000000.0);
|
||||
}
|
||||
hdr->start_time = stamp;
|
||||
DPRINTF("TimeSource()->Now() : %li\n", TimeSource()->Now());
|
||||
DPRINTF("hdr->start_time : %li\n", hdr->start_time);
|
||||
DPRINTF("mFramesSent : %li\n", mFramesSent);
|
||||
|
@ -169,6 +169,7 @@ private:
|
||||
bigtime_t mInternalLatency;
|
||||
bigtime_t mStartTime;
|
||||
uint64 mFramesSent;
|
||||
int32 mTooEarlyCount;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -155,11 +155,11 @@ BSoundPlayer::Start()
|
||||
BTimeSource *timeSource = roster->MakeTimeSourceFor(_m_node->Node());
|
||||
|
||||
// make sure we give the producer enough time to run buffers through
|
||||
// the node chain, otherwise it'll start up already late
|
||||
// the node chain, otherwise it'll start up already late
|
||||
bigtime_t latency = 0;
|
||||
status_t err = roster->GetLatencyFor(_m_node->Node(), &latency);
|
||||
|
||||
err = roster->StartNode(_m_node->Node(), timeSource->Now() + latency);
|
||||
err = roster->StartNode(_m_node->Node(), timeSource->Now() + latency + 5000);
|
||||
|
||||
timeSource->Release();
|
||||
|
||||
|
@ -155,11 +155,13 @@ _event_queue_imp::RemoveFirstEvent(media_timed_event * outEvent)
|
||||
if (fFirstEntry == 0)
|
||||
return B_ERROR;
|
||||
|
||||
if (outEvent != 0)
|
||||
if (outEvent != 0) {
|
||||
// No cleanup here
|
||||
*outEvent = fFirstEntry->event;
|
||||
else
|
||||
} else {
|
||||
CleanupEvent(&fFirstEntry->event);
|
||||
|
||||
}
|
||||
|
||||
RemoveEntry(fFirstEntry);
|
||||
|
||||
return B_OK;
|
||||
@ -560,6 +562,7 @@ _event_queue_imp::CleanupEvent(media_timed_event *event)
|
||||
// do nothing
|
||||
} else if (event->type == BTimedEventQueue::B_HANDLE_BUFFER && event->cleanup == BTimedEventQueue::B_RECYCLE_BUFFER) {
|
||||
((BBuffer *)event->pointer)->Recycle();
|
||||
DEBUG_ONLY(*const_cast<void **>(&event->pointer) = NULL);
|
||||
} else if (event->cleanup == BTimedEventQueue::B_EXPIRE_TIMER) {
|
||||
// call TimerExpired() on the event->data
|
||||
debugger("BTimedEventQueue cleanup: calling TimerExpired() should be implemented here\n");
|
||||
|
@ -13,6 +13,9 @@
|
||||
/* no locking used in this file, we assume that the caller (NodeManager) does it.
|
||||
*/
|
||||
|
||||
|
||||
#define MAX_NODE_INFOS 10
|
||||
|
||||
DefaultManager::DefaultManager()
|
||||
: fMixerConnected(false),
|
||||
fPhysicalVideoOut(-1),
|
||||
@ -230,14 +233,14 @@ DefaultManager::FindPhysicalVideoIn()
|
||||
void
|
||||
DefaultManager::FindPhysicalAudioOut()
|
||||
{
|
||||
live_node_info info[2];
|
||||
live_node_info info[MAX_NODE_INFOS];
|
||||
media_format input; /* a physical audio output has a logical data input */
|
||||
int32 count;
|
||||
status_t rv;
|
||||
|
||||
memset(&input, 0, sizeof(input));
|
||||
input.type = B_MEDIA_RAW_AUDIO;
|
||||
count = 2;
|
||||
count = MAX_NODE_INFOS;
|
||||
rv = BMediaRoster::Roster()->GetLiveNodes(&info[0], &count, &input, NULL, NULL, B_BUFFER_CONSUMER | B_PHYSICAL_OUTPUT);
|
||||
if (rv != B_OK || count < 1) {
|
||||
printf("Couldn't find physical audio output node\n");
|
||||
@ -249,7 +252,7 @@ DefaultManager::FindPhysicalAudioOut()
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (0 == strcmp(info[i].name, "None Out")) // skip the Null audio driver
|
||||
continue;
|
||||
printf("Default physical audio output created!\n");
|
||||
printf("Default physical audio output \"%s\" created!\n", info[i].name);
|
||||
fPhysicalAudioOut = info[i].node.node;
|
||||
return;
|
||||
}
|
||||
@ -258,14 +261,14 @@ DefaultManager::FindPhysicalAudioOut()
|
||||
void
|
||||
DefaultManager::FindPhysicalAudioIn()
|
||||
{
|
||||
live_node_info info[2];
|
||||
live_node_info info[MAX_NODE_INFOS];
|
||||
media_format output; /* a physical audio input has a logical data output */
|
||||
int32 count;
|
||||
status_t rv;
|
||||
|
||||
memset(&output, 0, sizeof(output));
|
||||
output.type = B_MEDIA_RAW_AUDIO;
|
||||
count = 2;
|
||||
count = MAX_NODE_INFOS;
|
||||
rv = BMediaRoster::Roster()->GetLiveNodes(&info[0], &count, NULL, &output, NULL, B_BUFFER_PRODUCER | B_PHYSICAL_INPUT);
|
||||
if (rv != B_OK || count < 1) {
|
||||
printf("Couldn't find physical audio input node\n");
|
||||
@ -277,7 +280,7 @@ DefaultManager::FindPhysicalAudioIn()
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (0 == strcmp(info[i].name, "None In")) // skip the Null audio driver
|
||||
continue;
|
||||
printf("Default physical audio input created!\n");
|
||||
printf("Default physical audio input \"%s\" created!\n", info[i].name);
|
||||
fPhysicalAudioIn = info[i].node.node;
|
||||
return;
|
||||
}
|
||||
@ -286,26 +289,54 @@ DefaultManager::FindPhysicalAudioIn()
|
||||
void
|
||||
DefaultManager::FindTimeSource()
|
||||
{
|
||||
live_node_info info;
|
||||
live_node_info info[MAX_NODE_INFOS];
|
||||
media_format input; /* a physical audio output has a logical data input (DAC)*/
|
||||
int32 count;
|
||||
status_t rv;
|
||||
|
||||
/* We try to use the default physical audio out node,
|
||||
* as it most likely is a timesource.
|
||||
* XXX if that fails, we might use other audio or video clock timesources
|
||||
/* First try to use the current default physical audio out
|
||||
*/
|
||||
if (fPhysicalAudioOut != -1) {
|
||||
media_node clone;
|
||||
if (B_OK == BMediaRoster::Roster()->GetNodeFor(fPhysicalAudioOut, &clone)) {
|
||||
if (clone.kind & B_TIME_SOURCE) {
|
||||
fTimeSource = clone.node;
|
||||
BMediaRoster::Roster()->ReleaseNode(clone);
|
||||
printf("Default DAC timesource created!\n");
|
||||
return;
|
||||
}
|
||||
BMediaRoster::Roster()->ReleaseNode(clone);
|
||||
} else {
|
||||
printf("Default DAC is not a timesource!\n");
|
||||
}
|
||||
} else {
|
||||
printf("Default DAC node does not exist!\n");
|
||||
}
|
||||
|
||||
/* Now try to find another physical audio out node
|
||||
*/
|
||||
|
||||
memset(&input, 0, sizeof(input));
|
||||
input.type = B_MEDIA_RAW_AUDIO;
|
||||
count = 1;
|
||||
rv = BMediaRoster::Roster()->GetLiveNodes(&info, &count, &input, NULL, NULL, B_TIME_SOURCE | B_PHYSICAL_OUTPUT);
|
||||
if (rv != B_OK || count != 1) {
|
||||
count = MAX_NODE_INFOS;
|
||||
rv = BMediaRoster::Roster()->GetLiveNodes(&info[0], &count, &input, NULL, NULL, B_TIME_SOURCE | B_PHYSICAL_OUTPUT);
|
||||
if (rv == B_OK && count >= 1) {
|
||||
for (int i = 0; i < count; i++)
|
||||
printf("info[%d].name %s\n", i, info[i].name);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
// The BeOS R5 None Out node pretend to be a physical time source, that is pretty dumb
|
||||
if (0 == strcmp(info[i].name, "None Out")) // skip the Null audio driver
|
||||
continue;
|
||||
printf("Default DAC timesource \"%s\" created!\n", info[i].name);
|
||||
fTimeSource = info[i].node.node;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
printf("Couldn't find DAC timesource node\n");
|
||||
return;
|
||||
}
|
||||
fTimeSource = info.node.node;
|
||||
printf("Default DAC timesource created!\n");
|
||||
}
|
||||
|
||||
/* XXX we might use other audio or video clock timesources
|
||||
*/
|
||||
}
|
||||
|
||||
void
|
||||
@ -334,6 +365,8 @@ DefaultManager::ConnectMixerToOutput()
|
||||
media_node soundcard;
|
||||
media_input input;
|
||||
media_output output;
|
||||
media_input newinput;
|
||||
media_output newoutput;
|
||||
media_format format;
|
||||
BTimeSource * ts;
|
||||
bigtime_t start_at;
|
||||
@ -372,20 +405,58 @@ DefaultManager::ConnectMixerToOutput()
|
||||
rv = B_ERROR;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
memset(&format, 0, sizeof(format));
|
||||
format.type = B_MEDIA_RAW_AUDIO;
|
||||
format.u.raw_audio.frame_rate = 44100;
|
||||
format.u.raw_audio.channel_count = 2;
|
||||
format.u.raw_audio.format = 0x2;
|
||||
|
||||
//roster->GetFormatFor(input, &format);
|
||||
|
||||
rv = roster->Connect(output.source, input.destination, &format, &output, &input);
|
||||
if (rv != B_OK) {
|
||||
printf("DefaultManager: connect failed\n");
|
||||
}
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
switch (i) {
|
||||
case 0:
|
||||
printf("DefaultManager: Trying connect in native format\n");
|
||||
if (B_OK != roster->GetFormatFor(input, &format)) {
|
||||
FATAL("DefaultManager: GetFormatFor failed\n");
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
printf("DefaultManager: Trying connect in format 1\n");
|
||||
memset(&format, 0, sizeof(format));
|
||||
format.type = B_MEDIA_RAW_AUDIO;
|
||||
format.u.raw_audio.frame_rate = 44100;
|
||||
format.u.raw_audio.channel_count = 2;
|
||||
format.u.raw_audio.format = 0x2;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
printf("DefaultManager: Trying connect in format 2\n");
|
||||
memset(&format, 0, sizeof(format));
|
||||
format.type = B_MEDIA_RAW_AUDIO;
|
||||
format.u.raw_audio.frame_rate = 48000;
|
||||
format.u.raw_audio.channel_count = 2;
|
||||
format.u.raw_audio.format = 0x2;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
printf("DefaultManager: Trying connect in format 3\n");
|
||||
memset(&format, 0, sizeof(format));
|
||||
format.type = B_MEDIA_RAW_AUDIO;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
printf("DefaultManager: Trying connect in format 4\n");
|
||||
memset(&format, 0, sizeof(format));
|
||||
break;
|
||||
}
|
||||
rv = roster->Connect(output.source, input.destination, &format, &newoutput, &newinput);
|
||||
if (rv == B_OK)
|
||||
break;
|
||||
}
|
||||
if (rv != B_OK) {
|
||||
FATAL("DefaultManager: connect failed\n");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
roster->SetRunModeNode(mixer, BMediaNode::B_INCREASE_LATENCY);
|
||||
roster->SetRunModeNode(soundcard, BMediaNode::B_RECORDING);
|
||||
|
||||
roster->GetTimeSource(×ource);
|
||||
roster->SetTimeSourceFor(mixer.node, timesource.node);
|
||||
roster->SetTimeSourceFor(soundcard.node, timesource.node);
|
||||
|
Loading…
Reference in New Issue
Block a user