#include "OpenSoundNode.h"
#include <AutoDeleter.h>
#include <Autolock.h>
#include <Buffer.h>
#include <BufferGroup.h>
#include <Debug.h>
#include <MediaAddOn.h>
#include <MediaRoster.h>
#include <scheduler.h>
#include <String.h>
#include <new>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#ifdef DEBUG
#define PRINTING
#endif
#include "debug.h"
#include "OpenSoundDevice.h"
#include "OpenSoundDeviceEngine.h"
#include "OpenSoundDeviceMixer.h"
#include "SupportFunctions.h"
using std::nothrow;
#include <private/shared/FunctionTracer.h>
#ifdef TRACE
# undef TRACE
#endif
#ifdef CALLED
# undef CALLED
#endif
#ifdef TRACE_OSS_NODE
static int32 sDepth;
# define TRACE(x...) printf(x)
# define CALLED(x...) FunctionTracer _ft(printf, NULL, __PRETTY_FUNCTION__, sDepth)
# define PRINTING
#else
# define TRACE(x...)
# define CALLED(x...)
#endif
class OpenSoundNode::NodeInput {
public:
NodeInput(const media_input& input, int engineIndex, int ossFormatFlags,
OpenSoundNode* node)
: fNode(node),
fEngineIndex(engineIndex),
fRealEngine(NULL),
fOSSFormatFlags(ossFormatFlags),
fInput(input),
fPreferredFormat(input.format),
fThread(-1),
fBuffers(4),
fTestTonePhase(0)
{
CALLED();
fInput.format.u.raw_audio.format
= media_raw_audio_format::wildcard.format;
}
~NodeInput()
{
CALLED();
fNode->_StopPlayThread(this);
RecycleAllBuffers();
}
status_t Write(void* data, size_t size)
{
CALLED();
ssize_t written = fRealEngine->Write(data, size);
if (written < 0)
return (status_t)written;
if (written < (ssize_t)size)
return B_IO_ERROR;
return B_OK;
}
void WriteTestTone(size_t bytes)
{
uint8 buffer[bytes];
float sampleRate = fInput.format.u.raw_audio.frame_rate;
const static int kSineBuffer[48] = {
0, 4276, 8480, 12539, 16383, 19947, 23169, 25995,
28377, 30272, 31650, 32486, 32767, 32486, 31650, 30272,
28377, 25995, 23169, 19947, 16383, 12539, 8480, 4276,
0, -4276, -8480, -12539, -16383, -19947, -23169, -25995,
-28377, -30272, -31650, -32486, -32767, -32486, -31650, -30272,
-28377, -25995, -23169, -19947, -16383, -12539, -8480, -4276
};
short* b = (short*)buffer;
int32 channels = fInput.format.u.raw_audio.channel_count;
int32 frames = bytes / bytes_per_frame(fInput.format);
for (int32 i = 0; i < frames; i ++) {
uint32 p = (uint32)((fTestTonePhase * sampleRate) / 48000);
fTestTonePhase = (fTestTonePhase + 1) % 4800;
for (int32 k = 0; k < channels; k++)
b[k] = kSineBuffer[p % 48];
b += channels;
}
ssize_t written = fRealEngine->Write(buffer, bytes);
if (written != (ssize_t)bytes) {
}
}
void RecycleAllBuffers()
{
CALLED();
while (BBuffer* buffer = (BBuffer*)fBuffers.RemoveItem((int32)0))
buffer->Recycle();
}
OpenSoundNode* fNode;
int32 fEngineIndex;
OpenSoundDeviceEngine* fRealEngine;
int fOSSFormatFlags;
media_input fInput;
media_format fPreferredFormat;
thread_id fThread;
BList fBuffers;
uint32 fTestTonePhase;
};
class OpenSoundNode::NodeOutput {
public:
NodeOutput(const media_output& output, const media_format& format)
: fNode(NULL),
fEngineIndex(-1),
fRealEngine(NULL),
fOSSFormatFlags(0),
fOutput(output),
fPreferredFormat(format),
fThread(-1),
fBufferGroup(NULL),
fUsingOwnBufferGroup(true),
fOutputEnabled(true),
fSamplesSent(0)
{
CALLED();
}
~NodeOutput()
{
CALLED();
fNode->_StopRecThread(this);
FreeBuffers();
}
status_t AllocateBuffers(bigtime_t bufferDuration, bigtime_t latency)
{
TRACE("NodeOutput::AllocateBuffers(bufferDuration = %" B_PRIdBIGTIME ", "
"latency = %" B_PRIdBIGTIME ")\n", bufferDuration, latency);
FreeBuffers();
size_t size = fOutput.format.u.raw_audio.buffer_size;
int32 count = int32(latency / bufferDuration + 1 + 1);
fBufferGroup = new (nothrow) BBufferGroup(size, count);
fUsingOwnBufferGroup = true;
return fBufferGroup != NULL ? fBufferGroup->InitCheck() : B_NO_MEMORY;
}
status_t SetExternalBuffers(BBufferGroup* bufferGroup)
{
TRACE("NodeOutput::SetExternalBuffers(%p)\n", bufferGroup);
fBufferGroup = bufferGroup;
fUsingOwnBufferGroup = false;
return fBufferGroup->InitCheck();
}
void FreeBuffers()
{
TRACE("NodeOutput::FreeBuffers(): %p (own %d)\n", fBufferGroup,
fUsingOwnBufferGroup);
delete fBufferGroup;
fBufferGroup = NULL;
}
BBuffer* FillNextBuffer(bigtime_t bufferDuration)
{
if (fBufferGroup == NULL)
return NULL;
BBuffer* buffer = fBufferGroup->RequestBuffer(
fOutput.format.u.raw_audio.buffer_size, bufferDuration);
if (!buffer)
return NULL;
ssize_t sizeUsed = fRealEngine->Read(buffer->Data(),
fOutput.format.u.raw_audio.buffer_size);
if (sizeUsed < 0) {
TRACE("NodeOutput::%s: %s\n", __FUNCTION__,
strerror(sizeUsed));
buffer->Recycle();
return NULL;
}
if (sizeUsed < (ssize_t)fOutput.format.u.raw_audio.buffer_size) {
TRACE("NodeOutput::%s: requested %zu, got %zu\n", __FUNCTION__,
fOutput.format.u.raw_audio.buffer_size, sizeUsed);
}
media_header* hdr = buffer->Header();
if (hdr != NULL) {
hdr->type = B_MEDIA_RAW_AUDIO;
hdr->size_used = sizeUsed;
}
return buffer;
}
OpenSoundNode* fNode;
int32 fEngineIndex;
OpenSoundDeviceEngine* fRealEngine;
int fOSSFormatFlags;
media_output fOutput;
media_format fPreferredFormat;
thread_id fThread;
BBufferGroup* fBufferGroup;
bool fUsingOwnBufferGroup;
bool fOutputEnabled;
uint64 fSamplesSent;
};
OpenSoundNode::OpenSoundNode(BMediaAddOn* addon, const char* name,
OpenSoundDevice* device, int32 internal_id, BMessage* config)
: BMediaNode(name),
BBufferConsumer(B_MEDIA_RAW_AUDIO),
BBufferProducer(B_MEDIA_RAW_AUDIO),
BTimeSource(),
BMediaEventLooper(),
fInitCheckStatus(B_NO_INIT),
fDevice(device),
fTimeSourceStarted(false),
fTimeSourceStartTime(0),
fWeb(NULL),
fConfig((uint32)0)
{
CALLED();
if (fDevice == NULL)
return;
fAddOn = addon;
fId = internal_id;
AddNodeKind(B_PHYSICAL_OUTPUT);
AddNodeKind(B_PHYSICAL_INPUT);
#if 1
fPreferredFormat = media_format();
fPreferredFormat.type = B_MEDIA_RAW_AUDIO;
fPreferredFormat.u.raw_audio = media_multi_audio_format::wildcard;
fPreferredFormat.u.raw_audio.channel_count = 2;
fPreferredFormat.u.raw_audio.byte_order = B_MEDIA_HOST_ENDIAN;
OpenSoundDeviceEngine *engine = fDevice->EngineAt(0);
if (engine) {
const oss_audioinfo* ai = engine->Info();
int fmt = OpenSoundDevice::select_oss_format(ai->oformats);
fPreferredFormat.u.raw_audio.format
= OpenSoundDevice::convert_oss_format_to_media_format(fmt);
fPreferredFormat.u.raw_audio.valid_bits
= OpenSoundDevice::convert_oss_format_to_valid_bits(fmt);
fPreferredFormat.u.raw_audio.frame_rate
= OpenSoundDevice::convert_oss_rate_to_media_rate(ai->max_rate);
}
fPreferredFormat.u.raw_audio.buffer_size = DEFAULT_BUFFER_SIZE
* (fPreferredFormat.u.raw_audio.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK)
* fPreferredFormat.u.raw_audio.channel_count;
#endif
if (config != NULL) {
fConfig = *config;
PRINT_OBJECT(fConfig);
}
fInitCheckStatus = B_OK;
}
OpenSoundNode::~OpenSoundNode()
{
CALLED();
fAddOn->GetConfigurationFor(this, NULL);
int32 count = fInputs.CountItems();
for (int32 i = 0; i < count; i++)
delete (NodeInput*)fInputs.ItemAtFast(i);
count = fOutputs.CountItems();
for (int32 i = 0; i < count; i++)
delete (NodeOutput*)fOutputs.ItemAtFast(i);
BMediaEventLooper::Quit();
fWeb = NULL;
}
status_t
OpenSoundNode::InitCheck() const
{
CALLED();
return fInitCheckStatus;
}
BMediaAddOn*
OpenSoundNode::AddOn(int32* internal_id) const
{
CALLED();
if (fAddOn != 0) {
if (internal_id != 0) {
*internal_id = fId;
}
}
return fAddOn;
}
void
OpenSoundNode::Preroll()
{
CALLED();
BMediaNode::Preroll();
}
status_t
OpenSoundNode::HandleMessage(int32 message, const void* data, size_t size)
{
CALLED();
return B_ERROR;
}
void
OpenSoundNode::NodeRegistered()
{
CALLED();
if (fInitCheckStatus != B_OK) {
ReportError(B_NODE_IN_DISTRESS);
return;
}
TRACE("NodeRegistered: %d engines\n", fDevice->CountEngines());
for (int32 i = 0; i < fDevice->CountEngines(); i++) {
OpenSoundDeviceEngine* engine = fDevice->EngineAt(i);
if (engine == NULL)
continue;
if (engine->Caps() & PCM_CAP_SHADOW)
continue;
if ((engine->Caps() & PCM_CAP_OUTPUT) == 0)
continue;
TRACE("NodeRegistered: engine[%d]: .caps=0x%08x, .oformats=0x%08x\n",
i, engine->Caps(), engine->Info()->oformats);
for (int32 f = 0; gSupportedFormats[f]; f++) {
int fmt = gSupportedFormats[f] & engine->Info()->oformats;
if (fmt == 0)
continue;
TRACE("NodeRegistered() : creating an input for engine %i, "
"format[%i]\n", i, f);
media_input mediaInput;
status_t err = engine->PreferredFormatFor(fmt, mediaInput.format);
if (err < B_OK)
continue;
mediaInput.destination.port = ControlPort();
mediaInput.destination.id = fInputs.CountItems();
mediaInput.node = Node();
const char *prefix = "";
if (strstr(engine->Info()->name, "SPDIF"))
prefix = "S/PDIF ";
sprintf(mediaInput.name, "%sOutput %" B_PRId32 " (%s)", prefix,
mediaInput.destination.id, gSupportedFormatsNames[f]);
NodeInput* input = new (nothrow) NodeInput(mediaInput, i, fmt,
this);
if (input == NULL || !fInputs.AddItem(input)) {
delete input;
continue;
}
}
}
for (int32 i = 0; i < fDevice->CountEngines(); i++) {
OpenSoundDeviceEngine* engine = fDevice->EngineAt(i);
if (engine == NULL)
continue;
if (engine->Caps() & PCM_CAP_SHADOW)
continue;
if ((engine->Caps() & PCM_CAP_INPUT) == 0)
continue;
TRACE("NodeRegistered: engine[%d]: .caps=0x%08x, .iformats=0x%08x\n",
i, engine->Caps(), engine->Info()->iformats);
for (int32 f = 0; gSupportedFormats[f]; f++) {
int fmt = gSupportedFormats[f] & engine->Info()->iformats;
if (fmt == 0)
continue;
TRACE("NodeRegistered() : creating an output for engine %i, "
"format[%i]\n", i, f);
media_format preferredFormat;
status_t err = engine->PreferredFormatFor(fmt, preferredFormat);
if (err < B_OK)
continue;
media_output mediaOutput;
mediaOutput.format = preferredFormat;
mediaOutput.destination = media_destination::null;
mediaOutput.source.port = ControlPort();
mediaOutput.source.id = fOutputs.CountItems();
mediaOutput.node = Node();
const char *prefix = "";
if (strstr(engine->Info()->name, "SPDIF"))
prefix = "S/PDIF ";
sprintf(mediaOutput.name, "%sInput %" B_PRId32 " (%s)", prefix,
mediaOutput.source.id, gSupportedFormatsNames[f]);
NodeOutput* output = new (nothrow) NodeOutput(mediaOutput,
preferredFormat);
if (output == NULL || !fOutputs.AddItem(output)) {
delete output;
continue;
}
output->fOutput.format = output->fPreferredFormat;
output->fEngineIndex = i;
output->fOSSFormatFlags = fmt;
output->fNode = this;
}
}
fWeb = MakeParameterWeb();
SetParameterWeb(fWeb);
#ifdef TRACE_OSS_NODE
bigtime_t start = system_time();
#endif
int32 index = 0;
int32 parameterID = 0;
while (fConfig.FindInt32("parameterID", index, ¶meterID) == B_OK) {
const void* data;
ssize_t size;
if (fConfig.FindData("parameterData", B_RAW_TYPE, index, &data,
&size) == B_OK) {
SetParameterValue(parameterID, TimeSource()->Now(), data, size);
}
index++;
}
TRACE("apply configuration in : %" B_PRIdBIGTIME "µs\n", system_time() - start);
SetPriority(B_REAL_TIME_PRIORITY);
Run();
}
status_t
OpenSoundNode::RequestCompleted(const media_request_info& info)
{
CALLED();
return B_OK;
}
void
OpenSoundNode::SetTimeSource(BTimeSource* timeSource)
{
CALLED();
}
status_t
OpenSoundNode::AcceptFormat(const media_destination& dest,
media_format* format)
{
CALLED();
NodeInput* channel = _FindInput(dest);
if (channel == NULL) {
fprintf(stderr, "OpenSoundNode::AcceptFormat()"
" - B_MEDIA_BAD_DESTINATION");
return B_MEDIA_BAD_DESTINATION;
}
if (format == NULL) {
fprintf(stderr, "OpenSoundNode::AcceptFormat() - B_BAD_VALUE\n");
return B_BAD_VALUE;
}
BAutolock L(fDevice->Locker());
OpenSoundDeviceEngine* engine = fDevice->NextFreeEngineAt(
channel->fEngineIndex, false);
if (!engine)
return B_BUSY;
status_t err = engine->AcceptFormatFor(channel->fOSSFormatFlags, *format,
false);
if (err < B_OK)
return err;
channel->fRealEngine = engine;
channel->fInput.format = *format;
return B_OK;
}
status_t
OpenSoundNode::GetNextInput(int32* cookie, media_input* out_input)
{
CALLED();
if (out_input == NULL) {
fprintf(stderr,"OpenSoundNode::GetNextInput() - B_BAD_VALUE\n");
return B_BAD_VALUE;
}
if (*cookie >= fInputs.CountItems() || *cookie < 0)
return B_BAD_INDEX;
NodeInput* channel = (NodeInput*)fInputs.ItemAt(*cookie);
*out_input = channel->fInput;
*cookie += 1;
TRACE("input.format : %u\n", channel->fInput.format.u.raw_audio.format);
return B_OK;
}
void
OpenSoundNode::DisposeInputCookie(int32 cookie)
{
CALLED();
}
void
OpenSoundNode::BufferReceived(BBuffer* buffer)
{
CALLED();
switch (buffer->Header()->type) {
case B_MEDIA_RAW_AUDIO:
if (buffer->Flags() & BBuffer::B_SMALL_BUFFER) {
fprintf(stderr, "OpenSoundNode::BufferReceived() - "
"B_SMALL_BUFFER not implemented\n");
buffer->Recycle();
} else {
media_timed_event event(buffer->Header()->start_time,
BTimedEventQueue::B_HANDLE_BUFFER, buffer,
BTimedEventQueue::B_RECYCLE_BUFFER);
status_t status = EventQueue()->AddEvent(event);
if (status != B_OK) {
fprintf(stderr, "OpenSoundNode::BufferReceived() - "
"EventQueue()->AddEvent() failed: %s\n",
strerror(status));
buffer->Recycle();
}
}
break;
default:
fprintf(stderr, "OpenSoundNode::BufferReceived() - unexpected "
"buffer type\n");
buffer->Recycle();
break;
}
}
void
OpenSoundNode::ProducerDataStatus(const media_destination& for_whom,
int32 status, bigtime_t at_performance_time)
{
CALLED();
NodeInput* channel = _FindInput(for_whom);
if (channel == NULL) {
fprintf(stderr,"OpenSoundNode::ProducerDataStatus() - "
"invalid destination\n");
return;
}
media_timed_event event(at_performance_time,
BTimedEventQueue::B_DATA_STATUS, &channel->fInput,
BTimedEventQueue::B_NO_CLEANUP, status, 0, NULL);
EventQueue()->AddEvent(event);
}
status_t
OpenSoundNode::GetLatencyFor(const media_destination& for_whom,
bigtime_t* out_latency, media_node_id* out_timesource)
{
CALLED();
if (out_latency == NULL || out_timesource == NULL) {
fprintf(stderr,"OpenSoundNode::GetLatencyFor() - B_BAD_VALUE\n");
return B_BAD_VALUE;
}
NodeInput* channel = _FindInput(for_whom);
if (channel == NULL || channel->fRealEngine == NULL) {
fprintf(stderr,"OpenSoundNode::GetLatencyFor() - "
"B_MEDIA_BAD_DESTINATION\n");
return B_MEDIA_BAD_DESTINATION;
}
*out_latency = EventLatency();
bigtime_t bufferLatency = channel->fRealEngine->PlaybackLatency();
*out_latency += bufferLatency;
TRACE("OpenSoundNode::GetLatencyFor() - EventLatency %" B_PRIdBIGTIME ", OSS %" B_PRIdBIGTIME
"\n",
EventLatency(), bufferLatency);
*out_timesource = TimeSource()->ID();
return B_OK;
}
status_t
OpenSoundNode::Connected(const media_source& producer,
const media_destination& where, const media_format& with_format,
media_input* out_input)
{
CALLED();
if (out_input == NULL) {
fprintf(stderr,"OpenSoundNode::Connected() - B_BAD_VALUE\n");
return B_BAD_VALUE;
}
NodeInput* channel = _FindInput(where);
if (channel == NULL) {
fprintf(stderr,"OpenSoundNode::Connected() - "
"B_MEDIA_BAD_DESTINATION\n");
return B_MEDIA_BAD_DESTINATION;
}
BAutolock L(fDevice->Locker());
size_t bufferSize = channel->fRealEngine->DriverBufferSize() / 2;
fInternalLatency = time_for_buffer(bufferSize, with_format);
TRACE(" internal latency = %" B_PRIdBIGTIME "\n", fInternalLatency);
SetEventLatency(fInternalLatency);
channel->fInput.source = producer;
channel->fInput.format = with_format;
*out_input = channel->fInput;
_StartPlayThread(channel);
return B_OK;
}
void
OpenSoundNode::Disconnected(const media_source& producer,
const media_destination& where)
{
CALLED();
NodeInput* channel = _FindInput(where);
if (channel == NULL) {
fprintf(stderr,"OpenSoundNode::Disconnected() - "
"B_MEDIA_BAD_DESTINATION\n");
return;
}
if (channel->fInput.source != producer) {
fprintf(stderr,"OpenSoundNode::Disconnected() - "
"B_MEDIA_BAD_SOURCE\n");
return;
}
_StopPlayThread(channel);
channel->RecycleAllBuffers();
channel->fInput.source = media_source::null;
channel->fInput.format = channel->fPreferredFormat;
if (channel->fRealEngine)
channel->fRealEngine->Close();
channel->fRealEngine = NULL;
}
status_t
OpenSoundNode::FormatChanged(const media_source& producer,
const media_destination& consumer, int32 change_tag,
const media_format& format)
{
CALLED();
NodeInput* channel = _FindInput(consumer);
if (channel == NULL) {
fprintf(stderr,"OpenSoundNode::FormatChanged() - "
"B_MEDIA_BAD_DESTINATION\n");
return B_MEDIA_BAD_DESTINATION;
}
if (channel->fInput.source != producer) {
fprintf(stderr,"OpenSoundNode::FormatChanged() - "
"B_MEDIA_BAD_SOURCE\n");
return B_MEDIA_BAD_SOURCE;
}
return B_ERROR;
}
status_t
OpenSoundNode::SeekTagRequested(const media_destination& destination,
bigtime_t in_target_time, uint32 in_flags, media_seek_tag* out_seek_tag,
bigtime_t* out_tagged_time, uint32* out_flags)
{
CALLED();
return BBufferConsumer::SeekTagRequested(destination, in_target_time,
in_flags, out_seek_tag, out_tagged_time, out_flags);
}
status_t
OpenSoundNode::FormatSuggestionRequested(media_type type, int32 ,
media_format* format)
{
CALLED();
if (format == NULL) {
fprintf(stderr, "\tERROR - NULL format pointer passed in!\n");
return B_BAD_VALUE;
}
*format = fPreferredFormat;
if (type == B_MEDIA_UNKNOWN_TYPE)
type = B_MEDIA_RAW_AUDIO;
if (type != B_MEDIA_RAW_AUDIO)
return B_MEDIA_BAD_FORMAT;
return B_OK;
}
status_t
OpenSoundNode::FormatProposal(const media_source& output, media_format* format)
{
CALLED();
NodeOutput* channel = _FindOutput(output);
if (channel == NULL) {
fprintf(stderr, "OpenSoundNode::FormatProposal returning "
"B_MEDIA_BAD_SOURCE\n");
return B_MEDIA_BAD_SOURCE;
}
media_type requestedType = format->type;
#ifdef ENABLE_REC
OpenSoundDeviceEngine* engine = fDevice->NextFreeEngineAt(
channel->fEngineIndex, true);
status_t err = engine->PreferredFormatFor(channel->fOSSFormatFlags, *format, true);
if (err < B_OK)
return err;
#else
*format = fPreferredFormat;
#endif
if (requestedType != B_MEDIA_UNKNOWN_TYPE
&& requestedType != B_MEDIA_RAW_AUDIO
#ifdef ENABLE_NON_RAW_SUPPORT
&& requestedType != B_MEDIA_ENCODED_AUDIO
#endif
)
{
fprintf(stderr, "OpenSoundNode::FormatProposal returning "
"B_MEDIA_BAD_FORMAT\n");
return B_MEDIA_BAD_FORMAT;
}
return B_OK;
}
status_t
OpenSoundNode::FormatChangeRequested(const media_source& source,
const media_destination& destination, media_format* io_format,
int32* _deprecated_)
{
CALLED();
return B_ERROR;
}
status_t
OpenSoundNode::GetNextOutput(int32* cookie, media_output* out_output)
{
CALLED();
if (*cookie >= fOutputs.CountItems() || *cookie < 0)
return B_BAD_INDEX;
NodeOutput *channel = (NodeOutput*)fOutputs.ItemAt(*cookie);
*out_output = channel->fOutput;
*cookie += 1;
return B_OK;
}
status_t
OpenSoundNode::DisposeOutputCookie(int32 cookie)
{
CALLED();
return B_OK;
}
status_t
OpenSoundNode::SetBufferGroup(const media_source& for_source,
BBufferGroup* newGroup)
{
CALLED();
NodeOutput* channel = _FindOutput(for_source);
if (channel == NULL) {
fprintf(stderr, "OpenSoundNode::SetBufferGroup() returning "
"B_MEDIA_BAD_SOURCE\n");
return B_MEDIA_BAD_SOURCE;
}
if (newGroup == channel->fBufferGroup)
return B_OK;
channel->FreeBuffers();
if (newGroup != NULL) {
return channel->SetExternalBuffers(newGroup);
} else {
return channel->AllocateBuffers(BufferDuration(), fLatency);
}
}
status_t
OpenSoundNode::PrepareToConnect(const media_source& what,
const media_destination& where, media_format* format,
media_source* out_source, char* out_name)
{
CALLED();
status_t err;
NodeOutput *channel = _FindOutput(what);
if (channel == NULL) {
fprintf(stderr, "OpenSoundNode::PrepareToConnect returning "
"B_MEDIA_BAD_SOURCE\n");
return B_MEDIA_BAD_SOURCE;
}
if (channel->fOutput.destination != media_destination::null)
return B_MEDIA_ALREADY_CONNECTED;
BAutolock L(fDevice->Locker());
OpenSoundDeviceEngine *engine = fDevice->NextFreeEngineAt(
channel->fEngineIndex, false);
if (engine == NULL)
return B_BUSY;
if (format->type != B_MEDIA_RAW_AUDIO) {
fprintf(stderr, "\tnon-raw-audio format?!\n");
return B_MEDIA_BAD_FORMAT;
}
err = engine->SpecializeFormatFor(channel->fOSSFormatFlags, *format, true);
if (err < B_OK)
return err;
channel->fRealEngine = engine;
#if 0
if (format->u.raw_audio.buffer_size
== media_raw_audio_format::wildcard.buffer_size) {
format->u.raw_audio.buffer_size = 2048;
fprintf(stderr, "\tno buffer size provided, suggesting %lu\n",
format->u.raw_audio.buffer_size);
} else {
fprintf(stderr, "\tconsumer suggested buffer_size %lu\n",
format->u.raw_audio.buffer_size);
}
#endif
channel->fOutput.destination = where;
channel->fOutput.format = *format;
*out_source = channel->fOutput.source;
strncpy(out_name, channel->fOutput.name, B_MEDIA_NAME_LENGTH);
return B_OK;
}
void
OpenSoundNode::Connect(status_t error, const media_source& source,
const media_destination& destination, const media_format& format,
char* io_name)
{
CALLED();
NodeOutput *channel = _FindOutput(source);
if (channel == NULL) {
fprintf(stderr, "OpenSoundNode::Connect returning (cause : "
"B_MEDIA_BAD_SOURCE)\n");
return;
}
OpenSoundDeviceEngine *engine = channel->fRealEngine;
if (engine == NULL)
return;
if (error) {
channel->fOutput.destination = media_destination::null;
channel->fOutput.format = channel->fPreferredFormat;
engine->Close();
channel->fRealEngine = NULL;
return;
}
channel->fOutput.destination = destination;
channel->fOutput.format = format;
strncpy(io_name, channel->fOutput.name, B_MEDIA_NAME_LENGTH);
bigtime_t duration = channel->fOutput.format.u.raw_audio.buffer_size
* 10000
/ ( (channel->fOutput.format.u.raw_audio.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK)
* channel->fOutput.format.u.raw_audio.channel_count)
/ ((int32)(channel->fOutput.format.u.raw_audio.frame_rate / 100));
SetBufferDuration(duration);
media_node_id id;
FindLatencyFor(channel->fOutput.destination, &fLatency, &id);
TRACE("\tdownstream latency = %" B_PRIdBIGTIME "\n", fLatency);
fInternalLatency = BufferDuration();
TRACE("\tbuffer-filling took %" B_PRIdBIGTIME " usec on this machine\n",
fInternalLatency);
if (channel->fBufferGroup == NULL)
channel->AllocateBuffers(BufferDuration(), fLatency);
engine->StartRecording();
_StartRecThread(channel);
}
void
OpenSoundNode::Disconnect(const media_source& what,
const media_destination& where)
{
CALLED();
NodeOutput *channel = _FindOutput(what);
if (channel == NULL) {
fprintf(stderr, "OpenSoundNode::Disconnect() returning (cause : "
"B_MEDIA_BAD_SOURCE)\n");
return;
}
_StopRecThread(channel);
BAutolock L(fDevice->Locker());
OpenSoundDeviceEngine* engine = channel->fRealEngine;
if (where == channel->fOutput.destination
&& what == channel->fOutput.source) {
if (engine)
engine->Close();
channel->fRealEngine = NULL;
channel->fOutput.destination = media_destination::null;
channel->fOutput.format = channel->fPreferredFormat;
channel->FreeBuffers();
} else {
fprintf(stderr, "\tDisconnect() called with wrong source/destination "
"(%" B_PRId32 "/%" B_PRId32 "), ours is (%" B_PRId32 "/%" B_PRId32
")\n", what.id, where.id, channel->fOutput.source.id,
channel->fOutput.destination.id);
}
}
void
OpenSoundNode::LateNoticeReceived(const media_source& what, bigtime_t how_much,
bigtime_t performance_time)
{
CALLED();
NodeOutput *channel = _FindOutput(what);
if (channel == NULL)
return;
if (RunMode() == B_RECORDING) {
} else if (RunMode() == B_INCREASE_LATENCY) {
fInternalLatency += how_much;
SetEventLatency(fLatency + fInternalLatency);
fprintf(stderr, "\tincreasing latency to %" B_PRIdBIGTIME "\n",
fLatency + fInternalLatency);
} else {
fprintf(stderr, "\tskipping a buffer to try to catch up\n");
}
}
void
OpenSoundNode::EnableOutput(const media_source& what, bool enabled,
int32* _deprecated_)
{
CALLED();
NodeOutput *channel = _FindOutput(what);
if (channel != NULL)
{
channel->fOutputEnabled = enabled;
}
}
void
OpenSoundNode::AdditionalBufferRequested(const media_source& source,
media_buffer_id prev_buffer, bigtime_t prev_time,
const media_seek_tag* prev_tag)
{
CALLED();
return;
}
void
OpenSoundNode::HandleEvent(const media_timed_event* event, bigtime_t lateness,
bool realTimeEvent)
{
CALLED();
switch (event->type) {
case BTimedEventQueue::B_START:
HandleStart(event,lateness,realTimeEvent);
break;
case BTimedEventQueue::B_SEEK:
HandleSeek(event,lateness,realTimeEvent);
break;
case BTimedEventQueue::B_WARP:
HandleWarp(event,lateness,realTimeEvent);
break;
case BTimedEventQueue::B_STOP:
HandleStop(event,lateness,realTimeEvent);
break;
case BTimedEventQueue::B_HANDLE_BUFFER:
if (RunState() == BMediaEventLooper::B_STARTED) {
HandleBuffer(event,lateness,realTimeEvent);
}
break;
case BTimedEventQueue::B_DATA_STATUS:
HandleDataStatus(event,lateness,realTimeEvent);
break;
case BTimedEventQueue::B_PARAMETER:
HandleParameter(event,lateness,realTimeEvent);
break;
default:
fprintf(stderr," unknown event type: %" B_PRId32 "\n",
event->type);
break;
}
}
status_t
OpenSoundNode::HandleBuffer(const media_timed_event* event,
bigtime_t lateness, bool realTimeEvent)
{
CALLED();
BBuffer* buffer = const_cast<BBuffer*>((BBuffer*)event->pointer);
if (buffer == NULL) {
fprintf(stderr,"OpenSoundNode::HandleBuffer() - B_BAD_VALUE\n");
return B_BAD_VALUE;
}
NodeInput *channel = _FindInput(buffer->Header()->destination);
if (channel == NULL) {
buffer->Recycle();
fprintf(stderr,"OpenSoundNode::HandleBuffer() - "
"B_MEDIA_BAD_DESTINATION\n");
return B_MEDIA_BAD_DESTINATION;
}
media_header* hdr = buffer->Header();
bigtime_t now = TimeSource()->Now();
bigtime_t perf_time = hdr->start_time;
bigtime_t how_early = perf_time - EventLatency() - now;
if (RunMode() != B_OFFLINE
&& RunMode() != B_RECORDING
&& how_early < 0LL
&& false) {
NotifyLateProducer(channel->fInput.source, -how_early, perf_time);
fprintf(stderr," <- LATE BUFFER : %" B_PRIdBIGTIME "\n", how_early);
buffer->Recycle();
} else {
fDevice->Locker()->Lock();
if (channel->fBuffers.CountItems() > 10) {
fDevice->Locker()->Unlock();
TRACE("OpenSoundNode::HandleBuffer too many buffers, "
"recycling\n");
buffer->Recycle();
} else {
if (!channel->fBuffers.AddItem(buffer))
buffer->Recycle();
fDevice->Locker()->Unlock();
}
}
return B_OK;
}
status_t
OpenSoundNode::HandleDataStatus(const media_timed_event* event,
bigtime_t lateness, bool realTimeEvent)
{
BString message("OpenSoundNode::HandleDataStatus status: ");
switch(event->data) {
case B_DATA_NOT_AVAILABLE:
message << "No data";
break;
case B_DATA_AVAILABLE:
message << "Data";
break;
case B_PRODUCER_STOPPED:
message << "Stopped";
break;
default:
message << "???";
break;
}
message << ", lateness: " << lateness;
printf("%s\n", message.String());
return B_OK;
}
status_t
OpenSoundNode::HandleStart(const media_timed_event* event, bigtime_t lateness,
bool realTimeEvent)
{
CALLED();
if (RunState() != B_STARTED) {
}
return B_OK;
}
status_t
OpenSoundNode::HandleSeek(const media_timed_event* event, bigtime_t lateness,
bool realTimeEvent)
{
CALLED();
TRACE("OpenSoundNode::HandleSeek(t=%" B_PRIdBIGTIME ", d=%" B_PRId32 ", bd=%" B_PRId64 ")\n",
event->event_time, event->data, event->bigdata);
return B_OK;
}
status_t
OpenSoundNode::HandleWarp(const media_timed_event* event,
bigtime_t lateness, bool realTimeEvent)
{
CALLED();
return B_OK;
}
status_t
OpenSoundNode::HandleStop(const media_timed_event* event, bigtime_t lateness,
bool realTimeEvent)
{
CALLED();
EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true,
BTimedEventQueue::B_HANDLE_BUFFER);
return B_OK;
}
status_t
OpenSoundNode::HandleParameter(const media_timed_event* event,
bigtime_t lateness, bool realTimeEvent)
{
CALLED();
return B_OK;
}
void
OpenSoundNode::SetRunMode(run_mode mode)
{
CALLED();
TRACE("OpenSoundNode::SetRunMode(%d)\n", mode);
}
status_t
OpenSoundNode::TimeSourceOp(const time_source_op_info& op, void* _reserved)
{
CALLED();
switch(op.op) {
case B_TIMESOURCE_START:
TRACE("TimeSourceOp op B_TIMESOURCE_START\n");
if (RunState() != BMediaEventLooper::B_STARTED) {
fTimeSourceStarted = true;
fTimeSourceStartTime = RealTime();
media_timed_event startEvent(0, BTimedEventQueue::B_START);
EventQueue()->AddEvent(startEvent);
}
break;
case B_TIMESOURCE_STOP:
TRACE("TimeSourceOp op B_TIMESOURCE_STOP\n");
if (RunState() == BMediaEventLooper::B_STARTED) {
media_timed_event stopEvent(0, BTimedEventQueue::B_STOP);
EventQueue()->AddEvent(stopEvent);
fTimeSourceStarted = false;
PublishTime(0, 0, 1.0f);
}
break;
case B_TIMESOURCE_STOP_IMMEDIATELY:
TRACE("TimeSourceOp op B_TIMESOURCE_STOP_IMMEDIATELY\n");
if (RunState() == BMediaEventLooper::B_STARTED) {
media_timed_event stopEvent(0, BTimedEventQueue::B_STOP);
EventQueue()->AddEvent(stopEvent);
fTimeSourceStarted = false;
PublishTime(0, 0, 1.0f);
}
break;
case B_TIMESOURCE_SEEK:
printf("TimeSourceOp op B_TIMESOURCE_SEEK, real %" B_PRIdBIGTIME ", "
"perf %" B_PRIdBIGTIME "\n", op.real_time, op.performance_time);
BroadcastTimeWarp(op.real_time, op.performance_time);
break;
default:
break;
}
return B_OK;
}
status_t
OpenSoundNode::GetParameterValue(int32 id, bigtime_t* last_change, void* value,
size_t* ioSize)
{
CALLED();
int channelCount = 1;
int sliderShift = 8;
OpenSoundDeviceMixer* mixer = fDevice->MixerAt(0);
if (!mixer)
return ENODEV;
TRACE("id : %i, *ioSize=%zd\n", id, *ioSize);
oss_mixext mixext;
status_t err = mixer->GetExtInfo(id, &mixext);
if (err < B_OK)
return err;
oss_mixer_value mixval;
mixval.ctrl = mixext.ctrl;
mixval.timestamp = mixext.timestamp;
err = mixer->GetMixerValue(&mixval);
if (err < B_OK)
return err;
if (!(mixext.flags & MIXF_READABLE))
return EINVAL;
BParameter *parameter = NULL;
for (int32 i = 0; i < fWeb->CountParameters(); i++) {
parameter = fWeb->ParameterAt(i);
if(parameter->ID() == id)
break;
}
if (!parameter)
return ENODEV;
TRACE("%s: value = 0x%08x\n", __FUNCTION__, mixval.value);
*last_change = system_time();
switch (mixext.type) {
case MIXT_DEVROOT:
case MIXT_GROUP:
break;
case MIXT_ONOFF:
if (*ioSize < sizeof(bool))
return EINVAL;
*(int32 *)value = mixval.value?true:false;
*ioSize = sizeof(bool);
return B_OK;
case MIXT_ENUM:
if (*ioSize < sizeof(int32))
return EINVAL;
*(int32 *)value = mixval.value;
*ioSize = sizeof(int32);
return B_OK;
break;
case MIXT_STEREODB:
case MIXT_STEREOSLIDER16:
case MIXT_STEREOSLIDER:
channelCount = 2;
case MIXT_SLIDER:
case MIXT_MONODB:
case MIXT_MONOSLIDER16:
case MIXT_MONOSLIDER:
if (*ioSize < channelCount * sizeof(float))
return EINVAL;
if (parameter->Type() != BParameter::B_CONTINUOUS_PARAMETER)
return EINVAL;
if (mixext.type == MIXT_STEREOSLIDER16 ||
mixext.type == MIXT_MONOSLIDER16)
sliderShift = 16;
*ioSize = channelCount * sizeof(float);
((float *)value)[0] = (float)(mixval.value & ((1 << sliderShift) - 1));
TRACE("%s: value[O] = %f\n", __FUNCTION__, ((float *)value)[0]);
if (channelCount < 2)
return B_OK;
((float *)value)[1] = (float)((mixval.value >> sliderShift)
& ((1 << sliderShift) - 1));
TRACE("%s: value[1] = %f\n", __FUNCTION__, ((float *)value)[1]);
return B_OK;
break;
case MIXT_MESSAGE:
break;
case MIXT_MONOVU:
break;
case MIXT_STEREOVU:
break;
case MIXT_MONOPEAK:
break;
case MIXT_STEREOPEAK:
break;
case MIXT_RADIOGROUP:
break;
case MIXT_MARKER:
break;
case MIXT_VALUE:
break;
case MIXT_HEXVALUE:
break;
case MIXT_3D:
break;
default:
TRACE("OpenSoundNode::%s: unknown mixer control type %d\n",
__FUNCTION__, mixext.type);
}
*ioSize = 0;
return EINVAL;
}
void
OpenSoundNode::SetParameterValue(int32 id, bigtime_t performance_time,
const void* value, size_t size)
{
CALLED();
TRACE("id : %i, performance_time : %" B_PRIdBIGTIME ", size : %zi\n", id,
performance_time, size);
OpenSoundDeviceMixer *mixer = fDevice->MixerAt(0);
if (mixer == NULL)
return;
oss_mixext mixext;
if (mixer->GetExtInfo(id, &mixext) < B_OK)
return;
if (!(mixext.flags & MIXF_WRITEABLE))
return;
oss_mixer_value mixval;
mixval.ctrl = mixext.ctrl;
mixval.timestamp = mixext.timestamp;
status_t err = mixer->GetMixerValue(&mixval);
if (err < B_OK)
return;
mixval.ctrl = mixext.ctrl;
mixval.timestamp = mixext.timestamp;
BParameter *parameter = NULL;
for(int32 i=0; i<fWeb->CountParameters(); i++) {
parameter = fWeb->ParameterAt(i);
if(parameter->ID() == id)
break;
}
if (!parameter)
return;
int channelCount = 1;
int sliderShift = 8;
switch (mixext.type) {
case MIXT_DEVROOT:
case MIXT_GROUP:
break;
case MIXT_ONOFF:
if (size < sizeof(bool))
return;
mixval.value = (int)*(int32 *)value;
mixer->SetMixerValue(&mixval);
_PropagateParameterChanges(mixext.ctrl, mixext.type, mixext.id);
return;
case MIXT_ENUM:
if (size < sizeof(int32))
return;
mixval.value = (int)*(int32 *)value;
mixer->SetMixerValue(&mixval);
break;
case MIXT_STEREODB:
case MIXT_STEREOSLIDER16:
case MIXT_STEREOSLIDER:
channelCount = 2;
case MIXT_SLIDER:
case MIXT_MONODB:
case MIXT_MONOSLIDER16:
case MIXT_MONOSLIDER:
if (size < channelCount * sizeof(float))
return;
if (parameter->Type() != BParameter::B_CONTINUOUS_PARAMETER)
return;
if (mixext.type == MIXT_STEREOSLIDER16 ||
mixext.type == MIXT_MONOSLIDER16)
sliderShift = 16;
mixval.value = 0;
TRACE("-------- sliderShift=%d, v = %08x, v & %08x = %08x\n",
sliderShift, mixval.value, ((1 << sliderShift) - 1),
mixval.value & ((1 << sliderShift) - 1));
mixval.value |= ((int)(((float *)value)[0]))
& ((1 << sliderShift) - 1);
if (channelCount > 1) {
mixval.value |= (((int)(((float *)value)[1]))
& ((1 << sliderShift) - 1)) << sliderShift;
}
TRACE("%s: value = 0x%08x\n", __FUNCTION__, mixval.value);
mixer->SetMixerValue(&mixval);
return;
break;
case MIXT_MESSAGE:
break;
case MIXT_MONOVU:
break;
case MIXT_STEREOVU:
break;
case MIXT_MONOPEAK:
break;
case MIXT_STEREOPEAK:
break;
case MIXT_RADIOGROUP:
break;
case MIXT_MARKER:
break;
case MIXT_VALUE:
break;
case MIXT_HEXVALUE:
break;
case MIXT_3D:
break;
default:
TRACE("OpenSoundNode::%s: unknown mixer control type %d\n",
__FUNCTION__, mixext.type);
}
return;
}
BParameterWeb*
OpenSoundNode::MakeParameterWeb()
{
CALLED();
BParameterWeb* web = new BParameterWeb;
OpenSoundDeviceMixer* mixer = fDevice->MixerAt(0);
if (mixer == NULL) {
BParameterGroup* child = web->MakeGroup("No mixer");
child->MakeNullParameter(1, B_MEDIA_UNKNOWN_TYPE, "No Mixer",
B_GENERIC);
return web;
}
int mixext_count = mixer->CountExtInfos();
TRACE("OpenSoundNode::MakeParameterWeb %i ExtInfos\n", mixext_count);
for (int32 i = 0; i < mixext_count; i++) {
oss_mixext mixext;
if (mixer->GetExtInfo(i, &mixext) < B_OK)
continue;
if (mixext.type == MIXT_DEVROOT) {
oss_mixext_root* extroot = (oss_mixext_root*)mixext.data;
TRACE("OpenSoundNode: mixext[%d]: ROOT\n", i);
int32 nb = 0;
const char* childName = mixext.extname;
childName = extroot->id;
BParameterGroup *child = web->MakeGroup(childName);
_ProcessGroup(child, i, nb);
}
}
return web;
}
void
OpenSoundNode::_ProcessGroup(BParameterGroup *group, int32 index,
int32& nbParameters)
{
CALLED();
OpenSoundDeviceMixer* mixer = fDevice->MixerAt(0);
int mixext_count = mixer->CountExtInfos();
for (int32 i = 0; i < mixext_count; i++) {
oss_mixext mixext;
if (mixer->GetExtInfo(i, &mixext) < B_OK)
continue;
if (mixext.parent != index)
continue;
int32 nb = 1;
TRACE("OpenSoundNode: mixext[%d]: { %s/%s, type=%d, parent=%d, "
"min=%d, max=%d, flags=0x%08x, control_no=%d, desc=%d, "
"update_counter=%d }\n", i,
(mixext.type != MIXT_MARKER) ? mixext.id : "",
(mixext.type != MIXT_MARKER) ? mixext.extname : "",
mixext.type, mixext.parent,
mixext.minvalue, mixext.maxvalue,
mixext.flags, mixext.control_no,
mixext.desc, mixext.update_counter);
const char *childName = mixext.extname;
if (mixext.flags & MIXF_MAINVOL)
childName = "Master Gain";
const char *sliderUnit = "";
if (mixext.flags & MIXF_HZ)
sliderUnit = "Hz";
const char *continuousKind = B_GAIN;
BParameterGroup* child;
switch (mixext.type) {
case MIXT_DEVROOT:
break;
case MIXT_GROUP:
TRACE("OpenSoundNode: mixext[%d]: GROUP\n", i);
child = group->MakeGroup(childName);
child->MakeNullParameter(i, B_MEDIA_RAW_AUDIO, childName,
B_WEB_BUFFER_OUTPUT);
_ProcessGroup(child, i, nb);
break;
case MIXT_ONOFF:
TRACE("OpenSoundNode: mixext[%d]: ONOFF\n", i);
if (0) {
group->MakeDiscreteParameter(i, B_MEDIA_RAW_AUDIO, childName,
B_MUTE);
} else {
group->MakeDiscreteParameter(i, B_MEDIA_RAW_AUDIO, childName,
B_ENABLE);
}
if (nbParameters > 0) {
(group->ParameterAt(nbParameters - 1))->AddOutput(
group->ParameterAt(nbParameters));
nbParameters++;
}
break;
case MIXT_ENUM:
{
TRACE("OpenSoundNode: mixext[%d]: ENUM\n", i);
BDiscreteParameter *parameter =
group->MakeDiscreteParameter(i, B_MEDIA_RAW_AUDIO, childName,
B_INPUT_MUX);
if (nbParameters > 0) {
(group->ParameterAt(nbParameters - 1))->AddOutput(
group->ParameterAt(nbParameters));
nbParameters++;
}
_ProcessMux(parameter, i);
break;
}
case MIXT_MONODB:
case MIXT_STEREODB:
sliderUnit = "dB";
case MIXT_SLIDER:
case MIXT_MONOSLIDER16:
case MIXT_STEREOSLIDER16:
case MIXT_MONOSLIDER:
case MIXT_STEREOSLIDER:
TRACE("OpenSoundNode: mixext[%d]: [MONO|STEREO]SLIDER\n", i);
if (mixext.flags & MIXF_MAINVOL)
continuousKind = B_MASTER_GAIN;
group->MakeContinuousParameter(i, B_MEDIA_RAW_AUDIO, childName,
continuousKind, sliderUnit, mixext.minvalue, mixext.maxvalue,
1);
if (mixext.type == MIXT_STEREOSLIDER ||
mixext.type == MIXT_STEREOSLIDER16 ||
mixext.type == MIXT_STEREODB)
group->ParameterAt(nbParameters)->SetChannelCount(2);
TRACE("nb parameters : %d\n", nbParameters);
if (nbParameters > 0) {
(group->ParameterAt(nbParameters - 1))->AddOutput(
group->ParameterAt(nbParameters));
nbParameters++;
}
break;
case MIXT_MESSAGE:
break;
case MIXT_MONOVU:
break;
case MIXT_STEREOVU:
break;
case MIXT_MONOPEAK:
break;
case MIXT_STEREOPEAK:
break;
case MIXT_RADIOGROUP:
break;
case MIXT_MARKER:
break;
case MIXT_VALUE:
break;
case MIXT_HEXVALUE:
break;
case MIXT_3D:
break;
default:
TRACE("OpenSoundNode::_ProcessGroup: unknown mixer control "
"type %d\n", mixext.type);
}
}
}
void
OpenSoundNode::_ProcessMux(BDiscreteParameter* parameter, int32 index)
{
CALLED();
OpenSoundDeviceMixer *mixer = fDevice->MixerAt(0);
oss_mixer_enuminfo enuminfo;
status_t err = mixer->GetEnumInfo(index, &enuminfo);
if (err < B_OK) {
oss_mixext mixext;
if (mixer->GetExtInfo(index, &mixext) < B_OK)
return;
for (int32 i = 0; i < mixext.maxvalue; i++) {
BString s;
s << i;
parameter->AddItem(i, s.String());
}
return;
}
for (int32 i = 0; i < enuminfo.nvalues; i++) {
parameter->AddItem(i, &enuminfo.strings[enuminfo.strindex[i]]);
}
return;
}
status_t
OpenSoundNode::_PropagateParameterChanges(int from, int type, const char* id)
{
CALLED();
TRACE("OpenSoundNode::_PropagateParameterChanges(from %i, type %i, "
"id %s)\n", from, type, id);
OpenSoundDeviceMixer* mixer = fDevice->MixerAt(0);
if (mixer == NULL)
return ENODEV;
return B_OK;
char newValues[128];
size_t newValuesSize;
for (int i = 0; i < mixer->CountExtInfos(); i++) {
oss_mixext mixext;
status_t err = mixer->GetExtInfo(i, &mixext);
if (err < B_OK)
continue;
if (!(mixext.flags & MIXF_READABLE))
continue;
if (type > -1 && mixext.type != type)
continue;
if (id && strncmp(mixext.id, id, 16))
continue;
newValuesSize = 128;
bigtime_t last;
if (GetParameterValue(mixext.ctrl, &last, newValues,
&newValuesSize) < B_OK) {
continue;
}
TRACE("OpenSoundNode::%s: updating mixer control %d\n",
__FUNCTION__, mixext.ctrl);
BroadcastNewParameterValue(last, mixext.ctrl, newValues,
newValuesSize);
}
return B_OK;
}
int32
OpenSoundNode::_PlayThread(NodeInput* input)
{
CALLED();
signal(SIGUSR1, &_SignalHandler);
OpenSoundDeviceEngine* engine = input->fRealEngine;
if (!engine || !engine->InUse())
return B_NO_INIT;
if (input->fInput.source == media_source::null
&& input->fEngineIndex == 0)
return B_NO_INIT;
ASSERT(engine->OpenMode() & OPEN_WRITE);
size_t driverBufferSize = engine->DriverBufferSize();
size_t bufferSize = input->fInput.format.u.raw_audio.buffer_size;
if (driverBufferSize != bufferSize) {
printf("warning, OSS driver buffer size: %ld, audio buffer "
"size: %ld", driverBufferSize, bufferSize);
}
uint8* silenceBuffer = (uint8*)malloc(bufferSize);
if (silenceBuffer == NULL)
return B_NO_MEMORY;
MemoryDeleter deleter(silenceBuffer);
uint8 formatSilence = 0;
if (input->fInput.format.u.raw_audio.format
== media_raw_audio_format::B_AUDIO_UCHAR)
formatSilence = 128;
memset(silenceBuffer, formatSilence, bufferSize);
input->Write(silenceBuffer, bufferSize);
int64 bytesWritten = 0;
bigtime_t lastRealTime = RealTime();
bigtime_t lastPerformanceTime = 0;
const int32 driftValueCount = 64;
int32 currentDriftValueIndex = 0;
float driftValues[driftValueCount];
for (int32 i = 0; i < driftValueCount; i++)
driftValues[i] = 1.0;
do {
if (!fDevice->Locker()->Lock())
break;
TRACE("OpenSoundNode::_PlayThread: buffers: %" B_PRId32 "\n",
input->fBuffers.CountItems());
BBuffer* buffer = (BBuffer*)input->fBuffers.RemoveItem((int32)0);
fDevice->Locker()->Unlock();
if (input->fThread < 0) {
if (buffer)
buffer->Recycle();
break;
}
int32 additionalBytesWritten = 0;
if (buffer != NULL) {
if (input->Write(buffer->Data(), buffer->SizeUsed()) == B_OK)
additionalBytesWritten = buffer->SizeUsed();
buffer->Recycle();
} else {
input->Write(silenceBuffer, bufferSize);
additionalBytesWritten = bufferSize;
}
if (input->fEngineIndex == 0 && input->fThread >= 0) {
bigtime_t realTime = RealTime();
bigtime_t realPlaybackDuration = realTime - lastRealTime;
bigtime_t performanceTime
= time_for_buffer(bytesWritten, input->fInput.format);
float drift = (double)(performanceTime
- lastPerformanceTime) / realPlaybackDuration;
lastPerformanceTime = performanceTime;
lastRealTime = realTime;
driftValues[currentDriftValueIndex++] = drift;
if (currentDriftValueIndex == driftValueCount)
currentDriftValueIndex = 0;
drift = 0.0;
for (int32 i = 0; i < driftValueCount; i++)
drift += driftValues[i];
drift /= driftValueCount;
if (fDevice->Locker()->Lock()) {
if (input->fThread >= 0)
_UpdateTimeSource(performanceTime, realTime, drift);
fDevice->Locker()->Unlock();
}
}
bytesWritten += additionalBytesWritten;
} while (input->fThread > -1);
return 0;
}
int32
OpenSoundNode::_RecThread(NodeOutput* output)
{
CALLED();
signal(SIGUSR1, &_SignalHandler);
OpenSoundDeviceEngine *engine = output->fRealEngine;
if (!engine || !engine->InUse())
return B_NO_INIT;
if ((RunState() != BMediaEventLooper::B_STARTED)
|| (output->fOutput.destination == media_destination::null)) {
return B_NO_INIT;
}
ASSERT(engine->OpenMode() & OPEN_READ);
#ifdef ENABLE_REC
fDevice->Locker()->Lock();
do {
audio_buf_info abinfo;
fDevice->Locker()->Unlock();
BBuffer* buffer = _FillNextBuffer(&abinfo, *output);
fDevice->Locker()->Lock();
if (buffer) {
status_t err = B_ERROR;
if (output->fOutputEnabled) {
err = SendBuffer(buffer, output->fOutput.source,
output->fOutput.destination);
}
if (err != B_OK) {
buffer->Recycle();
} else {
size_t nSamples = buffer->SizeUsed()
/ (output->fOutput.format.u.raw_audio.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK);
output->fSamplesSent += nSamples;
}
}
} while (output->fThread > -1);
fDevice->Locker()->Unlock();
#endif
return 0;
}
status_t
OpenSoundNode::_StartPlayThread(NodeInput* input)
{
CALLED();
BAutolock L(fDevice->Locker());
if (input->fThread > B_OK)
return B_OK;
input->fThread = spawn_thread(_PlayThreadEntry,
"OpenSound audio output", B_REAL_TIME_PRIORITY, input);
if (input->fThread < B_OK) {
return B_ERROR;
}
resume_thread(input->fThread);
return B_OK;
}
status_t
OpenSoundNode::_StopPlayThread(NodeInput* input)
{
if (input->fThread < 0)
return B_OK;
CALLED();
thread_id th;
{
BAutolock L(fDevice->Locker());
th = input->fThread;
input->fThread = -1;
}
status_t ret;
wait_for_thread(th, &ret);
return B_OK;
}
status_t
OpenSoundNode::_StartRecThread(NodeOutput* output)
{
CALLED();
if (output->fThread > B_OK)
return B_OK;
output->fThread = spawn_thread(_RecThreadEntry, "OpenSound audio input",
B_REAL_TIME_PRIORITY, output);
if (output->fThread < B_OK) {
return B_ERROR;
}
resume_thread(output->fThread);
return B_OK;
}
status_t
OpenSoundNode::_StopRecThread(NodeOutput* output)
{
if (output->fThread < 0)
return B_OK;
CALLED();
thread_id th = output->fThread;
output->fThread = -1;
{
BAutolock L(fDevice->Locker());
}
status_t ret;
wait_for_thread(th, &ret);
return B_OK;
}
void
OpenSoundNode::_UpdateTimeSource(bigtime_t performanceTime,
bigtime_t realTime, float drift)
{
if (!fTimeSourceStarted)
return;
PublishTime(performanceTime, realTime, drift);
}
BBuffer*
OpenSoundNode::_FillNextBuffer(audio_buf_info* abinfo, NodeOutput& channel)
{
CALLED();
BBuffer* buffer = channel.FillNextBuffer(BufferDuration());
if (!buffer)
return NULL;
if (fDevice == NULL)
fprintf(stderr, "OpenSoundNode::_FillNextBuffer() - fDevice NULL\n");
if (buffer->Header() == NULL) {
fprintf(stderr, "OpenSoundNode::_FillNextBuffer() - "
"buffer->Header() NULL\n");
}
if (TimeSource() == NULL) {
fprintf(stderr, "OpenSoundNode::_FillNextBuffer() - "
"TimeSource() NULL\n");
}
media_header* hdr = buffer->Header();
if (hdr != NULL) {
hdr->time_source = TimeSource()->ID();
hdr->start_time = PerformanceTimeFor(system_time());
}
return buffer;
}
status_t
OpenSoundNode::GetConfigurationFor(BMessage* into_message)
{
CALLED();
if (!into_message)
return B_BAD_VALUE;
size_t size = 128;
void* buffer = malloc(size);
for (int32 i = 0; i < fWeb->CountParameters(); i++) {
BParameter* parameter = fWeb->ParameterAt(i);
if (parameter->Type() != BParameter::B_CONTINUOUS_PARAMETER
&& parameter->Type() != BParameter::B_DISCRETE_PARAMETER)
continue;
TRACE("getting parameter %i\n", parameter->ID());
size = 128;
bigtime_t last_change;
status_t err;
while ((err = GetParameterValue(parameter->ID(), &last_change, buffer,
&size)) == B_NO_MEMORY) {
size += 128;
free(buffer);
buffer = malloc(size);
}
if (err == B_OK && size > 0) {
into_message->AddInt32("parameterID", parameter->ID());
into_message->AddData("parameterData", B_RAW_TYPE, buffer, size,
false);
} else {
TRACE("parameter err : %s\n", strerror(err));
}
}
free(buffer);
PRINT_OBJECT(*into_message);
return B_OK;
}
OpenSoundNode::NodeOutput*
OpenSoundNode::_FindOutput(const media_source& source) const
{
int32 count = fOutputs.CountItems();
for (int32 i = 0; i < count; i++) {
NodeOutput* channel = (NodeOutput*)fOutputs.ItemAtFast(i);
if (source == channel->fOutput.source)
return channel;
}
return NULL;
}
OpenSoundNode::NodeInput*
OpenSoundNode::_FindInput(const media_destination& dest) const
{
int32 count = fInputs.CountItems();
for (int32 i = 0; i < count; i++) {
NodeInput* channel = (NodeInput*)fInputs.ItemAtFast(i);
if (dest == channel->fInput.destination)
return channel;
}
return NULL;
}
OpenSoundNode::NodeInput*
OpenSoundNode::_FindInput(int32 destinationId)
{
int32 count = fInputs.CountItems();
for (int32 i = 0; i < count; i++) {
NodeInput* channel = (NodeInput*)fInputs.ItemAtFast(i);
if (destinationId == channel->fInput.destination.id)
return channel;
}
return NULL;
}
void
OpenSoundNode::_SignalHandler(int sig)
{
}
int32
OpenSoundNode::_PlayThreadEntry(void* data)
{
CALLED();
NodeInput* channel = static_cast<NodeInput*>(data);
return channel->fNode->_PlayThread(channel);
}
int32
OpenSoundNode::_RecThreadEntry(void* data)
{
CALLED();
NodeOutput* channel = static_cast<NodeOutput*>(data);
return channel->fNode->_RecThread(channel);
}
void
OpenSoundNode::GetFlavor(flavor_info* outInfo, int32 id)
{
CALLED();
if (outInfo == NULL)
return;
outInfo->flavor_flags = 0;
outInfo->possible_count = 1;
outInfo->in_format_count = 0;
outInfo->in_formats = 0;
outInfo->out_format_count = 0;
outInfo->out_formats = 0;
outInfo->internal_id = id;
outInfo->name = "OpenSoundNode Node";
outInfo->info = "The OpenSoundNode outputs to OpenSound System v4 drivers.";
outInfo->kinds = B_BUFFER_CONSUMER | B_BUFFER_PRODUCER | B_TIME_SOURCE
| B_PHYSICAL_OUTPUT | B_PHYSICAL_INPUT | B_CONTROLLABLE;
outInfo->in_format_count = 1;
media_format * informats = new media_format[outInfo->in_format_count];
GetFormat(&informats[0]);
outInfo->in_formats = informats;
outInfo->out_format_count = 1;
media_format * outformats = new media_format[outInfo->out_format_count];
GetFormat(&outformats[0]);
outInfo->out_formats = outformats;
}
void
OpenSoundNode::GetFormat(media_format* outFormat)
{
CALLED();
if (outFormat == NULL)
return;
outFormat->type = B_MEDIA_RAW_AUDIO;
outFormat->require_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS;
outFormat->deny_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS;
outFormat->u.raw_audio = media_raw_audio_format::wildcard;
}