/*
* Copyright (c) 2002, 2003 Jerome Duval (jerome.duval@free.fr)
* Distributed under the terms of the MIT License.
*/
//! Media add-on for drivers that use the multi audio interface
#include "MultiAudioNode.h"
#include <stdio.h>
#include <string.h>
#include <Autolock.h>
#include <Buffer.h>
#include <BufferGroup.h>
#include <Catalog.h>
#include <ParameterWeb.h>
#include <String.h>
#include <Referenceable.h>
#include "MultiAudioUtility.h"
#ifdef DEBUG
# define PRINTING
#endif
#include "debug.h"
#include "Resampler.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "MultiAudio"
#define PARAMETER_ID_INPUT_FREQUENCY 1
#define PARAMETER_ID_OUTPUT_FREQUENCY 2
//This represent an hardware output
class node_input {
public:
node_input(media_input& input, media_format format);
~node_input();
int32 fChannelId;
media_input fInput;
media_format fPreferredFormat;
media_format fFormat;
volatile uint32 fBufferCycle;
multi_buffer_info fOldBufferInfo;
BBuffer* fBuffer;
Resampler *fResampler;
};
//This represent an hardware input
class node_output {
public:
node_output(media_output& output, media_format format);
~node_output();
int32 fChannelId;
media_output fOutput;
media_format fPreferredFormat;
media_format fFormat;
BBufferGroup* fBufferGroup;
bool fOutputEnabled;
uint64 fSamplesSent;
volatile uint32 fBufferCycle;
multi_buffer_info fOldBufferInfo;
Resampler* fResampler;
};
struct FrameRateChangeCookie : public BReferenceable {
float oldFrameRate;
uint32 id;
};
struct sample_rate_info {
uint32 multiAudioRate;
const char* name;
};
static const sample_rate_info kSampleRateInfos[] = {
{B_SR_8000, "8000"},
{B_SR_11025, "11025"},
{B_SR_12000, "12000"},
{B_SR_16000, "16000"},
{B_SR_22050, "22050"},
{B_SR_24000, "24000"},
{B_SR_32000, "32000"},
{B_SR_44100, "44100"},
{B_SR_48000, "48000"},
{B_SR_64000, "64000"},
{B_SR_88200, "88200"},
{B_SR_96000, "96000"},
{B_SR_176400, "176400"},
{B_SR_192000, "192000"},
{B_SR_384000, "384000"},
{B_SR_1536000, "1536000"},
{}
};
const char* kMultiControlString[] = {
"NAME IS ATTACHED",
B_TRANSLATE("Output"), B_TRANSLATE("Input"), B_TRANSLATE("Setup"),
B_TRANSLATE("Tone control"), B_TRANSLATE("Extended Setup"),
B_TRANSLATE("Enhanced Setup"), B_TRANSLATE("Master"), B_TRANSLATE("Beep"),
B_TRANSLATE("Phone"), B_TRANSLATE("Mic"), B_TRANSLATE("Line"),
B_TRANSLATE("CD"), B_TRANSLATE("Video"), B_TRANSLATE("Aux"),
B_TRANSLATE("Wave"), B_TRANSLATE("Gain"), B_TRANSLATE("Level"),
B_TRANSLATE("Volume"), B_TRANSLATE("Mute"), B_TRANSLATE("Enable"),
B_TRANSLATE("Stereo mix"), B_TRANSLATE("Mono mix"),
B_TRANSLATE("Output stereo mix"), B_TRANSLATE("Output mono mix"),
B_TRANSLATE("Output bass"), B_TRANSLATE("Output treble"),
B_TRANSLATE("Output 3D center"), B_TRANSLATE("Output 3D depth"),
B_TRANSLATE("Headphones"), B_TRANSLATE("SPDIF")
};
// #pragma mark -
node_input::node_input(media_input& input, media_format format)
{
CALLED();
fInput = input;
fPreferredFormat = format;
fBufferCycle = 1;
fBuffer = NULL;
fResampler = NULL;
}
node_input::~node_input()
{
CALLED();
}
// #pragma mark -
node_output::node_output(media_output& output, media_format format)
:
fBufferGroup(NULL),
fOutputEnabled(true)
{
CALLED();
fOutput = output;
fPreferredFormat = format;
fBufferCycle = 1;
fResampler = NULL;
}
node_output::~node_output()
{
CALLED();
}
// #pragma mark -
MultiAudioNode::MultiAudioNode(BMediaAddOn* addon, const char* name,
MultiAudioDevice* device, int32 internalID, BMessage* config)
:
BMediaNode(name),
BBufferConsumer(B_MEDIA_RAW_AUDIO),
BBufferProducer(B_MEDIA_RAW_AUDIO),
BMediaEventLooper(),
fBufferLock("multi audio buffers"),
fQuitThread(0),
fThread(-1),
fDevice(device),
fTimeSourceStarted(false),
fWeb(NULL),
fConfig()
{
CALLED();
fInitStatus = B_NO_INIT;
if (!device)
return;
fAddOn = addon;
fId = internalID;
AddNodeKind(B_PHYSICAL_OUTPUT);
AddNodeKind(B_PHYSICAL_INPUT);
// initialize our preferred format objects
fOutputPreferredFormat.type = B_MEDIA_RAW_AUDIO;
fOutputPreferredFormat.u.raw_audio.format
= MultiAudio::convert_to_media_format(
fDevice->FormatInfo().output.format);
fOutputPreferredFormat.u.raw_audio.valid_bits
= MultiAudio::convert_to_valid_bits(
fDevice->FormatInfo().output.format);
fOutputPreferredFormat.u.raw_audio.channel_count = 2;
fOutputPreferredFormat.u.raw_audio.frame_rate
= MultiAudio::convert_to_sample_rate(fDevice->FormatInfo().output.rate);
// measured in Hertz
fOutputPreferredFormat.u.raw_audio.byte_order = B_MEDIA_HOST_ENDIAN;
// we'll use the consumer's preferred buffer size, if any
fOutputPreferredFormat.u.raw_audio.buffer_size
= fDevice->BufferList().return_playback_buffer_size
* (fOutputPreferredFormat.u.raw_audio.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK)
* fOutputPreferredFormat.u.raw_audio.channel_count;
// initialize our preferred format objects
fInputPreferredFormat.type = B_MEDIA_RAW_AUDIO;
fInputPreferredFormat.u.raw_audio.format
= MultiAudio::convert_to_media_format(
fDevice->FormatInfo().input.format);
fInputPreferredFormat.u.raw_audio.valid_bits
= MultiAudio::convert_to_valid_bits(fDevice->FormatInfo().input.format);
fInputPreferredFormat.u.raw_audio.channel_count = 2;
fInputPreferredFormat.u.raw_audio.frame_rate
= MultiAudio::convert_to_sample_rate(fDevice->FormatInfo().input.rate);
// measured in Hertz
fInputPreferredFormat.u.raw_audio.byte_order = B_MEDIA_HOST_ENDIAN;
// we'll use the consumer's preferred buffer size, if any
fInputPreferredFormat.u.raw_audio.buffer_size
= fDevice->BufferList().return_record_buffer_size
* (fInputPreferredFormat.u.raw_audio.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK)
* fInputPreferredFormat.u.raw_audio.channel_count;
if (config != NULL) {
fConfig = *config;
PRINT_OBJECT(*config);
}
fInitStatus = B_OK;
}
MultiAudioNode::~MultiAudioNode()
{
CALLED();
fAddOn->GetConfigurationFor(this, NULL);
_StopOutputThread();
BMediaEventLooper::Quit();
}
status_t
MultiAudioNode::InitCheck() const
{
CALLED();
return fInitStatus;
}
void
MultiAudioNode::GetFlavor(flavor_info* info, int32 id)
{
CALLED();
if (info == NULL)
return;
info->flavor_flags = 0;
info->possible_count = 1;
// one flavor at a time
info->in_format_count = 0;
// no inputs
info->in_formats = 0;
info->out_format_count = 0;
// no outputs
info->out_formats = 0;
info->internal_id = id;
info->name = const_cast<char*>("MultiAudioNode Node");
info->info = const_cast<char*>("The MultiAudioNode node outputs to "
"multi_audio drivers.");
info->kinds = B_BUFFER_CONSUMER | B_BUFFER_PRODUCER | B_TIME_SOURCE
| B_PHYSICAL_OUTPUT | B_PHYSICAL_INPUT | B_CONTROLLABLE;
info->in_format_count = 1;
// 1 input
media_format* inFormats = new media_format[info->in_format_count];
GetFormat(&inFormats[0]);
info->in_formats = inFormats;
info->out_format_count = 1;
// 1 output
media_format* outFormats = new media_format[info->out_format_count];
GetFormat(&outFormats[0]);
info->out_formats = outFormats;
}
void
MultiAudioNode::GetFormat(media_format* format)
{
CALLED();
if (format == NULL)
return;
format->type = B_MEDIA_RAW_AUDIO;
format->require_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS;
format->deny_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS;
format->u.raw_audio = media_raw_audio_format::wildcard;
}
//#pragma mark - BMediaNode
BMediaAddOn*
MultiAudioNode::AddOn(int32* _internalID) const
{
CALLED();
// BeBook says this only gets called if we were in an add-on.
if (fAddOn != 0 && _internalID != NULL)
*_internalID = fId;
return fAddOn;
}
void
MultiAudioNode::Preroll()
{
CALLED();
// TODO: Performance opportunity
BMediaNode::Preroll();
}
status_t
MultiAudioNode::HandleMessage(int32 message, const void* data, size_t size)
{
CALLED();
return B_ERROR;
}
void
MultiAudioNode::NodeRegistered()
{
CALLED();
if (fInitStatus != B_OK) {
ReportError(B_NODE_IN_DISTRESS);
return;
}
node_input *currentInput = NULL;
int32 currentId = 0;
for (int32 i = 0; i < fDevice->Description().output_channel_count; i++) {
if (currentInput == NULL
|| (fDevice->Description().channels[i].designations
& B_CHANNEL_MONO_BUS) != 0
|| ((fDevice->Description().channels[currentId].designations
& B_CHANNEL_STEREO_BUS) != 0
&& ((fDevice->Description().channels[i].designations
& B_CHANNEL_LEFT) != 0
|| (fDevice->Description().channels[i].designations
& B_CHANNEL_STEREO_BUS) == 0))
|| ((fDevice->Description().channels[currentId].designations
& B_CHANNEL_SURROUND_BUS) != 0
&& ((fDevice->Description().channels[i].designations
& B_CHANNEL_LEFT) != 0
|| (fDevice->Description().channels[i].designations
& B_CHANNEL_SURROUND_BUS) == 0))) {
PRINT(("NodeRegistered(): creating an input for %" B_PRIi32 "\n",
i));
PRINT(("%" B_PRId32 "\t%d\t0x%" B_PRIx32 "\t0x%" B_PRIx32 "\n",
fDevice->Description().channels[i].channel_id,
fDevice->Description().channels[i].kind,
fDevice->Description().channels[i].designations,
fDevice->Description().channels[i].connectors));
media_input* input = new media_input;
input->format = fOutputPreferredFormat;
input->destination.port = ControlPort();
input->destination.id = fInputs.CountItems();
input->node = Node();
sprintf(input->name, "output %" B_PRId32, input->destination.id);
currentInput = new node_input(*input, fOutputPreferredFormat);
currentInput->fPreferredFormat.u.raw_audio.channel_count = 1;
currentInput->fInput.format = currentInput->fPreferredFormat;
delete currentInput->fResampler;
currentInput->fResampler = new
Resampler(currentInput->fPreferredFormat.AudioFormat(),
fOutputPreferredFormat.AudioFormat());
currentInput->fChannelId
= fDevice->Description().channels[i].channel_id;
fInputs.AddItem(currentInput);
currentId = i;
} else {
PRINT(("NodeRegistered(): adding a channel\n"));
currentInput->fPreferredFormat.u.raw_audio.channel_count++;
currentInput->fInput.format = currentInput->fPreferredFormat;
}
currentInput->fInput.format.u.raw_audio.format
= media_raw_audio_format::wildcard.format;
}
node_output *currentOutput = NULL;
currentId = 0;
for (int32 i = fDevice->Description().output_channel_count;
i < fDevice->Description().output_channel_count
+ fDevice->Description().input_channel_count; i++) {
if (currentOutput == NULL
|| (fDevice->Description().channels[i].designations
& B_CHANNEL_MONO_BUS) != 0
|| ((fDevice->Description().channels[currentId].designations
& B_CHANNEL_STEREO_BUS) != 0
&& ((fDevice->Description().channels[i].designations
& B_CHANNEL_LEFT) != 0
|| (fDevice->Description().channels[i].designations
& B_CHANNEL_STEREO_BUS) == 0))
|| ((fDevice->Description().channels[currentId].designations
& B_CHANNEL_SURROUND_BUS) != 0
&& ((fDevice->Description().channels[i].designations
& B_CHANNEL_LEFT) != 0
|| (fDevice->Description().channels[i].designations
& B_CHANNEL_SURROUND_BUS) == 0))) {
PRINT(("NodeRegistered(): creating an output for %" B_PRIi32 "\n",
i));
PRINT(("%" B_PRId32 "\t%d\t0x%" B_PRIx32 "\t0x%" B_PRIx32 "\n",
fDevice->Description().channels[i].channel_id,
fDevice->Description().channels[i].kind,
fDevice->Description().channels[i].designations,
fDevice->Description().channels[i].connectors));
media_output *output = new media_output;
output->format = fInputPreferredFormat;
output->destination = media_destination::null;
output->source.port = ControlPort();
output->source.id = fOutputs.CountItems();
output->node = Node();
sprintf(output->name, "input %" B_PRId32, output->source.id);
currentOutput = new node_output(*output, fInputPreferredFormat);
currentOutput->fPreferredFormat.u.raw_audio.channel_count = 1;
currentOutput->fOutput.format = currentOutput->fPreferredFormat;
delete currentOutput->fResampler;
currentOutput->fResampler = new
Resampler(fInputPreferredFormat.AudioFormat(),
currentOutput->fPreferredFormat.AudioFormat());
currentOutput->fChannelId
= fDevice->Description().channels[i].channel_id;
fOutputs.AddItem(currentOutput);
currentId = i;
} else {
PRINT(("NodeRegistered(): adding a channel\n"));
currentOutput->fPreferredFormat.u.raw_audio.channel_count++;
currentOutput->fOutput.format = currentOutput->fPreferredFormat;
}
}
// Set up our parameter web
fWeb = MakeParameterWeb();
SetParameterWeb(fWeb);
// Apply configuration
#ifdef PRINTING
bigtime_t start = system_time();
#endif
int32 index = 0;
int32 parameterID = 0;
const void *data;
ssize_t size;
while (fConfig.FindInt32("parameterID", index, ¶meterID) == B_OK) {
if (fConfig.FindData("parameterData", B_RAW_TYPE, index, &data, &size)
== B_OK) {
SetParameterValue(parameterID, TimeSource()->Now(), data, size);
}
index++;
}
PRINT(("apply configuration in: %" B_PRIdBIGTIME "\n",
system_time() - start));
SetPriority(B_REAL_TIME_PRIORITY);
Run();
}
status_t
MultiAudioNode::RequestCompleted(const media_request_info& info)
{
CALLED();
if (info.what != media_request_info::B_REQUEST_FORMAT_CHANGE)
return B_OK;
FrameRateChangeCookie* cookie
= (FrameRateChangeCookie*)info.user_data;
if (cookie == NULL)
return B_OK;
BReference<FrameRateChangeCookie> cookieReference(cookie, true);
// if the request failed, we reset the frame rate
if (info.status != B_OK) {
if (cookie->id == PARAMETER_ID_INPUT_FREQUENCY) {
_SetNodeInputFrameRate(cookie->oldFrameRate);
if (fDevice->Description().output_rates & B_SR_SAME_AS_INPUT)
_SetNodeOutputFrameRate(cookie->oldFrameRate);
} else if (cookie->id == PARAMETER_ID_OUTPUT_FREQUENCY)
_SetNodeOutputFrameRate(cookie->oldFrameRate);
// TODO: If we have multiple connections, we should request to change
// the format back!
}
return B_OK;
}
void
MultiAudioNode::SetTimeSource(BTimeSource* timeSource)
{
CALLED();
}
// #pragma mark - BBufferConsumer
status_t
MultiAudioNode::AcceptFormat(const media_destination& dest,
media_format* format)
{
// Check to make sure the format is okay, then remove
// any wildcards corresponding to our requirements.
CALLED();
if (format == NULL)
return B_BAD_VALUE;
if (format->type != B_MEDIA_RAW_AUDIO)
return B_MEDIA_BAD_FORMAT;
node_input *channel = _FindInput(dest);
if (channel == NULL)
return B_MEDIA_BAD_DESTINATION;
/* media_format * myFormat = GetFormat();
fprintf(stderr,"proposed format: ");
print_media_format(format);
fprintf(stderr,"\n");
fprintf(stderr,"my format: ");
print_media_format(myFormat);
fprintf(stderr,"\n");*/
// Be's format_is_compatible doesn't work.
// if (!format_is_compatible(*format,*myFormat)) {
channel->fFormat = channel->fPreferredFormat;
/*if(format->u.raw_audio.format == media_raw_audio_format::B_AUDIO_FLOAT
&& channel->fPreferredFormat.u.raw_audio.format == media_raw_audio_format::B_AUDIO_SHORT)
format->u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT;
else*/
format->u.raw_audio.format = channel->fPreferredFormat.u.raw_audio.format;
format->u.raw_audio.valid_bits
= channel->fPreferredFormat.u.raw_audio.valid_bits;
format->u.raw_audio.frame_rate
= channel->fPreferredFormat.u.raw_audio.frame_rate;
format->u.raw_audio.channel_count
= channel->fPreferredFormat.u.raw_audio.channel_count;
format->u.raw_audio.byte_order = B_MEDIA_HOST_ENDIAN;
format->u.raw_audio.buffer_size
= fDevice->BufferList().return_playback_buffer_size
* (format->u.raw_audio.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK)
* format->u.raw_audio.channel_count;
/*media_format myFormat;
GetFormat(&myFormat);
if (!format_is_acceptible(*format,myFormat)) {
fprintf(stderr,"<- B_MEDIA_BAD_FORMAT\n");
return B_MEDIA_BAD_FORMAT;
}*/
//AddRequirements(format);
return B_OK;
}
status_t
MultiAudioNode::GetNextInput(int32* cookie, media_input* _input)
{
CALLED();
if (_input == NULL)
return B_BAD_VALUE;
if (*cookie >= fInputs.CountItems() || *cookie < 0)
return B_BAD_INDEX;
node_input* channel = (node_input*)fInputs.ItemAt(*cookie);
*_input = channel->fInput;
*cookie += 1;
PRINT(("input.format: %" B_PRIu32 "\n",
channel->fInput.format.u.raw_audio.format));
return B_OK;
}
void
MultiAudioNode::DisposeInputCookie(int32 cookie)
{
CALLED();
// nothing to do since our cookies are just integers
}
void
MultiAudioNode::BufferReceived(BBuffer* buffer)
{
//CALLED();
switch (buffer->Header()->type) {
/*case B_MEDIA_PARAMETERS:
{
status_t status = ApplyParameterData(buffer->Data(),buffer->SizeUsed());
if (status != B_OK) {
fprintf(stderr,"ApplyParameterData in MultiAudioNode::BufferReceived failed\n");
}
buffer->Recycle();
}
break;*/
case B_MEDIA_RAW_AUDIO:
if ((buffer->Flags() & BBuffer::B_SMALL_BUFFER) != 0) {
fprintf(stderr, "NOT IMPLEMENTED: B_SMALL_BUFFER in "
"MultiAudioNode::BufferReceived\n");
// TODO: implement this part
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, "EventQueue()->AddEvent(event) in "
"MultiAudioNode::BufferReceived failed\n");
buffer->Recycle();
}
}
break;
default:
fprintf(stderr, "unexpected buffer type in "
"MultiAudioNode::BufferReceived\n");
buffer->Recycle();
break;
}
}
void
MultiAudioNode::ProducerDataStatus(const media_destination& forWhom,
int32 status, bigtime_t atPerformanceTime)
{
node_input* channel = _FindInput(forWhom);
if (channel == NULL) {
fprintf(stderr, "invalid destination received in "
"MultiAudioNode::ProducerDataStatus\n");
return;
}
media_timed_event event(atPerformanceTime, BTimedEventQueue::B_DATA_STATUS,
&channel->fInput, BTimedEventQueue::B_NO_CLEANUP, status, 0, NULL);
EventQueue()->AddEvent(event);
}
status_t
MultiAudioNode::GetLatencyFor(const media_destination& forWhom,
bigtime_t* _latency, media_node_id* _timeSource)
{
CALLED();
if (_latency == NULL || _timeSource == NULL)
return B_BAD_VALUE;
node_input* channel = _FindInput(forWhom);
if (channel == NULL)
return B_MEDIA_BAD_DESTINATION;
*_latency = EventLatency();
*_timeSource = TimeSource()->ID();
return B_OK;
}
status_t
MultiAudioNode::Connected(const media_source& producer,
const media_destination& where, const media_format& with_format,
media_input* out_input)
{
CALLED();
if (out_input == 0) {
fprintf(stderr, "<- B_BAD_VALUE\n");
return B_BAD_VALUE;
}
node_input* channel = _FindInput(where);
if (channel == NULL) {
fprintf(stderr, "<- B_MEDIA_BAD_DESTINATION\n");
return B_MEDIA_BAD_DESTINATION;
}
_UpdateInternalLatency(with_format);
// record the agreed upon values
channel->fInput.source = producer;
channel->fInput.format = with_format;
*out_input = channel->fInput;
_StartOutputThreadIfNeeded();
return B_OK;
}
void
MultiAudioNode::Disconnected(const media_source& producer,
const media_destination& where)
{
CALLED();
node_input* channel = _FindInput(where);
if (channel == NULL || channel->fInput.source != producer)
return;
channel->fInput.source = media_source::null;
channel->fInput.format = channel->fPreferredFormat;
BAutolock locker(fBufferLock);
_FillWithZeros(*channel);
//GetFormat(&channel->fInput.format);
}
status_t
MultiAudioNode::FormatChanged(const media_source& producer,
const media_destination& consumer, int32 change_tag,
const media_format& format)
{
CALLED();
node_input* channel = _FindInput(consumer);
if (channel==NULL) {
fprintf(stderr, "<- B_MEDIA_BAD_DESTINATION\n");
return B_MEDIA_BAD_DESTINATION;
}
if (channel->fInput.source != producer)
return B_MEDIA_BAD_SOURCE;
return B_ERROR;
}
status_t
MultiAudioNode::SeekTagRequested(const media_destination& destination,
bigtime_t targetTime, uint32 flags, media_seek_tag* _seekTag,
bigtime_t* _taggedTime, uint32* _flags)
{
CALLED();
return BBufferConsumer::SeekTagRequested(destination, targetTime, flags,
_seekTag, _taggedTime, _flags);
}
// #pragma mark - BBufferProducer
status_t
MultiAudioNode::FormatSuggestionRequested(media_type type, int32 /*quality*/,
media_format* format)
{
// FormatSuggestionRequested() is not necessarily part of the format
// negotiation process; it's simply an interrogation -- the caller
// wants to see what the node's preferred data format is, given a
// suggestion by the caller.
CALLED();
if (format == NULL) {
fprintf(stderr, "\tERROR - NULL format pointer passed in!\n");
return B_BAD_VALUE;
}
// this is the format we'll be returning (our preferred format)
*format = fInputPreferredFormat;
// a wildcard type is okay; we can specialize it
if (type == B_MEDIA_UNKNOWN_TYPE)
type = B_MEDIA_RAW_AUDIO;
// we only support raw audio
if (type != B_MEDIA_RAW_AUDIO)
return B_MEDIA_BAD_FORMAT;
return B_OK;
}
status_t
MultiAudioNode::FormatProposal(const media_source& output, media_format* format)
{
// FormatProposal() is the first stage in the BMediaRoster::Connect()
// process. We hand out a suggested format, with wildcards for any
// variations we support.
CALLED();
// is this a proposal for our select output?
node_output* channel = _FindOutput(output);
if (channel == NULL) {
fprintf(stderr, "MultiAudioNode::FormatProposal returning "
"B_MEDIA_BAD_SOURCE\n");
return B_MEDIA_BAD_SOURCE;
}
// We only support floating-point raw audio, so we always return that,
// but we supply an error code depending on whether we found the proposal
// acceptable.
media_type requestedType = format->type;
*format = channel->fPreferredFormat;
if (requestedType != B_MEDIA_UNKNOWN_TYPE
&& requestedType != B_MEDIA_RAW_AUDIO) {
fprintf(stderr, "MultiAudioNode::FormatProposal returning "
"B_MEDIA_BAD_FORMAT\n");
return B_MEDIA_BAD_FORMAT;
}
// raw audio or wildcard type, either is okay by us
return B_OK;
}
status_t
MultiAudioNode::FormatChangeRequested(const media_source& source,
const media_destination& destination, media_format* format,
int32* _deprecated_)
{
CALLED();
// we don't support any other formats, so we just reject any format changes.
return B_ERROR;
}
status_t
MultiAudioNode::GetNextOutput(int32* cookie, media_output* _output)
{
CALLED();
if (*cookie < fOutputs.CountItems() && *cookie >= 0) {
node_output* channel = (node_output*)fOutputs.ItemAt(*cookie);
*_output = channel->fOutput;
*cookie += 1;
return B_OK;
}
return B_BAD_INDEX;
}
status_t
MultiAudioNode::DisposeOutputCookie(int32 cookie)
{
CALLED();
// do nothing because we don't use the cookie for anything special
return B_OK;
}
status_t
MultiAudioNode::SetBufferGroup(const media_source& forSource,
BBufferGroup* newGroup)
{
CALLED();
// is this our output?
node_output* channel = _FindOutput(forSource);
if (channel == NULL) {
fprintf(stderr, "MultiAudioNode::SetBufferGroup returning "
"B_MEDIA_BAD_SOURCE\n");
return B_MEDIA_BAD_SOURCE;
}
// Are we being passed the buffer group we're already using?
if (newGroup == channel->fBufferGroup)
return B_OK;
// Ahh, someone wants us to use a different buffer group. At this point
// we delete the one we are using and use the specified one instead.
// If the specified group is NULL, we need to recreate one ourselves, and
// use *that*. Note that if we're caching a BBuffer that we requested
// earlier, we have to Recycle() that buffer *before* deleting the buffer
// group, otherwise we'll deadlock waiting for that buffer to be recycled!
delete channel->fBufferGroup;
// waits for all buffers to recycle
if (newGroup != NULL) {
// we were given a valid group; just use that one from now on
channel->fBufferGroup = newGroup;
} else {
// we were passed a NULL group pointer; that means we construct
// our own buffer group to use from now on
size_t size = channel->fOutput.format.u.raw_audio.buffer_size;
int32 count = int32(fLatency / BufferDuration() + 1 + 1);
BBufferGroup* group = new BBufferGroup(size, count);
if (group == NULL || group->InitCheck() != B_OK) {
delete group;
fprintf(stderr, "MultiAudioNode::SetBufferGroup failed to"
"instantiate a new group.\n");
return B_ERROR;
}
channel->fBufferGroup = group;
}
return B_OK;
}
status_t
MultiAudioNode::PrepareToConnect(const media_source& what,
const media_destination& where, media_format* format,
media_source* source, char* name)
{
CALLED();
// is this our output?
node_output* channel = _FindOutput(what);
if (channel == NULL) {
fprintf(stderr, "MultiAudioNode::PrepareToConnect returning "
"B_MEDIA_BAD_SOURCE\n");
return B_MEDIA_BAD_SOURCE;
}
// are we already connected?
if (channel->fOutput.destination != media_destination::null)
return B_MEDIA_ALREADY_CONNECTED;
// the format may not yet be fully specialized (the consumer might have
// passed back some wildcards). Finish specializing it now, and return an
// error if we don't support the requested format.
if (format->type != B_MEDIA_RAW_AUDIO) {
fprintf(stderr, "\tnon-raw-audio format?!\n");
return B_MEDIA_BAD_FORMAT;
}
// !!! validate all other fields except for buffer_size here, because the
// consumer might have supplied different values from AcceptFormat()?
// check the buffer size, which may still be wildcarded
if (format->u.raw_audio.buffer_size
== media_raw_audio_format::wildcard.buffer_size) {
format->u.raw_audio.buffer_size = 2048;
// pick something comfortable to suggest
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);
}
// Now reserve the connection, and return information about it
channel->fOutput.destination = where;
channel->fOutput.format = *format;
*source = channel->fOutput.source;
strlcpy(name, channel->fOutput.name, B_MEDIA_NAME_LENGTH);
return B_OK;
}
void
MultiAudioNode::Connect(status_t error, const media_source& source,
const media_destination& destination, const media_format& format,
char* name)
{
CALLED();
// is this our output?
node_output* channel = _FindOutput(source);
if (channel == NULL) {
fprintf(stderr, "MultiAudioNode::Connect returning (cause: "
"B_MEDIA_BAD_SOURCE)\n");
return;
}
// If something earlier failed, Connect() might still be called, but with
// a non-zero error code. When that happens we simply unreserve the
// connection and do nothing else.
if (error != B_OK) {
channel->fOutput.destination = media_destination::null;
channel->fOutput.format = channel->fPreferredFormat;
return;
}
// Okay, the connection has been confirmed. Record the destination and
// format that we agreed on, and report our connection name again.
channel->fOutput.destination = destination;
channel->fOutput.format = format;
strlcpy(name, channel->fOutput.name, B_MEDIA_NAME_LENGTH);
// reset our buffer duration, etc. to avoid later calculations
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);
// Now that we're connected, we can determine our downstream latency.
// Do so, then make sure we get our events early enough.
media_node_id id;
FindLatencyFor(channel->fOutput.destination, &fLatency, &id);
PRINT(("\tdownstream latency = %" B_PRIdBIGTIME "\n", fLatency));
fInternalLatency = BufferDuration();
PRINT(("\tbuffer-filling took %" B_PRIdBIGTIME " usec on this machine\n",
fInternalLatency));
//SetEventLatency(fLatency + fInternalLatency);
// Set up the buffer group for our connection, as long as nobody handed us
// a buffer group (via SetBufferGroup()) prior to this. That can happen,
// for example, if the consumer calls SetOutputBuffersFor() on us from
// within its Connected() method.
if (channel->fBufferGroup == NULL)
_AllocateBuffers(*channel);
_StartOutputThreadIfNeeded();
}
void
MultiAudioNode::Disconnect(const media_source& what,
const media_destination& where)
{
CALLED();
// is this our output?
node_output* channel = _FindOutput(what);
if (channel == NULL) {
fprintf(stderr, "MultiAudioNode::Disconnect() returning (cause: "
"B_MEDIA_BAD_SOURCE)\n");
return;
}
// Make sure that our connection is the one being disconnected
if (where == channel->fOutput.destination
&& what == channel->fOutput.source) {
channel->fOutput.destination = media_destination::null;
channel->fOutput.format = channel->fPreferredFormat;
delete channel->fBufferGroup;
channel->fBufferGroup = NULL;
} 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
MultiAudioNode::LateNoticeReceived(const media_source& what, bigtime_t howMuch,
bigtime_t performanceTime)
{
CALLED();
// is this our output?
node_output* channel = _FindOutput(what);
if (channel == NULL)
return;
// If we're late, we need to catch up. Respond in a manner appropriate
// to our current run mode.
if (RunMode() == B_RECORDING) {
// A hardware capture node can't adjust; it simply emits buffers at
// appropriate points. We (partially) simulate this by not adjusting
// our behavior upon receiving late notices -- after all, the hardware
// can't choose to capture "sooner"....
} else if (RunMode() == B_INCREASE_LATENCY) {
// We're late, and our run mode dictates that we try to produce buffers
// earlier in order to catch up. This argues that the downstream nodes
// are not properly reporting their latency, but there's not much we can
// do about that at the moment, so we try to start producing buffers
// earlier to compensate.
fInternalLatency += howMuch;
SetEventLatency(fLatency + fInternalLatency);
fprintf(stderr, "\tincreasing latency to %" B_PRIdBIGTIME"\n",
fLatency + fInternalLatency);
} else {
// The other run modes dictate various strategies for sacrificing data
// quality in the interests of timely data delivery. The way *we* do
// this is to skip a buffer, which catches us up in time by one buffer
// duration.
/*size_t nSamples = fOutput.format.u.raw_audio.buffer_size / sizeof(float);
mSamplesSent += nSamples;*/
fprintf(stderr, "\tskipping a buffer to try to catch up\n");
}
}
void
MultiAudioNode::EnableOutput(const media_source& what, bool enabled,
int32* _deprecated_)
{
CALLED();
// If I had more than one output, I'd have to walk my list of output
// records to see which one matched the given source, and then
// enable/disable that one. But this node only has one output, so I
// just make sure the given source matches, then set the enable state
// accordingly.
node_output* channel = _FindOutput(what);
if (channel != NULL)
channel->fOutputEnabled = enabled;
}
void
MultiAudioNode::AdditionalBufferRequested(const media_source& source,
media_buffer_id previousBuffer, bigtime_t previousTime,
const media_seek_tag* previousTag)
{
CALLED();
// we don't support offline mode
return;
}
// #pragma mark - BMediaEventLooper
void
MultiAudioNode::HandleEvent(const media_timed_event* event, bigtime_t lateness,
bool realTimeEvent)
{
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
MultiAudioNode::_HandleBuffer(const media_timed_event* event,
bigtime_t lateness, bool realTimeEvent)
{
BBuffer* buffer = const_cast<BBuffer*>((BBuffer*)event->pointer);
if (buffer == NULL)
return B_BAD_VALUE;
//PRINT(("buffer->Header()->destination: %i\n", buffer->Header()->destination));
node_input* channel = _FindInput(buffer->Header()->destination);
if (channel == NULL) {
buffer->Recycle();
return B_MEDIA_BAD_DESTINATION;
}
// if the buffer is late, we ignore it and report the fact to the producer
// who sent it to us
if (RunMode() != B_OFFLINE && RunMode() != B_RECORDING && lateness > 0) {
// lateness doesn't matter in offline mode or in recording mode
//mLateBuffers++;
NotifyLateProducer(channel->fInput.source, lateness, event->event_time);
fprintf(stderr," <- LATE BUFFER: %" B_PRIdBIGTIME "\n", lateness);
buffer->Recycle();
} else {
//WriteBuffer(buffer, *channel);
// TODO: This seems like a very fragile mechanism to wait until
// the previous buffer for this channel has been processed...
if (channel->fBuffer != NULL) {
PRINT(("MultiAudioNode::HandleBuffer snoozing recycling channelId: "
"%" B_PRIi32 ", how_early:%" B_PRIdBIGTIME "\n",
channel->fChannelId, lateness));
//channel->fBuffer->Recycle();
snooze(100);
if (channel->fBuffer != NULL)
buffer->Recycle();
else
channel->fBuffer = buffer;
} else {
//PRINT(("MultiAudioNode::HandleBuffer writing channelId: %li, how_early:%Ld\n", channel->fChannelId, howEarly));
channel->fBuffer = buffer;
}
}
return B_OK;
}
status_t
MultiAudioNode::_HandleDataStatus(const media_timed_event* event,
bigtime_t lateness, bool realTimeEvent)
{
PRINT(("MultiAudioNode::HandleDataStatus status:%" B_PRIi32 ", lateness:%"
B_PRIiBIGTIME "\n", event->data, lateness));
switch (event->data) {
case B_DATA_NOT_AVAILABLE:
break;
case B_DATA_AVAILABLE:
break;
case B_PRODUCER_STOPPED:
break;
default:
break;
}
return B_OK;
}
status_t
MultiAudioNode::_HandleStart(const media_timed_event* event, bigtime_t lateness,
bool realTimeEvent)
{
CALLED();
if (RunState() != B_STARTED) {
}
return B_OK;
}
status_t
MultiAudioNode::_HandleSeek(const media_timed_event* event, bigtime_t lateness,
bool realTimeEvent)
{
CALLED();
PRINT(("MultiAudioNode::HandleSeek(t=%" B_PRIdBIGTIME ",d=%" B_PRIi32
",bd=%" B_PRId64 ")\n",
event->event_time,event->data,event->bigdata));
return B_OK;
}
status_t
MultiAudioNode::_HandleWarp(const media_timed_event* event, bigtime_t lateness,
bool realTimeEvent)
{
CALLED();
return B_OK;
}
status_t
MultiAudioNode::_HandleStop(const media_timed_event* event, bigtime_t lateness,
bool realTimeEvent)
{
CALLED();
// flush the queue so downstreamers don't get any more
EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true,
BTimedEventQueue::B_HANDLE_BUFFER);
//_StopOutputThread();
return B_OK;
}
status_t
MultiAudioNode::_HandleParameter(const media_timed_event* event,
bigtime_t lateness, bool realTimeEvent)
{
CALLED();
return B_OK;
}
// #pragma mark - BTimeSource
void
MultiAudioNode::SetRunMode(run_mode mode)
{
CALLED();
PRINT(("MultiAudioNode::SetRunMode mode:%i\n", mode));
//BTimeSource::SetRunMode(mode);
}
status_t
MultiAudioNode::TimeSourceOp(const time_source_op_info& op, void* _reserved)
{
CALLED();
switch (op.op) {
case B_TIMESOURCE_START:
PRINT(("TimeSourceOp op B_TIMESOURCE_START\n"));
if (RunState() != BMediaEventLooper::B_STARTED) {
fTimeSourceStarted = true;
_StartOutputThreadIfNeeded();
media_timed_event startEvent(0, BTimedEventQueue::B_START);
EventQueue()->AddEvent(startEvent);
}
break;
case B_TIMESOURCE_STOP:
PRINT(("TimeSourceOp op B_TIMESOURCE_STOP\n"));
if (RunState() == BMediaEventLooper::B_STARTED) {
media_timed_event stopEvent(0, BTimedEventQueue::B_STOP);
EventQueue()->AddEvent(stopEvent);
fTimeSourceStarted = false;
_StopOutputThread();
PublishTime(0, 0, 0);
}
break;
case B_TIMESOURCE_STOP_IMMEDIATELY:
PRINT(("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;
_StopOutputThread();
PublishTime(0, 0, 0);
}
break;
case B_TIMESOURCE_SEEK:
PRINT(("TimeSourceOp op B_TIMESOURCE_SEEK\n"));
BroadcastTimeWarp(op.real_time, op.performance_time);
break;
default:
break;
}
return B_OK;
}
// #pragma mark - BControllable
status_t
MultiAudioNode::GetParameterValue(int32 id, bigtime_t* lastChange, void* value,
size_t* size)
{
CALLED();
PRINT(("id: %" B_PRIi32 "\n", id));
BParameter* parameter = NULL;
for (int32 i = 0; i < fWeb->CountParameters(); i++) {
parameter = fWeb->ParameterAt(i);
if (parameter->ID() == id)
break;
}
if (parameter == NULL) {
// Hmmm, we were asked for a parameter that we don't actually
// support. Report an error back to the caller.
PRINT(("\terror - asked for illegal parameter %" B_PRId32 "\n", id));
return B_ERROR;
}
if (id == PARAMETER_ID_INPUT_FREQUENCY
|| id == PARAMETER_ID_OUTPUT_FREQUENCY) {
const multi_format_info& info = fDevice->FormatInfo();
uint32 rate = id == PARAMETER_ID_INPUT_FREQUENCY
? info.input.rate : info.output.rate;
if (*size < sizeof(rate))
return B_ERROR;
memcpy(value, &rate, sizeof(rate));
*size = sizeof(rate);
return B_OK;
}
multi_mix_value_info info;
multi_mix_value values[2];
info.values = values;
info.item_count = 0;
multi_mix_control* controls = fDevice->MixControlInfo().controls;
int32 control_id = controls[id - 100].id;
if (*size < sizeof(float))
return B_ERROR;
if (parameter->Type() == BParameter::B_CONTINUOUS_PARAMETER) {
info.item_count = 1;
values[0].id = control_id;
if (parameter->CountChannels() == 2) {
if (*size < 2*sizeof(float))
return B_ERROR;
info.item_count = 2;
values[1].id = controls[id + 1 - 100].id;
}
} else if (parameter->Type() == BParameter::B_DISCRETE_PARAMETER) {
info.item_count = 1;
values[0].id = control_id;
}
if (info.item_count > 0) {
status_t status = fDevice->GetMix(&info);
if (status != B_OK) {
fprintf(stderr, "Failed on DRIVER_GET_MIX\n");
} else {
if (parameter->Type() == BParameter::B_CONTINUOUS_PARAMETER) {
((float*)value)[0] = values[0].gain;
*size = sizeof(float);
if (parameter->CountChannels() == 2) {
((float*)value)[1] = values[1].gain;
*size = 2*sizeof(float);
}
for (uint32 i = 0; i < *size / sizeof(float); i++) {
PRINT(("GetParameterValue B_CONTINUOUS_PARAMETER value[%"
B_PRIi32 "]: %f\n", i, ((float*)value)[i]));
}
} else if (parameter->Type() == BParameter::B_DISCRETE_PARAMETER) {
BDiscreteParameter* discrete = (BDiscreteParameter*)parameter;
if (discrete->CountItems() <= 2)
((int32*)value)[0] = values[0].enable ? 1 : 0;
else
((int32*)value)[0] = values[0].mux;
*size = sizeof(int32);
for (uint32 i = 0; i < *size / sizeof(int32); i++) {
PRINT(("GetParameterValue B_DISCRETE_PARAMETER value[%"
B_PRIi32 "]: %" B_PRIi32 "\n", i, ((int32*)value)[i]));
}
}
}
}
return B_OK;
}
void
MultiAudioNode::SetParameterValue(int32 id, bigtime_t performanceTime,
const void* value, size_t size)
{
CALLED();
PRINT(("id: %" B_PRIi32 ", performance_time: %" B_PRIdBIGTIME
", size: %" B_PRIuSIZE "\n", id, performanceTime, size));
BParameter* parameter = NULL;
for (int32 i = 0; i < fWeb->CountParameters(); i++) {
parameter = fWeb->ParameterAt(i);
if (parameter->ID() == id)
break;
}
if (parameter == NULL)
return;
if (id == PARAMETER_ID_OUTPUT_FREQUENCY
|| (id == PARAMETER_ID_INPUT_FREQUENCY
&& (fDevice->Description().output_rates
& B_SR_SAME_AS_INPUT) != 0)) {
uint32 rate;
if (size < sizeof(rate))
return;
memcpy(&rate, value, sizeof(rate));
if (rate == fOutputPreferredFormat.u.raw_audio.frame_rate)
return;
// create a cookie RequestCompleted() can get the old frame rate from,
// if anything goes wrong
FrameRateChangeCookie* cookie = new(std::nothrow) FrameRateChangeCookie;
if (cookie == NULL)
return;
cookie->oldFrameRate = fOutputPreferredFormat.u.raw_audio.frame_rate;
cookie->id = id;
BReference<FrameRateChangeCookie> cookieReference(cookie, true);
// NOTE: What we should do is call RequestFormatChange() for all
// connections and change the device's format in RequestCompleted().
// Unfortunately we need the new buffer size first, which we only get
// from the device after changing the format. So we do that now and
// reset it in RequestCompleted(), if something went wrong. This causes
// the buffers we receive until then to be played incorrectly leading
// to unpleasant noise.
float frameRate = MultiAudio::convert_to_sample_rate(rate);
if (_SetNodeInputFrameRate(frameRate) != B_OK)
return;
for (int32 i = 0; i < fInputs.CountItems(); i++) {
node_input* channel = (node_input*)fInputs.ItemAt(i);
if (channel->fInput.source == media_source::null)
continue;
media_format newFormat = channel->fInput.format;
newFormat.u.raw_audio.frame_rate = frameRate;
newFormat.u.raw_audio.buffer_size
= fOutputPreferredFormat.u.raw_audio.buffer_size;
int32 changeTag = 0;
status_t error = RequestFormatChange(channel->fInput.source,
channel->fInput.destination, newFormat, NULL, &changeTag);
if (error == B_OK)
cookie->AcquireReference();
}
if (id != PARAMETER_ID_INPUT_FREQUENCY)
return;
//Do not return cause we should go in the next if
}
if (id == PARAMETER_ID_INPUT_FREQUENCY) {
uint32 rate;
if (size < sizeof(rate))
return;
memcpy(&rate, value, sizeof(rate));
if (rate == fInputPreferredFormat.u.raw_audio.frame_rate)
return;
// create a cookie RequestCompleted() can get the old frame rate from,
// if anything goes wrong
FrameRateChangeCookie* cookie = new(std::nothrow) FrameRateChangeCookie;
if (cookie == NULL)
return;
cookie->oldFrameRate = fInputPreferredFormat.u.raw_audio.frame_rate;
cookie->id = id;
BReference<FrameRateChangeCookie> cookieReference(cookie, true);
// NOTE: What we should do is call RequestFormatChange() for all
// connections and change the device's format in RequestCompleted().
// Unfortunately we need the new buffer size first, which we only get
// from the device after changing the format. So we do that now and
// reset it in RequestCompleted(), if something went wrong. This causes
// the buffers we receive until then to be played incorrectly leading
// to unpleasant noise.
float frameRate = MultiAudio::convert_to_sample_rate(rate);
if (_SetNodeOutputFrameRate(frameRate) != B_OK)
return;
for (int32 i = 0; i < fOutputs.CountItems(); i++) {
node_output* channel = (node_output*)fOutputs.ItemAt(i);
if (channel->fOutput.source == media_source::null)
continue;
media_format newFormat = channel->fOutput.format;
newFormat.u.raw_audio.frame_rate = frameRate;
newFormat.u.raw_audio.buffer_size
= fInputPreferredFormat.u.raw_audio.buffer_size;
int32 changeTag = 0;
status_t error = RequestFormatChange(channel->fOutput.source,
channel->fOutput.destination, newFormat, NULL, &changeTag);
if (error == B_OK)
cookie->AcquireReference();
}
return;
}
multi_mix_value_info info;
multi_mix_value values[2];
info.values = values;
info.item_count = 0;
multi_mix_control* controls = fDevice->MixControlInfo().controls;
int32 control_id = controls[id - 100].id;
if (parameter->Type() == BParameter::B_CONTINUOUS_PARAMETER) {
for (uint32 i = 0; i < size / sizeof(float); i++) {
PRINT(("SetParameterValue B_CONTINUOUS_PARAMETER value[%" B_PRIi32
"]: %f\n", i, ((float*)value)[i]));
}
info.item_count = 1;
values[0].id = control_id;
values[0].gain = ((float*)value)[0];
if (parameter->CountChannels() == 2) {
info.item_count = 2;
values[1].id = controls[id + 1 - 100].id;
values[1].gain = ((float*)value)[1];
}
} else if (parameter->Type() == BParameter::B_DISCRETE_PARAMETER) {
for (uint32 i = 0; i < size / sizeof(int32); i++) {
PRINT(("SetParameterValue B_DISCRETE_PARAMETER value[%" B_PRIi32
"]: %" B_PRIi32 "\n", i, ((int32*)value)[i]));
}
BDiscreteParameter* discrete = (BDiscreteParameter*)parameter;
if (discrete->CountItems() <= 2) {
info.item_count = 1;
values[0].id = control_id;
values[0].enable = ((int32*)value)[0] == 1;
} else {
info.item_count = 1;
values[0].id = control_id;
values[0].mux = ((uint32*)value)[0];
}
}
if (info.item_count > 0) {
status_t status = fDevice->SetMix(&info);
if (status != B_OK)
fprintf(stderr, "Failed on DRIVER_SET_MIX\n");
}
}
BParameterWeb*
MultiAudioNode::MakeParameterWeb()
{
CALLED();
BParameterWeb* web = new BParameterWeb();
PRINT(("MixControlInfo().control_count: %" B_PRIi32 "\n",
fDevice->MixControlInfo().control_count));
BParameterGroup* generalGroup = web->MakeGroup(B_TRANSLATE("General"));
const multi_description& description = fDevice->Description();
if ((description.output_rates & B_SR_SAME_AS_INPUT) != 0) {
_CreateFrequencyParameterGroup(generalGroup,
B_TRANSLATE("Input & Output"), PARAMETER_ID_INPUT_FREQUENCY,
description.input_rates);
} else {
_CreateFrequencyParameterGroup(generalGroup, B_TRANSLATE("Input"),
PARAMETER_ID_INPUT_FREQUENCY, description.input_rates);
_CreateFrequencyParameterGroup(generalGroup, B_TRANSLATE("Output"),
PARAMETER_ID_OUTPUT_FREQUENCY, description.output_rates);
}
multi_mix_control* controls = fDevice->MixControlInfo().controls;
for (int i = 0; i < fDevice->MixControlInfo().control_count; i++) {
if ((controls[i].flags & B_MULTI_MIX_GROUP) != 0
&& controls[i].parent == 0) {
PRINT(("NEW_GROUP\n"));
BParameterGroup* child = web->MakeGroup(
_GetControlName(controls[i]));
int32 numParameters = 0;
_ProcessGroup(child, i, numParameters);
}
}
return web;
}
const char*
MultiAudioNode::_GetControlName(multi_mix_control& control)
{
if (control.string != S_null)
return kMultiControlString[control.string];
return control.name;
}
void
MultiAudioNode::_ProcessGroup(BParameterGroup* group, int32 index,
int32& numParameters)
{
CALLED();
multi_mix_control* parent = &fDevice->MixControlInfo().controls[index];
multi_mix_control* controls = fDevice->MixControlInfo().controls;
for (int32 i = 0; i < fDevice->MixControlInfo().control_count; i++) {
if (controls[i].parent != parent->id)
continue;
const char* name = _GetControlName(controls[i]);
if (controls[i].flags & B_MULTI_MIX_GROUP) {
PRINT(("NEW_GROUP\n"));
BParameterGroup* child = group->MakeGroup(name);
child->MakeNullParameter(100 + i, B_MEDIA_RAW_AUDIO, name,
B_WEB_BUFFER_OUTPUT);
int32 num = 1;
_ProcessGroup(child, i, num);
} else if (controls[i].flags & B_MULTI_MIX_MUX) {
PRINT(("NEW_MUX\n"));
BDiscreteParameter* parameter = group->MakeDiscreteParameter(
100 + i, B_MEDIA_RAW_AUDIO, name, B_INPUT_MUX);
if (numParameters > 0) {
(group->ParameterAt(numParameters - 1))->AddOutput(
group->ParameterAt(numParameters));
numParameters++;
}
_ProcessMux(parameter, i);
} else if (controls[i].flags & B_MULTI_MIX_GAIN) {
PRINT(("NEW_GAIN\n"));
group->MakeContinuousParameter(100 + i,
B_MEDIA_RAW_AUDIO, "", B_MASTER_GAIN, "dB",
controls[i].gain.min_gain, controls[i].gain.max_gain,
controls[i].gain.granularity);
if (i + 1 < fDevice->MixControlInfo().control_count
&& controls[i + 1].master == controls[i].id
&& (controls[i + 1].flags & B_MULTI_MIX_GAIN) != 0) {
group->ParameterAt(numParameters)->SetChannelCount(
group->ParameterAt(numParameters)->CountChannels() + 1);
i++;
}
PRINT(("num parameters: %" B_PRId32 "\n", numParameters));
if (numParameters > 0) {
group->ParameterAt(numParameters - 1)->AddOutput(
group->ParameterAt(numParameters));
numParameters++;
}
} else if (controls[i].flags & B_MULTI_MIX_ENABLE) {
PRINT(("NEW_ENABLE\n"));
if (controls[i].string == S_MUTE) {
group->MakeDiscreteParameter(100 + i,
B_MEDIA_RAW_AUDIO, name, B_MUTE);
} else {
group->MakeDiscreteParameter(100 + i,
B_MEDIA_RAW_AUDIO, name, B_ENABLE);
}
if (numParameters > 0) {
group->ParameterAt(numParameters - 1)->AddOutput(
group->ParameterAt(numParameters));
numParameters++;
}
}
}
}
void
MultiAudioNode::_ProcessMux(BDiscreteParameter* parameter, int32 index)
{
CALLED();
multi_mix_control* parent = &fDevice->MixControlInfo().controls[index];
multi_mix_control* controls = fDevice->MixControlInfo().controls;
int32 itemIndex = 0;
for (int32 i = 0; i < fDevice->MixControlInfo().control_count; i++) {
if (controls[i].parent != parent->id)
continue;
if ((controls[i].flags & B_MULTI_MIX_MUX_VALUE) != 0) {
PRINT(("NEW_MUX_VALUE\n"));
parameter->AddItem(itemIndex, _GetControlName(controls[i]));
itemIndex++;
}
}
}
void
MultiAudioNode::_CreateFrequencyParameterGroup(BParameterGroup* parentGroup,
const char* name, int32 parameterID, uint32 rateMask)
{
BParameterGroup* group = parentGroup->MakeGroup(name);
BDiscreteParameter* frequencyParam = group->MakeDiscreteParameter(
parameterID, B_MEDIA_NO_TYPE,
BString(name) << B_TRANSLATE(" frequency:"),
B_GENERIC);
for (int32 i = 0; kSampleRateInfos[i].name != NULL; i++) {
const sample_rate_info& info = kSampleRateInfos[i];
if ((rateMask & info.multiAudioRate) != 0) {
frequencyParam->AddItem(info.multiAudioRate,
BString(info.name) << " Hz");
}
}
}
// #pragma mark - MultiAudioNode specific functions
int32
MultiAudioNode::_OutputThread()
{
CALLED();
multi_buffer_info bufferInfo;
bufferInfo.info_size = sizeof(multi_buffer_info);
bufferInfo.playback_buffer_cycle = 0;
bufferInfo.record_buffer_cycle = 0;
// init the performance time computation
{
BAutolock locker(fBufferLock);
fTimeComputer.Init(fOutputPreferredFormat.u.raw_audio.frame_rate,
system_time());
}
while (atomic_get(&fQuitThread) == 0) {
BAutolock locker(fBufferLock);
// make sure the buffers don't change while we're playing with them
// send buffer
fDevice->BufferExchange(&bufferInfo);
//PRINT(("MultiAudioNode::RunThread: buffer exchanged\n"));
//PRINT(("MultiAudioNode::RunThread: played_real_time: %Ld\n", bufferInfo.played_real_time));
//PRINT(("MultiAudioNode::RunThread: played_frames_count: %Ld\n", bufferInfo.played_frames_count));
//PRINT(("MultiAudioNode::RunThread: buffer_cycle: %li\n", bufferInfo.playback_buffer_cycle));
for (int32 i = 0; i < fInputs.CountItems(); i++) {
node_input* input = (node_input*)fInputs.ItemAt(i);
if (bufferInfo.playback_buffer_cycle >= 0
&& bufferInfo.playback_buffer_cycle
< fDevice->BufferList().return_playback_buffers
&& (input->fOldBufferInfo.playback_buffer_cycle
!= bufferInfo.playback_buffer_cycle
|| fDevice->BufferList().return_playback_buffers == 1)
&& (input->fInput.source != media_source::null
|| input->fChannelId == 0)) {
//PRINT(("playback_buffer_cycle ok input: %li %ld\n", i, bufferInfo.playback_buffer_cycle));
input->fBufferCycle = (bufferInfo.playback_buffer_cycle - 1
+ fDevice->BufferList().return_playback_buffers)
% fDevice->BufferList().return_playback_buffers;
// update the timesource
if (input->fChannelId == 0) {
//PRINT(("updating timesource\n"));
_UpdateTimeSource(bufferInfo, input->fOldBufferInfo,
*input);
}
input->fOldBufferInfo = bufferInfo;
if (input->fBuffer != NULL) {
_FillNextBuffer(*input, input->fBuffer);
input->fBuffer->Recycle();
input->fBuffer = NULL;
} else {
// put zeros in current buffer
if (input->fInput.source != media_source::null)
_WriteZeros(*input, input->fBufferCycle);
//PRINT(("MultiAudioNode::Runthread WriteZeros\n"));
}
} else {
//PRINT(("playback_buffer_cycle non ok input: %i\n", i));
}
}
PRINT(("MultiAudioNode::RunThread: recorded_real_time: %" B_PRIdBIGTIME
"\n", bufferInfo.recorded_real_time));
PRINT(("MultiAudioNode::RunThread: recorded_frames_count: %"
B_PRId64 "\n", bufferInfo.recorded_frames_count));
PRINT(("MultiAudioNode::RunThread: record_buffer_cycle: %" B_PRIi32
"\n", bufferInfo.record_buffer_cycle));
for (int32 i = 0; i < fOutputs.CountItems(); i++) {
node_output* output = (node_output*)fOutputs.ItemAt(i);
// make sure we're both started *and* connected before delivering a
// buffer
if (RunState() == BMediaEventLooper::B_STARTED
&& output->fOutput.destination != media_destination::null) {
if (bufferInfo.record_buffer_cycle >= 0
&& bufferInfo.record_buffer_cycle
< fDevice->BufferList().return_record_buffers
&& (output->fOldBufferInfo.record_buffer_cycle
!= bufferInfo.record_buffer_cycle
|| fDevice->BufferList().return_record_buffers == 1)) {
//PRINT(("record_buffer_cycle ok\n"));
output->fBufferCycle = bufferInfo.record_buffer_cycle;
// Get the next buffer of data
BBuffer* buffer = _FillNextBuffer(bufferInfo, *output);
if (buffer != NULL) {
// send the buffer downstream if and only if output is
// enabled
status_t err = B_ERROR;
if (output->fOutputEnabled) {
err = SendBuffer(buffer, output->fOutput.source,
output->fOutput.destination);
}
if (err != B_OK) {
buffer->Recycle();
} else {
// track how much media we've delivered so far
size_t numSamples
= 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 += numSamples;
}
}
output->fOldBufferInfo = bufferInfo;
} else {
//PRINT(("record_buffer_cycle non ok\n"));
}
}
}
}
return B_OK;
}
void
MultiAudioNode::_WriteZeros(node_input& input, uint32 bufferCycle)
{
//CALLED();
/*int32 samples = input.fInput.format.u.raw_audio.buffer_size;
if(input.fInput.format.u.raw_audio.format == media_raw_audio_format::B_AUDIO_UCHAR) {
uint8 *sample = (uint8*)fDevice->BufferList().playback_buffers[input.fBufferCycle][input.fChannelId].base;
for(int32 i = samples-1; i>=0; i--)
*sample++ = 128;
} else {
int32 *sample = (int32*)fDevice->BufferList().playback_buffers[input.fBufferCycle][input.fChannelId].base;
for(int32 i = (samples / 4)-1; i>=0; i--)
*sample++ = 0;
}*/
uint32 channelCount = input.fFormat.u.raw_audio.channel_count;
uint32 bufferSize = fDevice->BufferList().return_playback_buffer_size;
size_t stride = fDevice->BufferList().playback_buffers[bufferCycle]
[input.fChannelId].stride;
switch (input.fFormat.u.raw_audio.format) {
case media_raw_audio_format::B_AUDIO_FLOAT:
for (uint32 channel = 0; channel < channelCount; channel++) {
char* dest = _PlaybackBuffer(bufferCycle,
input.fChannelId + channel);
for (uint32 i = bufferSize; i > 0; i--) {
*(float*)dest = 0;
dest += stride;
}
}
break;
case media_raw_audio_format::B_AUDIO_DOUBLE:
for (uint32 channel = 0; channel < channelCount; channel++) {
char* dest = _PlaybackBuffer(bufferCycle,
input.fChannelId + channel);
for (uint32 i = bufferSize; i > 0; i--) {
*(double*)dest = 0;
dest += stride;
}
}
break;
case media_raw_audio_format::B_AUDIO_INT:
for (uint32 channel = 0; channel < channelCount; channel++) {
char* dest = _PlaybackBuffer(bufferCycle,
input.fChannelId + channel);
for (uint32 i = bufferSize; i > 0; i--) {
*(int32*)dest = 0;
dest += stride;
}
}
break;
case media_raw_audio_format::B_AUDIO_SHORT:
for (uint32 channel = 0; channel < channelCount; channel++) {
char* dest = _PlaybackBuffer(bufferCycle,
input.fChannelId + channel);
for (uint32 i = bufferSize; i > 0; i--) {
*(int16*)dest = 0;
dest += stride;
}
}
break;
case media_raw_audio_format::B_AUDIO_UCHAR:
for (uint32 channel = 0; channel < channelCount; channel++) {
char* dest = _PlaybackBuffer(bufferCycle,
input.fChannelId + channel);
for (uint32 i = bufferSize; i > 0; i--) {
*(uint8*)dest = 128;
dest += stride;
}
}
break;
case media_raw_audio_format::B_AUDIO_CHAR:
for (uint32 channel = 0; channel < channelCount; channel++) {
char* dest = _PlaybackBuffer(bufferCycle,
input.fChannelId + channel);
for (uint32 i = bufferSize; i > 0; i--) {
*(int8*)dest = 0;
dest += stride;
}
}
break;
default:
fprintf(stderr, "ERROR in WriteZeros format not handled\n");
}
}
void
MultiAudioNode::_FillWithZeros(node_input& input)
{
CALLED();
for (int32 i = 0; i < fDevice->BufferList().return_playback_buffers; i++)
_WriteZeros(input, i);
}
void
MultiAudioNode::_FillNextBuffer(node_input& input, BBuffer* buffer)
{
uint32 channelCount = input.fInput.format.u.raw_audio.channel_count;
size_t inputSampleSize = input.fInput.format.u.raw_audio.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
uint32 bufferSize = fDevice->BufferList().return_playback_buffer_size;
if (buffer->SizeUsed() / inputSampleSize / channelCount != bufferSize) {
_WriteZeros(input, input.fBufferCycle);
return;
}
if (channelCount != input.fFormat.u.raw_audio.channel_count) {
PRINT(("Channel count is different"));
return;
}
if (input.fResampler != NULL) {
size_t srcStride = channelCount * inputSampleSize;
for (uint32 channel = 0; channel < channelCount; channel++) {
char* src = (char*)buffer->Data() + channel * inputSampleSize;
char* dst = _PlaybackBuffer(input.fBufferCycle,
input.fChannelId + channel);
size_t dstStride = _PlaybackStride(input.fBufferCycle,
input.fChannelId + channel);
input.fResampler->Resample(src, srcStride,
dst, dstStride, bufferSize);
}
}
}
status_t
MultiAudioNode::_StartOutputThreadIfNeeded()
{
CALLED();
// the thread is already started ?
if (fThread >= 0)
return B_OK;
PublishTime(-50, 0, 0);
fThread = spawn_thread(_OutputThreadEntry, "multi_audio audio output",
B_REAL_TIME_PRIORITY, this);
if (fThread < 0)
return fThread;
resume_thread(fThread);
return B_OK;
}
status_t
MultiAudioNode::_StopOutputThread()
{
CALLED();
atomic_set(&fQuitThread, 1);
wait_for_thread(fThread, NULL);
fThread = -1;
return B_OK;
}
void
MultiAudioNode::_AllocateBuffers(node_output &channel)
{
CALLED();
// allocate enough buffers to span our downstream latency, plus one
size_t size = channel.fOutput.format.u.raw_audio.buffer_size;
int32 count = int32(fLatency / BufferDuration() + 1 + 1);
PRINT(("\tlatency = %" B_PRIdBIGTIME ", buffer duration = %" B_PRIdBIGTIME
"\n", fLatency, BufferDuration()));
PRINT(("\tcreating group of %" B_PRId32 " buffers, size = %" B_PRIuSIZE
"\n", count, size));
channel.fBufferGroup = new BBufferGroup(size, count);
}
void
MultiAudioNode::_UpdateTimeSource(multi_buffer_info& info,
multi_buffer_info& oldInfo, node_input& input)
{
//CALLED();
if (!fTimeSourceStarted || oldInfo.played_real_time == 0)
return;
fTimeComputer.AddTimeStamp(info.played_real_time,
info.played_frames_count);
PublishTime(fTimeComputer.PerformanceTime(), fTimeComputer.RealTime(),
fTimeComputer.Drift());
}
BBuffer*
MultiAudioNode::_FillNextBuffer(multi_buffer_info& info, node_output& output)
{
//CALLED();
// get a buffer from our buffer group
//PRINT(("buffer size: %i, buffer duration: %i\n", fOutput.format.u.raw_audio.buffer_size, BufferDuration()));
//PRINT(("MBI.record_buffer_cycle: %i\n", MBI.record_buffer_cycle));
//PRINT(("MBI.recorded_real_time: %i\n", MBI.recorded_real_time));
//PRINT(("MBI.recorded_frames_count: %i\n", MBI.recorded_frames_count));
if (output.fBufferGroup == NULL)
return NULL;
BBuffer* buffer = output.fBufferGroup->RequestBuffer(
output.fOutput.format.u.raw_audio.buffer_size, BufferDuration());
if (buffer == NULL) {
// If we fail to get a buffer (for example, if the request times out),
// we skip this buffer and go on to the next, to avoid locking up the
// control thread.
fprintf(stderr, "Buffer is null");
return NULL;
}
if (fDevice == NULL)
fprintf(stderr, "fDevice NULL\n");
if (buffer->Header() == NULL)
fprintf(stderr, "buffer->Header() NULL\n");
if (TimeSource() == NULL)
fprintf(stderr, "TimeSource() NULL\n");
uint32 channelCount = output.fOutput.format.u.raw_audio.channel_count;
size_t outputSampleSize = output.fOutput.format.u.raw_audio.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
uint32 bufferSize = fDevice->BufferList().return_record_buffer_size;
if (output.fResampler != NULL) {
size_t dstStride = channelCount * outputSampleSize;
uint32 channelId = output.fChannelId
- fDevice->Description().output_channel_count;
for (uint32 channel = 0; channel < channelCount; channel++) {
char* src = _RecordBuffer(output.fBufferCycle,
channelId + channel);
size_t srcStride = _RecordStride(output.fBufferCycle,
channelId + channel);
char* dst = (char*)buffer->Data() + channel * outputSampleSize;
output.fResampler->Resample(src, srcStride, dst, dstStride,
bufferSize);
}
}
// fill in the buffer header
media_header* header = buffer->Header();
header->type = B_MEDIA_RAW_AUDIO;
header->size_used = output.fOutput.format.u.raw_audio.buffer_size;
header->time_source = TimeSource()->ID();
header->start_time = PerformanceTimeFor(info.recorded_real_time);
return buffer;
}
status_t
MultiAudioNode::GetConfigurationFor(BMessage* message)
{
CALLED();
if (message == NULL)
return B_BAD_VALUE;
size_t bufferSize = 128;
void* buffer = malloc(bufferSize);
if (buffer == NULL)
return B_NO_MEMORY;
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;
PRINT(("getting parameter %" B_PRIi32 "\n", parameter->ID()));
size_t size = bufferSize;
bigtime_t lastChange;
status_t err;
while ((err = GetParameterValue(parameter->ID(), &lastChange, buffer,
&size)) == B_NO_MEMORY && bufferSize < 128 * 1024) {
bufferSize += 128;
free(buffer);
buffer = malloc(bufferSize);
if (buffer == NULL)
return B_NO_MEMORY;
}
if (err == B_OK && size > 0) {
message->AddInt32("parameterID", parameter->ID());
message->AddData("parameterData", B_RAW_TYPE, buffer, size, false);
} else {
PRINT(("parameter err: %s\n", strerror(err)));
}
}
free(buffer);
PRINT_OBJECT(*message);
return B_OK;
}
node_output*
MultiAudioNode::_FindOutput(media_source source)
{
node_output* channel = NULL;
for (int32 i = 0; i < fOutputs.CountItems(); i++) {
channel = (node_output*)fOutputs.ItemAt(i);
if (source == channel->fOutput.source)
break;
}
if (source != channel->fOutput.source)
return NULL;
return channel;
}
node_input*
MultiAudioNode::_FindInput(media_destination dest)
{
node_input* channel = NULL;
for (int32 i = 0; i < fInputs.CountItems(); i++) {
channel = (node_input*)fInputs.ItemAt(i);
if (dest == channel->fInput.destination)
break;
}
if (dest != channel->fInput.destination)
return NULL;
return channel;
}
node_input*
MultiAudioNode::_FindInput(int32 destinationId)
{
node_input* channel = NULL;
for (int32 i = 0; i < fInputs.CountItems(); i++) {
channel = (node_input*)fInputs.ItemAt(i);
if (destinationId == channel->fInput.destination.id)
break;
}
if (destinationId != channel->fInput.destination.id)
return NULL;
return channel;
}
/*static*/ status_t
MultiAudioNode::_OutputThreadEntry(void* data)
{
CALLED();
return static_cast<MultiAudioNode*>(data)->_OutputThread();
}
status_t
MultiAudioNode::_SetNodeInputFrameRate(float frameRate)
{
// check whether the frame rate is supported
uint32 multiAudioRate = MultiAudio::convert_from_sample_rate(frameRate);
if ((fDevice->Description().output_rates & multiAudioRate) == 0)
return B_BAD_VALUE;
BAutolock locker(fBufferLock);
// already set?
if (fDevice->FormatInfo().output.rate == multiAudioRate)
return B_OK;
// set the frame rate on the device
status_t error = fDevice->SetOutputFrameRate(multiAudioRate);
if (error != B_OK)
return error;
// it went fine -- update all formats
fOutputPreferredFormat.u.raw_audio.frame_rate = frameRate;
fOutputPreferredFormat.u.raw_audio.buffer_size
= fDevice->BufferList().return_playback_buffer_size
* (fOutputPreferredFormat.u.raw_audio.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK)
* fOutputPreferredFormat.u.raw_audio.channel_count;
for (int32 i = 0; node_input* channel = (node_input*)fInputs.ItemAt(i);
i++) {
channel->fPreferredFormat.u.raw_audio.frame_rate = frameRate;
channel->fPreferredFormat.u.raw_audio.buffer_size
= fOutputPreferredFormat.u.raw_audio.buffer_size;
channel->fFormat.u.raw_audio.frame_rate = frameRate;
channel->fFormat.u.raw_audio.buffer_size
= fOutputPreferredFormat.u.raw_audio.buffer_size;
channel->fInput.format.u.raw_audio.frame_rate = frameRate;
channel->fInput.format.u.raw_audio.buffer_size
= fOutputPreferredFormat.u.raw_audio.buffer_size;
}
// make sure the time base is reset
fTimeComputer.SetFrameRate(frameRate);
// update internal latency
_UpdateInternalLatency(fOutputPreferredFormat);
return B_OK;
}
status_t
MultiAudioNode::_SetNodeOutputFrameRate(float frameRate)
{
// check whether the frame rate is supported
uint32 multiAudioRate = MultiAudio::convert_from_sample_rate(frameRate);
if ((fDevice->Description().input_rates & multiAudioRate) == 0)
return B_BAD_VALUE;
BAutolock locker(fBufferLock);
// already set?
if (fDevice->FormatInfo().input.rate == multiAudioRate)
return B_OK;
// set the frame rate on the device
status_t error = fDevice->SetInputFrameRate(multiAudioRate);
if (error != B_OK)
return error;
// it went fine -- update all formats
fInputPreferredFormat.u.raw_audio.frame_rate = frameRate;
fInputPreferredFormat.u.raw_audio.buffer_size
= fDevice->BufferList().return_record_buffer_size
* (fInputPreferredFormat.u.raw_audio.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK)
* fInputPreferredFormat.u.raw_audio.channel_count;
for (int32 i = 0; node_output* channel = (node_output*)fOutputs.ItemAt(i);
i++) {
channel->fPreferredFormat.u.raw_audio.frame_rate = frameRate;
channel->fPreferredFormat.u.raw_audio.buffer_size
= fInputPreferredFormat.u.raw_audio.buffer_size;
channel->fFormat.u.raw_audio.frame_rate = frameRate;
channel->fFormat.u.raw_audio.buffer_size
= fInputPreferredFormat.u.raw_audio.buffer_size;
channel->fOutput.format.u.raw_audio.frame_rate = frameRate;
channel->fOutput.format.u.raw_audio.buffer_size
= fInputPreferredFormat.u.raw_audio.buffer_size;
}
// make sure the time base is reset
fTimeComputer.SetFrameRate(frameRate);
// update internal latency
_UpdateInternalLatency(fInputPreferredFormat);
return B_OK;
}
void
MultiAudioNode::_UpdateInternalLatency(const media_format& format)
{
// use half a buffer length latency
fInternalLatency = format.u.raw_audio.buffer_size * 10000 / 2
/ ((format.u.raw_audio.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK)
* format.u.raw_audio.channel_count)
/ ((int32)(format.u.raw_audio.frame_rate / 100));
PRINT((" internal latency = %" B_PRIdBIGTIME "\n", fInternalLatency));
SetEventLatency(fInternalLatency);
}
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fChannelId, fSamplesSent, fOldBufferInfo.
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fLatency, fInternalLatency, fBufferPeriod.
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fChannelId, fOldBufferInfo.