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
15static int32 VHostCallback(VSTEffect* effect, int32 opcode, int32 index,
16	int32 value, void* ptr, float opt);
17
18//Trim string
19static void
20TrimString(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
38VSTParameter::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
132VSTParameter::~VSTParameter()
133{
134}
135
136BString*
137VSTParameter::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
176int
177VSTParameter::ListCount(void)
178{
179	return fDropList.CountItems();
180}
181
182DropListValue*
183VSTParameter::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
192float
193VSTParameter::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
214void
215VSTParameter::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
237bigtime_t
238VSTParameter::LastChangeTime(void)
239{
240	return fChanged;
241}
242
243const char*
244VSTParameter::MinimumValue(void)
245{
246	return fMinValue.String();
247}
248
249const char*
250VSTParameter::MaximumValue(void)
251{
252	return fMaxValue.String();
253}
254
255const char*
256VSTParameter::Unit(void)
257{
258	return fUnit.String();
259}
260
261int
262VSTParameter::Index(void)
263{
264	return fIndex;
265}
266
267int
268VSTParameter::Type(void)
269{
270	return fType;
271}
272
273const char*
274VSTParameter::Name(void)
275{
276	return fName.String();
277}
278
279//VST Plugin class
280VSTPlugin::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
294VSTPlugin::~VSTPlugin()
295{
296	fParameters.MakeEmpty();
297	UnLoadModule();
298}
299
300int
301VSTPlugin::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
361int
362VSTPlugin::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
375int
376VSTPlugin::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
388int
389VSTPlugin::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
396float
397VSTPlugin::SampleRate(void)
398{
399	return fSampleRate;
400}
401
402int
403VSTPlugin::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
411const char*
412VSTPlugin::Path(void)
413{
414	return fPath.Path();
415}
416
417int
418VSTPlugin::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
448size_t
449VSTPlugin::BlockSize(void)
450{
451	return fBlockSize;
452}
453
454int
455VSTPlugin::ParametersCount(void)
456{
457	return fParameters.CountItems();
458}
459
460VSTParameter*
461VSTPlugin::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
471VSTEffect*
472VSTPlugin::Effect(void)
473{
474	return fEffect;
475}
476
477const char*
478VSTPlugin::EffectName(void)
479{
480	return fEffectName.String();
481}
482
483const char*
484VSTPlugin::ModuleName(void)
485{
486	return fModuleName.String();
487}
488
489const char*
490VSTPlugin::Vendor(void)
491{
492	return fVendorString.String();
493}
494
495const char*
496VSTPlugin::Product(void)
497{
498	return fProductString.String();
499}
500
501
502void
503VSTPlugin::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
539static int32
540VHostCallback(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