#include "AudioFilterNode.h"
#include "AudioBuffer.h"
#include "IParameterSet.h"
#include "IAudioOpFactory.h"
#include "IAudioOp.h"
#include "SoundUtils.h"
#include <Buffer.h>
#include <BufferGroup.h>
#include <ByteOrder.h>
#include <Catalog.h>
#include <ParameterWeb.h>
#include <String.h>
#include <TimeSource.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "CortexAddOnsCommon"
enum input_id_t {
ID_AUDIO_INPUT
};
enum output_id_t {
ID_AUDIO_MIX_OUTPUT
};
status_t AudioFilterNode::getPreferredInputFormat(
media_format& ioFormat) {
return getPreferredOutputFormat(ioFormat);
}
status_t AudioFilterNode::getPreferredOutputFormat(
media_format& ioFormat) {
if(ioFormat.type != B_MEDIA_RAW_AUDIO)
return B_MEDIA_BAD_FORMAT;
media_raw_audio_format& f = ioFormat.u.raw_audio;
f.format = media_raw_audio_format::B_AUDIO_FLOAT;
f.frame_rate = 44100.0;
f.channel_count = 1;
f.byte_order = B_MEDIA_HOST_ENDIAN;
f.buffer_size = 1024;
return B_OK;
}
status_t AudioFilterNode::_validate_raw_audio_format(
const media_format& preferredFormat,
media_format& ioProposedFormat) {
char formatStr[256];
PRINT(("AudioFilterNode::_validate_raw_audio_format()\n"));
ASSERT(preferredFormat.type == B_MEDIA_RAW_AUDIO);
string_for_format(preferredFormat, formatStr, 255);
PRINT(("\ttemplate format: %s\n", formatStr));
string_for_format(ioProposedFormat, formatStr, 255);
PRINT(("\tincoming proposed format: %s\n", formatStr));
if (ioProposedFormat.type != B_MEDIA_RAW_AUDIO) {
ioProposedFormat = preferredFormat;
return B_MEDIA_BAD_FORMAT;
}
if (!format_is_compatible(preferredFormat, ioProposedFormat)) {
string_for_format(ioProposedFormat, formatStr, 255);
PRINT((
"\tformat conflict; suggesting:\n\tformat %s\n", formatStr));
return B_MEDIA_BAD_FORMAT;
}
ioProposedFormat.SpecializeTo(&preferredFormat);
string_for_format(ioProposedFormat, formatStr, 255);
PRINT(("\toutbound proposed format: %s\n", formatStr));
return B_OK;
}
status_t AudioFilterNode::validateProposedInputFormat(
const media_format& preferredFormat,
media_format& ioProposedFormat) {
return _validate_raw_audio_format(
preferredFormat, ioProposedFormat);
}
status_t AudioFilterNode::validateProposedOutputFormat(
const media_format& preferredFormat,
media_format& ioProposedFormat) {
return _validate_raw_audio_format(
preferredFormat, ioProposedFormat);
}
void AudioFilterNode::_specialize_raw_audio_format(
const media_format& templateFormat,
media_format& ioFormat) {
ASSERT(templateFormat.type == B_MEDIA_RAW_AUDIO);
ASSERT(ioFormat.type == B_MEDIA_RAW_AUDIO);
media_raw_audio_format& f = ioFormat.u.raw_audio;
const media_raw_audio_format& p = templateFormat.u.raw_audio;
const media_raw_audio_format& w = media_raw_audio_format::wildcard;
if(f.format == w.format) {
ASSERT(p.format);
f.format = p.format;
}
if(f.channel_count == w.channel_count) {
ASSERT(p.channel_count);
f.channel_count = p.channel_count;
}
if(f.frame_rate == w.frame_rate) {
ASSERT(p.frame_rate);
f.frame_rate = p.frame_rate;
}
if(f.byte_order == w.byte_order) {
ASSERT(p.byte_order);
f.byte_order = p.byte_order;
}
if(f.buffer_size == w.buffer_size) {
ASSERT(p.buffer_size);
f.buffer_size = p.buffer_size;
}
}
AudioFilterNode::~AudioFilterNode() {
Quit();
if(m_parameterSet) delete m_parameterSet;
if(m_opFactory) delete m_opFactory;
if(m_op) delete m_op;
}
AudioFilterNode::AudioFilterNode(
const char* name,
IAudioOpFactory* opFactory,
BMediaAddOn* addOn) :
BMediaNode(name),
BBufferConsumer(B_MEDIA_RAW_AUDIO),
BBufferProducer(B_MEDIA_RAW_AUDIO),
BControllable(),
BMediaEventLooper(),
m_outputEnabled(true),
m_downstreamLatency(0),
m_processingLatency(0),
m_bufferGroup(0),
m_parameterSet(opFactory->createParameterSet()),
m_opFactory(opFactory),
m_op(0),
m_addOn(addOn) {
ASSERT(m_opFactory);
ASSERT(m_parameterSet);
PRINT((
"AudioFilterNode::AudioFilterNode()\n"));
}
status_t AudioFilterNode::HandleMessage(
int32 code,
const void* data,
size_t size) {
if(
BBufferConsumer::HandleMessage(code, data, size) &&
BBufferProducer::HandleMessage(code, data, size) &&
BControllable::HandleMessage(code, data, size) &&
BMediaNode::HandleMessage(code, data, size))
BMediaNode::HandleBadMessage(code, data, size);
return B_OK;
}
BMediaAddOn* AudioFilterNode::AddOn(
int32* outID) const {
if(m_addOn)
*outID = 0;
return m_addOn;
}
void AudioFilterNode::SetRunMode(
run_mode mode) {
if(mode == B_OFFLINE)
ReportError(B_NODE_FAILED_SET_RUN_MODE);
BMediaEventLooper::SetRunMode(mode);
}
void AudioFilterNode::HandleEvent(
const media_timed_event* event,
bigtime_t howLate,
bool realTimeEvent) {
ASSERT(event);
switch(event->type) {
case BTimedEventQueue::B_PARAMETER:
handleParameterEvent(event);
break;
case BTimedEventQueue::B_START:
handleStartEvent(event);
break;
case BTimedEventQueue::B_STOP:
handleStopEvent(event);
break;
default:
ignoreEvent(event);
break;
}
}
void AudioFilterNode::NodeRegistered() {
PRINT(("AudioFilterNode::NodeRegistered()\n"));
status_t err;
m_input.destination.port = ControlPort();
m_input.destination.id = ID_AUDIO_INPUT;
m_input.node = Node();
m_input.source = media_source::null;
m_input.format.type = B_MEDIA_RAW_AUDIO;
err = getRequiredInputFormat(m_input.format);
ASSERT(err == B_OK);
strlcpy(m_input.name, B_TRANSLATE("Audio input"), B_MEDIA_NAME_LENGTH);
m_output.source.port = ControlPort();
m_output.source.id = ID_AUDIO_MIX_OUTPUT;
m_output.node = Node();
m_output.destination = media_destination::null;
m_output.format.type = B_MEDIA_RAW_AUDIO;
err = getRequiredOutputFormat(m_output.format);
ASSERT(err == B_OK);
strlcpy(m_output.name, B_TRANSLATE("Audio output"), B_MEDIA_NAME_LENGTH);
initParameterWeb();
SetPriority(B_REAL_TIME_PRIORITY);
Run();
}
bigtime_t AudioFilterNode::OfflineTime() {
return 0LL;
}
status_t AudioFilterNode::AcceptFormat(
const media_destination& destination,
media_format* ioFormat) {
PRINT(("AudioFilterNode::AcceptFormat()\n"));
status_t err;
if(destination != m_input.destination) {
PRINT(("\tbad destination\n"));
return B_MEDIA_BAD_DESTINATION;
}
if(ioFormat->type != B_MEDIA_RAW_AUDIO) {
PRINT(("\tnot B_MEDIA_RAW_AUDIO\n"));
return B_MEDIA_BAD_FORMAT;
}
media_format required;
required.type = B_MEDIA_RAW_AUDIO;
err = getRequiredInputFormat(required);
ASSERT(err == B_OK);
err = validateProposedInputFormat(required, *ioFormat);
if(err < B_OK)
return err;
if (m_output.destination != media_destination::null) {
const bool setFrameSize = ioFormat->u.raw_audio.buffer_size
== media_raw_audio_format::wildcard.buffer_size;
ioFormat->SpecializeTo(&m_output.format);
if (setFrameSize) {
ioFormat->u.raw_audio.buffer_size =
bytes_per_frame(ioFormat->u.raw_audio)
* (m_output.format.u.raw_audio.buffer_size
/ bytes_per_frame(m_output.format.u.raw_audio));
}
ASSERT(m_opFactory);
IAudioOp* op = m_opFactory->createOp(
this,
ioFormat->u.raw_audio,
m_output.format.u.raw_audio);
if(!op) {
char fmt[256];
string_for_format(*ioFormat, fmt, 255);
PRINT((
"*** AcceptFormat(): format validated, but no operation found:\n"
" %s\n",
fmt));
return B_MEDIA_BAD_FORMAT;
}
delete op;
}
return B_OK;
}
void AudioFilterNode::BufferReceived(
BBuffer* buffer) {
ASSERT(buffer);
if(buffer->Header()->destination !=
m_input.destination.id) {
PRINT(("AudioFilterNode::BufferReceived():\n"
"\tBad destination.\n"));
buffer->Recycle();
return;
}
if(buffer->Header()->time_source != TimeSource()->ID()) {
PRINT(("* timesource mismatch\n"));
}
if(m_output.destination == media_destination::null ||
!m_outputEnabled) {
buffer->Recycle();
return;
}
BBuffer* outBuffer;
if(m_bufferGroup) {
outBuffer = m_bufferGroup->RequestBuffer(
m_output.format.u.raw_audio.buffer_size, -1);
ASSERT(outBuffer);
outBuffer->Header()->type = B_MEDIA_RAW_AUDIO;
outBuffer->Header()->time_source = buffer->Header()->time_source;
outBuffer->Header()->start_time = buffer->Header()->start_time;
}
else {
outBuffer = buffer;
}
processBuffer(buffer, outBuffer);
status_t err = SendBuffer(outBuffer, m_output.source, m_output.destination);
if (err < B_OK) {
PRINT(("AudioFilterNode::BufferReceived():\n"
"\tSendBuffer() failed: %s\n", strerror(err)));
outBuffer->Recycle();
}
if(buffer != outBuffer)
buffer->Recycle();
}
status_t AudioFilterNode::Connected(
const media_source& source,
const media_destination& destination,
const media_format& format,
media_input* outInput) {
PRINT(("AudioFilterNode::Connected()\n"
"\tto source %" B_PRId32 "\n", source.id));
if(destination != m_input.destination) {
PRINT(("\tbad destination\n"));
return B_MEDIA_BAD_DESTINATION;
}
if(m_input.source != media_source::null) {
PRINT(("\talready connected\n"));
return B_MEDIA_ALREADY_CONNECTED;
}
m_input.source = source;
m_input.format = format;
*outInput = m_input;
updateOperation();
return B_OK;
}
void AudioFilterNode::Disconnected(
const media_source& source,
const media_destination& destination) {
PRINT(("AudioFilterNode::Disconnected()\n"));
if(m_input.source != source) {
PRINT(("\tsource mismatch: expected ID %" B_PRId32 ", got %" B_PRId32
"\n", m_input.source.id, source.id));
return;
}
if(destination != m_input.destination) {
PRINT(("\tdestination mismatch: expected ID %" B_PRId32 ", got %"
B_PRId32 "\n", m_input.destination.id, destination.id));
return;
}
m_input.source = media_source::null;
#ifdef DEBUG
status_t err =
#endif
getRequiredInputFormat(m_input.format);
ASSERT(err == B_OK);
if(m_op) {
delete m_op;
m_op = 0;
}
updateBufferGroup();
}
void AudioFilterNode::DisposeInputCookie(
int32 cookie) {}
status_t AudioFilterNode::FormatChanged(
const media_source& source,
const media_destination& destination,
int32 changeTag,
const media_format& newFormat) {
return B_MEDIA_BAD_FORMAT;
}
status_t AudioFilterNode::GetLatencyFor(
const media_destination& destination,
bigtime_t* outLatency,
media_node_id* outTimeSource) {
PRINT(("AudioFilterNode::GetLatencyFor()\n"));
if(destination != m_input.destination) {
PRINT(("\tbad destination\n"));
return B_MEDIA_BAD_DESTINATION;
}
*outLatency = m_downstreamLatency + m_processingLatency;
PRINT(("\treturning %" B_PRIdBIGTIME "\n", *outLatency));
*outTimeSource = TimeSource()->ID();
return B_OK;
}
status_t AudioFilterNode::GetNextInput(
int32* ioCookie,
media_input* outInput) {
if(*ioCookie)
return B_BAD_INDEX;
++*ioCookie;
*outInput = m_input;
return B_OK;
}
void AudioFilterNode::ProducerDataStatus(
const media_destination& destination,
int32 status,
bigtime_t tpWhen) {
PRINT(("AudioFilterNode::ProducerDataStatus(%" B_PRId32 " at %"
B_PRIdBIGTIME ")\n", status, tpWhen));
if(destination != m_input.destination) {
PRINT(("\tbad destination\n"));
}
if(m_output.destination != media_destination::null) {
status_t err = SendDataStatus(
status,
m_output.destination,
tpWhen);
if(err < B_OK) {
PRINT(("\tSendDataStatus(): %s\n", strerror(err)));
}
}
}
status_t AudioFilterNode::SeekTagRequested(
const media_destination& destination,
bigtime_t targetTime,
uint32 flags,
media_seek_tag* outSeekTag,
bigtime_t* outTaggedTime,
uint32* outFlags) {
PRINT(("AudioFilterNode::SeekTagRequested()\n"
"\tNot implemented.\n"));
return B_ERROR;
}
void AudioFilterNode::AdditionalBufferRequested(
const media_source& source,
media_buffer_id previousBufferID,
bigtime_t previousTime,
const media_seek_tag* previousTag) {
PRINT(("AudioFilterNode::AdditionalBufferRequested\n"
"\tOffline mode not implemented."));
}
void AudioFilterNode::Connect(
status_t status,
const media_source& source,
const media_destination& destination,
const media_format& format,
char* ioName) {
PRINT(("AudioFilterNode::Connect()\n"));
status_t err;
#if DEBUG
char formatStr[256];
string_for_format(format, formatStr, 255);
PRINT(("\tformat: %s\n", formatStr));
#endif
if(status < B_OK) {
PRINT(("\tCONNECTION FAILED: Status '%s'\n", strerror(status)));
m_output.destination = media_destination::null;
return;
}
strncpy(ioName, m_output.name, B_MEDIA_NAME_LENGTH);
m_output.destination = destination;
media_node_id timeSource;
err = FindLatencyFor(m_output.destination, &m_downstreamLatency, &timeSource);
if(err < B_OK) {
PRINT(("\t!!! FindLatencyFor(): %s\n", strerror(err)));
}
PRINT(("\tdownstream latency = %" B_PRIdBIGTIME "\n", m_downstreamLatency));
SetBufferDuration(
buffer_duration(
m_output.format.u.raw_audio));
updateOperation();
updateBufferGroup();
}
void AudioFilterNode::Disconnect(
const media_source& source,
const media_destination& destination) {
PRINT(("AudioFilterNode::Disconnect()\n"));
if(source != m_output.source) {
PRINT(("\tbad source\n"));
return;
}
if(destination != m_output.destination) {
PRINT(("\tbad destination\n"));
return;
}
m_output.destination = media_destination::null;
#ifdef DEBUG
status_t err =
#endif
getRequiredOutputFormat(m_output.format);
ASSERT(err == B_OK);
updateBufferGroup();
if(m_op) {
delete m_op;
m_op = 0;
}
}
status_t AudioFilterNode::DisposeOutputCookie(
int32 cookie) {
return B_OK;
}
void AudioFilterNode::EnableOutput(
const media_source& source,
bool enabled,
int32* _deprecated_) {
PRINT(("AudioFilterNode::EnableOutput()\n"));
if(source != m_output.source) {
PRINT(("\tbad source\n"));
return;
}
m_outputEnabled = enabled;
}
status_t AudioFilterNode::FormatChangeRequested(
const media_source& source,
const media_destination& destination,
media_format* ioFormat,
int32* _deprecated_) {
PRINT(("AudioFilterNode::FormatChangeRequested()\n"
"\tNot supported.\n"));
return B_MEDIA_BAD_FORMAT;
}
status_t AudioFilterNode::FormatProposal(
const media_source& source,
media_format* ioFormat) {
PRINT(("AudioFilterNode::FormatProposal()\n"));
status_t err;
if(source != m_output.source) {
PRINT(("\tbad source\n"));
return B_MEDIA_BAD_SOURCE;
}
if(ioFormat->type != B_MEDIA_RAW_AUDIO) {
PRINT(("\tbad type\n"));
return B_MEDIA_BAD_FORMAT;
}
media_format required;
required.type = B_MEDIA_RAW_AUDIO;
err = getRequiredOutputFormat(required);
ASSERT(err == B_OK);
err = validateProposedOutputFormat(
required,
*ioFormat);
if(err < B_OK)
return err;
return B_OK;
}
status_t AudioFilterNode::FormatSuggestionRequested(
media_type type,
int32 quality,
media_format* outFormat) {
PRINT(("AudioFilterNode::FormatSuggestionRequested()\n"));
if(type != B_MEDIA_RAW_AUDIO) {
PRINT(("\tbad type\n"));
return B_MEDIA_BAD_FORMAT;
}
outFormat->type = type;
return getPreferredOutputFormat(*outFormat);
}
status_t AudioFilterNode::GetLatency(
bigtime_t* outLatency) {
PRINT(("AudioFilterNode::GetLatency()\n"));
*outLatency = EventLatency() + SchedulingLatency();
PRINT(("\treturning %" B_PRIdBIGTIME "\n", *outLatency));
return B_OK;
}
status_t AudioFilterNode::GetNextOutput(
int32* ioCookie,
media_output* outOutput) {
if(*ioCookie)
return B_BAD_INDEX;
++*ioCookie;
*outOutput = m_output;
return B_OK;
}
void AudioFilterNode::LatencyChanged(
const media_source& source,
const media_destination& destination,
bigtime_t newLatency,
uint32 flags) {
PRINT(("AudioFilterNode::LatencyChanged()\n"));
if(source != m_output.source) {
PRINT(("\tBad source.\n"));
return;
}
if(destination != m_output.destination) {
PRINT(("\tBad destination.\n"));
return;
}
m_downstreamLatency = newLatency;
SetEventLatency(m_downstreamLatency + m_processingLatency);
if(m_input.source != media_source::null) {
status_t err = SendLatencyChange(
m_input.source,
m_input.destination,
EventLatency() + SchedulingLatency());
if(err < B_OK)
PRINT(("\t!!! SendLatencyChange(): %s\n", strerror(err)));
}
}
void AudioFilterNode::LateNoticeReceived(
const media_source& source,
bigtime_t howLate,
bigtime_t tpWhen) {
PRINT(("AudioFilterNode::LateNoticeReceived()\n"
"\thowLate == %" B_PRIdBIGTIME "\n"
"\twhen == %" B_PRIdBIGTIME "\n", howLate, tpWhen));
if(source != m_output.source) {
PRINT(("\tBad source.\n"));
return;
}
if(m_input.source == media_source::null) {
PRINT(("\t!!! No input to blame.\n"));
return;
}
NotifyLateProducer(
m_input.source,
howLate,
tpWhen);
}
status_t AudioFilterNode::PrepareToConnect(
const media_source& source,
const media_destination& destination,
media_format* ioFormat,
media_source* outSource,
char* outName) {
status_t err;
char formatStr[256];
string_for_format(*ioFormat, formatStr, 255);
PRINT(("AudioFilterNode::PrepareToConnect()\n"
"\tproposed format: %s\n", formatStr));
if(source != m_output.source) {
PRINT(("\tBad source.\n"));
return B_MEDIA_BAD_SOURCE;
}
if(m_output.destination != media_destination::null) {
PRINT(("\tAlready connected.\n"));
return B_MEDIA_ALREADY_CONNECTED;
}
if(ioFormat->type != B_MEDIA_RAW_AUDIO) {
PRINT(("\tBad format type.\n"));
return B_MEDIA_BAD_FORMAT;
}
media_format required;
required.type = B_MEDIA_RAW_AUDIO;
err = getRequiredOutputFormat(required);
ASSERT(err == B_OK);
err = validateProposedOutputFormat(
required, *ioFormat);
if(err < B_OK) {
return err;
}
specializeOutputFormat(*ioFormat);
string_for_format(*ioFormat, formatStr, 255);
PRINT(("FINAL FORMAT: %s\n", formatStr));
m_output.destination = destination;
m_output.format = *ioFormat;
*outSource = m_output.source;
strncpy(outName, m_output.name, B_MEDIA_NAME_LENGTH);
return B_OK;
}
status_t AudioFilterNode::SetBufferGroup(
const media_source& source,
BBufferGroup* group) {
PRINT(("AudioFilterNode::SetBufferGroup()\n"));
if(source != m_output.source) {
PRINT(("\tBad source.\n"));
return B_MEDIA_BAD_SOURCE;
}
if(m_bufferGroup)
delete m_bufferGroup;
m_bufferGroup = group;
return B_OK;
}
status_t AudioFilterNode::SetPlayRate(
int32 numerator,
int32 denominator) {
return B_ERROR;
}
status_t AudioFilterNode::VideoClippingChanged(
const media_source& source,
int16 numShorts,
int16* clipData,
const media_video_display_info& display,
int32* outFromChangeTag) {
return B_ERROR;
}
status_t AudioFilterNode::GetParameterValue(
int32 id,
bigtime_t* outLastChangeTime,
void* outValue,
size_t* ioSize) {
ASSERT(m_parameterSet);
return m_parameterSet->getValue(
id,
outLastChangeTime,
outValue,
ioSize);
}
void AudioFilterNode::SetParameterValue(
int32 id,
bigtime_t changeTime,
const void* value,
size_t size) {
if(RunState() != B_STARTED) {
ASSERT(m_parameterSet);
m_parameterSet->setValue(
id,
changeTime,
value,
size);
return;
}
if(size > 64) {
DEBUGGER((
"!!! AudioFilterNode::SetParameterValue(): parameter data too large\n"));
}
media_timed_event ev(
changeTime,
BTimedEventQueue::B_PARAMETER,
0,
BTimedEventQueue::B_NO_CLEANUP,
size,
id,
(char*)value, size);
EventQueue()->AddEvent(ev);
}
IParameterSet* AudioFilterNode::parameterSet() const {
return m_parameterSet;
}
void AudioFilterNode::handleParameterEvent(
const media_timed_event* event) {
void* value = (void*)event->user_data;
int32 id = event->bigdata;
size_t size = event->data;
bigtime_t changeTime = event->event_time;
status_t err;
ASSERT(m_parameterSet);
err = m_parameterSet->setValue(id, changeTime, value, size);
if(err < B_OK) {
PRINT((
"* AudioFilterNode::handleParameterEvent(): m_parameterSet->SetValue() failed:\n"
" %s\n", strerror(err)));
}
}
void AudioFilterNode::handleStartEvent(
const media_timed_event* event) {
PRINT(("AudioFilterNode::handleStartEvent\n"));
ASSERT(m_op);
m_op->init();
}
void AudioFilterNode::handleStopEvent(
const media_timed_event* event) {
PRINT(("AudioFilterNode::handleStopEvent\n"));
}
void AudioFilterNode::ignoreEvent(
const media_timed_event* event) {
PRINT(("AudioFilterNode::ignoreEvent\n"));
}
status_t
AudioFilterNode::prepareFormatChange(const media_format &newFormat)
{
media_format required;
required.type = B_MEDIA_RAW_AUDIO;
status_t err = getRequiredOutputFormat(required);
ASSERT(err == B_OK);
media_format proposed = newFormat;
err = validateProposedOutputFormat(
required,
proposed);
return err;
}
void
AudioFilterNode::doFormatChange(const media_format &newFormat)
{
m_output.format = newFormat;
updateOperation();
}
void AudioFilterNode::initParameterWeb() {
ASSERT(m_parameterSet);
BParameterWeb* web = new BParameterWeb();
BString groupName = B_TRANSLATE("%groupname% parameters");
groupName.ReplaceFirst("%groupname%", Name());
BParameterGroup* group = web->MakeGroup(groupName.String());
m_parameterSet->populateGroup(group);
SetParameterWeb(web);
}
void AudioFilterNode::updateOperation() {
if(m_input.source == media_source::null ||
m_output.destination == media_destination::null)
return;
ASSERT(m_opFactory);
IAudioOp* op = m_opFactory->createOp(
this,
m_input.format.u.raw_audio,
m_output.format.u.raw_audio);
if(!op) {
PRINT((
"!!! AudioFilterNode::updateOperation(): no operation created!\n"));
delete m_op;
m_op = 0;
return;
}
op->replace(m_op);
m_op = op;
m_processingLatency = calcProcessingLatency();
PRINT(("\tprocessing latency = %" B_PRIdBIGTIME "\n", m_processingLatency));
SetEventLatency(m_downstreamLatency + m_processingLatency);
status_t err = SendLatencyChange(
m_input.source,
m_input.destination,
EventLatency() + SchedulingLatency());
if(err < B_OK)
PRINT(("\t!!! SendLatencyChange(): %s\n", strerror(err)));
}
void AudioFilterNode::updateBufferGroup() {
status_t err;
size_t inputSize = bytes_per_frame(m_input.format.u.raw_audio);
size_t outputSize = bytes_per_frame(m_output.format.u.raw_audio);
if(m_input.source == media_source::null ||
m_output.destination == media_destination::null ||
inputSize >= outputSize) {
PRINT(("###### NO BUFFER GROUP NEEDED\n"));
if(m_bufferGroup) {
delete m_bufferGroup;
m_bufferGroup = 0;
}
return;
}
int32 bufferCount = EventLatency() / BufferDuration() + 1 + 1;
if(bufferCount < 5)
bufferCount = 5;
if(m_bufferGroup) {
int32 curBufferCount;
err = m_bufferGroup->CountBuffers(&curBufferCount);
if(err == B_OK && curBufferCount >= bufferCount) {
BBuffer* buf = m_bufferGroup->RequestBuffer(
outputSize, -1);
if(buf) {
buf->Recycle();
return;
}
}
delete m_bufferGroup;
m_bufferGroup = 0;
}
PRINT((
"##### AudioFilterNode::updateBufferGroup():\n"
"##### creating %" B_PRId32 " buffers of size %" B_PRIuSIZE "\n",
bufferCount, m_output.format.u.raw_audio.buffer_size));
m_bufferGroup = new BBufferGroup(
m_output.format.u.raw_audio.buffer_size,
bufferCount);
}
bigtime_t AudioFilterNode::calcProcessingLatency() {
PRINT(("AudioFilterNode::calcProcessingLatency()\n"));
ASSERT(m_input.source != media_source::null);
ASSERT(m_output.destination != media_destination::null);
ASSERT(m_op);
m_op->init();
size_t maxSize = max_c(
m_input.format.u.raw_audio.buffer_size,
m_output.format.u.raw_audio.buffer_size);
BBufferGroup* testGroup = new BBufferGroup(
maxSize, 1);
BBuffer* buffer = testGroup->RequestBuffer(
maxSize, -1);
ASSERT(buffer);
buffer->Header()->type = B_MEDIA_RAW_AUDIO;
buffer->Header()->size_used = m_input.format.u.raw_audio.buffer_size;
bigtime_t preTest = system_time();
processBuffer(buffer, buffer);
bigtime_t elapsed = system_time()-preTest;
buffer->Recycle();
delete testGroup;
m_op->init();
return elapsed;
}
void AudioFilterNode::processBuffer(
BBuffer* inputBuffer,
BBuffer* outputBuffer) {
ASSERT(inputBuffer);
ASSERT(outputBuffer);
ASSERT(m_op);
AudioBuffer input(m_input.format.u.raw_audio, inputBuffer);
AudioBuffer output(m_output.format.u.raw_audio, outputBuffer);
double sourceOffset = 0.0;
uint32 destinationOffset = 0L;
bigtime_t startTime = outputBuffer->Header()->start_time;
bigtime_t targetTime = startTime;
bigtime_t endTime = startTime + BufferDuration();
uint32 framesRemaining = input.frames();
while(framesRemaining) {
bigtime_t nextEventTime = endTime;
int64 toProcess = frames_for_duration(output.format(), nextEventTime - targetTime);
ASSERT(toProcess > 0);
if (toProcess > framesRemaining)
toProcess = framesRemaining;
uint32 processed = m_op->process(
input, output, sourceOffset, destinationOffset, (uint32)toProcess, targetTime);
if(processed < toProcess) {
PRINT((
"*** AudioFilterNode::processBuffer(): insufficient frames filled\n"));
}
framesRemaining -= toProcess;
targetTime = nextEventTime;
}
outputBuffer->Header()->size_used = input.frames() * bytes_per_frame(m_output.format.u.raw_audio);
}