1 /*
2 * Copyright 2012, Gerasim Troeglazov (3dEyes**), 3dEyes@gmail.com.
3 * All rights reserved.
4 * Distributed under the terms of the MIT License.
5 */
6
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <image.h>
10
11 #include <Application.h>
12
13 #include "VSTHost.h"
14
15 static int32 VHostCallback(VSTEffect* effect, int32 opcode, int32 index,
16 int32 value, void* ptr, float opt);
17
18 //Trim string
19 static void
TrimString(BString * string)20 TrimString(BString *string) {
21 char* str = string->LockBuffer(256);
22 uint32 k = 0;
23 uint32 i = 0;
24 for(i=0; str[i]!='\0';) {
25 if (isspace(str[i])) {
26 k = i;
27 for(uint32 j = i; j < strlen(str) - 1; j++)
28 str[j] = str[j + 1];
29 str[strlen(str) - 1] = '\0';
30 i = k;
31 } else
32 i++;
33 }
34 string->UnlockBuffer();
35 }
36
37 //VST Parameter class
VSTParameter(VSTPlugin * plugin,int index)38 VSTParameter::VSTParameter(VSTPlugin* plugin, int index)
39 {
40 fIndex = index;
41 fEffect = plugin->Effect();
42 fDropList.MakeEmpty();
43
44 char temp[256];
45 //get parameter name
46 temp[0] = 0;
47 fEffect->dispatcher(fEffect, VST_GET_PARAM_NAME, index, 0, temp, 0);
48 fName.SetTo(temp);
49 TrimString(&fName);
50 //get parameter label (unit)
51 temp[0] = 0;
52 fEffect->dispatcher(fEffect, VST_GET_PARAM_UNIT, index, 0, temp, 0);
53 fUnit.SetTo(temp);
54 ValidateValues(&fUnit);
55 //store current value
56 float val = fEffect->getParameter(fEffect, index);
57 //test for minimum value
58 fEffect->setParameter(fEffect, index, 0);
59 temp[0] = 0;
60 fEffect->dispatcher(fEffect, VST_GET_PARAM_STR, index, 0, temp, 0);
61 fMinValue.SetTo(temp);
62 ValidateValues(&fMinValue);
63 //test for maximum value
64 temp[0] = 0;
65 fEffect->setParameter(fEffect, index, 1.0);
66 fEffect->dispatcher(fEffect, VST_GET_PARAM_STR, index, 0, temp, 0);
67 fMaxValue.SetTo(temp);
68 ValidateValues(&fMaxValue);
69 //test for discrete values
70 char test_disp[VST_PARAM_TEST_COUNT][256];
71 float test_values[VST_PARAM_TEST_COUNT];
72 float delta = 1.0 / (float)VST_PARAM_TEST_COUNT;
73 int test_cnt = 0;
74 for(int tst_val = 0; tst_val < VST_PARAM_TEST_COUNT; tst_val++) {
75 float v = (float)tst_val / (float)VST_PARAM_TEST_COUNT;
76
77 if (tst_val >= VST_PARAM_TEST_COUNT - 1)
78 v = 1.0;
79
80 fEffect->setParameter(fEffect, index, v);
81
82 float new_value = fEffect->getParameter(fEffect, index);
83 bool valtest = false;
84 for(int i = 0; i < test_cnt; i++) {
85 if (fabs(test_values[i] - new_value) < delta) {
86 valtest = true;
87 break;
88 }
89 }
90 if (valtest == false) {
91 test_values[test_cnt] = new_value;
92 fEffect->dispatcher(fEffect, VST_GET_PARAM_STR, index,
93 0, test_disp[test_cnt], 0);
94 test_cnt++;
95 }
96 }
97
98 //restore value
99 fEffect->setParameter(fEffect, index, val);
100
101 //detect param type
102 if (test_cnt == 2) {
103 fType = VST_PARAM_CHECKBOX;
104
105 DropListValue* min_item = new DropListValue();
106 min_item->Value = 0.0;
107 min_item->Index = 0;
108 min_item->Name = fMinValue;
109 fDropList.AddItem(min_item);
110
111 DropListValue* max_item = new DropListValue();
112 max_item->Value = 1.0;
113 max_item->Index = 1;
114 max_item->Name = fMaxValue;
115 fDropList.AddItem(max_item);
116 } else if (test_cnt > 2 && test_cnt < VST_PARAM_TEST_COUNT / 2) {
117 fType = VST_PARAM_DROPLIST;
118
119 for(int i = 0; i < test_cnt; i++) {
120 DropListValue* item = new DropListValue();
121 item->Value = test_values[i];
122 item->Index = i;
123 item->Name = test_disp[i];
124 fDropList.AddItem(item);
125 }
126 } else {
127 fType = VST_PARAM_SLIDER;
128 }
129 fChanged = 0LL;
130 }
131
~VSTParameter()132 VSTParameter::~VSTParameter()
133 {
134 }
135
136 BString*
ValidateValues(BString * string)137 VSTParameter::ValidateValues(BString* string)
138 {
139 if (string->Length() == 0)
140 return string;
141
142 bool isNum = true;
143
144 const char *ptr = string->String();
145 for(; *ptr!=0; ptr++) {
146 char ch = *ptr;
147 if (!((ch >= '0' && ch <= '9') || ch == '.' || ch == '-')) {
148 isNum = false;
149 break;
150 }
151 }
152
153 if (isNum) {
154 float val = atof(string->String());
155
156 if (val <= -pow(2, 31)) {
157 string->SetTo("-∞");
158 } else if (val >= pow(2, 31)) {
159 string->SetTo("∞");
160 } else {
161 char temp[256];
162 sprintf(temp, "%g", val);
163 string->SetTo(temp);
164 }
165 } else {
166 TrimString(string);
167 if (*string == "oo" || *string == "inf")
168 string->SetTo("∞");
169 if (*string == "-oo" || *string == "-inf")
170 string->SetTo("-∞");
171
172 }
173 return string;
174 }
175
176 int
ListCount(void)177 VSTParameter::ListCount(void)
178 {
179 return fDropList.CountItems();
180 }
181
182 DropListValue*
ListItemAt(int index)183 VSTParameter::ListItemAt(int index)
184 {
185 DropListValue* item = NULL;
186 if (index >= 0 && index < fDropList.CountItems())
187 item = (DropListValue*)fDropList.ItemAt(index);
188 return item;
189 }
190
191
192 float
Value()193 VSTParameter::Value()
194 {
195 float value = fEffect->getParameter(fEffect, fIndex);
196 if (fType == VST_PARAM_DROPLIST) {
197 //scan for near value
198 int min_index = 0;
199 float min_delta = 1.0;
200 for(int i = 0; i < fDropList.CountItems(); i++) {
201 DropListValue* item = (DropListValue*)fDropList.ItemAt(i);
202 float delta = fabs(item->Value - value);
203 if (delta <= min_delta) {
204 min_delta = delta;
205 min_index = i;
206 }
207 }
208 value = min_index;
209 }
210 fLastValue = value;
211 return value;
212 }
213
214 void
SetValue(float value)215 VSTParameter::SetValue(float value)
216 {
217 if (value == fLastValue)
218 return;
219
220 if (fType == VST_PARAM_DROPLIST) {
221 //take value by index
222 int index = (int)vstround(value);
223 if (index >= 0 && index < fDropList.CountItems()) {
224 DropListValue *item = (DropListValue*)fDropList.ItemAt(index);
225 value = item->Value;
226 fLastValue = index;
227 } else {
228 return;
229 }
230 } else {
231 fLastValue = value;
232 }
233 fChanged = system_time();
234 fEffect->setParameter(fEffect, fIndex, value);
235 }
236
237 bigtime_t
LastChangeTime(void)238 VSTParameter::LastChangeTime(void)
239 {
240 return fChanged;
241 }
242
243 const char*
MinimumValue(void)244 VSTParameter::MinimumValue(void)
245 {
246 return fMinValue.String();
247 }
248
249 const char*
MaximumValue(void)250 VSTParameter::MaximumValue(void)
251 {
252 return fMaxValue.String();
253 }
254
255 const char*
Unit(void)256 VSTParameter::Unit(void)
257 {
258 return fUnit.String();
259 }
260
261 int
Index(void)262 VSTParameter::Index(void)
263 {
264 return fIndex;
265 }
266
267 int
Type(void)268 VSTParameter::Type(void)
269 {
270 return fType;
271 }
272
273 const char*
Name(void)274 VSTParameter::Name(void)
275 {
276 return fName.String();
277 }
278
279 //VST Plugin class
VSTPlugin()280 VSTPlugin::VSTPlugin()
281 {
282 fActive = false;
283 fEffect = NULL;
284 VSTMainProc = NULL;
285 fInputChannels = 0;
286 fOutputChannels = 0;
287 fSampleRate = 44100.f;
288 fBlockSize = 0;
289 inputs = NULL;
290 outputs = NULL;
291 fParameters.MakeEmpty();
292 }
293
~VSTPlugin()294 VSTPlugin::~VSTPlugin()
295 {
296 fParameters.MakeEmpty();
297 UnLoadModule();
298 }
299
300 int
LoadModule(const char * path)301 VSTPlugin::LoadModule(const char *path)
302 {
303 char effectName[256] = {0};
304 char vendorString[256] = {0};
305 char productString[256] = {0};
306
307 if (fActive)
308 return VST_ERR_ALREADY_LOADED;
309
310 fPath = BPath(path);
311
312 fModule = load_add_on(path);
313 if (fModule <= 0)
314 return VST_ERR_NOT_LOADED;
315
316 if (get_image_symbol(fModule, "main_plugin", B_SYMBOL_TYPE_TEXT,
317 (void**)&VSTMainProc) != B_OK) {
318 unload_add_on(fModule);
319 return VST_ERR_NO_MAINPROC;
320 }
321
322 fEffect = VSTMainProc(VHostCallback);
323 if (fEffect==NULL) {
324 unload_add_on(fModule);
325 return VST_ERR_NOT_LOADED;
326 }
327
328 fEffect->dispatcher(fEffect, VST_OPEN, 0, 0, 0, 0);
329
330 fEffect->dispatcher(fEffect, VST_GET_EFFECT_NAME, 0, 0, effectName, 0);
331 fEffectName.SetTo(effectName);
332 TrimString(&fEffectName);
333
334 fModuleName.SetTo("VST:");
335 fModuleName.Append(fPath.Leaf());
336
337 fEffect->dispatcher(fEffect, VST_GET_VENDOR_STR, 0, 0, vendorString, 0);
338 fVendorString.SetTo(vendorString);
339 TrimString(&fVendorString);
340
341 fEffect->dispatcher(fEffect, VST_GET_PRODUCT_STR, 0, 0, productString, 0);
342 fProductString.SetTo(productString);
343 TrimString(&fProductString);
344
345 fInputChannels = fEffect->numInputs;
346 fOutputChannels = fEffect->numOutputs;
347
348 for(int i=0; i < fEffect->numParams; i++) {
349 VSTParameter *param = new VSTParameter(this, i);
350 fParameters.AddItem(param);
351 }
352
353 fEffect->dispatcher(fEffect, VST_STATE_CHANGED, 0, 1, 0, 0);
354
355 ReAllocBuffers();
356
357 fActive = true;
358 return B_OK;
359 }
360
361 int
UnLoadModule(void)362 VSTPlugin::UnLoadModule(void)
363 {
364 if (!fActive || fModule <= 0)
365 return VST_ERR_NOT_LOADED;
366
367 fEffect->dispatcher(fEffect, VST_STATE_CHANGED, 0, 0, 0, 0);
368 fEffect->dispatcher(fEffect, VST_CLOSE, 0, 0, 0, 0);
369
370 unload_add_on(fModule);
371
372 return B_OK;
373 }
374
375 int
Channels(int mode)376 VSTPlugin::Channels(int mode)
377 {
378 switch(mode) {
379 case VST_INPUT_CHANNELS:
380 return fInputChannels;
381 case VST_OUTPUT_CHANNELS:
382 return fOutputChannels;
383 default:
384 return 0;
385 }
386 }
387
388 int
SetSampleRate(float rate)389 VSTPlugin::SetSampleRate(float rate)
390 {
391 fSampleRate = rate;
392 fEffect->dispatcher(fEffect, VST_SET_SAMPLE_RATE, 0, 0, 0, rate);
393 return B_OK;
394 }
395
396 float
SampleRate(void)397 VSTPlugin::SampleRate(void)
398 {
399 return fSampleRate;
400 }
401
402 int
SetBlockSize(size_t size)403 VSTPlugin::SetBlockSize(size_t size)
404 {
405 fBlockSize = size;
406 fEffect->dispatcher(fEffect, VST_SET_BLOCK_SIZE, 0, size, 0, 0);
407 ReAllocBuffers();
408 return B_OK;
409 }
410
411 const char*
Path(void)412 VSTPlugin::Path(void)
413 {
414 return fPath.Path();
415 }
416
417 int
ReAllocBuffers(void)418 VSTPlugin::ReAllocBuffers(void)
419 {
420 if (inputs != NULL) {
421 for(int32 i = 0; i < fInputChannels; i++)
422 delete inputs[i];
423 }
424
425 if (outputs != NULL) {
426 for(int32 i = 0; i < fOutputChannels; i++)
427 delete outputs[i];
428 }
429
430 if (fInputChannels > 0) {
431 inputs = new float*[fInputChannels];
432 for(int32 i = 0; i < fInputChannels; i++) {
433 inputs[i] = new float[fBlockSize];
434 memset(inputs[i], 0, fBlockSize * sizeof(float));
435 }
436 }
437
438 if (fOutputChannels > 0) {
439 outputs = new float*[fOutputChannels];
440 for(int32_t i = 0; i < fOutputChannels; i++) {
441 outputs[i] = new float[fBlockSize];
442 memset (outputs[i], 0, fBlockSize * sizeof(float));
443 }
444 }
445 return B_OK;
446 }
447
448 size_t
BlockSize(void)449 VSTPlugin::BlockSize(void)
450 {
451 return fBlockSize;
452 }
453
454 int
ParametersCount(void)455 VSTPlugin::ParametersCount(void)
456 {
457 return fParameters.CountItems();
458 }
459
460 VSTParameter*
Parameter(int index)461 VSTPlugin::Parameter(int index)
462 {
463 VSTParameter* param = NULL;
464
465 if (index >= 0 && index < fParameters.CountItems())
466 param = (VSTParameter*)fParameters.ItemAt(index);
467
468 return param;
469 }
470
471 VSTEffect*
Effect(void)472 VSTPlugin::Effect(void)
473 {
474 return fEffect;
475 }
476
477 const char*
EffectName(void)478 VSTPlugin::EffectName(void)
479 {
480 return fEffectName.String();
481 }
482
483 const char*
ModuleName(void)484 VSTPlugin::ModuleName(void)
485 {
486 return fModuleName.String();
487 }
488
489 const char*
Vendor(void)490 VSTPlugin::Vendor(void)
491 {
492 return fVendorString.String();
493 }
494
495 const char*
Product(void)496 VSTPlugin::Product(void)
497 {
498 return fProductString.String();
499 }
500
501
502 void
Process(float * buffer,int samples,int channels)503 VSTPlugin::Process(float *buffer, int samples, int channels)
504 {
505 //todo: full channels remapping needed
506 float* src = buffer;
507
508 if (channels == fInputChannels) { //channel to channel
509 for(int j = 0; j < samples; j++) {
510 for(int c = 0; c < fInputChannels; c++)
511 inputs[c][j] = *src++;
512 }
513 } else if ( channels == 1) { //from mone to multichannel
514 for(int j = 0; j < samples; j++, src++) {
515 for(int c = 0; c < fInputChannels; c++)
516 inputs[c][j] = *src;
517 }
518 }
519
520 fEffect->processReplacing(fEffect, inputs, outputs, fBlockSize);
521
522 float* dst = buffer;
523
524 if (channels == fOutputChannels) { //channel to channel
525 for(int j = 0; j < samples; j++) {
526 for(int c = 0; c < fOutputChannels; c++)
527 *dst++ = outputs[c][j];
528 }
529 } else if (channels == 1) { //from multichannel to mono
530 for(int j = 0; j < samples; j++, dst++) {
531 float mix = 0;
532 for(int c = 0; c < fOutputChannels; c++)
533 mix += outputs[c][j];
534 *dst = mix / (float)fOutputChannels;
535 }
536 }
537 }
538
539 static int32
VHostCallback(VSTEffect * effect,int32 opcode,int32 index,int32 value,void * ptr,float opt)540 VHostCallback(VSTEffect* effect, int32 opcode, int32 index, int32 value,
541 void* ptr, float opt)
542 {
543 intptr_t result = 0;
544
545 switch(opcode)
546 {
547 case VST_MASTER_PRODUCT:
548 if (ptr) {
549 strcpy((char*)ptr, "VSTHost Media AddOn");
550 result = 1;
551 }
552 break;
553 case VST_MASTER_VERSION :
554 result = 2300;
555 break;
556 }
557
558 return result;
559 }
560