work in progress: use one thread per connected channel, and a BList of BBuffer * to avoid dropping them. Disabled early buffer recycling as latency calculation is still broken. I can now play something without missing buffers.

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25898 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
François Revol
2008-06-10 02:01:12 +00:00
parent 6d4ab8b00e
commit 344b81452e
6 changed files with 555 additions and 39 deletions
@@ -33,6 +33,13 @@
#define ENABLE_REC 1
// timeout in OpenSoundNode::RunThread()
#define MIN_SNOOZING 2000
// pretend we don't drift
#define DISABLE_DRIFT 1
/* bit mask of supported formats for raw_audio */
/* also used to mark the raw_audio node input&outputs */
//XXX: _OE ?
@@ -193,6 +193,25 @@ status_t OpenSoundDeviceEngine::UpdateInfo(void)
}
bigtime_t OpenSoundDeviceEngine::PlaybackLatency(void)
{
bigtime_t latency;
int delay;
delay = GetODelay();
delay = 0; //XXX
latency = ((double)delay * 1000000LL
/ (fMediaFormat.u.raw_audio.channel_count * fMediaFormat.u.raw_audio.frame_rate
* (fMediaFormat.AudioFormat() & media_raw_audio_format::B_AUDIO_SIZE_MASK)));
PRINT(("PlaybackLatency: odelay %d latency %Ld card %Ld\n", delay, latency, CardLatency()));
latency += CardLatency();
return latency;
}
bigtime_t OpenSoundDeviceEngine::RecordingLatency(void)
{
return 0LL; //XXX
}
int OpenSoundDeviceEngine::GetChannels(void)
@@ -307,6 +326,108 @@ size_t OpenSoundDeviceEngine::GetOSpace(audio_buf_info *info)
}
int64
OpenSoundDeviceEngine::GetCurrentIPtr(int32 *fifoed, oss_count_t *info)
{
oss_count_t ocount;
count_info cinfo;
CALLED();
if (!info)
info = &ocount;
memset(info, 0, sizeof(oss_count_t));
if (!(fOpenMode & OPEN_READ))
return 0;
if (ioctl(fFD, SNDCTL_DSP_CURRENT_IPTR, info, sizeof(oss_count_t)) < 0) {
PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
__FUNCTION__, "SNDCTL_DSP_CURRENT_IPTR", strerror(errno)));
//return EIO;
// fallback: try GET*PTR
if (ioctl(fFD, SNDCTL_DSP_GETIPTR, &cinfo, sizeof(count_info)) < 0) {
PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
__FUNCTION__, "SNDCTL_DSP_GETIPTR", strerror(errno)));
return 0;
}
// it's probably wrong...
info->samples = cinfo.bytes / (fMediaFormat.u.raw_audio.channel_count
* (fMediaFormat.AudioFormat() & media_raw_audio_format::B_AUDIO_SIZE_MASK));
info->fifo_samples = 0;
}
PRINT(("OpenSoundDeviceEngine::%s: IPTR: { samples=%Ld, fifo_samples=%d }\n", __FUNCTION__, info->samples, info->fifo_samples));
if (fifoed)
*fifoed = info->fifo_samples;
return info->samples;
}
int64
OpenSoundDeviceEngine::GetCurrentOPtr(int32 *fifoed, oss_count_t *info)
{
oss_count_t ocount;
count_info cinfo;
CALLED();
if (!info)
info = &ocount;
memset(info, 0, sizeof(oss_count_t));
if (!(fOpenMode & OPEN_WRITE))
return 0;
if (ioctl(fFD, SNDCTL_DSP_CURRENT_OPTR, info, sizeof(oss_count_t)) < 0) {
PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
__FUNCTION__, "SNDCTL_DSP_CURRENT_OPTR", strerror(errno)));
//return EIO;
// fallback: try GET*PTR
if (ioctl(fFD, SNDCTL_DSP_GETOPTR, &cinfo, sizeof(count_info)) < 0) {
PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
__FUNCTION__, "SNDCTL_DSP_GETOPTR", strerror(errno)));
return 0;
}
// it's probably wrong...
info->samples = cinfo.bytes / (fMediaFormat.u.raw_audio.channel_count
* (fMediaFormat.AudioFormat() & media_raw_audio_format::B_AUDIO_SIZE_MASK));
info->fifo_samples = 0;
}
//PRINT(("OpenSoundDeviceEngine::%s: OPTR: { samples=%Ld, fifo_samples=%d }\n", __FUNCTION__, info->samples, info->fifo_samples));
if (fifoed)
*fifoed = info->fifo_samples;
return info->samples;
}
int32
OpenSoundDeviceEngine::GetIOverruns()
{
audio_errinfo info;
CALLED();
memset(&info, 0, sizeof(info));
if (!(fOpenMode & OPEN_WRITE))
return 0;
if (ioctl(fFD, SNDCTL_DSP_GETERROR, &info, sizeof(info)) < 0) {
PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
__FUNCTION__, "SNDCTL_DSP_GETERROR", strerror(errno)));
return 0;
}
PRINT(("OpenSoundDeviceEngine::%s: IOVERRUNS: { overruns=%d }\n", __FUNCTION__, info.rec_overruns));
return info.rec_overruns;
}
int32
OpenSoundDeviceEngine::GetOUnderruns()
{
audio_errinfo info;
CALLED();
memset(&info, 0, sizeof(info));
if (!(fOpenMode & OPEN_WRITE))
return 0;
if (ioctl(fFD, SNDCTL_DSP_GETERROR, &info, sizeof(info)) < 0) {
PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
__FUNCTION__, "SNDCTL_DSP_GETERROR", strerror(errno)));
return 0;
}
//PRINT(("OpenSoundDeviceEngine::%s: OUNDERRUNS: { underruns=%d }\n", __FUNCTION__, info.play_underruns));
return info.play_underruns;
}
int OpenSoundDeviceEngine::GetODelay(void)
{
//CALLED();
@@ -340,6 +461,12 @@ status_t OpenSoundDeviceEngine::StartRecording(void)
int64 OpenSoundDeviceEngine::PlayedFramesCount(void)
{
int64 played;
int32 fifoed;
played = GetCurrentOPtr(&fifoed);
//played += fifoed;
//return played;
fPlayedFramesCount = played - fifoed;
return fPlayedFramesCount;//XXX
return fPlayedFramesCount - (GetODelay() / (fMediaFormat.u.raw_audio.channel_count
* (fMediaFormat.AudioFormat() & media_raw_audio_format::B_AUDIO_SIZE_MASK)));
@@ -443,8 +570,10 @@ status_t OpenSoundDeviceEngine::AcceptFormatFor(int fmt, media_format &format, b
{
status_t err;
int afmt = 0;
char buf[1024];
CALLED();
fmt &= rec ? Info()->iformats : Info()->oformats;
if (fmt == 0)
return B_MEDIA_BAD_FORMAT;
media_format wc;
@@ -540,12 +669,12 @@ status_t OpenSoundDeviceEngine::AcceptFormatFor(int fmt, media_format &format, b
raw.buffer_size = DEFAULT_BUFFER_SIZE;
} else {
PRINT(("%s: unknown media type\n", __FUNCTION__));
Close();
return EINVAL;
}
// cache it
fMediaFormat = format;
char buf[1024];
string_for_format(format, buf, 1024);
PRINT(("%s: %s\n", __FUNCTION__, buf));
return B_OK;
@@ -676,3 +805,4 @@ status_t OpenSoundDeviceEngine::SpecializeFormatFor(int fmt, media_format &forma
PRINT(("%s: %s\n", __FUNCTION__, buf));
return B_OK;
}
@@ -38,7 +38,9 @@ virtual ssize_t Write(const void *buffer, size_t size);
status_t UpdateInfo();
// shortcuts
int Caps() const { return fAudioInfo.caps; };
int Latency() const { return fAudioInfo.latency; };
bigtime_t CardLatency(void) const { return (fAudioInfo.latency<0) ? 0 : fAudioInfo.latency; };
bigtime_t PlaybackLatency(void);
bigtime_t RecordingLatency(void);
int GetChannels(void);
@@ -55,10 +57,16 @@ virtual ssize_t Write(const void *buffer, size_t size);
size_t GetISpace(audio_buf_info *info=NULL);
size_t GetOSpace(audio_buf_info *info=NULL);
int64 GetCurrentIPtr(int32 *fifoed=NULL, oss_count_t *info=NULL);
int64 GetCurrentOPtr(int32 *fifoed=NULL, oss_count_t *info=NULL);
int32 GetIOverruns();
int32 GetOUnderruns();
int GetODelay(void);
status_t StartRecording(void);
int64 PlayedFramesCount(void);
bigtime_t PlayedRealTime(void);
@@ -44,12 +44,13 @@
#include <Buffer.h>
#include <ParameterWeb.h>
#include <MediaRoster.h>
#include <limits.h>
#include <MediaDefs.h>
#include <Message.h>
#include <Debug.h>
#include <Autolock.h>
#include <String.h>
#include <limits.h>
#include <signal.h>
#include "OpenSoundNode.h"
#include "driver_io.h"
@@ -73,13 +74,15 @@ const char * multi_string[] =
node_input::node_input(media_input &input, media_format format)
{
CALLED();
fNode = NULL;
fEngineId = -1;
fRealEngine = NULL;
fAFmt = 0;
fInput = input;
fPreferredFormat = format;
fBufferCycle = 1;
fBuffer = NULL;
//fBuffer = NULL;
fThread = -1;
}
node_input::~node_input()
@@ -92,12 +95,14 @@ node_output::node_output(media_output &output, media_format format)
fOutputEnabled(true)
{
CALLED();
fNode = NULL;
fEngineId = -1;
fRealEngine = NULL;
fAFmt = 0;
fOutput = output;
fPreferredFormat = format;
fBufferCycle = 1;
fThread = -1;
}
node_output::~node_output()
@@ -115,7 +120,7 @@ OpenSoundNode::~OpenSoundNode(void)
CALLED();
fAddOn->GetConfigurationFor(this, NULL);
StopThread();
//StopThread();
BMediaEventLooper::Quit();
fWeb = NULL;
@@ -283,6 +288,7 @@ void OpenSoundNode::NodeRegistered(void)
currentInput->fChannelId = i;//XXX:REMOVEME fDevice->MD.channels[i].channel_id;
currentInput->fEngineId = i;
currentInput->fAFmt = fmt;
currentInput->fNode = this;
fInputs.AddItem(currentInput);
currentInput->fInput.format.u.raw_audio.format = media_raw_audio_format::wildcard.format;
@@ -332,6 +338,7 @@ void OpenSoundNode::NodeRegistered(void)
currentOutput->fChannelId = i;//XXX:REMOVEME fDevice->MD.channels[i].channel_id;
currentOutput->fEngineId = i;
currentOutput->fAFmt = fmt;
currentOutput->fNode = this;
fOutputs.AddItem(currentOutput);
}
}
@@ -553,11 +560,15 @@ status_t OpenSoundNode::GetLatencyFor(
node_input *channel = FindInput(for_whom);
if(channel==NULL) {
if(channel==NULL || channel->fRealEngine==NULL) {
fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n");
return B_MEDIA_BAD_DESTINATION;
}
*out_latency = EventLatency();
*out_latency = EventLatency(); // mmu_man: that's the own node latency (1 buffer)
// add the OSS driver buffer's latency as well
*out_latency += channel->fRealEngine->PlaybackLatency();
PRINT(("############ OpenSoundNode::%s: EventLatency %Ld, OSS %Ld\n", __FUNCTION__, EventLatency(), channel->fRealEngine->PlaybackLatency()));
//*out_latency += MIN_SNOOZING;
*out_timesource = TimeSource()->ID();
return B_OK;
}
@@ -599,7 +610,8 @@ status_t OpenSoundNode::Connected(
*out_input = channel->fInput;
// we are sure the thread is started
StartThread();
//StartThread();
StartPlayThread(channel);
return B_OK;
}
@@ -619,7 +631,8 @@ void OpenSoundNode::Disconnected(
fprintf(stderr,"<- B_MEDIA_BAD_SOURCE\n");
return;
}
StopPlayThread(channel);
channel->fInput.source = media_source::null;
channel->fInput.format = channel->fPreferredFormat;
if (channel->fRealEngine)
@@ -942,7 +955,8 @@ OpenSoundNode::Connect(status_t error, const media_source& source, const media_d
engine->StartRecording();
// we are sure the thread is started
StartThread();
//StartThread();
StartRecThread(channel);
}
void
@@ -959,6 +973,8 @@ OpenSoundNode::Disconnect(const media_source& what, const media_destination& whe
return;
}
StopRecThread(channel);
BAutolock L(fDevice->Locker());
OpenSoundDeviceEngine *engine = channel->fRealEngine;
@@ -1129,26 +1145,49 @@ status_t OpenSoundNode::HandleBuffer(
// who sent it to us
if ((RunMode() != B_OFFLINE) && // lateness doesn't matter in offline mode...
(RunMode() != B_RECORDING) && // ...or in recording mode
(how_early < 0LL))
(how_early < 0LL) && false /* XXX:DEBUG */)
{
//mLateBuffers++;
NotifyLateProducer(channel->fInput.source, -how_early, perf_time);
fprintf(stderr," <- LATE BUFFER : %lli\n", how_early);
buffer->Recycle();
release_sem(fBufferAvailableSem);
} else {
//WriteBuffer(buffer, *channel);
if(channel->fBuffer != NULL) {
fDevice->Locker()->Lock();
if (channel->fBuffers.CountItems() > 10) {
fDevice->Locker()->Unlock();
PRINT(("OpenSoundNode::HandleBuffer too many buffers, recycling\n"));
buffer->Recycle();
release_sem(fBufferAvailableSem);
} else {
//PRINT(("OpenSoundNode::HandleBuffer writing channelId : %i, how_early:%lli\n", channel->fChannelId, how_early));
channel->fBuffers.AddItem(buffer);
fDevice->Locker()->Unlock();
release_sem(fBufferAvailableSem);
}
#if 0
if (channel->fBuffer != NULL) {
PRINT(("OpenSoundNode::HandleBuffer snoozing recycling channelId : %i, how_early:%lli\n", channel->fChannelId, how_early));
//channel->fBuffer->Recycle();
release_sem(fBufferAvailableSem);
snooze(100);
if(channel->fBuffer != NULL)
if(channel->fBuffer != NULL) {
PRINT(("OpenSoundNode::HandleBuffer old buffer still there, recycling\n"));
buffer->Recycle();
else
//release_sem(fBufferAvailableSem);
} else {
channel->fBuffer = buffer;
release_sem(fBufferAvailableSem);
}
} else {
//PRINT(("OpenSoundNode::HandleBuffer writing channelId : %i, how_early:%lli\n", channel->fChannelId, how_early));
channel->fBuffer = buffer;
release_sem(fBufferAvailableSem);
}
#endif
}
return B_OK;
}
@@ -1247,7 +1286,7 @@ OpenSoundNode::TimeSourceOp(const time_source_op_info &op, void *_reserved)
PRINT(("TimeSourceOp op B_TIMESOURCE_START\n"));
if (RunState() != BMediaEventLooper::B_STARTED) {
fTimeSourceStarted = true;
StartThread();
//StartThread();
media_timed_event startEvent(0, BTimedEventQueue::B_START);
EventQueue()->AddEvent(startEvent);
@@ -1259,7 +1298,7 @@ OpenSoundNode::TimeSourceOp(const time_source_op_info &op, void *_reserved)
media_timed_event stopEvent(0, BTimedEventQueue::B_STOP);
EventQueue()->AddEvent(stopEvent);
fTimeSourceStarted = false;
StopThread();
//StopThread();
PublishTime(0, 0, 0);
}
break;
@@ -1269,7 +1308,7 @@ OpenSoundNode::TimeSourceOp(const time_source_op_info &op, void *_reserved)
media_timed_event stopEvent(0, BTimedEventQueue::B_STOP);
EventQueue()->AddEvent(stopEvent);
fTimeSourceStarted = false;
StopThread();
//StopThread();
PublishTime(0, 0, 0);
}
break;
@@ -2044,19 +2083,43 @@ OpenSoundNode::PropagateParameterChanges(int from, int type, const char *id)
// OpenSoundNode specific functions
// -------------------------------------------------------- //
#if 0
int32
OpenSoundNode::RunThread()
{
int32 i;
int32 bufferAvailReq = 1;
bigtime_t timeout = MIN_SNOOZING;
CALLED();
//set_thread_priority(find_thread(NULL), 5);//XXX:DEBUG
return 0; //XXX: REMOVE
while ( 1 ) {
status_t err;
bigtime_t before = system_time();
// XXX: use select() here when it gets implemented!
if (bufferAvailReq < 1)
bufferAvailReq = 1;
PRINT(("OpenSoundNode::RunThread: waiting for %d buffers, timeout %lld\n", bufferAvailReq, timeout));
//acquire buffer if any
if ( acquire_sem_etc( fBuffer_free, 1, B_RELATIVE_TIMEOUT, 0 ) == B_BAD_SEM_ID ) {
err = acquire_sem_etc( fBufferAvailableSem, bufferAvailReq, B_RELATIVE_TIMEOUT, timeout );
if (err == B_BAD_SEM_ID ) {
return B_OK;
}
if (err == B_OK)
bufferAvailReq = 0;//1;
if (err == B_TIMED_OUT) {
PRINT(("OpenSoundNode::RunThread: acquire_sem timed out\n"));
bufferAvailReq = 0;//1;
} else {
PRINT(("OpenSoundNode::RunThread: acquire_sem done, waited %Ld\n", system_time() - before));
}
timeout = MIN_SNOOZING;
//PRINT(("OpenSoundNode::RunThread: RunState= %d, EventQ: %d events\n", RunState(), EventQueue()->EventCount()));
@@ -2082,7 +2145,8 @@ OpenSoundNode::RunThread()
if (avail < 1)
continue;
#if 0
//engine->GetOUnderruns();
#if 1
// update the timesource
if(input->fChannelId==0) {
//PRINT(("updating timesource\n"));
@@ -2093,21 +2157,39 @@ OpenSoundNode::RunThread()
if(input->fBuffer!=NULL) {
if (avail < input->fBuffer->SizeUsed())
continue;
//PRINT(("OpenSoundNode::RunThread: input[%d]: sending buffer\n", i));
PRINT(("OpenSoundNode::RunThread: input[%d]: sending buffer (%d bytes)\n", i, input->fBuffer->SizeUsed()));
{
bigtime_t tout; // = input->fBuffer->SizeUsed();
tout = abinfo.fragsize * abinfo.fragstotal - abinfo.bytes;
tout = tout * 1000000LL / (input->fInput.format.u.raw_audio.channel_count
* (input->fInput.format.AudioFormat() & media_raw_audio_format::B_AUDIO_SIZE_MASK)
* input->fInput.format.u.raw_audio.frame_rate);
if (tout && (tout < timeout)) {
//PRINT(("new timeout: %Ld\n", tout));
timeout = tout;
}
// let's try this...
//SendLatencyChange(input->fInput.source, input->fInput.destination, EventLatency()+tout);
}
FillNextBuffer(*input, input->fBuffer);
input->fBuffer->Recycle();
input->fBuffer = NULL;
bufferAvailReq++;
} else {
if (avail < abinfo.fragsize)
continue;
//XXX: write silence
// write a nulled fragment
PRINT(("OpenSoundNode::RunThread: input[%d]: sending zeros\n", i));
//#ifdef WRITE_ZEROS
if(input->fInput.source != media_source::null)
WriteZeros(*input, abinfo.fragsize);
//#endif
}
#if 1
#if 0
// update the timesource
if(input->fChannelId==0) {
//PRINT(("updating timesource\n"));
@@ -2164,8 +2246,8 @@ OpenSoundNode::RunThread()
fDevice->Locker()->Unlock();
//XXX
snooze(5000);
release_sem(fBuffer_free);//XXX
//snooze(1000);
//release_sem(fBufferAvailableSem);//XXX
}
#if MA
multi_buffer_info MBI;//, oldMBI;
@@ -2175,7 +2257,7 @@ OpenSoundNode::RunThread()
while ( 1 ) {
//acquire buffer if any
if ( acquire_sem_etc( fBuffer_free, 1, B_RELATIVE_TIMEOUT, 0 ) == B_BAD_SEM_ID ) {
if ( acquire_sem_etc( fBufferAvailableSem, 1, B_RELATIVE_TIMEOUT, 0 ) == B_BAD_SEM_ID ) {
return B_OK;
}
@@ -2227,7 +2309,7 @@ OpenSoundNode::RunThread()
}
//mark buffer free
release_sem( fBuffer_free );
release_sem( fBufferAvailableSem );
} else {
//PRINT(("playback_buffer_cycle non ok input : %i\n", i));
}
@@ -2274,6 +2356,157 @@ OpenSoundNode::RunThread()
#endif
return B_OK;
}
#endif
int32
OpenSoundNode::EnginePlayThread(node_input *input)
{
//int32 i;
CALLED();
//set_thread_priority(find_thread(NULL), 5);//XXX:DEBUG
signal(SIGUSR1, &_sig_handler_);
OpenSoundDeviceEngine *engine = input->fRealEngine;
if (!engine || !engine->InUse())
return B_NO_INIT;
// skip unconnected or non-busy engines
if (input->fInput.source == media_source::null
&& input->fChannelId == 0)
return B_NO_INIT;
// must be open for write
ASSERT (engine->OpenMode() & OPEN_WRITE);
do {
fDevice->Locker()->Lock();
audio_buf_info abinfo;
size_t avail = engine->GetOSpace(&abinfo);
//PRINT(("OpenSoundNode::EnginePlayThread: avail: %d\n", avail));
#if 1
// TODO: do not assume channel 0 will always be running!
// update the timesource
if(input->fChannelId==0) {
//PRINT(("updating timesource\n"));
UpdateTimeSource(&abinfo, *input);
}
#endif
#if 0
BBuffer *buffer = input->fBuffer;
input->fBuffer = NULL;
#endif
BBuffer *buffer = (BBuffer *)input->fBuffers.RemoveItem((int32)0);
fDevice->Locker()->Unlock();
if (input->fThread < 0)
break;
if (buffer != NULL) {
//if (avail < input->fBuffer->SizeUsed())
// continue;
//PRINT(("OpenSoundNode::EnginePlayThread: input[%d]: sending buffer (%d bytes)\n", input->fChannelId, buffer->SizeUsed()));
#if 0
{
bigtime_t tout; // = input->fBuffer->SizeUsed();
tout = abinfo.fragsize * abinfo.fragstotal - abinfo.bytes;
tout = tout * 1000000LL / (input->fInput.format.u.raw_audio.channel_count
* (input->fInput.format.AudioFormat() & media_raw_audio_format::B_AUDIO_SIZE_MASK)
* input->fInput.format.u.raw_audio.frame_rate);
if (tout && (tout < timeout)) {
//PRINT(("new timeout: %Ld\n", tout));
timeout = tout;
}
// let's try this...
//SendLatencyChange(input->fInput.source, input->fInput.destination, EventLatency()+tout);
}
#endif
FillNextBuffer(*input, buffer);
buffer->Recycle();
} else {
//if (avail < abinfo.fragsize)
// continue;
//XXX: write silence
// write a nulled fragment
//PRINT(("OpenSoundNode::EnginePlayThread: input[%d]: sending zeros\n", input->fChannelId));
//#ifdef WRITE_ZEROS
if (input->fInput.source != media_source::null)
WriteZeros(*input, abinfo.fragsize);
//#endif
}
#if 0
// update the timesource
if(input->fChannelId==0) {
//PRINT(("updating timesource\n"));
UpdateTimeSource(&abinfo, *input);
}
#endif
} while (input->fThread > -1);
return 0;
}
int32
OpenSoundNode::EngineRecThread(node_output *output)
{
int32 i;
CALLED();
//set_thread_priority(find_thread(NULL), 5);//XXX:DEBUG
signal(SIGUSR1, &_sig_handler_);
OpenSoundDeviceEngine *engine = output->fRealEngine;
if (!engine || !engine->InUse())
return B_NO_INIT;
// make sure we're both started *and* connected before delivering a buffer
if ((RunState() != BMediaEventLooper::B_STARTED) || (output->fOutput.destination == media_destination::null))
return B_NO_INIT;
// must be open for read
ASSERT (engine->OpenMode() & OPEN_READ);
#ifdef ENABLE_REC
fDevice->Locker()->Lock();
do {
audio_buf_info abinfo;
size_t avail = engine->GetISpace(&abinfo);
//PRINT(("OpenSoundNode::RunThread: I avail: %d\n", avail));
// skip if less than 1 buffer
//if (avail < output->fOutput.format.u.raw_audio.buffer_size)
// continue;
fDevice->Locker()->Unlock();
// Get the next buffer of data
BBuffer* buffer = FillNextBuffer(&abinfo, *output);
fDevice->Locker()->Lock();
if (buffer) {
// send the buffer downstream if and only if output is enabled
status_t err = B_ERROR;
if (output->fOutputEnabled)
err = SendBuffer(buffer, output->fOutput.destination);
//PRINT(("OpenSoundNode::RunThread: I avail: %d, OE %d, %s\n", avail, output->fOutputEnabled, strerror(err)));
if (err) {
buffer->Recycle();
} else {
// track how much media we've delivered so far
size_t nSamples = output->fOutput.format.u.raw_audio.buffer_size
/ (output->fOutput.format.u.raw_audio.format & media_raw_audio_format::B_AUDIO_SIZE_MASK);
output->fSamplesSent += nSamples;
//PRINT(("OpenSoundNode::%s: sent %d samples\n", __FUNCTION__, nSamples));
}
}
} while (output->fThread > -1);
fDevice->Locker()->Unlock();
#endif
return 0;
}
void
OpenSoundNode::WriteZeros(node_input &input, size_t len)
@@ -2542,17 +2775,17 @@ OpenSoundNode::StartThread()
//allocate buffer free semaphore
int bufcount = MAX(fDevice->fFragments.fragstotal, 2);//XXX
// fBuffer_free = create_sem( fDevice->MBL.return_playback_buffers - 1, "multi_audio out buffer free" );
fBuffer_free = create_sem( bufcount - 1, "OpenSound out buffer free" );
// fBufferAvailableSem = create_sem( fDevice->MBL.return_playback_buffers - 1, "multi_audio out buffer free" );
fBufferAvailableSem = create_sem( bufcount - 1, "OpenSound out buffer free" );
if ( fBuffer_free < B_OK ) {
if ( fBufferAvailableSem < B_OK ) {
return B_ERROR;
}
fThread = spawn_thread( _run_thread_, "OpenSound audio output", B_REAL_TIME_PRIORITY, this );
if ( fThread < B_OK ) {
delete_sem( fBuffer_free );
delete_sem( fBufferAvailableSem );
return B_ERROR;
}
@@ -2564,11 +2797,101 @@ status_t
OpenSoundNode::StopThread()
{
CALLED();
delete_sem( fBuffer_free );
delete_sem( fBufferAvailableSem );
wait_for_thread( fThread, &fThread );
return B_OK;
}
status_t
OpenSoundNode::StartPlayThread(node_input *input)
{
CALLED();
BAutolock L(fDevice->Locker());
// the thread is already started ?
if (input->fThread > B_OK)
return B_OK;
//allocate buffer free semaphore
// int bufcount = MAX(fDevice->fFragments.fragstotal, 2);//XXX
// fBufferAvailableSem = create_sem( fDevice->MBL.return_playback_buffers - 1, "multi_audio out buffer free" );
// fBufferAvailableSem = create_sem( bufcount - 1, "OpenSound out buffer free" );
// if ( fBufferAvailableSem < B_OK ) {
// return B_ERROR;
// }
input->fThread = spawn_thread(_engine_play_thread_, "OpenSound audio output", B_REAL_TIME_PRIORITY, input);
if (input->fThread < B_OK) {
// delete_sem( fBufferAvailableSem );
return B_ERROR;
}
resume_thread(input->fThread);
return B_OK;
}
status_t
OpenSoundNode::StopPlayThread(node_input *input)
{
thread_id th;
status_t ret;
CALLED();
{
BAutolock L(fDevice->Locker());
th = input->fThread;
input->fThread = -1;
//kill(th, SIGUSR1);
}
wait_for_thread(th, &ret);
return B_OK;
}
status_t
OpenSoundNode::StartRecThread(node_output *output)
{
CALLED();
// the thread is already started ?
if (output->fThread > B_OK)
return B_OK;
//allocate buffer free semaphore
// int bufcount = MAX(fDevice->fFragments.fragstotal, 2);//XXX
// fBufferAvailableSem = create_sem( fDevice->MBL.return_playback_buffers - 1, "multi_audio out buffer free" );
// fBufferAvailableSem = create_sem( bufcount - 1, "OpenSound out buffer free" );
// if ( fBufferAvailableSem < B_OK ) {
// return B_ERROR;
// }
output->fThread = spawn_thread(_engine_rec_thread_, "OpenSound audio input", B_REAL_TIME_PRIORITY, output);
if (output->fThread < B_OK) {
//delete_sem( fBufferAvailableSem );
return B_ERROR;
}
resume_thread(output->fThread);
return B_OK;
}
status_t
OpenSoundNode::StopRecThread(node_output *output)
{
thread_id th = output->fThread;
status_t ret;
output->fThread = -1;
CALLED();
{
BAutolock L(fDevice->Locker());
//kill(th, SIGUSR1);
}
wait_for_thread(th, &ret);
return B_OK;
}
void
OpenSoundNode::AllocateBuffers(node_output &channel)
{
@@ -2593,17 +2916,18 @@ OpenSoundNode::UpdateTimeSource(audio_buf_info *abinfo, node_input &input)
if(fTimeSourceStarted) {
int64 played_frames = engine->PlayedFramesCount();
bigtime_t perf_time = (bigtime_t)(played_frames /
input.fInput.format.u.raw_audio.frame_rate * 1000000);
input.fInput.format.u.raw_audio.frame_rate * 1000000LL);
bigtime_t real_time = engine->PlayedRealTime(); //XXX!
//MBI.played_real_time;
/*PRINT(("TS: frames: last %Ld curr %Ld diff %Ld, time: last %Ld curr %Ld diff %Ld\n",
/*
PRINT(("TS: frames: last %Ld curr %Ld diff %Ld, time: last %Ld curr %Ld diff %Ld\n",
fOldPlayedFramesCount, played_frames, played_frames - fOldPlayedFramesCount,
fOldPlayedRealTime, real_time, real_time - fOldPlayedRealTime));
*/
float drift;
if (real_time - fOldPlayedRealTime)
drift = ((played_frames - fOldPlayedFramesCount)
/ input.fInput.format.u.raw_audio.frame_rate * 1000000)
/ input.fInput.format.u.raw_audio.frame_rate * 1000000LL)
/ (real_time - fOldPlayedRealTime);
else
drift = 1;
@@ -2620,7 +2944,9 @@ OpenSoundNode::UpdateTimeSource(audio_buf_info *abinfo, node_input &input)
* In theory we should pass it, but it seems to work better if we fake a perfect world...
* Maybe it interferes with OSS's queing.
*/
#ifdef DISABLE_DRIFT
drift = 1;
#endif
PublishTime(perf_time, real_time, drift);
@@ -2837,13 +3163,40 @@ OpenSoundNode::FindInput(int32 destinationId)
// static:
int32
OpenSoundNode::_run_thread_( void *data )
{
CALLED();
#if 0
return static_cast<OpenSoundNode *>( data )->RunThread();
#endif
}
void
OpenSoundNode::_sig_handler_(int sig)
{
}
int32
OpenSoundNode::_engine_play_thread_(void *data)
{
CALLED();
node_input *channel = static_cast<node_input *>(data);
return channel->fNode->EnginePlayThread(channel);
}
int32
OpenSoundNode::_engine_rec_thread_(void *data)
{
CALLED();
node_output *channel = static_cast<node_output *>(data);
return channel->fNode->EngineRecThread(channel);
}
void OpenSoundNode::GetFlavor(flavor_info * outInfo, int32 id)
{
CALLED();
@@ -42,8 +42,9 @@
#include <ParameterWeb.h>
#include <TimeSource.h>
#include <Controllable.h>
#include <File.h>
#include <Entry.h>
#include <File.h>
#include <List.h>
//#include "soundcard.h"
#include "OpenSoundDevice.h"
#include "OpenSoundDeviceEngine.h"
@@ -53,13 +54,15 @@
const media_format & producer_format,
const media_format & consumer_format);*/
class OpenSoundNode;
class node_input
{
public:
node_input(media_input &input, media_format format);
~node_input();
OpenSoundNode *fNode;
int32 fEngineId;
OpenSoundDeviceEngine *fRealEngine; // engine it's connected to. can be a shadow one (!= fEngineId)
int fAFmt; // AFMT_* for this one
@@ -67,9 +70,12 @@ class node_input
media_input fInput;
media_format fPreferredFormat;
media_format fFormat;
thread_id fThread;
uint32 fBufferCycle;
// multi_buffer_info fOldMBI;
BBuffer *fBuffer;
// BBuffer *fBuffer;
BList fBuffers; // of (BBuffer *)
};
class node_output
@@ -78,6 +84,7 @@ class node_output
node_output(media_output &output, media_format format);
~node_output();
OpenSoundNode *fNode;
int32 fEngineId;
OpenSoundDeviceEngine *fRealEngine; // engine it's connected to. can be a shadow one (!= fEngineId)
int fAFmt; // AFMT_* for this one
@@ -86,6 +93,7 @@ class node_output
media_format fPreferredFormat;
media_format fFormat;
thread_id fThread;
BBufferGroup *fBufferGroup;
bool fOutputEnabled;
uint64 fSamplesSent;
@@ -353,6 +361,16 @@ private:
status_t StartThread();
status_t StopThread();
static void _sig_handler_(int sig);
static int32 _engine_play_thread_( void *data );
static int32 _engine_rec_thread_( void *data );
int32 EnginePlayThread(node_input *input);
int32 EngineRecThread(node_output *output);
status_t StartPlayThread(node_input *input);
status_t StopPlayThread(node_input *input);
status_t StartRecThread(node_output *output);
status_t StopRecThread(node_output *output);
void AllocateBuffers(node_output &channel);
BBuffer* FillNextBuffer(audio_buf_info *abinfo, node_output &channel);
@@ -392,7 +410,7 @@ private:
//volatile uint32 fBufferCycle;
sem_id fBuffer_free;
sem_id fBufferAvailableSem;
thread_id fThread;
@@ -5,7 +5,7 @@ Distributed under the terms of the MIT License.
OpenSound System v4.0 drivers (c) 4Front Technologies.
Port by François Revol ([email protected])
Released under GPL. Sources available by mail.
Released under BSD licence. Sources available by mail.
Release and dev notes