/*
* Copyright 2012, Gerasim Troeglazov (3dEyes**), 3dEyes@gmail.com.
* All rights reserved.
* Distributed under the terms of the MIT License.
*/
#include <ByteOrder.h>
#include <Buffer.h>
#include <BufferGroup.h>
#include <TimeSource.h>
#include <ParameterWeb.h>
#include <String.h>
#include <stdio.h>
#include <string.h>
#include "VSTNode.h"
//VSTNode
VSTNode::~VSTNode()
{
Quit();
}
VSTNode::VSTNode(BMediaAddOn* addon, const char* name, const char* path)
:
BMediaNode(name),
BBufferConsumer(B_MEDIA_RAW_AUDIO),
BBufferProducer(B_MEDIA_RAW_AUDIO),
BControllable(),
BMediaEventLooper(),
fAddOn(addon),
fOutputMediaEnabled(true),
fDownstreamLatency(0),
fProcessLatency(0)
{
fPlugin = new VSTPlugin();
fPlugin->LoadModule(path);
}
//BMediaNode
BMediaAddOn*
VSTNode::AddOn(int32* id) const
{
if(fAddOn)
*id = 0;
return fAddOn;
}
status_t
VSTNode::HandleMessage(int32 message, const void* data, size_t size)
{
if((BControllable::HandleMessage(message, data, size) != B_OK) &&
(BBufferConsumer::HandleMessage(message, data, size) != B_OK) &&
(BBufferProducer::HandleMessage(message, data, size) != B_OK) &&
(BControllable::HandleMessage(message, data, size) != B_OK) ) {
BMediaNode::HandleMessage(message, data, size);
return B_OK;
}
BMediaNode::HandleBadMessage(message, data, size);
return B_ERROR;
}
void
VSTNode::NodeRegistered()
{
fPreferredFormat.type = B_MEDIA_RAW_AUDIO;
fPreferredFormat.u.raw_audio.buffer_size = BUFF_SIZE;
fPreferredFormat.u.raw_audio = media_raw_audio_format::wildcard;
fPreferredFormat.u.raw_audio.channel_count =
media_raw_audio_format::wildcard.channel_count;
fPreferredFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT;
fFormat.type = B_MEDIA_RAW_AUDIO;
fFormat.u.raw_audio = media_raw_audio_format::wildcard;
fInputMedia.destination.port = ControlPort();
fInputMedia.destination.id = ID_AUDIO_INPUT;
fInputMedia.node = Node();
fInputMedia.source = media_source::null;
fInputMedia.format = fFormat;
strncpy(fInputMedia.name, "Audio Input", B_MEDIA_NAME_LENGTH);
fOutputMedia.source.port = ControlPort();
fOutputMedia.source.id = ID_AUDIO_OUTPUT;
fOutputMedia.node = Node();
fOutputMedia.destination = media_destination::null;
fOutputMedia.format = fFormat;
strncpy(fOutputMedia.name, "Audio Output", B_MEDIA_NAME_LENGTH);
InitParameterValues();
InitParameterWeb();
SetPriority(B_REAL_TIME_PRIORITY);
Run();
}
//BControllable
status_t
VSTNode::GetParameterValue(int32 id, bigtime_t* lastChangeTime, void* value,
size_t *size)
{
if (*size < sizeof(float) || *size < sizeof(int32))
return B_NO_MEMORY;
type_code v_type = B_FLOAT_TYPE;
BParameter *param;
for(int i = 0; i < fWeb->CountParameters(); i++) {
param = fWeb->ParameterAt(i);
if(param->ID() == id) {
v_type = param->ValueType();
break;
}
}
*size = sizeof(float);
if (id == P_MUTE) {
*(int32*)value = fMute;
*lastChangeTime = fMuteLastChanged;
return B_OK;
} else if (id == P_BYPASS) {
*(int32*)value = fByPass;
*lastChangeTime = fByPassLastChanged;
return B_OK;
} else {
int32 idx = id - P_PARAM;
if (idx >= 0 && idx < fPlugin->ParametersCount()) {
VSTParameter *param = fPlugin->Parameter(idx);
if (v_type == B_FLOAT_TYPE)
*(float*)value = param->Value();
if (v_type == B_INT32_TYPE)
*(int32*)value = (int32)ceil(param->Value());
*lastChangeTime = param->LastChangeTime();
return B_OK;
}
}
return B_ERROR;
}
void
VSTNode::SetParameterValue(int32 id, bigtime_t time, const void* value,
size_t size)
{
int32 idx = id - P_PARAM;
if ((idx >= 0 && idx < fPlugin->ParametersCount()) || id == P_MUTE ||
id == P_BYPASS) {
media_timed_event ev(time, BTimedEventQueue::B_PARAMETER, (void*)value,
BTimedEventQueue::B_NO_CLEANUP, size, id, "VSTParam");
//dirty hack for parameter processing (mediakit bug????)
ParameterEventProcessing(&ev);
EventQueue()->AddEvent(ev);
}
}
//BBufferConsumer
void
VSTNode::BufferReceived(BBuffer* buffer)
{
if (buffer->Header()->destination != fInputMedia.destination.id) {
buffer->Recycle();
return;
}
if (fOutputMedia.destination == media_destination::null ||
!fOutputMediaEnabled) {
buffer->Recycle();
return;
}
FilterBuffer(buffer);
status_t err = SendBuffer(buffer, fOutputMedia.source,
fOutputMedia.destination);
if (err < B_OK)
buffer->Recycle();
}
status_t
VSTNode::AcceptFormat(const media_destination &dst, media_format* format)
{
if (dst != fInputMedia.destination)
return B_MEDIA_BAD_DESTINATION;
if (format->type != B_MEDIA_RAW_AUDIO)
return B_MEDIA_BAD_FORMAT;
ValidateFormat(
(fFormat.u.raw_audio.format != media_raw_audio_format::wildcard.format) ?
fFormat : fPreferredFormat, *format);
return B_OK;
}
status_t
VSTNode::GetNextInput(int32* cookie, media_input* input)
{
if (*cookie)
return B_BAD_INDEX;
++*cookie;
*input = fInputMedia;
return B_OK;
}
void
VSTNode::DisposeInputCookie(int32 cookie)
{
}
status_t
VSTNode::FormatChanged(const media_source &src, const media_destination &dst,
int32 changeTag, const media_format &format)
{
return B_MEDIA_BAD_FORMAT;
}
void
VSTNode::ProducerDataStatus(const media_destination &dst, int32 status,
bigtime_t when)
{
if (fOutputMedia.destination != media_destination::null)
SendDataStatus(status, fOutputMedia.destination, when);
}
status_t
VSTNode::GetLatencyFor( const media_destination &dst, bigtime_t* latency,
media_node_id* outTimeSource)
{
if (dst != fInputMedia.destination)
return B_MEDIA_BAD_DESTINATION;
*latency = fDownstreamLatency + fProcessLatency;
*outTimeSource = TimeSource()->ID();
return B_OK;
}
status_t
VSTNode::Connected(const media_source& source,
const media_destination& destination, const media_format& format,
media_input* input)
{
if (destination != fInputMedia.destination)
return B_MEDIA_BAD_DESTINATION;
if (fInputMedia.source != media_source::null)
return B_MEDIA_ALREADY_CONNECTED;
fInputMedia.source = source;
fInputMedia.format = format;
*input = fInputMedia;
fFormat = format;
return B_OK;
}
void
VSTNode::Disconnected(const media_source &src, const media_destination &dst)
{
if(fInputMedia.source!=src || dst!=fInputMedia.destination)
return;
fInputMedia.source = media_source::null;
if(fOutputMedia.destination == media_destination::null)
fFormat.u.raw_audio = media_raw_audio_format::wildcard;
fInputMedia.format = fFormat;
}
//BBufferProducer
status_t
VSTNode::FormatSuggestionRequested(media_type type, int32 quality,
media_format* format)
{
if (type != B_MEDIA_RAW_AUDIO)
return B_MEDIA_BAD_FORMAT;
if (fFormat.u.raw_audio.format != media_raw_audio_format::wildcard.format)
*format = fFormat;
else
*format = fPreferredFormat;
return B_OK;
}
status_t
VSTNode::FormatProposal(const media_source &src, media_format* format)
{
if (src != fOutputMedia.source)
return B_MEDIA_BAD_SOURCE;
if (format->type != B_MEDIA_RAW_AUDIO)
return B_MEDIA_BAD_FORMAT;
ValidateFormat(
(fFormat.u.raw_audio.format != media_raw_audio_format::wildcard.format) ?
fFormat : fPreferredFormat, *format);
return B_OK;
}
status_t
VSTNode::FormatChangeRequested(const media_source &src,
const media_destination &dst, media_format* format, int32* _deprecated_)
{
return B_MEDIA_BAD_FORMAT;
}
void
VSTNode::LateNoticeReceived(const media_source &src,
bigtime_t late, bigtime_t when)
{
if (src != fOutputMedia.source || fInputMedia.source == media_source::null)
return;
NotifyLateProducer(fInputMedia.source, late, when);
}
status_t
VSTNode::GetNextOutput(int32 *cookie, media_output* output)
{
if (*cookie)
return B_BAD_INDEX;
++*cookie;
*output = fOutputMedia;
return B_OK;
}
status_t
VSTNode::DisposeOutputCookie(int32 cookie)
{
return B_OK;
}
status_t
VSTNode::SetBufferGroup(const media_source &src, BBufferGroup* group)
{
status_t ret;
int32 changeTag;
if (src != fOutputMedia.source)
return B_MEDIA_BAD_SOURCE;
if (fInputMedia.source == media_source::null)
return B_ERROR;
ret = SetOutputBuffersFor(fInputMedia.source, fInputMedia.destination,
group, 0, &changeTag);
return ret;
}
status_t
VSTNode::PrepareToConnect( const media_source &src, const media_destination &dst,
media_format* format, media_source* out_source, char* name)
{
status_t ret = B_OK;
if (src != fOutputMedia.source)
return B_MEDIA_BAD_SOURCE;
if (format->type != B_MEDIA_RAW_AUDIO)
return B_MEDIA_BAD_FORMAT;
if (fOutputMedia.destination != media_destination::null)
return B_MEDIA_ALREADY_CONNECTED;
ret = ValidateFormat(
(fFormat.u.raw_audio.format != media_raw_audio_format::wildcard.format) ?
fFormat : fPreferredFormat, *format);
if (ret < B_OK)
return ret;
SetOutputFormat(*format);
fOutputMedia.destination = dst;
fOutputMedia.format = *format;
*out_source = fOutputMedia.source;
strncpy(name, fOutputMedia.name, B_MEDIA_NAME_LENGTH);
return B_OK;
}
void
VSTNode::Connect(status_t status, const media_source &src,
const media_destination &dst, const media_format &format, char* name)
{
if (status < B_OK) {
fOutputMedia.destination = media_destination::null;
return;
}
strncpy(name, fOutputMedia.name, B_MEDIA_NAME_LENGTH);
fOutputMedia.destination = dst;
fFormat = format;
media_node_id timeSource;
FindLatencyFor(fOutputMedia.destination, &fDownstreamLatency, &timeSource);
InitFilter();
fProcessLatency = GetFilterLatency();
SetEventLatency(fDownstreamLatency + fProcessLatency);
if (fInputMedia.source != media_source::null) {
SendLatencyChange(fInputMedia.source, fInputMedia.destination,
EventLatency()+SchedulingLatency());
}
bigtime_t duration = 0;
int sample_size = (fFormat.u.raw_audio.format & 0xf)*
fFormat.u.raw_audio.channel_count;
if (fFormat.u.raw_audio.buffer_size > 0 &&
fFormat.u.raw_audio.frame_rate > 0 &&
sample_size > 0) {
duration = (bigtime_t)(((fFormat.u.raw_audio.buffer_size / sample_size) /
fFormat.u.raw_audio.frame_rate) * 1000000.0);
}
SetBufferDuration(duration);
}
void
VSTNode::Disconnect(const media_source &src, const media_destination &dst)
{
if (src != fOutputMedia.source)
return;
if (dst != fOutputMedia.destination)
return;
fOutputMedia.destination = media_destination::null;
if (fInputMedia.source == media_source::null)
fFormat.u.raw_audio = media_raw_audio_format::wildcard;
fOutputMedia.format = fFormat;
}
void
VSTNode::EnableOutput(const media_source &src, bool enabled, int32* _deprecated_)
{
if (src != fOutputMedia.source)
return;
fOutputMediaEnabled = enabled;
}
status_t
VSTNode::GetLatency(bigtime_t* latency)
{
*latency = EventLatency() + SchedulingLatency();
return B_OK;
}
void
VSTNode::LatencyChanged(const media_source &src, const media_destination &dst,
bigtime_t latency, uint32 flags)
{
if (src != fOutputMedia.source || dst != fOutputMedia.destination)
return;
fDownstreamLatency = latency;
SetEventLatency(fDownstreamLatency + fProcessLatency);
if (fInputMedia.source != media_source::null) {
SendLatencyChange(fInputMedia.source,
fInputMedia.destination,EventLatency() + SchedulingLatency());
}
}
//BMediaEventLooper
bigtime_t
VSTNode::OfflineTime()
{
return 0LL;
}
//VSTNode
void
VSTNode::HandleEvent(const media_timed_event *event, bigtime_t late,
bool realTime)
{
if(event->type == BTimedEventQueue::B_PARAMETER)
ParameterEventProcessing(event);
}
void
VSTNode::ParameterEventProcessing(const media_timed_event* event)
{
float value = 0.0;
int32 value32 = 0;
int32 id = event->bigdata;
size_t size = event->data;
bigtime_t now = TimeSource()->Now();
type_code v_type = B_FLOAT_TYPE;
BParameter* web_param;
for(int i = 0; i < fWeb->CountParameters(); i++) {
web_param = fWeb->ParameterAt(i);
if(web_param->ID() == id) {
v_type = web_param->ValueType();
break;
}
}
if (v_type == B_FLOAT_TYPE)
value = *((float*)event->pointer);
if (v_type == B_INT32_TYPE) {
value32 = *((int32*)event->pointer);
value = (float)value32;
}
if (id == P_MUTE) {
fMute = value32;
fMuteLastChanged = now;
BroadcastNewParameterValue(now, id, event->pointer, size);
} else if (id == P_BYPASS) {
fByPass = value32;
fByPassLastChanged = now;
BroadcastNewParameterValue(now, id, event->pointer, size);
} else {
int32 idx = id - P_PARAM;
if (idx >= 0 && idx < fPlugin->ParametersCount()) {
VSTParameter *param = fPlugin->Parameter(idx);
param->SetValue(value);
BroadcastNewParameterValue(now, id, &value, size);
}
}
}
status_t
VSTNode::ValidateFormat(const media_format &preferredFormat,
media_format &proposedFormat)
{
status_t ret = B_OK;
if (proposedFormat.type != B_MEDIA_RAW_AUDIO) {
proposedFormat = preferredFormat;
return B_MEDIA_BAD_FORMAT;
}
media_raw_audio_format &wild = media_raw_audio_format::wildcard;
media_raw_audio_format &f = proposedFormat.u.raw_audio;
const media_raw_audio_format &pref = preferredFormat.u.raw_audio;
if(pref.frame_rate != wild.frame_rate && f.frame_rate != pref.frame_rate) {
if(f.frame_rate != wild.frame_rate)
ret = B_MEDIA_BAD_FORMAT;
f.frame_rate = pref.frame_rate;
}
if(pref.channel_count != wild.channel_count &&
f.channel_count != pref.channel_count) {
if(f.channel_count != wild.channel_count)
ret = B_MEDIA_BAD_FORMAT;
f.channel_count = pref.channel_count;
}
if(pref.format != wild.format && f.format != pref.format) {
if(f.format != wild.format)
ret = B_MEDIA_BAD_FORMAT;
f.format = pref.format;
}
if(pref.byte_order != wild.byte_order &&
f.byte_order != pref.byte_order) {
if(f.byte_order != wild.byte_order)
ret = B_MEDIA_BAD_FORMAT;
f.byte_order = pref.byte_order;
}
if(pref.buffer_size != wild.buffer_size &&
f.buffer_size != pref.buffer_size) {
if(f.buffer_size != wild.buffer_size)
ret = B_MEDIA_BAD_FORMAT;
f.buffer_size = pref.buffer_size;
}
return ret;
}
void
VSTNode::SetOutputFormat(media_format &format)
{
media_raw_audio_format &f = format.u.raw_audio;
media_raw_audio_format &w = media_raw_audio_format::wildcard;
if (f.frame_rate == w.frame_rate)
f.frame_rate = 44100.0;
if (f.channel_count == w.channel_count) {
if(fInputMedia.source != media_source::null)
f.channel_count = fInputMedia.format.u.raw_audio.channel_count;
else
f.channel_count = fPlugin->Channels(VST_OUTPUT_CHANNELS);
}
if (f.format == w.format)
f.format = media_raw_audio_format::B_AUDIO_FLOAT;
if (f.byte_order == w.format) {
f.byte_order = (B_HOST_IS_BENDIAN) ?
B_MEDIA_BIG_ENDIAN : B_MEDIA_LITTLE_ENDIAN;
}
if (f.buffer_size == w.buffer_size)
f.buffer_size = BUFF_SIZE;
}
void
VSTNode::InitParameterValues()
{
fMute = 0;
fByPass = 0;
fMuteLastChanged = 0LL;
fByPassLastChanged = 0LL;
}
void
VSTNode::InitParameterWeb()
{
fWeb = new BParameterWeb();
bool switch_group_needed = false;
for(int i = 0; i < fPlugin->ParametersCount(); i++) {
VSTParameter* param = fPlugin->Parameter(i);
if (param->Type() == VST_PARAM_CHECKBOX ||
param->Type() == VST_PARAM_DROPLIST) {
switch_group_needed = true;
break;
}
}
BParameterGroup* fParamGroup = fWeb->MakeGroup("Parameters");
BParameterGroup* fSwitchesGroup = switch_group_needed ?
fWeb->MakeGroup("Switches") : NULL;
BParameterGroup* fAboutGroup = fWeb->MakeGroup("About");
BParameter* value;
BNullParameter* label;
BParameterGroup* group;
BParameterGroup* fFControlGroup = fParamGroup->MakeGroup("FilterControl");
BParameterGroup* fCheckBoxGroup = switch_group_needed ?
fSwitchesGroup->MakeGroup("CheckBoxes") : NULL;
BParameterGroup* fSelectorsGroup = switch_group_needed ?
fSwitchesGroup->MakeGroup("Selectors") : NULL;
fFControlGroup->MakeDiscreteParameter(P_MUTE,
B_MEDIA_NO_TYPE,"Mute", B_ENABLE);
fFControlGroup->MakeDiscreteParameter(P_BYPASS,
B_MEDIA_NO_TYPE,"ByPass", B_ENABLE);
for(int i = 0; i < fPlugin->ParametersCount(); i++) {
VSTParameter *param = fPlugin->Parameter(i);
switch(param->Type()) {
case VST_PARAM_CHECKBOX:
{
BString str;
str << param->Name() << " (" << param->MinimumValue()
<< "/" << param->MaximumValue() << ")";
value = fCheckBoxGroup->MakeDiscreteParameter(
P_PARAM + param->Index(), B_MEDIA_NO_TYPE,
str.String(), B_ENABLE);
break;
}
case VST_PARAM_DROPLIST:
{
BDiscreteParameter *dvalue =
fSelectorsGroup->MakeDiscreteParameter(P_PARAM + param->Index(),
B_MEDIA_NO_TYPE, param->Name(), B_OUTPUT_MUX);
for(int j = 0; j < param->ListCount(); j++) {
dvalue->AddItem( param->ListItemAt(j)->Index,
param->ListItemAt(j)->Name.String());
}
break;
}
//sliders
default:
{
BString str;
group = fParamGroup->MakeGroup(param->Name());
label = group->MakeNullParameter(P_LABEL + param->Index(),
B_MEDIA_NO_TYPE, param->Name(), B_GENERIC);
str.SetTo(param->MaximumValue());
str << " " << param->Unit();
group->MakeNullParameter(P_LABEL2 + param->Index(),
B_MEDIA_NO_TYPE, str.String(), B_GENERIC);
value = group->MakeContinuousParameter(P_PARAM + param->Index(),
B_MEDIA_NO_TYPE, "", B_GAIN, "", 0.0, 1.0, 0.01);
label->AddOutput(value);
value->AddInput(label);
str.SetTo(param->MinimumValue());
str << " " << param->Unit();
group->MakeNullParameter(P_LABEL3 + param->Index(),
B_MEDIA_NO_TYPE, str.String(), B_GENERIC);
break;
}
}
}
BString str("About plugin");
label = fAboutGroup->MakeNullParameter(P_ABOUT + 0,
B_MEDIA_NO_TYPE, str.String(), B_GENERIC);
str.SetTo("Effect name: ");
if (strlen(fPlugin->EffectName()) != 0)
str.Append(fPlugin->EffectName());
else
str.Append("not specified");
label = fAboutGroup->MakeNullParameter(P_ABOUT + 1,
B_MEDIA_NO_TYPE, str.String(), B_GENERIC);
str.SetTo("Vendor: ");
if (strlen(fPlugin->Vendor()) != 0)
str.Append(fPlugin->Vendor());
else
str.Append("not specified");
label = fAboutGroup->MakeNullParameter(P_ABOUT + 2,
B_MEDIA_NO_TYPE, str.String(), B_GENERIC);
str.SetTo("Product: ");
if (strlen(fPlugin->Product()) != 0)
str.Append(fPlugin->Product());
else
str.Append("not specified");
label = fAboutGroup->MakeNullParameter(P_ABOUT + 3,
B_MEDIA_NO_TYPE, str.String(), B_GENERIC);
str.SetTo("Input Channels: ");
str<<fPlugin->Channels(VST_INPUT_CHANNELS);
label = fAboutGroup->MakeNullParameter(P_ABOUT + 4,
B_MEDIA_NO_TYPE, str.String(), B_GENERIC);
str.SetTo("Output Channels: ");
str<<fPlugin->Channels(VST_OUTPUT_CHANNELS);
label = fAboutGroup->MakeNullParameter(P_ABOUT + 5,
B_MEDIA_NO_TYPE, str.String(), B_GENERIC);
SetParameterWeb(fWeb);
}
void
VSTNode::InitFilter()
{
fBlockSize = fFormat.u.raw_audio.buffer_size /
(fFormat.u.raw_audio.channel_count * sizeof(float));
fPlugin->SetBlockSize(fBlockSize);
fPlugin->SetSampleRate(fFormat.u.raw_audio.frame_rate);
}
bigtime_t
VSTNode::GetFilterLatency()
{
if (fOutputMedia.destination == media_destination::null)
return 0LL;
BBufferGroup* temp_group =
new BBufferGroup(fOutputMedia.format.u.raw_audio.buffer_size, 1);
BBuffer *buffer =
temp_group->RequestBuffer(fOutputMedia.format.u.raw_audio.buffer_size);
buffer->Header()->type = B_MEDIA_RAW_AUDIO;
buffer->Header()->size_used = fOutputMedia.format.u.raw_audio.buffer_size;
bigtime_t begin = system_time();
FilterBuffer(buffer);
bigtime_t latency = system_time()-begin;
InitFilter();
buffer->Recycle();
delete temp_group;
return latency;
}
void
VSTNode::FilterBuffer(BBuffer* buffer)
{
uint32 m_frameSize = (fFormat.u.raw_audio.format & 0x0f)*
fFormat.u.raw_audio.channel_count;
uint32 samples = buffer->Header()->size_used / m_frameSize;
uint32 channels = fFormat.u.raw_audio.channel_count;
if (fMute != 0) {
memset(buffer->Data(), 0, buffer->Header()->size_used);
} else {
if (fByPass == 0) {
fPlugin->Process((float*)buffer->Data(), samples, channels);
}
}
}
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fWeb, fBlockSize, fMute, fByPass, fMuteLastChanged, fByPassLastChanged.