xref: /haiku/src/add-ons/media/media-add-ons/vst_host/VSTNode.cpp (revision 02354704729d38c3b078c696adc1bbbd33cbcf72)
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
20 VSTNode::~VSTNode()
21 {
22 	Quit();
23 }
24 
25 VSTNode::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
42 BMediaAddOn*
43 VSTNode::AddOn(int32* id) const
44 {
45 	if(fAddOn)
46 		*id = 0;
47 	return fAddOn;
48 }
49 
50 status_t
51 VSTNode::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 
64 void
65 VSTNode::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
99 status_t
100 VSTNode::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 
145 void
146 VSTNode::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
161 void
162 VSTNode::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 
184 status_t
185 VSTNode::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 
200 status_t
201 VSTNode::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 
211 void
212 VSTNode::DisposeInputCookie(int32 cookie)
213 {
214 }
215 
216 status_t
217 VSTNode::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 
223 void
224 VSTNode::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 
231 status_t
232 VSTNode::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 
243 status_t
244 VSTNode::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 
262 void
263 VSTNode::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
277 status_t
278 VSTNode::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 
292 status_t
293 VSTNode::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 
308 status_t
309 VSTNode::FormatChangeRequested(const media_source &src,
310 	const media_destination &dst, media_format* format, int32* _deprecated_)
311 {
312 	return B_MEDIA_BAD_FORMAT;
313 }
314 
315 void
316 VSTNode::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 
325 status_t
326 VSTNode::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 
336 status_t
337 VSTNode::DisposeOutputCookie(int32 cookie)
338 {
339 	return B_OK;
340 }
341 
342 status_t
343 VSTNode::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 
360 status_t
361 VSTNode::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 
393 void
394 VSTNode::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 
433 void
434 VSTNode::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 
450 void
451 VSTNode::EnableOutput(const media_source &src, bool enabled, int32* _deprecated_)
452 {
453 	if (src != fOutputMedia.source)
454 		return;
455 
456 	fOutputMediaEnabled = enabled;
457 }
458 
459 status_t
460 VSTNode::GetLatency(bigtime_t* latency)
461 {
462 	*latency = EventLatency() + SchedulingLatency();
463 	return B_OK;
464 }
465 
466 void
467 VSTNode::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
483 bigtime_t
484 VSTNode::OfflineTime()
485 {
486 	return 0LL;
487 }
488 
489 //VSTNode
490 void
491 VSTNode::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 
498 void
499 VSTNode::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 
544 status_t
545 VSTNode::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 
596 void
597 VSTNode::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 
624 void
625 VSTNode::InitParameterValues()
626 {
627 	fMute = 0;
628 	fByPass = 0;
629 	fMuteLastChanged = 0LL;
630 	fByPassLastChanged = 0LL;
631 }
632 
633 void
634 VSTNode::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 
764 void
765 VSTNode::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 
774 bigtime_t
775 VSTNode::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 
800 void
801 VSTNode::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