xref: /haiku/src/add-ons/media/media-add-ons/vst_host/VSTHost.cpp (revision 21258e2674226d6aa732321b6f8494841895af5f)
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
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
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 
132 VSTParameter::~VSTParameter()
133 {
134 }
135 
136 BString*
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
177 VSTParameter::ListCount(void)
178 {
179 	return fDropList.CountItems();
180 }
181 
182 DropListValue*
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
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
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
238 VSTParameter::LastChangeTime(void)
239 {
240 	return fChanged;
241 }
242 
243 const char*
244 VSTParameter::MinimumValue(void)
245 {
246 	return fMinValue.String();
247 }
248 
249 const char*
250 VSTParameter::MaximumValue(void)
251 {
252 	return fMaxValue.String();
253 }
254 
255 const char*
256 VSTParameter::Unit(void)
257 {
258 	return fUnit.String();
259 }
260 
261 int
262 VSTParameter::Index(void)
263 {
264 	return fIndex;
265 }
266 
267 int
268 VSTParameter::Type(void)
269 {
270 	return fType;
271 }
272 
273 const char*
274 VSTParameter::Name(void)
275 {
276 	return fName.String();
277 }
278 
279 //VST Plugin class
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 
294 VSTPlugin::~VSTPlugin()
295 {
296 	fParameters.MakeEmpty();
297 	UnLoadModule();
298 }
299 
300 int
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
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
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
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
397 VSTPlugin::SampleRate(void)
398 {
399 	return fSampleRate;
400 }
401 
402 int
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*
412 VSTPlugin::Path(void)
413 {
414 	return fPath.Path();
415 }
416 
417 int
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
449 VSTPlugin::BlockSize(void)
450 {
451 	return fBlockSize;
452 }
453 
454 int
455 VSTPlugin::ParametersCount(void)
456 {
457 	return fParameters.CountItems();
458 }
459 
460 VSTParameter*
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*
472 VSTPlugin::Effect(void)
473 {
474 	return fEffect;
475 }
476 
477 const char*
478 VSTPlugin::EffectName(void)
479 {
480 	return fEffectName.String();
481 }
482 
483 const char*
484 VSTPlugin::ModuleName(void)
485 {
486 	return fModuleName.String();
487 }
488 
489 const char*
490 VSTPlugin::Vendor(void)
491 {
492 	return fVendorString.String();
493 }
494 
495 const char*
496 VSTPlugin::Product(void)
497 {
498 	return fProductString.String();
499 }
500 
501 
502 void
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
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