From df969bab298ad637c23c0e267ef786efc1db5fcf Mon Sep 17 00:00:00 2001 From: threedeyes <3dEyes@gmail.com> Date: Sat, 13 Oct 2012 17:49:08 +1100 Subject: [PATCH] Added VSTHost media add-on. --- src/add-ons/media/media-add-ons/Jamfile | 1 + .../media/media-add-ons/vst_host/Jamfile | 10 + .../media/media-add-ons/vst_host/VSTAddOn.cpp | 145 ++++ .../media/media-add-ons/vst_host/VSTAddOn.h | 30 + .../media/media-add-ons/vst_host/VSTHost.cpp | 567 +++++++++++++ .../media/media-add-ons/vst_host/VSTHost.h | 169 ++++ .../media/media-add-ons/vst_host/VSTHost.rdef | 15 + .../media/media-add-ons/vst_host/VSTNode.cpp | 802 ++++++++++++++++++ .../media/media-add-ons/vst_host/VSTNode.h | 149 ++++ 9 files changed, 1888 insertions(+) create mode 100644 src/add-ons/media/media-add-ons/vst_host/Jamfile create mode 100644 src/add-ons/media/media-add-ons/vst_host/VSTAddOn.cpp create mode 100644 src/add-ons/media/media-add-ons/vst_host/VSTAddOn.h create mode 100644 src/add-ons/media/media-add-ons/vst_host/VSTHost.cpp create mode 100644 src/add-ons/media/media-add-ons/vst_host/VSTHost.h create mode 100644 src/add-ons/media/media-add-ons/vst_host/VSTHost.rdef create mode 100644 src/add-ons/media/media-add-ons/vst_host/VSTNode.cpp create mode 100644 src/add-ons/media/media-add-ons/vst_host/VSTNode.h diff --git a/src/add-ons/media/media-add-ons/Jamfile b/src/add-ons/media/media-add-ons/Jamfile index 656bc2844f..ea59bbf90d 100644 --- a/src/add-ons/media/media-add-ons/Jamfile +++ b/src/add-ons/media/media-add-ons/Jamfile @@ -18,4 +18,5 @@ SubInclude HAIKU_TOP src add-ons media media-add-ons video_producer_demo ; SubInclude HAIKU_TOP src add-ons media media-add-ons videowindow ; SubInclude HAIKU_TOP src add-ons media media-add-ons video_mixer ; SubInclude HAIKU_TOP src add-ons media media-add-ons equalizer ; +SubInclude HAIKU_TOP src add-ons media media-add-ons vst_host ; diff --git a/src/add-ons/media/media-add-ons/vst_host/Jamfile b/src/add-ons/media/media-add-ons/vst_host/Jamfile new file mode 100644 index 0000000000..69453da15a --- /dev/null +++ b/src/add-ons/media/media-add-ons/vst_host/Jamfile @@ -0,0 +1,10 @@ +SubDir HAIKU_TOP src add-ons media media-add-ons vst_host ; + +AddResources vst_host.media_addon : VSTHost.rdef ; + +Addon vst_host.media_addon : + VSTHost.cpp + VSTAddOn.cpp + VSTNode.cpp + : be media +; diff --git a/src/add-ons/media/media-add-ons/vst_host/VSTAddOn.cpp b/src/add-ons/media/media-add-ons/vst_host/VSTAddOn.cpp new file mode 100644 index 0000000000..ee32b2ea45 --- /dev/null +++ b/src/add-ons/media/media-add-ons/vst_host/VSTAddOn.cpp @@ -0,0 +1,145 @@ +/* + * Copyright 2012, Gerasim Troeglazov (3dEyes**), 3dEyes@gmail.com. All rights reserved. + * Distributed under the terms of the MIT License. + */ + +#include +#include +#include + +#include "VSTNode.h" +#include "VSTAddOn.h" + +extern "C" _EXPORT BMediaAddOn* make_media_addon(image_id image) +{ + return new VSTAddOn(image); +} + +VSTAddOn::VSTAddOn(image_id image) : BMediaAddOn(image) +{ + const char vst_subdir[]={"media/vstplugins"}; + + BPath addons_dir; + fPluginsList.MakeEmpty(); + + find_directory(B_COMMON_ADDONS_DIRECTORY, &addons_dir); + addons_dir.Append(vst_subdir); + ScanPluginsFolders(addons_dir.Path(), true); + + find_directory(B_USER_ADDONS_DIRECTORY, &addons_dir); + addons_dir.Append(vst_subdir); + ScanPluginsFolders(addons_dir.Path() ,true); + + find_directory(B_BEOS_ADDONS_DIRECTORY, &addons_dir); + addons_dir.Append(vst_subdir); + ScanPluginsFolders(addons_dir.Path()); +} + +VSTAddOn::~VSTAddOn() +{ + +} + +status_t +VSTAddOn::InitCheck(const char **text) +{ + return B_OK; +} + +int32 +VSTAddOn::CountFlavors() +{ + return fPluginsList.CountItems(); +} + +status_t VSTAddOn::GetFlavorAt( int32 idx, const flavor_info **info) +{ + if(idx < 0 || idx >= fPluginsList.CountItems()) + return B_ERROR; + + VSTPlugin *plugin = (VSTPlugin*)fPluginsList.ItemAt(idx); + + flavor_info *f_info = new flavor_info; + f_info->internal_id = idx; + f_info->kinds = B_BUFFER_CONSUMER | B_BUFFER_PRODUCER | B_CONTROLLABLE; + f_info->possible_count = 0; + f_info->flavor_flags = 0; + f_info->name = (char *)plugin->ModuleName(); + f_info->info = (char *)plugin->Product(); + + media_format *format = new media_format; + format->type = B_MEDIA_RAW_AUDIO; + format->u.raw_audio = media_raw_audio_format::wildcard; + format->u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT; + + f_info->in_format_count = 1; + f_info->in_formats = format; + + format = new media_format; + format->type = B_MEDIA_RAW_AUDIO; + format->u.raw_audio = media_raw_audio_format::wildcard; + format->u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT; + + f_info->out_format_count = 1; + f_info->out_formats = format; + + *info = f_info; + + return B_OK; +} + +BMediaNode* +VSTAddOn::InstantiateNodeFor(const flavor_info *info, BMessage *config, + status_t *err) +{ + VSTPlugin *plugin = (VSTPlugin*)fPluginsList.ItemAt(info->internal_id); + VSTNode *node = new VSTNode(this, plugin->ModuleName(), plugin->Path()); + return node; +} + +int +VSTAddOn::ScanPluginsFolders(const char *path, bool make_dir) +{ + BEntry ent; + + BDirectory dir(path); + if(dir.InitCheck() != B_OK) { + create_directory(path, 0755); + return 0; + } + + while(dir.GetNextEntry(&ent) == B_OK) { + BPath p(&ent); + if(ent.IsDirectory()) { + ScanPluginsFolders(p.Path()); + } else { + VSTPlugin *plugin = new VSTPlugin(); + int ret = plugin->LoadModule(p.Path()); + if(ret == B_OK) { + plugin->UnLoadModule(); + fPluginsList.AddItem(plugin); + } else { + delete plugin; + } + } + } + return 0; +} + +status_t +VSTAddOn::GetConfigurationFor(BMediaNode *node, BMessage *message) +{ + return B_OK; +} + +bool +VSTAddOn::WantsAutoStart() +{ + return false; +} + +status_t +VSTAddOn::AutoStart(int count, BMediaNode **node, int32 *id, bool *more) +{ + return B_OK; +} diff --git a/src/add-ons/media/media-add-ons/vst_host/VSTAddOn.h b/src/add-ons/media/media-add-ons/vst_host/VSTAddOn.h new file mode 100644 index 0000000000..903ab8d744 --- /dev/null +++ b/src/add-ons/media/media-add-ons/vst_host/VSTAddOn.h @@ -0,0 +1,30 @@ +/* + * Copyright 2012, Gerasim Troeglazov (3dEyes**), 3dEyes@gmail.com. All rights reserved. + * Distributed under the terms of the MIT License. + */ + +#ifndef __VST_ADDON_H__ +#define __VST_ADDON_H__ + +#include +#include "VSTHost.h" + +class VSTAddOn : public BMediaAddOn +{ +public: + virtual ~VSTAddOn(); + explicit VSTAddOn(image_id image); + virtual status_t InitCheck(const char **text); + virtual int32 CountFlavors(); + virtual status_t GetFlavorAt(int32 idx, const flavor_info **info); + virtual BMediaNode* InstantiateNodeFor(const flavor_info *info, BMessage *config, + status_t *err); + virtual status_t GetConfigurationFor(BMediaNode *node, BMessage *message); + virtual bool WantsAutoStart(); + virtual status_t AutoStart(int count, BMediaNode **node, int32 *id, bool *more); +private: + int ScanPluginsFolders(const char *path, bool make_dir=false); + BList fPluginsList; +}; + +#endif //__VST_ADDON_H__ diff --git a/src/add-ons/media/media-add-ons/vst_host/VSTHost.cpp b/src/add-ons/media/media-add-ons/vst_host/VSTHost.cpp new file mode 100644 index 0000000000..fbabc75254 --- /dev/null +++ b/src/add-ons/media/media-add-ons/vst_host/VSTHost.cpp @@ -0,0 +1,567 @@ +/* + * Copyright 2012, Gerasim Troeglazov (3dEyes**), 3dEyes@gmail.com. All rights reserved. + * Distributed under the terms of the MIT License. + */ + +#include +#include +#include + +#include + +#include "VSTHost.h" + +static int32 VHostCallback(VSTEffect* effect, int32 opcode, int32 index, + int32 value, void* ptr, float opt); + +//Trim string +static void +TrimString(BString *string) { + char *str = string->LockBuffer(256); + uint32 k = 0; + uint32 i = 0; + for(i=0; str[i]!='\0';) { + if (isspace(str[i])) { + k = i; + for(uint32 j = i; j < strlen(str) - 1; j++) { + str[j] = str[j + 1]; + } + str[strlen(str) - 1] = '\0'; + i = k; + } else { + i++; + } + } + string->UnlockBuffer(); +} + +//VST Parameter class +VSTParameter::VSTParameter(VSTPlugin *plugin, int index) +{ + fIndex = index; + fEffect = plugin->Effect(); + fDropList.MakeEmpty(); + + char temp[256]; + //get parameter name + temp[0] = 0; + fEffect->dispatcher(fEffect, VST_GET_PARAM_NAME, index, 0, temp, 0); + fName.SetTo(temp); + TrimString(&fName); + //get parameter label (unit) + temp[0] = 0; + fEffect->dispatcher(fEffect, VST_GET_PARAM_UNIT, index, 0, temp, 0); + fUnit.SetTo(temp); + ValidateValues(&fUnit); + //store current value + float val = fEffect->getParameter(fEffect, index); + //test for minimum value + fEffect->setParameter(fEffect, index, 0); + temp[0] = 0; + fEffect->dispatcher(fEffect, VST_GET_PARAM_STR, index, 0, temp, 0); + fMinValue.SetTo(temp); + ValidateValues(&fMinValue); + //test for maximum value + temp[0] = 0; + fEffect->setParameter(fEffect, index, 1.0); + fEffect->dispatcher(fEffect, VST_GET_PARAM_STR, index, 0, temp, 0); + fMaxValue.SetTo(temp); + ValidateValues(&fMaxValue); + //test for discrete values + char test_disp[VST_PARAM_TEST_COUNT][256]; + float test_values[VST_PARAM_TEST_COUNT]; + float delta = 1.0 / (float)VST_PARAM_TEST_COUNT; + int test_cnt = 0; + for(int tst_val = 0; tst_val < VST_PARAM_TEST_COUNT; tst_val++){ + float v = (float)tst_val / (float)VST_PARAM_TEST_COUNT; + if(tst_val >= VST_PARAM_TEST_COUNT - 1) { + v=1.0; + } + fEffect->setParameter(fEffect, index, v); + float new_value = fEffect->getParameter(fEffect, index); + bool valtest = false; + for(int i = 0; i < test_cnt; i++) { + if(fabs(test_values[i] - new_value) < delta) { + valtest = true; + break; + } + } + if(valtest == false) { + test_values[test_cnt] = new_value; + fEffect->dispatcher(fEffect, VST_GET_PARAM_STR, index, 0, test_disp[test_cnt], 0); + test_cnt++; + } + } + + //restore value + fEffect->setParameter(fEffect, index, val); + + //detect param type + if(test_cnt == 2) { + fType = VST_PARAM_CHECKBOX; + + DropListValue *min_item = new DropListValue(); + min_item->Value = 0.0; + min_item->Index = 0; + min_item->Name = fMinValue; + fDropList.AddItem(min_item); + + DropListValue *max_item = new DropListValue(); + max_item->Value = 1.0; + max_item->Index = 1; + max_item->Name = fMaxValue; + fDropList.AddItem(max_item); + } else if(test_cnt > 2 && test_cnt < VST_PARAM_TEST_COUNT / 2) { + fType = VST_PARAM_DROPLIST; + + for(int i = 0; i < test_cnt; i++) { + DropListValue *item = new DropListValue(); + item->Value = test_values[i]; + item->Index = i; + item->Name = test_disp[i]; + fDropList.AddItem(item); + } + } else { + fType = VST_PARAM_SLIDER; + } + fChanged = 0LL; +} + +VSTParameter::~VSTParameter() +{ +} + +BString* +VSTParameter::ValidateValues(BString *string) +{ + if(string->Length() == 0) { + return string; + } + + bool isNum = true; + + const char *ptr = string->String(); + for(;*ptr!=0;ptr++) { + char ch = *ptr; + if(!((ch >= '0' && ch <= '9') || ch == '.' || ch == '-')) { + isNum = false; + break; + } + } + + if(isNum) { + float val = atof(string->String()); + + if(val <= -pow(2, 31)) { + string->SetTo("-∞"); + } else if(val >= pow(2, 31)) { + string->SetTo("∞"); + } else { + char temp[256]; + sprintf(temp, "%g", val); + string->SetTo(temp); + } + } else { + TrimString(string); + if(*string == "oo" || *string == "inf") + string->SetTo("∞"); + if(*string == "-oo" || *string == "-inf") + string->SetTo("-∞"); + + } + return string; +} + +int +VSTParameter::ListCount(void) +{ + return fDropList.CountItems(); +} + +DropListValue* +VSTParameter::ListItemAt(int index) +{ + DropListValue *item = NULL; + if(index >= 0 && index < fDropList.CountItems()) { + item = (DropListValue*)fDropList.ItemAt(index); + } + return item; +} + + +float +VSTParameter::Value() +{ + float value = fEffect->getParameter(fEffect, fIndex); + if(fType == VST_PARAM_DROPLIST) { + //scan for near value + int min_index = 0; + float min_delta = 1.0; + for(int i = 0; i < fDropList.CountItems(); i++) { + DropListValue *item = (DropListValue*)fDropList.ItemAt(i); + float delta = fabs(item->Value - value); + if(delta <= min_delta) { + min_delta = delta; + min_index = i; + } + } + value = min_index; + } + fLastValue = value; + return value; +} + +void +VSTParameter::SetValue(float value) +{ + if(value == fLastValue) { + return; + } + + if(fType == VST_PARAM_DROPLIST) { + //take value by index + int index = (int)round(value); + if(index >= 0 && index < fDropList.CountItems()) { + DropListValue *item = (DropListValue*)fDropList.ItemAt(index); + value = item->Value; + fLastValue = index; + } else { + return; + } + } else { + fLastValue = value; + } + fChanged = system_time(); + fEffect->setParameter(fEffect, fIndex, value); +} + +bigtime_t +VSTParameter::LastChangeTime(void) +{ + return fChanged; +} + +const char* +VSTParameter::MinimumValue(void) +{ + return fMinValue.String(); +} + +const char* +VSTParameter::MaximumValue(void) +{ + return fMaxValue.String(); +} + +const char* +VSTParameter::Unit(void) +{ + return fUnit.String(); +} + +int +VSTParameter::Index(void) +{ + return fIndex; +} + +int +VSTParameter::Type(void) +{ + return fType; +} + +const char* +VSTParameter::Name(void) +{ + return fName.String(); +} + +//VST Plugin class +VSTPlugin::VSTPlugin() +{ + fActive = false; + fEffect = NULL; + VSTMainProc = NULL; + fInputChannels = 0; + fOutputChannels = 0; + fSampleRate = 44100.f; + fBlockSize = 0; + inputs = NULL; + outputs = NULL; + fParameters.MakeEmpty(); +} + +VSTPlugin::~VSTPlugin() +{ + fParameters.MakeEmpty(); + UnLoadModule(); +} + +int +VSTPlugin::LoadModule(const char *path) +{ + char effectName[256] = {0}; + char vendorString[256] = {0}; + char productString[256] = {0}; + + if(fActive) { + return VST_ERR_ALREADY_LOADED; + } + + fPath = BPath(path); + + fModule = load_add_on(path); + if(fModule <= 0) { + return VST_ERR_NOT_LOADED; + } + + if(get_image_symbol(fModule, "main_plugin", B_SYMBOL_TYPE_TEXT, (void**)&VSTMainProc) != B_OK) { + unload_add_on(fModule); + return VST_ERR_NO_MAINPROC; + } + + fEffect = VSTMainProc(VHostCallback); + if(fEffect==NULL) { + unload_add_on(fModule); + return VST_ERR_NOT_LOADED; + } + + fEffect->dispatcher(fEffect, VST_OPEN, 0, 0, 0, 0); + + fEffect->dispatcher(fEffect, VST_GET_EFFECT_NAME, 0, 0, effectName, 0); + fEffectName.SetTo(effectName); + TrimString(&fEffectName); + + fModuleName.SetTo("VST:"); + fModuleName.Append(fPath.Leaf()); + + fEffect->dispatcher(fEffect, VST_GET_VENDOR_STR, 0, 0, vendorString, 0); + fVendorString.SetTo(vendorString); + TrimString(&fVendorString); + + fEffect->dispatcher(fEffect, VST_GET_PRODUCT_STR, 0, 0, productString, 0); + fProductString.SetTo(productString); + TrimString(&fProductString); + + fInputChannels = fEffect->numInputs; + fOutputChannels = fEffect->numOutputs; + + for(int i=0; i < fEffect->numParams; i++) { + VSTParameter *param = new VSTParameter(this, i); + fParameters.AddItem(param); + } + + fEffect->dispatcher(fEffect, VST_STATE_CHANGED, 0, 1, 0, 0); + + ReAllocBuffers(); + + fActive = true; + return B_OK; +} + +int +VSTPlugin::UnLoadModule(void) +{ + if(!fActive || fModule <= 0) { + return VST_ERR_NOT_LOADED; + } + fEffect->dispatcher(fEffect, VST_STATE_CHANGED, 0, 0, 0, 0); + fEffect->dispatcher(fEffect, VST_CLOSE, 0, 0, 0, 0); + + unload_add_on(fModule); + + return B_OK; +} + +int +VSTPlugin::Channels(int mode) +{ + switch(mode) { + case VST_INPUT_CHANNELS: + return fInputChannels; + case VST_OUTPUT_CHANNELS: + return fOutputChannels; + default: + return 0; + } +} + +int +VSTPlugin::SetSampleRate(float rate) +{ + fSampleRate = rate; + fEffect->dispatcher(fEffect, VST_SET_SAMPLE_RATE, 0, 0, 0, rate); + return B_OK; +} + +float +VSTPlugin::SampleRate(void) +{ + return fSampleRate; +} + +int +VSTPlugin::SetBlockSize(size_t size) +{ + fBlockSize = size; + fEffect->dispatcher(fEffect, VST_SET_BLOCK_SIZE, 0, size, 0, 0); + ReAllocBuffers(); + return B_OK; +} + +const char* +VSTPlugin::Path(void) +{ + return fPath.Path(); +} + +int +VSTPlugin::ReAllocBuffers(void) +{ + if(inputs!=NULL) { + for(int32 i = 0; i < fInputChannels; i++) { + delete inputs[i]; + } + } + + if(outputs!=NULL) { + for(int32 i = 0; i < fOutputChannels; i++) { + delete outputs[i]; + } + } + + if(fInputChannels > 0) { + inputs = new float*[fInputChannels]; + for(int32 i = 0; i < fInputChannels; i++) { + inputs[i] = new float[fBlockSize]; + memset (inputs[i], 0, fBlockSize * sizeof(float)); + } + } + + if(fOutputChannels > 0) { + outputs = new float*[fOutputChannels]; + for(int32_t i = 0; i < fOutputChannels; i++) { + outputs[i] = new float[fBlockSize]; + memset (outputs[i], 0, fBlockSize * sizeof(float)); + } + } + return B_OK; +} + +size_t +VSTPlugin::BlockSize(void) +{ + return fBlockSize; +} + +int +VSTPlugin::ParametersCount(void) +{ + return fParameters.CountItems(); +} + +VSTParameter* +VSTPlugin::Parameter(int index) +{ + VSTParameter *param = NULL; + + if(index >= 0 && index < fParameters.CountItems()) { + param = (VSTParameter*)fParameters.ItemAt(index); + } + + return param; +} + +VSTEffect* +VSTPlugin::Effect(void) +{ + return fEffect; +} + +const char* +VSTPlugin::EffectName(void) +{ + return fEffectName.String(); +} + +const char* +VSTPlugin::ModuleName(void) +{ + return fModuleName.String(); +} + +const char* +VSTPlugin::Vendor(void) +{ + return fVendorString.String(); +} + +const char* +VSTPlugin::Product(void) +{ + return fProductString.String(); +} + + +void +VSTPlugin::Process(float *buffer, int samples, int channels) +{ + //todo: full channels remapping needed + float *src = buffer; + + if(channels == fInputChannels) { //channel to channel + for(int j = 0; j < samples; j++) { + for(int c = 0; c < fInputChannels; c++) { + inputs[c][j] = *src++; + } + } + } else if( channels == 1) { //from mone to multichannel + for(int j = 0; j < samples; j++, src++) { + for(int c = 0; c < fInputChannels; c++) { + inputs[c][j] = *src; + } + } + } + + fEffect->processReplacing(fEffect, inputs, outputs, fBlockSize); + + float *dst = buffer; + + if(channels == fOutputChannels) { //channel to channel + for(int j = 0; j < samples; j++) { + for(int c = 0; c < fOutputChannels; c++) { + *dst++ = outputs[c][j]; + } + } + } else if(channels == 1) { //from multichannel to mono + for(int j = 0; j < samples; j++, dst++) { + float mix = 0; + for(int c = 0; c < fOutputChannels; c++) { + mix += outputs[c][j]; + } + *dst = mix / (float)fOutputChannels; + } + } +} + +static int32 +VHostCallback(VSTEffect* effect, int32 opcode, int32 index, int32 value, void* ptr, float opt) +{ + intptr_t result = 0; + + switch(opcode) + { + case VST_MASTER_PRODUCT: + if(ptr) { + strcpy((char*)ptr, "VSTHost Media AddOn"); + result = 1; + } + break; + case VST_MASTER_VERSION : + result = 2300; + break; + } + + return result; +} diff --git a/src/add-ons/media/media-add-ons/vst_host/VSTHost.h b/src/add-ons/media/media-add-ons/vst_host/VSTHost.h new file mode 100644 index 0000000000..903d1ca4aa --- /dev/null +++ b/src/add-ons/media/media-add-ons/vst_host/VSTHost.h @@ -0,0 +1,169 @@ +/* + * Copyright 2012, Gerasim Troeglazov (3dEyes**), 3dEyes@gmail.com. All rights reserved. + * Distributed under the terms of the MIT License. + */ + +#ifndef __VST_HOST_H__ +#define __VST_HOST_H__ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define VST_PARAM_TEST_COUNT 100 + +//error codes +#define VST_ERR_ALREADY_LOADED -1 +#define VST_ERR_NOT_LOADED -2 +#define VST_ERR_NO_MAINPROC -3 + +//param types +#define VST_PARAM_SLIDER 1 +#define VST_PARAM_CHECKBOX 2 +#define VST_PARAM_DROPLIST 3 + +//channels +#define VST_INPUT_CHANNELS 1 +#define VST_OUTPUT_CHANNELS 2 + +//vst callback opcodes +#define VST_MASTER_AUTOMATE 0x00 +#define VST_MASTER_VERSION 0x01 +#define VST_MASTER_IDLE 0x03 +#define VST_MASTER_VENDOR 0x20 +#define VST_MASTER_PRODUCT 0x21 + +//vst actions +#define VST_OPEN 0x00 +#define VST_CLOSE 0x01 +#define VST_GET_PARAM_UNIT 0x06 +#define VST_GET_PARAM_STR 0x07 +#define VST_GET_PARAM_NAME 0x08 +#define VST_SET_SAMPLE_RATE 0x0A +#define VST_SET_BLOCK_SIZE 0x0B +#define VST_STATE_CHANGED 0x0C +#define VST_GET_EFFECT_NAME 0x2D +#define VST_GET_VENDOR_STR 0x2F +#define VST_GET_PRODUCT_STR 0x30 +#define VST_GET_VENDOR_VER 0x31 + +//vst plugin structure +struct VSTEffect +{ + int cookie; + int32 (*dispatcher)(struct VSTEffect*, int32, int32, int32, void*, float); + void (*process)(struct VSTEffect*, float**, float**, int32); + void (*setParameter)(struct VSTEffect*, int32, float); + float (*getParameter)(struct VSTEffect*, int32); + int32 numPrograms; + int32 numParams; + int32 numInputs; + int32 numOutputs; + int32 flags; + void* _notused_pointer1; + void* _notused_pointer2; + char _notused_block1[12]; + float _notused_float; + void* _notused_pointer3; + void* _notused_pointer4; + int32 ID; + char _notused_block2[4]; + void (*processReplacing)(struct VSTEffect*, float**, float**, int); +}; + +//typedefs +typedef int32 (*audioMasterCallback)(VSTEffect*, int32, int32, int32, void*, float); +typedef VSTEffect* (*VSTEntryProc)(audioMasterCallback audioMaster); +inline float round(float x) {return ceil(x-0.5);} + +//structure for droplist parameters +struct DropListValue { + int Index; + float Value; + BString Name; +}; + +class VSTPlugin; +//vst parameter class adapter +class VSTParameter { +public: + VSTParameter(VSTPlugin* plugin, int index); + ~VSTParameter(); + float Value(void); + void SetValue(float value); + const char* MinimumValue(void); + const char* MaximumValue(void); + const char* Unit(void); + int Index(void); + int Type(void); + const char* Name(void); + int ListCount(void); + DropListValue* ListItemAt(int index); + bigtime_t LastChangeTime(void); +private: + BString* ValidateValues(BString *string); + VSTPlugin* fPlugin; + VSTEffect* fEffect; + int fIndex; + int fType; + BString fName; + BString fUnit; + BString fMinValue; + BString fMaxValue; + BList fDropList; + bigtime_t fChanged; + float fLastValue; +}; + +//vst plugin interface +class VSTPlugin { +public: + VSTPlugin(); + ~VSTPlugin(); + int LoadModule(const char *path); + int UnLoadModule(void); + int SetSampleRate(float rate); + float SampleRate(void); + int SetBlockSize(size_t size); + const char* Path(void); + size_t BlockSize(void); + VSTEffect* Effect(void); + const char* EffectName(void); + const char* ModuleName(void); + const char* Vendor(void); + const char* Product(void); + int ParametersCount(void); + VSTParameter* Parameter(int index); + int Channels(int mode); + int ReAllocBuffers(void); + void Process(float *buffer, int samples, int channels); +private: + VSTEntryProc GetMainEntry(); + VSTEffect* fEffect; + VSTEntryProc VSTMainProc; + bool fActive; + image_id fModule; + BPath fPath; + size_t fBlockSize; + float fSampleRate; + int fInputChannels; + int fOutputChannels; + BString fModuleName; + BString fEffectName; + BString fVendorString; + BString fProductString; + BList fParameters; + float **inputs; + float **outputs; +}; + +#endif //__VST_HOST_H__ diff --git a/src/add-ons/media/media-add-ons/vst_host/VSTHost.rdef b/src/add-ons/media/media-add-ons/vst_host/VSTHost.rdef new file mode 100644 index 0000000000..f5d2b2aa92 --- /dev/null +++ b/src/add-ons/media/media-add-ons/vst_host/VSTHost.rdef @@ -0,0 +1,15 @@ +/* + * VSTHost.rdef + */ + +resource app_signature "application/x-vnd.vst_host.media_addon"; + +resource app_version { + major = 0, + middle = 0, + minor = 2, + variety = 0, + internal = 0, + short_info = "0.0.2", + long_info = "VST Loader media add-on." +}; diff --git a/src/add-ons/media/media-add-ons/vst_host/VSTNode.cpp b/src/add-ons/media/media-add-ons/vst_host/VSTNode.cpp new file mode 100644 index 0000000000..88abfc634a --- /dev/null +++ b/src/add-ons/media/media-add-ons/vst_host/VSTNode.cpp @@ -0,0 +1,802 @@ +/* + * Copyright 2012, Gerasim Troeglazov (3dEyes**), 3dEyes@gmail.com. All rights reserved. + * Distributed under the terms of the MIT License. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#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; + } else { + BMediaNode::HandleBadMessage(message, data, size); + return B_ERROR; + } +} + +void +VSTNode::NodeRegistered() +{ + SetPriority(B_REAL_TIME_PRIORITY); + Run(); + + 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(); +} + +//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"); + ParameterEventProcessing(&ev); //dirty hack for parameter processing (mediakit bug????) + 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* poInput) +{ + 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; + *poInput = 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) +{ + int32 changeTag; + + if(src != fOutputMedia.source) { + return B_MEDIA_BAD_SOURCE; + } + if(fInputMedia.source == media_source::null) { + return B_ERROR; + } + + return SetOutputBuffersFor(fInputMedia.source, fInputMedia.destination, group, 0, &changeTag); +} + +status_t +VSTNode::PrepareToConnect( const media_source &src, const media_destination &dst, + media_format *format, media_source *out_source, char *name) +{ + 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; + } + + status_t err = ValidateFormat((fFormat.u.raw_audio.format != media_raw_audio_format::wildcard.format) ? + fFormat : fPreferredFormat, *format); + + if(err < B_OK) { + return err; + } + + 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) +{ + status_t err; + 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; + err = 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<Channels(VST_INPUT_CHANNELS); + label = fAboutGroup->MakeNullParameter(P_ABOUT + 4, + B_MEDIA_NO_TYPE, str.String(), B_GENERIC); + + str.SetTo("Output Channels: "); + str<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); + } + } +} diff --git a/src/add-ons/media/media-add-ons/vst_host/VSTNode.h b/src/add-ons/media/media-add-ons/vst_host/VSTNode.h new file mode 100644 index 0000000000..41b7d9a0d4 --- /dev/null +++ b/src/add-ons/media/media-add-ons/vst_host/VSTNode.h @@ -0,0 +1,149 @@ +/* + * Copyright 2012, Gerasim Troeglazov (3dEyes**), 3dEyes@gmail.com. All rights reserved. + * Distributed under the terms of the MIT License. + */ + +#ifndef __VST_NODE_H__ +#define __VST_NODE_H__ + +#include +#include +#include +#include + +#include "VSTHost.h" + +class BMediaAddOn; +class BBufferGroup; + +#define BUFF_SIZE 0x2000 + +#define ID_AUDIO_INPUT 1 +#define ID_AUDIO_OUTPUT 2 + +//parameter IDs +enum param_id_t { + P_MUTE = 50, + P_BYPASS, + + P_PARAM = 5000, + P_ABOUT = 6000, + P_LABEL = 7000, + P_LABEL2 = 8000, + P_LABEL3 = 9000, +}; + + +class VSTNode : public BBufferConsumer, + public BBufferProducer, + public BControllable, + public BMediaEventLooper { +public: + virtual ~VSTNode(); + VSTNode(BMediaAddOn *addon, const char *name, const char* path); + //BMediaNode +public: + virtual BMediaAddOn *AddOn(int32 *id) const; + virtual status_t HandleMessage(int32 code, const void *data, size_t size); +protected: + virtual void NodeRegistered(); + //BControllable +public: + virtual status_t GetParameterValue(int32 id, bigtime_t *lastChangeTime, + void *value, size_t *size); + virtual void SetParameterValue(int32 id, bigtime_t changeTime, + const void *value, size_t size); + //BBufferConsumer +public: + virtual void BufferReceived(BBuffer *buffer); + virtual status_t AcceptFormat(const media_destination &dst, + media_format *format); + virtual status_t GetNextInput(int32 *cookie, media_input *input); + virtual void DisposeInputCookie(int32 cookie); + virtual status_t FormatChanged(const media_source &src, + const media_destination &dst, + int32 change_tag, const media_format &format); + virtual void ProducerDataStatus(const media_destination &dst, + int32 status, + bigtime_t when); + virtual status_t GetLatencyFor(const media_destination &dst, + bigtime_t *latency, + media_node_id *time_src); + virtual status_t Connected(const media_source &src, + const media_destination &dst, + const media_format &format, + media_input *input); + virtual void Disconnected(const media_source &src, + const media_destination &dst); + //BBufferProducer +public: + virtual status_t FormatSuggestionRequested(media_type type, + int32 quality, media_format *format); + virtual status_t FormatProposal(const media_source &src, + media_format *format); + virtual status_t FormatChangeRequested(const media_source &src, + const media_destination &dst, + media_format *format, + int32* _deprecated_); + virtual void LateNoticeReceived(const media_source &src, + bigtime_t late, bigtime_t when); + virtual status_t GetNextOutput(int32 *cookie, media_output *output); + virtual status_t DisposeOutputCookie(int32 cookie); + virtual status_t SetBufferGroup(const media_source &src, + BBufferGroup *group); + virtual status_t PrepareToConnect(const media_source &src, + const media_destination &dst, + media_format *format, media_source *out_source, + char *name); + virtual void Connect(status_t status, + const media_source &src, + const media_destination &dst, + const media_format &format, + char* name); + virtual void Disconnect(const media_source &src, + const media_destination &dst); + virtual void EnableOutput(const media_source &src, + bool enabled, int32* _deprecated_); + virtual status_t GetLatency(bigtime_t *outLatency); + virtual void LatencyChanged( const media_source &src, + const media_destination &dst, + bigtime_t latency, uint32 flags); + //BMediaEventLooper +protected: + virtual bigtime_t OfflineTime(); + //VSTNode +protected: + virtual void HandleEvent(const media_timed_event *event, + bigtime_t late, + bool realTimeEvent=false); + void ParameterEventProcessing(const media_timed_event *event); + status_t ValidateFormat(const media_format& preferredFormat, + media_format &proposedFormat); + void SetOutputFormat(media_format& ioFormat); + virtual void InitParameterValues(); + virtual void InitParameterWeb(); + virtual void InitFilter(); + virtual bigtime_t GetFilterLatency(); + virtual void FilterBuffer(BBuffer* pBuffer); + +private: + BMediaAddOn *fAddOn; + VSTPlugin *fPlugin; + BParameterWeb *fWeb; + + media_format fPreferredFormat; + media_format fFormat; + media_input fInputMedia; + media_output fOutputMedia; + bool fOutputMediaEnabled; + bigtime_t fDownstreamLatency; + bigtime_t fProcessLatency; + + int32 fBlockSize; + int32 fMute; + int32 fByPass; + bigtime_t fMuteLastChanged; + bigtime_t fByPassLastChanged; +}; + +#endif //__VST_NODE_H__