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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user