AudioMixer: Review and rework synchronization
* The event time is managed through the main node control loop. * Make the mix thread to activate only when needed. * Make the locking more simple and remove a race condition, this will make the thread to be more silent too.
This commit is contained in:
@@ -212,6 +212,14 @@ 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
|
||||||
|
// except the message we are using to schedule output events for the
|
||||||
|
// process thread.
|
||||||
|
|
||||||
|
if (message == MIXER_SCHEDULE_EVENT) {
|
||||||
|
RealTimeQueue()->AddEvent(*(const media_timed_event*)data);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1131,6 +1139,10 @@ AudioMixer::HandleEvent(const media_timed_event *event, bigtime_t lateness,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case MIXER_PROCESS_EVENT:
|
||||||
|
fCore->Process();
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2003-2010 Haiku Inc. All rights reserved.
|
* Copyright 2003-2016 Haiku Inc. All rights reserved.
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*
|
*
|
||||||
* Authors:
|
* Authors:
|
||||||
* Marcus Overhagen
|
* Marcus Overhagen
|
||||||
|
* Dario Casalinuovo
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
@@ -83,6 +84,7 @@ MixerCore::MixerCore(AudioMixer *node)
|
|||||||
fTimeSource(0),
|
fTimeSource(0),
|
||||||
fMixThread(-1),
|
fMixThread(-1),
|
||||||
fMixThreadWaitSem(-1),
|
fMixThreadWaitSem(-1),
|
||||||
|
fHasEvent(false),
|
||||||
fOutputGain(1.0)
|
fOutputGain(1.0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -377,13 +379,13 @@ MixerCore::StopMixThread()
|
|||||||
ASSERT(fMixThread > 0);
|
ASSERT(fMixThread > 0);
|
||||||
ASSERT(fMixThreadWaitSem > 0);
|
ASSERT(fMixThreadWaitSem > 0);
|
||||||
|
|
||||||
|
fRunning = false;
|
||||||
status_t unused;
|
status_t unused;
|
||||||
delete_sem(fMixThreadWaitSem);
|
delete_sem(fMixThreadWaitSem);
|
||||||
wait_for_thread(fMixThread, &unused);
|
wait_for_thread(fMixThread, &unused);
|
||||||
|
|
||||||
fMixThread = -1;
|
fMixThread = -1;
|
||||||
fMixThreadWaitSem = -1;
|
fMixThreadWaitSem = -1;
|
||||||
fRunning = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -477,7 +479,7 @@ MixerCore::_MixThread()
|
|||||||
// The broken BeOS R5 multiaudio node starts with time 0,
|
// The broken BeOS R5 multiaudio node starts with time 0,
|
||||||
// then publishes negative times for about 50ms, publishes 0
|
// then publishes negative times for about 50ms, publishes 0
|
||||||
// again until it finally reaches time values > 0
|
// again until it finally reaches time values > 0
|
||||||
if (!LockFromMixThread())
|
if (!Lock())
|
||||||
return;
|
return;
|
||||||
bigtime_t start = fTimeSource->Now();
|
bigtime_t start = fTimeSource->Now();
|
||||||
Unlock();
|
Unlock();
|
||||||
@@ -485,15 +487,13 @@ MixerCore::_MixThread()
|
|||||||
TRACE("MixerCore: delaying _MixThread start, timesource is at %Ld\n",
|
TRACE("MixerCore: delaying _MixThread start, timesource is at %Ld\n",
|
||||||
start);
|
start);
|
||||||
snooze(5000);
|
snooze(5000);
|
||||||
if (!LockFromMixThread())
|
if (!Lock())
|
||||||
return;
|
return;
|
||||||
start = fTimeSource->Now();
|
start = fTimeSource->Now();
|
||||||
Unlock();
|
Unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!LockFromMixThread())
|
fEventLatency = max((bigtime_t)3600, bigtime_t(0.4 * buffer_duration(
|
||||||
return;
|
|
||||||
bigtime_t latency = max((bigtime_t)3600, bigtime_t(0.4 * buffer_duration(
|
|
||||||
fOutput->MediaOutput().format.u.raw_audio)));
|
fOutput->MediaOutput().format.u.raw_audio)));
|
||||||
|
|
||||||
// TODO: when the format changes while running, everything is wrong!
|
// TODO: when the format changes while running, everything is wrong!
|
||||||
@@ -510,7 +510,6 @@ MixerCore::_MixThread()
|
|||||||
int64 frameBase = ((temp / fMixBufferFrameCount) + 1)
|
int64 frameBase = ((temp / fMixBufferFrameCount) + 1)
|
||||||
* fMixBufferFrameCount;
|
* fMixBufferFrameCount;
|
||||||
bigtime_t timeBase = duration_for_frames(fMixBufferFrameRate, frameBase);
|
bigtime_t timeBase = duration_for_frames(fMixBufferFrameRate, frameBase);
|
||||||
Unlock();
|
|
||||||
|
|
||||||
TRACE("MixerCore: starting _MixThread, start %Ld, timeBase %Ld, "
|
TRACE("MixerCore: starting _MixThread, start %Ld, timeBase %Ld, "
|
||||||
"frameBase %Ld\n", start, timeBase, frameBase);
|
"frameBase %Ld\n", start, timeBase, frameBase);
|
||||||
@@ -526,21 +525,23 @@ MixerCore::_MixThread()
|
|||||||
BStackOrHeapArray<chan_info_list, 16> mixChanInfos(fMixBufferChannelCount);
|
BStackOrHeapArray<chan_info_list, 16> mixChanInfos(fMixBufferChannelCount);
|
||||||
// TODO: this does not support changing output channel count
|
// TODO: this does not support changing output channel count
|
||||||
|
|
||||||
bigtime_t eventTime = timeBase;
|
fEventTime = timeBase;
|
||||||
int64 framePos = 0;
|
int64 framePos = 0;
|
||||||
for (;;) {
|
status_t ret = B_ERROR;
|
||||||
if (!LockFromMixThread())
|
|
||||||
return;
|
while(fRunning == true) {
|
||||||
bigtime_t waitUntil = fTimeSource->RealTimeFor(eventTime, 0)
|
if (fHasEvent == false)
|
||||||
- latency - fDownstreamLatency;
|
goto schedule_next_event;
|
||||||
Unlock();
|
|
||||||
status_t rv = acquire_sem_etc(fMixThreadWaitSem, 1, B_ABSOLUTE_TIMEOUT,
|
ret = acquire_sem_etc(fMixThreadWaitSem, 1,
|
||||||
waitUntil);
|
B_RELATIVE_TIMEOUT | B_DO_NOT_RESCHEDULE, 100000);
|
||||||
if (rv == B_INTERRUPTED)
|
if (ret == B_TIMED_OUT)
|
||||||
continue;
|
continue;
|
||||||
if (rv != B_TIMED_OUT && rv < B_OK)
|
else if (ret != B_OK)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
fHasEvent = false;
|
||||||
|
|
||||||
if (!LockWithTimeout(10000)) {
|
if (!LockWithTimeout(10000)) {
|
||||||
ERROR("MixerCore: LockWithTimeout failed\n");
|
ERROR("MixerCore: LockWithTimeout failed\n");
|
||||||
continue;
|
continue;
|
||||||
@@ -559,7 +560,7 @@ MixerCore::_MixThread()
|
|||||||
hdr->type = B_MEDIA_RAW_AUDIO;
|
hdr->type = B_MEDIA_RAW_AUDIO;
|
||||||
hdr->size_used = size;
|
hdr->size_used = size;
|
||||||
hdr->time_source = fTimeSource->ID();
|
hdr->time_source = fTimeSource->ID();
|
||||||
hdr->start_time = eventTime;
|
hdr->start_time = fEventTime;
|
||||||
if (fNode->SendBuffer(buffer, fOutput) != B_OK) {
|
if (fNode->SendBuffer(buffer, fOutput) != B_OK) {
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
ERROR("MixerCore: SendBuffer failed for buffer %Ld\n",
|
ERROR("MixerCore: SendBuffer failed for buffer %Ld\n",
|
||||||
@@ -587,7 +588,7 @@ MixerCore::_MixThread()
|
|||||||
ASSERT(currentFramePos % fMixBufferFrameCount == 0);
|
ASSERT(currentFramePos % fMixBufferFrameCount == 0);
|
||||||
|
|
||||||
PRINT(4, "create new buffer event at %Ld, reading input frames at "
|
PRINT(4, "create new buffer event at %Ld, reading input frames at "
|
||||||
"%Ld\n", eventTime, currentFramePos);
|
"%Ld\n", fEventTime, currentFramePos);
|
||||||
|
|
||||||
// Init the channel information for each MixerInput.
|
// Init the channel information for each MixerInput.
|
||||||
for (int i = 0; MixerInput* input = Input(i); i++) {
|
for (int i = 0; MixerInput* input = Input(i); i++) {
|
||||||
@@ -598,7 +599,7 @@ MixerCore::_MixThread()
|
|||||||
uint32 sampleOffset;
|
uint32 sampleOffset;
|
||||||
float gain;
|
float gain;
|
||||||
if (!input->GetMixerChannelInfo(channel, currentFramePos,
|
if (!input->GetMixerChannelInfo(channel, currentFramePos,
|
||||||
eventTime, &base, &sampleOffset, &type, &gain)) {
|
fEventTime, &base, &sampleOffset, &type, &gain)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (type < 0 || type >= MAX_CHANNEL_TYPES)
|
if (type < 0 || type >= MAX_CHANNEL_TYPES)
|
||||||
@@ -689,7 +690,7 @@ MixerCore::_MixThread()
|
|||||||
hdr->size_used
|
hdr->size_used
|
||||||
= fOutput->MediaOutput().format.u.raw_audio.buffer_size;
|
= fOutput->MediaOutput().format.u.raw_audio.buffer_size;
|
||||||
hdr->time_source = fTimeSource->ID();
|
hdr->time_source = fTimeSource->ID();
|
||||||
hdr->start_time = eventTime;
|
hdr->start_time = fEventTime;
|
||||||
|
|
||||||
// swap byte order if necessary
|
// swap byte order if necessary
|
||||||
fOutput->AdjustByteOrder(buffer);
|
fOutput->AdjustByteOrder(buffer);
|
||||||
@@ -721,11 +722,23 @@ MixerCore::_MixThread()
|
|||||||
mixChanInfos[i].MakeEmpty();
|
mixChanInfos[i].MakeEmpty();
|
||||||
|
|
||||||
schedule_next_event:
|
schedule_next_event:
|
||||||
|
Unlock();
|
||||||
|
|
||||||
// schedule next event
|
// schedule next event
|
||||||
framePos += fMixBufferFrameCount;
|
framePos += fMixBufferFrameCount;
|
||||||
eventTime = timeBase + bigtime_t((1000000LL * framePos)
|
fEventTime = timeBase + bigtime_t((1000000LL * framePos)
|
||||||
/ fMixBufferFrameRate);
|
/ fMixBufferFrameRate);
|
||||||
Unlock();
|
|
||||||
|
media_timed_event mixerEvent(PickEvent(),
|
||||||
|
MIXER_PROCESS_EVENT, 0, BTimedEventQueue::B_NO_CLEANUP);
|
||||||
|
|
||||||
|
ret = write_port(fNode->ControlPort(), MIXER_SCHEDULE_EVENT,
|
||||||
|
&mixerEvent, sizeof(mixerEvent));
|
||||||
|
if (ret != B_OK)
|
||||||
|
TRACE("MixerCore::_MixThread: can't write to owner port\n");
|
||||||
|
|
||||||
|
fHasEvent = true;
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
bufferIndex++;
|
bufferIndex++;
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ class Resampler;
|
|||||||
// but for now we redefine type 12
|
// but for now we redefine type 12
|
||||||
#define B_CHANNEL_MONO B_CHANNEL_TOP_CENTER
|
#define B_CHANNEL_MONO B_CHANNEL_TOP_CENTER
|
||||||
|
|
||||||
|
#define MIXER_PROCESS_EVENT BTimedEventQueue::B_USER_EVENT+10
|
||||||
|
#define MIXER_SCHEDULE_EVENT BTimedEventQueue::B_USER_EVENT+11
|
||||||
|
|
||||||
|
|
||||||
class MixerCore {
|
class MixerCore {
|
||||||
public:
|
public:
|
||||||
MixerCore(AudioMixer* node);
|
MixerCore(AudioMixer* node);
|
||||||
@@ -53,11 +57,14 @@ public:
|
|||||||
MixerInput* Input(int index);
|
MixerInput* Input(int index);
|
||||||
MixerOutput* Output();
|
MixerOutput* Output();
|
||||||
|
|
||||||
void Lock();
|
bool Lock();
|
||||||
bool LockWithTimeout(bigtime_t timeout);
|
bool LockWithTimeout(bigtime_t timeout);
|
||||||
bool LockFromMixThread();
|
bool IsLocked() const;
|
||||||
void Unlock();
|
void Unlock();
|
||||||
|
|
||||||
|
void Process();
|
||||||
|
bigtime_t PickEvent();
|
||||||
|
|
||||||
void BufferReceived(BBuffer* buffer,
|
void BufferReceived(BBuffer* buffer,
|
||||||
bigtime_t lateness);
|
bigtime_t lateness);
|
||||||
|
|
||||||
@@ -114,6 +121,9 @@ private:
|
|||||||
BTimeSource* fTimeSource;
|
BTimeSource* fTimeSource;
|
||||||
thread_id fMixThread;
|
thread_id fMixThread;
|
||||||
sem_id fMixThreadWaitSem;
|
sem_id fMixThreadWaitSem;
|
||||||
|
bool fHasEvent;
|
||||||
|
bigtime_t fEventTime;
|
||||||
|
bigtime_t fEventLatency;
|
||||||
float fOutputGain;
|
float fOutputGain;
|
||||||
|
|
||||||
friend class MixerInput;
|
friend class MixerInput;
|
||||||
@@ -121,10 +131,10 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
inline void
|
inline bool
|
||||||
MixerCore::Lock()
|
MixerCore::Lock()
|
||||||
{
|
{
|
||||||
fLocker->Lock();
|
return fLocker->Lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -135,6 +145,13 @@ MixerCore::LockWithTimeout(bigtime_t timeout)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline bool
|
||||||
|
MixerCore::IsLocked() const
|
||||||
|
{
|
||||||
|
return fLocker->IsLocked();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
MixerCore::Unlock()
|
MixerCore::Unlock()
|
||||||
{
|
{
|
||||||
@@ -142,18 +159,18 @@ MixerCore::Unlock()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline bool
|
inline void
|
||||||
MixerCore::LockFromMixThread()
|
MixerCore::Process()
|
||||||
{
|
{
|
||||||
for (;;) {
|
release_sem(fMixThreadWaitSem);
|
||||||
if (LockWithTimeout(10000))
|
}
|
||||||
return true;
|
|
||||||
// XXX accessing fMixThreadWaitSem is still a race condition :(
|
|
||||||
if (acquire_sem_etc(fMixThreadWaitSem, 1, B_RELATIVE_TIMEOUT, 0)
|
inline bigtime_t
|
||||||
!= B_WOULD_BLOCK) {
|
MixerCore::PickEvent()
|
||||||
return false;
|
{
|
||||||
}
|
return fTimeSource->RealTimeFor(fEventTime, 0)
|
||||||
}
|
- fEventLatency - fDownstreamLatency;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user