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 <ByteOrder.h>
8#include <Buffer.h>
9#include <BufferGroup.h>
10#include <TimeSource.h>
11#include <ParameterWeb.h>
12#include <String.h>
13
14#include <stdio.h>
15#include <string.h>
16
17#include "VSTNode.h"
18
19//VSTNode
20VSTNode::~VSTNode()
21{
22	Quit();
23}
24
25VSTNode::VSTNode(BMediaAddOn* addon, const char* name, const char* path)
26				:
27				BMediaNode(name),
28				BBufferConsumer(B_MEDIA_RAW_AUDIO),
29				BBufferProducer(B_MEDIA_RAW_AUDIO),
30				BControllable(),
31				BMediaEventLooper(),
32				fAddOn(addon),
33				fOutputMediaEnabled(true),
34				fDownstreamLatency(0),
35				fProcessLatency(0)
36{
37	fPlugin = new VSTPlugin();
38	fPlugin->LoadModule(path);
39}
40
41//BMediaNode
42BMediaAddOn*
43VSTNode::AddOn(int32* id) const
44{
45	if(fAddOn)
46		*id = 0;
47	return fAddOn;
48}
49
50status_t
51VSTNode::HandleMessage(int32 message, const void* data, size_t size)
52{
53	if((BControllable::HandleMessage(message, data, size) != B_OK) &&
54		(BBufferConsumer::HandleMessage(message, data, size) != B_OK) &&
55		(BBufferProducer::HandleMessage(message, data, size) != B_OK) &&
56		(BControllable::HandleMessage(message, data, size) != B_OK) ) {
57   			BMediaNode::HandleMessage(message, data, size);
58		return B_OK;
59	}
60	BMediaNode::HandleBadMessage(message, data, size);
61	return B_ERROR;
62}
63
64void
65VSTNode::NodeRegistered()
66{
67	fPreferredFormat.type = B_MEDIA_RAW_AUDIO;
68	fPreferredFormat.u.raw_audio.buffer_size = BUFF_SIZE;
69	fPreferredFormat.u.raw_audio = media_raw_audio_format::wildcard;
70	fPreferredFormat.u.raw_audio.channel_count =
71		media_raw_audio_format::wildcard.channel_count;
72	fPreferredFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT;
73
74	fFormat.type = B_MEDIA_RAW_AUDIO;
75	fFormat.u.raw_audio = media_raw_audio_format::wildcard;
76
77	fInputMedia.destination.port = ControlPort();
78	fInputMedia.destination.id = ID_AUDIO_INPUT;
79	fInputMedia.node = Node();
80	fInputMedia.source = media_source::null;
81	fInputMedia.format = fFormat;
82	strncpy(fInputMedia.name, "Audio Input", B_MEDIA_NAME_LENGTH);
83
84	fOutputMedia.source.port = ControlPort();
85	fOutputMedia.source.id = ID_AUDIO_OUTPUT;
86	fOutputMedia.node = Node();
87	fOutputMedia.destination = media_destination::null;
88	fOutputMedia.format = fFormat;
89	strncpy(fOutputMedia.name, "Audio Output", B_MEDIA_NAME_LENGTH);
90
91	InitParameterValues();
92	InitParameterWeb();
93
94	SetPriority(B_REAL_TIME_PRIORITY);
95	Run();
96}
97
98//BControllable
99status_t
100VSTNode::GetParameterValue(int32 id, bigtime_t* lastChangeTime, void* value,
101	size_t *size)
102{
103	if (*size < sizeof(float) || *size < sizeof(int32))
104		return B_NO_MEMORY;
105
106	type_code v_type = B_FLOAT_TYPE;
107
108	BParameter *param;
109	for(int i = 0; i < fWeb->CountParameters(); i++) {
110		param = fWeb->ParameterAt(i);
111		if(param->ID() == id) {
112			v_type = param->ValueType();
113			break;
114		}
115	}
116
117	*size = sizeof(float);
118
119	if (id == P_MUTE) {
120		*(int32*)value = fMute;
121		*lastChangeTime = fMuteLastChanged;
122		return B_OK;
123	} else if (id == P_BYPASS) {
124		*(int32*)value = fByPass;
125		*lastChangeTime = fByPassLastChanged;
126		return B_OK;
127	} else {
128		int32 idx = id - P_PARAM;
129		if (idx >= 0 && idx < fPlugin->ParametersCount()) {
130			VSTParameter *param = fPlugin->Parameter(idx);
131
132			if (v_type == B_FLOAT_TYPE)
133				*(float*)value = param->Value();
134
135			if (v_type == B_INT32_TYPE)
136				*(int32*)value = (int32)ceil(param->Value());
137
138			*lastChangeTime = param->LastChangeTime();
139			return B_OK;
140		}
141	}
142	return B_ERROR;
143}
144
145void
146VSTNode::SetParameterValue(int32 id, bigtime_t time, const void* value,
147	size_t size)
148{
149	int32 idx = id - P_PARAM;
150	if ((idx >= 0 && idx < fPlugin->ParametersCount()) || id == P_MUTE ||
151		id == P_BYPASS) {
152		media_timed_event ev(time, BTimedEventQueue::B_PARAMETER, (void*)value,
153			BTimedEventQueue::B_NO_CLEANUP, size, id, "VSTParam");
154		//dirty hack for parameter processing (mediakit bug????)
155		ParameterEventProcessing(&ev);
156		EventQueue()->AddEvent(ev);
157	}
158}
159
160//BBufferConsumer
161void
162VSTNode::BufferReceived(BBuffer* buffer)
163{
164	if (buffer->Header()->destination != fInputMedia.destination.id) {
165		buffer->Recycle();
166		return;
167	}
168
169	if (fOutputMedia.destination == media_destination::null ||
170		!fOutputMediaEnabled) {
171		buffer->Recycle();
172		return;
173	}
174
175	FilterBuffer(buffer);
176
177	status_t err = SendBuffer(buffer, fOutputMedia.source,
178		fOutputMedia.destination);
179
180	if (err < B_OK)
181		buffer->Recycle();
182}
183
184status_t
185VSTNode::AcceptFormat(const media_destination &dst, media_format* format)
186{
187	if (dst != fInputMedia.destination)
188		return B_MEDIA_BAD_DESTINATION;
189
190	if (format->type != B_MEDIA_RAW_AUDIO)
191		return B_MEDIA_BAD_FORMAT;
192
193	ValidateFormat(
194		(fFormat.u.raw_audio.format != media_raw_audio_format::wildcard.format) ?
195		fFormat : fPreferredFormat, *format);
196
197	return B_OK;
198}
199
200status_t
201VSTNode::GetNextInput(int32* cookie, media_input* input)
202{
203	if (*cookie)
204		return B_BAD_INDEX;
205
206	++*cookie;
207	*input = fInputMedia;
208	return B_OK;
209}
210
211void
212VSTNode::DisposeInputCookie(int32 cookie)
213{
214}
215
216status_t
217VSTNode::FormatChanged(const media_source &src, const media_destination &dst,
218							int32 changeTag, const media_format &format)
219{
220	return B_MEDIA_BAD_FORMAT;
221}
222
223void
224VSTNode::ProducerDataStatus(const media_destination &dst, int32 status,
225	bigtime_t when)
226{
227	if (fOutputMedia.destination != media_destination::null)
228		SendDataStatus(status, fOutputMedia.destination, when);
229}
230
231status_t
232VSTNode::GetLatencyFor( const media_destination &dst, bigtime_t* latency,
233	media_node_id* outTimeSource)
234{
235	if (dst != fInputMedia.destination)
236		return B_MEDIA_BAD_DESTINATION;
237
238	*latency = fDownstreamLatency + fProcessLatency;
239	*outTimeSource = TimeSource()->ID();
240	return B_OK;
241}
242
243status_t
244VSTNode::Connected(const media_source& source,
245	const media_destination& destination, const media_format& format,
246	media_input* input)
247{
248	if (destination != fInputMedia.destination)
249		return B_MEDIA_BAD_DESTINATION;
250
251	if (fInputMedia.source != media_source::null)
252		return B_MEDIA_ALREADY_CONNECTED;
253
254	fInputMedia.source = source;
255	fInputMedia.format = format;
256	*input = fInputMedia;
257	fFormat = format;
258
259	return B_OK;
260}
261
262void
263VSTNode::Disconnected(const media_source &src, const media_destination &dst)
264{
265	if(fInputMedia.source!=src || dst!=fInputMedia.destination)
266		return;
267
268	fInputMedia.source = media_source::null;
269
270	if(fOutputMedia.destination == media_destination::null)
271		fFormat.u.raw_audio = media_raw_audio_format::wildcard;
272
273	fInputMedia.format = fFormat;
274}
275
276//BBufferProducer
277status_t
278VSTNode::FormatSuggestionRequested(media_type type, int32 quality,
279	media_format* format)
280{
281	if (type != B_MEDIA_RAW_AUDIO)
282		return B_MEDIA_BAD_FORMAT;
283
284	if (fFormat.u.raw_audio.format != media_raw_audio_format::wildcard.format)
285		*format = fFormat;
286	else
287		*format = fPreferredFormat;
288
289	return B_OK;
290}
291
292status_t
293VSTNode::FormatProposal(const media_source &src, media_format* format)
294{
295	if (src != fOutputMedia.source)
296		return B_MEDIA_BAD_SOURCE;
297
298	if (format->type != B_MEDIA_RAW_AUDIO)
299		return B_MEDIA_BAD_FORMAT;
300
301	ValidateFormat(
302		(fFormat.u.raw_audio.format != media_raw_audio_format::wildcard.format) ?
303		fFormat : fPreferredFormat, *format);
304
305	return B_OK;
306}
307
308status_t
309VSTNode::FormatChangeRequested(const media_source &src,
310	const media_destination &dst, media_format* format, int32* _deprecated_)
311{
312	return B_MEDIA_BAD_FORMAT;
313}
314
315void
316VSTNode::LateNoticeReceived(const media_source &src,
317	bigtime_t late, bigtime_t when)
318{
319	if (src != fOutputMedia.source || fInputMedia.source == media_source::null)
320		return;
321
322	NotifyLateProducer(fInputMedia.source, late, when);
323}
324
325status_t
326VSTNode::GetNextOutput(int32 *cookie, media_output* output)
327{
328	if (*cookie)
329		return B_BAD_INDEX;
330
331	++*cookie;
332	*output = fOutputMedia;
333	return B_OK;
334}
335
336status_t
337VSTNode::DisposeOutputCookie(int32 cookie)
338{
339	return B_OK;
340}
341
342status_t
343VSTNode::SetBufferGroup(const media_source &src, BBufferGroup* group)
344{
345	status_t ret;
346	int32 changeTag;
347
348	if (src != fOutputMedia.source)
349		return B_MEDIA_BAD_SOURCE;
350
351	if (fInputMedia.source == media_source::null)
352		return B_ERROR;
353
354	ret = SetOutputBuffersFor(fInputMedia.source, fInputMedia.destination,
355		group, 0, &changeTag);
356
357	return ret;
358}
359
360status_t
361VSTNode::PrepareToConnect( const media_source &src, const media_destination &dst,
362	media_format* format, media_source* out_source, char* name)
363{
364	status_t ret = B_OK;
365
366	if (src != fOutputMedia.source)
367		return B_MEDIA_BAD_SOURCE;
368
369	if (format->type != B_MEDIA_RAW_AUDIO)
370		return B_MEDIA_BAD_FORMAT;
371
372	if (fOutputMedia.destination != media_destination::null)
373		return B_MEDIA_ALREADY_CONNECTED;
374
375	ret = ValidateFormat(
376		(fFormat.u.raw_audio.format != media_raw_audio_format::wildcard.format) ?
377		fFormat : fPreferredFormat, *format);
378
379	if (ret < B_OK)
380		return ret;
381
382	SetOutputFormat(*format);
383
384	fOutputMedia.destination = dst;
385	fOutputMedia.format = *format;
386
387	*out_source = fOutputMedia.source;
388	strncpy(name, fOutputMedia.name, B_MEDIA_NAME_LENGTH);
389
390	return B_OK;
391}
392
393void
394VSTNode::Connect(status_t status, const media_source &src,
395	const media_destination &dst, const media_format &format, char* name)
396{
397	if (status < B_OK) {
398		fOutputMedia.destination = media_destination::null;
399		return;
400	}
401
402	strncpy(name, fOutputMedia.name, B_MEDIA_NAME_LENGTH);
403	fOutputMedia.destination = dst;
404	fFormat = format;
405
406	media_node_id timeSource;
407	FindLatencyFor(fOutputMedia.destination, &fDownstreamLatency, &timeSource);
408
409	InitFilter();
410
411	fProcessLatency = GetFilterLatency();
412	SetEventLatency(fDownstreamLatency + fProcessLatency);
413
414	if (fInputMedia.source != media_source::null) {
415		SendLatencyChange(fInputMedia.source, fInputMedia.destination,
416							EventLatency()+SchedulingLatency());
417	}
418
419	bigtime_t duration = 0;
420
421	int sample_size = (fFormat.u.raw_audio.format & 0xf)*
422						fFormat.u.raw_audio.channel_count;
423	if (fFormat.u.raw_audio.buffer_size > 0 &&
424		fFormat.u.raw_audio.frame_rate > 0 &&
425		sample_size > 0) {
426		duration = (bigtime_t)(((fFormat.u.raw_audio.buffer_size / sample_size) /
427			fFormat.u.raw_audio.frame_rate) * 1000000.0);
428	}
429
430	SetBufferDuration(duration);
431}
432
433void
434VSTNode::Disconnect(const media_source &src, const media_destination &dst)
435{
436	if (src != fOutputMedia.source)
437		return;
438
439	if (dst != fOutputMedia.destination)
440		return;
441
442	fOutputMedia.destination = media_destination::null;
443
444	if (fInputMedia.source == media_source::null)
445		fFormat.u.raw_audio = media_raw_audio_format::wildcard;
446
447	fOutputMedia.format = fFormat;
448}
449
450void
451VSTNode::EnableOutput(const media_source &src, bool enabled, int32* _deprecated_)
452{
453	if (src != fOutputMedia.source)
454		return;
455
456	fOutputMediaEnabled = enabled;
457}
458
459status_t
460VSTNode::GetLatency(bigtime_t* latency)
461{
462	*latency = EventLatency() + SchedulingLatency();
463	return B_OK;
464}
465
466void
467VSTNode::LatencyChanged(const media_source &src, const media_destination &dst,
468	bigtime_t latency, uint32 flags)
469{
470	if (src != fOutputMedia.source || dst != fOutputMedia.destination)
471		return;
472
473	fDownstreamLatency = latency;
474	SetEventLatency(fDownstreamLatency + fProcessLatency);
475
476	if (fInputMedia.source != media_source::null) {
477		SendLatencyChange(fInputMedia.source,
478			fInputMedia.destination,EventLatency() + SchedulingLatency());
479	}
480}
481
482//BMediaEventLooper
483bigtime_t
484VSTNode::OfflineTime()
485{
486	return 0LL;
487}
488
489//VSTNode
490void
491VSTNode::HandleEvent(const media_timed_event *event, bigtime_t late,
492	bool realTime)
493{
494	if(event->type == BTimedEventQueue::B_PARAMETER)
495		ParameterEventProcessing(event);
496}
497
498void
499VSTNode::ParameterEventProcessing(const media_timed_event* event)
500{
501	float value = 0.0;
502	int32 value32 = 0;
503
504	int32 id = event->bigdata;
505	size_t size = event->data;
506	bigtime_t now = TimeSource()->Now();
507
508	type_code v_type = B_FLOAT_TYPE;
509
510	BParameter* web_param;
511	for(int i = 0; i < fWeb->CountParameters(); i++) {
512		web_param = fWeb->ParameterAt(i);
513		if(web_param->ID() == id) {
514			v_type = web_param->ValueType();
515			break;
516		}
517	}
518
519	if (v_type == B_FLOAT_TYPE)
520		value = *((float*)event->pointer);
521	if (v_type == B_INT32_TYPE) {
522		value32 = *((int32*)event->pointer);
523		value = (float)value32;
524	}
525
526	if (id == P_MUTE) {
527		fMute = value32;
528		fMuteLastChanged = now;
529		BroadcastNewParameterValue(now,	id,	event->pointer, size);
530	} else if (id == P_BYPASS) {
531		fByPass = value32;
532		fByPassLastChanged = now;
533		BroadcastNewParameterValue(now,	id,	event->pointer, size);
534	} else {
535		int32 idx = id - P_PARAM;
536		if (idx >= 0 && idx < fPlugin->ParametersCount()) {
537			VSTParameter *param = fPlugin->Parameter(idx);
538			param->SetValue(value);
539			BroadcastNewParameterValue(now,	id,	&value,	size);
540		}
541	}
542}
543
544status_t
545VSTNode::ValidateFormat(const media_format &preferredFormat,
546							media_format &proposedFormat)
547{
548	status_t ret = B_OK;
549
550	if (proposedFormat.type != B_MEDIA_RAW_AUDIO) {
551		proposedFormat = preferredFormat;
552		return B_MEDIA_BAD_FORMAT;
553	}
554
555	const media_raw_audio_format &wild = media_raw_audio_format::wildcard;
556	media_raw_audio_format &f = proposedFormat.u.raw_audio;
557	const media_raw_audio_format &pref = preferredFormat.u.raw_audio;
558
559	if(pref.frame_rate != wild.frame_rate && f.frame_rate != pref.frame_rate) {
560		if(f.frame_rate != wild.frame_rate)
561			ret = B_MEDIA_BAD_FORMAT;
562		f.frame_rate = pref.frame_rate;
563	}
564
565	if(pref.channel_count != wild.channel_count &&
566		f.channel_count != pref.channel_count) {
567		if(f.channel_count != wild.channel_count)
568			ret = B_MEDIA_BAD_FORMAT;
569		f.channel_count = pref.channel_count;
570	}
571
572	if(pref.format != wild.format && f.format != pref.format) {
573		if(f.format != wild.format)
574			ret = B_MEDIA_BAD_FORMAT;
575		f.format = pref.format;
576	}
577
578	if(pref.byte_order != wild.byte_order &&
579		f.byte_order != pref.byte_order) {
580		if(f.byte_order != wild.byte_order)
581			ret = B_MEDIA_BAD_FORMAT;
582		f.byte_order = pref.byte_order;
583	}
584
585	if(pref.buffer_size != wild.buffer_size &&
586		f.buffer_size != pref.buffer_size) {
587		if(f.buffer_size != wild.buffer_size)
588			ret = B_MEDIA_BAD_FORMAT;
589		f.buffer_size = pref.buffer_size;
590	}
591
592	return ret;
593}
594
595
596void
597VSTNode::SetOutputFormat(media_format &format)
598{
599	media_raw_audio_format &f = format.u.raw_audio;
600	const media_raw_audio_format &w = media_raw_audio_format::wildcard;
601
602	if (f.frame_rate == w.frame_rate)
603		f.frame_rate = 44100.0;
604
605	if (f.channel_count == w.channel_count) {
606		if(fInputMedia.source != media_source::null)
607			f.channel_count = fInputMedia.format.u.raw_audio.channel_count;
608		else
609			f.channel_count = fPlugin->Channels(VST_OUTPUT_CHANNELS);
610	}
611
612	if (f.format == w.format)
613		f.format = media_raw_audio_format::B_AUDIO_FLOAT;
614
615	if (f.byte_order == w.format) {
616		f.byte_order = (B_HOST_IS_BENDIAN) ?
617			B_MEDIA_BIG_ENDIAN : B_MEDIA_LITTLE_ENDIAN;
618	}
619
620	if (f.buffer_size == w.buffer_size)
621		f.buffer_size = BUFF_SIZE;
622}
623
624void
625VSTNode::InitParameterValues()
626{
627	fMute = 0;
628	fByPass = 0;
629	fMuteLastChanged = 0LL;
630	fByPassLastChanged = 0LL;
631}
632
633void
634VSTNode::InitParameterWeb()
635{
636	fWeb = new BParameterWeb();
637
638	bool switch_group_needed = false;
639	for(int i = 0; i < fPlugin->ParametersCount(); i++) {
640		VSTParameter* param = fPlugin->Parameter(i);
641		if (param->Type() == VST_PARAM_CHECKBOX ||
642		   param->Type() == VST_PARAM_DROPLIST) {
643		   switch_group_needed = true;
644		   break;
645		}
646	}
647
648	BParameterGroup* fParamGroup = fWeb->MakeGroup("Parameters");
649	BParameterGroup* fSwitchesGroup = switch_group_needed ?
650		fWeb->MakeGroup("Switches") : NULL;
651	BParameterGroup* fAboutGroup = fWeb->MakeGroup("About");
652
653	BParameter* value;
654	BNullParameter* label;
655	BParameterGroup* group;
656
657	BParameterGroup* fFControlGroup = fParamGroup->MakeGroup("FilterControl");
658	BParameterGroup* fCheckBoxGroup = switch_group_needed ?
659		fSwitchesGroup->MakeGroup("CheckBoxes") : NULL;
660	BParameterGroup* fSelectorsGroup = switch_group_needed ?
661		fSwitchesGroup->MakeGroup("Selectors") : NULL;
662
663	fFControlGroup->MakeDiscreteParameter(P_MUTE,
664		B_MEDIA_NO_TYPE,"Mute", B_ENABLE);
665	fFControlGroup->MakeDiscreteParameter(P_BYPASS,
666		B_MEDIA_NO_TYPE,"ByPass", B_ENABLE);
667
668	for(int i = 0; i < fPlugin->ParametersCount(); i++) {
669		VSTParameter *param = fPlugin->Parameter(i);
670		switch(param->Type()) {
671			case VST_PARAM_CHECKBOX:
672			{
673				BString str;
674				str << param->Name() << " (" << param->MinimumValue()
675					<< "/" << param->MaximumValue() << ")";
676				value = fCheckBoxGroup->MakeDiscreteParameter(
677					P_PARAM + param->Index(), B_MEDIA_NO_TYPE,
678					str.String(), B_ENABLE);
679				break;
680 			}
681			case VST_PARAM_DROPLIST:
682			{
683				BDiscreteParameter *dvalue =
684					fSelectorsGroup->MakeDiscreteParameter(P_PARAM + param->Index(),
685						B_MEDIA_NO_TYPE, param->Name(), B_OUTPUT_MUX);
686				for(int j = 0; j < param->ListCount(); j++) {
687					dvalue->AddItem( param->ListItemAt(j)->Index,
688						param->ListItemAt(j)->Name.String());
689				}
690				break;
691			}
692			//sliders
693			default:
694			{
695				BString str;
696				group = fParamGroup->MakeGroup(param->Name());
697				label = group->MakeNullParameter(P_LABEL + param->Index(),
698								B_MEDIA_NO_TYPE, param->Name(), B_GENERIC);
699
700				str.SetTo(param->MaximumValue());
701				str << " " << param->Unit();
702
703				group->MakeNullParameter(P_LABEL2 + param->Index(),
704								B_MEDIA_NO_TYPE, str.String(), B_GENERIC);
705				value = group->MakeContinuousParameter(P_PARAM + param->Index(),
706								B_MEDIA_NO_TYPE, "", B_GAIN, "", 0.0, 1.0, 0.01);
707				label->AddOutput(value);
708				value->AddInput(label);
709
710				str.SetTo(param->MinimumValue());
711				str << " " << param->Unit();
712
713				group->MakeNullParameter(P_LABEL3 + param->Index(),
714								B_MEDIA_NO_TYPE, str.String(), B_GENERIC);
715				break;
716			}
717		}
718	}
719
720	BString str("About plugin");
721	label = fAboutGroup->MakeNullParameter(P_ABOUT + 0,
722						B_MEDIA_NO_TYPE, str.String(), B_GENERIC);
723
724	str.SetTo("Effect name: ");
725	if (strlen(fPlugin->EffectName()) != 0)
726		str.Append(fPlugin->EffectName());
727	else
728		str.Append("not specified");
729
730	label = fAboutGroup->MakeNullParameter(P_ABOUT + 1,
731						B_MEDIA_NO_TYPE, str.String(), B_GENERIC);
732
733	str.SetTo("Vendor: ");
734	if (strlen(fPlugin->Vendor()) != 0)
735		str.Append(fPlugin->Vendor());
736	else
737		str.Append("not specified");
738
739	label = fAboutGroup->MakeNullParameter(P_ABOUT + 2,
740						B_MEDIA_NO_TYPE, str.String(), B_GENERIC);
741
742	str.SetTo("Product: ");
743	if (strlen(fPlugin->Product()) != 0)
744		str.Append(fPlugin->Product());
745	else
746		str.Append("not specified");
747
748	label = fAboutGroup->MakeNullParameter(P_ABOUT + 3,
749						B_MEDIA_NO_TYPE, str.String(), B_GENERIC);
750
751	str.SetTo("Input Channels: ");
752	str<<fPlugin->Channels(VST_INPUT_CHANNELS);
753	label = fAboutGroup->MakeNullParameter(P_ABOUT + 4,
754						B_MEDIA_NO_TYPE, str.String(), B_GENERIC);
755
756	str.SetTo("Output Channels: ");
757	str<<fPlugin->Channels(VST_OUTPUT_CHANNELS);
758	label = fAboutGroup->MakeNullParameter(P_ABOUT + 5,
759						B_MEDIA_NO_TYPE, str.String(), B_GENERIC);
760
761	SetParameterWeb(fWeb);
762}
763
764void
765VSTNode::InitFilter()
766{
767	fBlockSize = fFormat.u.raw_audio.buffer_size /
768				(fFormat.u.raw_audio.channel_count * sizeof(float));
769
770	fPlugin->SetBlockSize(fBlockSize);
771	fPlugin->SetSampleRate(fFormat.u.raw_audio.frame_rate);
772}
773
774bigtime_t
775VSTNode::GetFilterLatency()
776{
777	if (fOutputMedia.destination == media_destination::null)
778		return 0LL;
779
780	BBufferGroup* temp_group =
781		new BBufferGroup(fOutputMedia.format.u.raw_audio.buffer_size, 1);
782
783	BBuffer *buffer =
784		temp_group->RequestBuffer(fOutputMedia.format.u.raw_audio.buffer_size);
785	buffer->Header()->type = B_MEDIA_RAW_AUDIO;
786	buffer->Header()->size_used = fOutputMedia.format.u.raw_audio.buffer_size;
787
788	bigtime_t begin = system_time();
789	FilterBuffer(buffer);
790	bigtime_t latency = system_time()-begin;
791
792	InitFilter();
793
794	buffer->Recycle();
795	delete temp_group;
796
797	return latency;
798}
799
800void
801VSTNode::FilterBuffer(BBuffer* buffer)
802{
803	uint32 m_frameSize = (fFormat.u.raw_audio.format & 0x0f)*
804				 fFormat.u.raw_audio.channel_count;
805	uint32 samples = buffer->Header()->size_used / m_frameSize;
806	uint32 channels = fFormat.u.raw_audio.channel_count;
807
808	if (fMute != 0) {
809		memset(buffer->Data(), 0, buffer->Header()->size_used);
810	} else {
811		if (fByPass == 0) {
812			fPlugin->Process((float*)buffer->Data(), samples, channels);
813		}
814	}
815}
816