xref: /haiku/src/apps/cortex/addons/Flanger/FlangerNode.cpp (revision 7ba8de3cac46b202dd8c8034902b82bc6d4ac8de)
1 /*
2  * Copyright (c) 1999-2000, Eric Moon.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions, and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions, and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * 3. The name of the author may not be used to endorse or promote products
17  *    derived from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21  * OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
27  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 
32 // FlangerNode.cpp
33 // e.moon 16jun99
34 
35 #include "FlangerNode.h"
36 
37 #include "AudioBuffer.h"
38 #include "SoundUtils.h"
39 
40 #include <Buffer.h>
41 #include <BufferGroup.h>
42 #include <ByteOrder.h>
43 #include <Catalog.h>
44 #include <Debug.h>
45 #include <ParameterWeb.h>
46 #include <TimeSource.h>
47 
48 #include <cstdio>
49 #include <cstdlib>
50 #include <cstring>
51 #include <cmath>
52 
53 #undef B_TRANSLATION_CONTEXT
54 #define B_TRANSLATION_CONTEXT "CortexAddOnsFlanger"
55 
56 // -------------------------------------------------------- //
57 // local helpers
58 // -------------------------------------------------------- //
59 
60 float calc_sweep_delta(
61 	const media_raw_audio_format& format,
62 	float fRate);
63 
64 float calc_sweep_base(
65 	const media_raw_audio_format& format,
66 	float fDelay, float fDepth);
67 
68 float calc_sweep_factor(
69 	const media_raw_audio_format& format,
70 	float fDepth);
71 
72 // -------------------------------------------------------- //
73 // constants
74 // -------------------------------------------------------- //
75 
76 // input-ID symbols
77 enum input_id_t {
78 	ID_AUDIO_INPUT
79 };
80 
81 // output-ID symbols
82 enum output_id_t {
83 	ID_AUDIO_MIX_OUTPUT
84 	//ID_AUDIO_WET_OUTPUT ...
85 };
86 
87 // parameter ID
88 enum param_id_t {
89 	P_MIX_RATIO_LABEL		= 100,
90 	P_MIX_RATIO,
91 
92 	P_SWEEP_RATE_LABEL	= 200,
93 	P_SWEEP_RATE,
94 
95 	P_DELAY_LABEL				= 300,
96 	P_DELAY,
97 
98 	P_DEPTH_LABEL				= 400,
99 	P_DEPTH,
100 
101 	P_FEEDBACK_LABEL		= 500,
102 	P_FEEDBACK
103 };
104 
105 const float FlangerNode::s_fMaxDelay = 100.0;
106 const char* const FlangerNode::s_nodeName = "FlangerNode";
107 
108 
109 // -------------------------------------------------------- //
110 // ctor/dtor
111 // -------------------------------------------------------- //
112 
~FlangerNode()113 FlangerNode::~FlangerNode() {
114 	// shut down
115 	Quit();
116 
117 	// free delay buffer
118 	if(m_pDelayBuffer)
119 		delete m_pDelayBuffer;
120 }
121 
FlangerNode(BMediaAddOn * pAddOn)122 FlangerNode::FlangerNode(BMediaAddOn* pAddOn) :
123 	// * init base classes
124 	BMediaNode(s_nodeName), // (virtual base)
125 	BBufferConsumer(B_MEDIA_RAW_AUDIO),
126 	BBufferProducer(B_MEDIA_RAW_AUDIO),
127 	BControllable(),
128 	BMediaEventLooper(),
129 
130 	// * init connection state
131 	m_outputEnabled(true),
132 	m_downstreamLatency(0),
133 	m_processingLatency(0),
134 
135 	// * init filter state
136 	m_pDelayBuffer(0),
137 
138 	// * init add-on stuff
139 	m_pAddOn(pAddOn) {
140 
141 //	PRINT((
142 //		"\n"
143 //		"--*-- FlangerNode() [%s] --*--\n\n",
144 //		__BUILD_DATE));
145 //
146 	// the rest of the initialization happens in NodeRegistered().
147 }
148 
149 
150 // -------------------------------------------------------- //
151 // *** BMediaNode
152 // -------------------------------------------------------- //
153 
HandleMessage(int32 code,const void * pData,size_t size)154 status_t FlangerNode::HandleMessage(
155 	int32 code,
156 	const void* pData,
157 	size_t size) {
158 
159 	// pass off to each base class
160 	if(
161 		BBufferConsumer::HandleMessage(code, pData, size) &&
162 		BBufferProducer::HandleMessage(code, pData, size) &&
163 		BControllable::HandleMessage(code, pData, size) &&
164 		BMediaNode::HandleMessage(code, pData, size))
165 		BMediaNode::HandleBadMessage(code, pData, size);
166 
167 	// +++++ return error on bad message?
168 	return B_OK;
169 }
170 
AddOn(int32 * poID) const171 BMediaAddOn* FlangerNode::AddOn(
172 	int32* poID) const {
173 
174 	if(m_pAddOn)
175 		*poID = 0;
176 	return m_pAddOn;
177 }
178 
SetRunMode(run_mode mode)179 void FlangerNode::SetRunMode(
180 	run_mode mode) {
181 
182 	// disallow offline mode for now
183 	// +++++
184 	if(mode == B_OFFLINE)
185 		ReportError(B_NODE_FAILED_SET_RUN_MODE);
186 
187 	// +++++ any other work to do?
188 
189 	// hand off
190 	BMediaEventLooper::SetRunMode(mode);
191 }
192 
193 // -------------------------------------------------------- //
194 // *** BMediaEventLooper
195 // -------------------------------------------------------- //
196 
HandleEvent(const media_timed_event * pEvent,bigtime_t howLate,bool realTimeEvent)197 void FlangerNode::HandleEvent(
198 	const media_timed_event* pEvent,
199 	bigtime_t howLate,
200 	bool realTimeEvent) {
201 
202 	ASSERT(pEvent);
203 
204 	switch(pEvent->type) {
205 		case BTimedEventQueue::B_PARAMETER:
206 			handleParameterEvent(pEvent);
207 			break;
208 
209 		case BTimedEventQueue::B_START:
210 			handleStartEvent(pEvent);
211 			break;
212 
213 		case BTimedEventQueue::B_STOP:
214 			handleStopEvent(pEvent);
215 			break;
216 
217 		default:
218 			ignoreEvent(pEvent);
219 			break;
220 	}
221 }
222 
223 // "The Media Server calls this hook function after the node has
224 //  been registered.  This is derived from BMediaNode; BMediaEventLooper
225 //  implements it to call Run() automatically when the node is registered;
226 //  if you implement NodeRegistered() you should call through to
227 //  BMediaEventLooper::NodeRegistered() after you've done your custom
228 //  operations."
229 
NodeRegistered()230 void FlangerNode::NodeRegistered() {
231 
232 	PRINT(("FlangerNode::NodeRegistered()\n"));
233 
234 	// figure preferred ('template') format
235 	m_preferredFormat.type = B_MEDIA_RAW_AUDIO;
236 	getPreferredFormat(m_preferredFormat);
237 
238 	// initialize current format
239 	m_format.type = B_MEDIA_RAW_AUDIO;
240 	m_format.u.raw_audio = media_raw_audio_format::wildcard;
241 
242 	// init input
243 	m_input.destination.port = ControlPort();
244 	m_input.destination.id = ID_AUDIO_INPUT;
245 	m_input.node = Node();
246 	m_input.source = media_source::null;
247 	m_input.format = m_format;
248 	strlcpy(m_input.name, B_TRANSLATE("Audio input"), B_MEDIA_NAME_LENGTH);
249 
250 	// init output
251 	m_output.source.port = ControlPort();
252 	m_output.source.id = ID_AUDIO_MIX_OUTPUT;
253 	m_output.node = Node();
254 	m_output.destination = media_destination::null;
255 	m_output.format = m_format;
256 	strlcpy(m_output.name, B_TRANSLATE("Mix output"), B_MEDIA_NAME_LENGTH);
257 
258 	// init parameters
259 	initParameterValues();
260 	initParameterWeb();
261 
262 	// Start the BMediaEventLooper thread
263 	SetPriority(B_REAL_TIME_PRIORITY);
264 	Run();
265 }
266 
267 // "Augment OfflineTime() to compute the node's current time; it's called
268 //  by the Media Kit when it's in offline mode. Update any appropriate
269 //  internal information as well, then call through to the BMediaEventLooper
270 //  implementation."
271 
OfflineTime()272 bigtime_t FlangerNode::OfflineTime() {
273 	// +++++
274 	return 0LL;
275 }
276 
277 // -------------------------------------------------------- //
278 // *** BBufferConsumer
279 // -------------------------------------------------------- //
280 
AcceptFormat(const media_destination & destination,media_format * pioFormat)281 status_t FlangerNode::AcceptFormat(
282 	const media_destination& destination,
283 	media_format* pioFormat) {
284 
285 	PRINT(("FlangerNode::AcceptFormat()\n"));
286 
287 	// sanity checks
288 	if(destination != m_input.destination) {
289 		PRINT(("\tbad destination\n"));
290 		return B_MEDIA_BAD_DESTINATION;
291 	}
292 	if(pioFormat->type != B_MEDIA_RAW_AUDIO) {
293 		PRINT(("\tnot B_MEDIA_RAW_AUDIO\n"));
294 		return B_MEDIA_BAD_FORMAT;
295 	}
296 
297 	validateProposedFormat(
298 		(m_format.u.raw_audio.format != media_raw_audio_format::wildcard.format) ?
299 			m_format : m_preferredFormat,
300 		*pioFormat);
301 	return B_OK;
302 }
303 
304 // "If you're writing a node, and receive a buffer with the B_SMALL_BUFFER
305 //  flag set, you must recycle the buffer before returning."
306 
BufferReceived(BBuffer * pBuffer)307 void FlangerNode::BufferReceived(
308 	BBuffer* pBuffer) {
309 	ASSERT(pBuffer);
310 
311 	// check buffer destination
312 	if(pBuffer->Header()->destination !=
313 		m_input.destination.id) {
314 		PRINT(("FlangerNode::BufferReceived():\n"
315 			"\tBad destination.\n"));
316 		pBuffer->Recycle();
317 		return;
318 	}
319 
320 	if(pBuffer->Header()->time_source != TimeSource()->ID()) {
321 		PRINT(("* timesource mismatch\n"));
322 	}
323 
324 	// check output
325 	if(m_output.destination == media_destination::null ||
326 		!m_outputEnabled) {
327 		pBuffer->Recycle();
328 		return;
329 	}
330 
331 	// process and retransmit buffer
332 	filterBuffer(pBuffer);
333 
334 	status_t err = SendBuffer(pBuffer, m_output.source, m_output.destination);
335 	if (err < B_OK) {
336 		PRINT(("FlangerNode::BufferReceived():\n"
337 			"\tSendBuffer() failed: %s\n", strerror(err)));
338 		pBuffer->Recycle();
339 	}
340 	// sent!
341 }
342 
343 // * make sure to fill in poInput->format with the contents of
344 //   pFormat; as of R4.5 the Media Kit passes poInput->format to
345 //   the producer in BBufferProducer::Connect().
346 
347 status_t
Connected(const media_source & source,const media_destination & destination,const media_format & format,media_input * poInput)348 FlangerNode::Connected(const media_source& source,
349 	const media_destination& destination, const media_format& format,
350 	media_input* poInput)
351 {
352 	PRINT(("FlangerNode::Connected()\n"
353 		"\tto source %" B_PRId32 "\n", source.id));
354 
355 	// sanity check
356 	if(destination != m_input.destination) {
357 		PRINT(("\tbad destination\n"));
358 		return B_MEDIA_BAD_DESTINATION;
359 	}
360 	if(m_input.source != media_source::null) {
361 		PRINT(("\talready connected\n"));
362 		return B_MEDIA_ALREADY_CONNECTED;
363 	}
364 
365 	// initialize input
366 	m_input.source = source;
367 	m_input.format = format;
368 	*poInput = m_input;
369 
370 	// store format (this now constrains the output format)
371 	m_format = format;
372 
373 	return B_OK;
374 }
375 
Disconnected(const media_source & source,const media_destination & destination)376 void FlangerNode::Disconnected(
377 	const media_source& source,
378 	const media_destination& destination) {
379 
380 	PRINT(("FlangerNode::Disconnected()\n"));
381 
382 	// sanity checks
383 	if(m_input.source != source) {
384 		PRINT(("\tsource mismatch: expected ID %" B_PRId32 ", got %" B_PRId32
385 				"\n", m_input.source.id, source.id));
386 		return;
387 	}
388 	if(destination != m_input.destination) {
389 		PRINT(("\tdestination mismatch: expected ID %" B_PRId32 ", got %"
390 				B_PRId32 "\n", m_input.destination.id, destination.id));
391 		return;
392 	}
393 
394 	// mark disconnected
395 	m_input.source = media_source::null;
396 
397 	// no output? clear format:
398 	if(m_output.destination == media_destination::null) {
399 		m_format.u.raw_audio = media_raw_audio_format::wildcard;
400 	}
401 
402 	m_input.format = m_format;
403 
404 }
405 
DisposeInputCookie(int32 cookie)406 void FlangerNode::DisposeInputCookie(
407 	int32 cookie) {}
408 
409 // "You should implement this function so your node will know that the data
410 //  format is going to change. Note that this may be called in response to
411 //  your AcceptFormat() call, if your AcceptFormat() call alters any wildcard
412 //  fields in the specified format.
413 //
414 //  Because FormatChanged() is called by the producer, you don't need to (and
415 //  shouldn't) ask it if the new format is acceptable.
416 //
417 //  If the format change isn't possible, return an appropriate error from
418 //  FormatChanged(); this error will be passed back to the producer that
419 //  initiated the new format negotiation in the first place."
420 
FormatChanged(const media_source & source,const media_destination & destination,int32 changeTag,const media_format & newFormat)421 status_t FlangerNode::FormatChanged(
422 	const media_source& source,
423 	const media_destination& destination,
424 	int32 changeTag,
425 	const media_format& newFormat) {
426 
427 	// flat-out deny format changes
428 	return B_MEDIA_BAD_FORMAT;
429 }
430 
GetLatencyFor(const media_destination & destination,bigtime_t * poLatency,media_node_id * poTimeSource)431 status_t FlangerNode::GetLatencyFor(
432 	const media_destination& destination,
433 	bigtime_t* poLatency,
434 	media_node_id* poTimeSource) {
435 
436 	PRINT(("FlangerNode::GetLatencyFor()\n"));
437 
438 	// sanity check
439 	if(destination != m_input.destination) {
440 		PRINT(("\tbad destination\n"));
441 		return B_MEDIA_BAD_DESTINATION;
442 	}
443 
444 	*poLatency = m_downstreamLatency + m_processingLatency;
445 	PRINT(("\treturning %" B_PRIdBIGTIME "\n", *poLatency));
446 	*poTimeSource = TimeSource()->ID();
447 	return B_OK;
448 }
449 
GetNextInput(int32 * pioCookie,media_input * poInput)450 status_t FlangerNode::GetNextInput(
451 	int32* pioCookie,
452 	media_input* poInput) {
453 
454 	if(*pioCookie)
455 		return B_BAD_INDEX;
456 
457 	++*pioCookie;
458 	*poInput = m_input;
459 	return B_OK;
460 }
461 
ProducerDataStatus(const media_destination & destination,int32 status,bigtime_t tpWhen)462 void FlangerNode::ProducerDataStatus(
463 	const media_destination& destination,
464 	int32 status,
465 	bigtime_t tpWhen) {
466 
467 	PRINT(("FlangerNode::ProducerDataStatus()\n"));
468 
469 	// sanity check
470 	if(destination != m_input.destination) {
471 		PRINT(("\tbad destination\n"));
472 	}
473 
474 	if(m_output.destination != media_destination::null) {
475 		// pass status downstream
476 		status_t err = SendDataStatus(
477 			status,
478 			m_output.destination,
479 			tpWhen);
480 		if(err < B_OK) {
481 			PRINT(("\tSendDataStatus(): %s\n", strerror(err)));
482 		}
483 	}
484 }
485 
486 // "This function is provided to aid in supporting media formats in which the
487 //  outer encapsulation layer doesn't supply timing information. Producers will
488 //  tag the buffers they generate with seek tags; these tags can be used to
489 //  locate key frames in the media data."
490 
SeekTagRequested(const media_destination & destination,bigtime_t targetTime,uint32 flags,media_seek_tag * poSeekTag,bigtime_t * poTaggedTime,uint32 * poFlags)491 status_t FlangerNode::SeekTagRequested(
492 	const media_destination& destination,
493 	bigtime_t targetTime,
494 	uint32 flags,
495 	media_seek_tag* poSeekTag,
496 	bigtime_t* poTaggedTime,
497 	uint32* poFlags) {
498 
499 	PRINT(("FlangerNode::SeekTagRequested()\n"
500 		"\tNot implemented.\n"));
501 	return B_ERROR;
502 }
503 
504 // -------------------------------------------------------- //
505 // *** BBufferProducer
506 // -------------------------------------------------------- //
507 
508 // "When a consumer calls BBufferConsumer::RequestAdditionalBuffer(), this
509 //  function is called as a result. Its job is to call SendBuffer() to
510 //  immediately send the next buffer to the consumer. The previousBufferID,
511 //  previousTime, and previousTag arguments identify the last buffer the
512 //  consumer received. Your node should respond by sending the next buffer
513 //  after the one described.
514 //
515 //  The previousTag may be NULL.
516 //  Return B_OK if all is well; otherwise return an appropriate error code."
517 
AdditionalBufferRequested(const media_source & source,media_buffer_id previousBufferID,bigtime_t previousTime,const media_seek_tag * pPreviousTag)518 void FlangerNode::AdditionalBufferRequested(
519 	const media_source& source,
520 	media_buffer_id previousBufferID,
521 	bigtime_t previousTime,
522 	const media_seek_tag* pPreviousTag) {
523 
524 	PRINT(("FlangerNode::AdditionalBufferRequested\n"
525 		"\tOffline mode not implemented."));
526 }
527 
Connect(status_t status,const media_source & source,const media_destination & destination,const media_format & format,char * pioName)528 void FlangerNode::Connect(
529 	status_t status,
530 	const media_source& source,
531 	const media_destination& destination,
532 	const media_format& format,
533 	char* pioName) {
534 
535 	PRINT(("FlangerNode::Connect()\n"));
536 	status_t err;
537 
538 	// connection failed?
539 	if(status < B_OK) {
540 		PRINT(("\tStatus: %s\n", strerror(status)));
541 		// 'unreserve' the output
542 		m_output.destination = media_destination::null;
543 		return;
544 	}
545 
546 	// connection established:
547 	strncpy(pioName, m_output.name, B_MEDIA_NAME_LENGTH);
548 	m_output.destination = destination;
549 	m_format = format;
550 
551 	// figure downstream latency
552 	media_node_id timeSource;
553 	err = FindLatencyFor(m_output.destination, &m_downstreamLatency, &timeSource);
554 	if(err < B_OK) {
555 		PRINT(("\t!!! FindLatencyFor(): %s\n", strerror(err)));
556 	}
557 	PRINT(("\tdownstream latency = %" B_PRIdBIGTIME "\n", m_downstreamLatency));
558 
559 	// prepare the filter
560 	initFilter();
561 
562 	// figure processing time
563 	m_processingLatency = calcProcessingLatency();
564 	PRINT(("\tprocessing latency = %" B_PRIdBIGTIME "\n", m_processingLatency));
565 
566 	// store summed latency
567 	SetEventLatency(m_downstreamLatency + m_processingLatency);
568 
569 	if(m_input.source != media_source::null) {
570 		// pass new latency upstream
571 		err = SendLatencyChange(
572 			m_input.source,
573 			m_input.destination,
574 			EventLatency() + SchedulingLatency());
575 		if(err < B_OK)
576 			PRINT(("\t!!! SendLatencyChange(): %s\n", strerror(err)));
577 	}
578 
579 	// cache buffer duration
580 	SetBufferDuration(
581 		buffer_duration(
582 			m_format.u.raw_audio));
583 }
584 
Disconnect(const media_source & source,const media_destination & destination)585 void FlangerNode::Disconnect(
586 	const media_source& source,
587 	const media_destination& destination) {
588 
589 	PRINT(("FlangerNode::Disconnect()\n"));
590 
591 	// sanity checks
592 	if(source != m_output.source) {
593 		PRINT(("\tbad source\n"));
594 		return;
595 	}
596 	if(destination != m_output.destination) {
597 		PRINT(("\tbad destination\n"));
598 		return;
599 	}
600 
601 	// clean up
602 	m_output.destination = media_destination::null;
603 
604 	// no input? clear format:
605 	if(m_input.source == media_source::null) {
606 		m_format.u.raw_audio = media_raw_audio_format::wildcard;
607 	}
608 
609 	m_output.format = m_format;
610 
611 	// +++++ other cleanup goes here
612 }
613 
DisposeOutputCookie(int32 cookie)614 status_t FlangerNode::DisposeOutputCookie(
615 	int32 cookie) {
616 	return B_OK;
617 }
618 
EnableOutput(const media_source & source,bool enabled,int32 * _deprecated_)619 void FlangerNode::EnableOutput(
620 	const media_source& source,
621 	bool enabled,
622 	int32* _deprecated_) {
623 	PRINT(("FlangerNode::EnableOutput()\n"));
624 	if(source != m_output.source) {
625 		PRINT(("\tbad source\n"));
626 		return;
627 	}
628 
629 	m_outputEnabled = enabled;
630 }
631 
FormatChangeRequested(const media_source & source,const media_destination & destination,media_format * pioFormat,int32 * _deprecated_)632 status_t FlangerNode::FormatChangeRequested(
633 	const media_source& source,
634 	const media_destination& destination,
635 	media_format* pioFormat,
636 	int32* _deprecated_) {
637 
638 	// deny
639 	PRINT(("FlangerNode::FormatChangeRequested()\n"
640 		"\tNot supported.\n"));
641 
642 	return B_MEDIA_BAD_FORMAT;
643 }
644 
FormatProposal(const media_source & source,media_format * pioFormat)645 status_t FlangerNode::FormatProposal(
646 	const media_source& source,
647 	media_format* pioFormat) {
648 
649 	PRINT(("FlangerNode::FormatProposal()\n"));
650 
651 	if(source != m_output.source) {
652 		PRINT(("\tbad source\n"));
653 		return B_MEDIA_BAD_SOURCE;
654 	}
655 
656 	if(pioFormat->type != B_MEDIA_RAW_AUDIO) {
657 		PRINT(("\tbad type\n"));
658 		return B_MEDIA_BAD_FORMAT;
659 	}
660 
661 	validateProposedFormat(
662 		(m_format.u.raw_audio.format != media_raw_audio_format::wildcard.format) ?
663 			m_format :
664 			m_preferredFormat,
665 		*pioFormat);
666 	return B_OK;
667 }
668 
FormatSuggestionRequested(media_type type,int32 quality,media_format * poFormat)669 status_t FlangerNode::FormatSuggestionRequested(
670 	media_type type,
671 	int32 quality,
672 	media_format* poFormat) {
673 
674 	PRINT(("FlangerNode::FormatSuggestionRequested()\n"));
675 	if(type != B_MEDIA_RAW_AUDIO) {
676 		PRINT(("\tbad type\n"));
677 		return B_MEDIA_BAD_FORMAT;
678 	}
679 
680 	if(m_format.u.raw_audio.format != media_raw_audio_format::wildcard.format)
681 		*poFormat = m_format;
682 	else
683 		*poFormat = m_preferredFormat;
684 	return B_OK;
685 }
686 
GetLatency(bigtime_t * poLatency)687 status_t FlangerNode::GetLatency(
688 	bigtime_t* poLatency) {
689 
690 	PRINT(("FlangerNode::GetLatency()\n"));
691 	*poLatency = EventLatency() + SchedulingLatency();
692 	PRINT(("\treturning %" B_PRIdBIGTIME "\n", *poLatency));
693 
694 	return B_OK;
695 }
696 
GetNextOutput(int32 * pioCookie,media_output * poOutput)697 status_t FlangerNode::GetNextOutput(
698 	int32* pioCookie,
699 	media_output* poOutput) {
700 
701 	if(*pioCookie)
702 		return B_BAD_INDEX;
703 
704 	++*pioCookie;
705 	*poOutput = m_output;
706 
707 	return B_OK;
708 }
709 
710 // "This hook function is called when a BBufferConsumer that's receiving data
711 //  from you determines that its latency has changed. It will call its
712 //  BBufferConsumer::SendLatencyChange() function, and in response, the Media
713 //  Server will call your LatencyChanged() function.  The source argument
714 //  indicates your output that's involved in the connection, and destination
715 //  specifies the input on the consumer to which the connection is linked.
716 //  newLatency is the consumer's new latency. The flags are currently unused."
LatencyChanged(const media_source & source,const media_destination & destination,bigtime_t newLatency,uint32 flags)717 void FlangerNode::LatencyChanged(
718 	const media_source& source,
719 	const media_destination& destination,
720 	bigtime_t newLatency,
721 	uint32 flags) {
722 
723 	PRINT(("FlangerNode::LatencyChanged()\n"));
724 
725 	if(source != m_output.source) {
726 		PRINT(("\tBad source.\n"));
727 		return;
728 	}
729 	if(destination != m_output.destination) {
730 		PRINT(("\tBad destination.\n"));
731 		return;
732 	}
733 
734 	m_downstreamLatency = newLatency;
735 	SetEventLatency(m_downstreamLatency + m_processingLatency);
736 
737 	if(m_input.source != media_source::null) {
738 		// pass new latency upstream
739 		status_t err = SendLatencyChange(
740 			m_input.source,
741 			m_input.destination,
742 			EventLatency() + SchedulingLatency());
743 		if(err < B_OK)
744 			PRINT(("\t!!! SendLatencyChange(): %s\n", strerror(err)));
745 	}
746 }
747 
LateNoticeReceived(const media_source & source,bigtime_t howLate,bigtime_t tpWhen)748 void FlangerNode::LateNoticeReceived(
749 	const media_source& source,
750 	bigtime_t howLate,
751 	bigtime_t tpWhen) {
752 
753 	PRINT(("FlangerNode::LateNoticeReceived()\n"
754 		"\thowLate == %" B_PRIdBIGTIME "\n"
755 		"\twhen    == %" B_PRIdBIGTIME "\n", howLate, tpWhen));
756 
757 	if(source != m_output.source) {
758 		PRINT(("\tBad source.\n"));
759 		return;
760 	}
761 
762 	if(m_input.source == media_source::null) {
763 		PRINT(("\t!!! No input to blame.\n"));
764 		return;
765 	}
766 
767 	// +++++ check run mode?
768 
769 	// pass the buck, since this node doesn't schedule buffer
770 	// production
771 	NotifyLateProducer(
772 		m_input.source,
773 		howLate,
774 		tpWhen);
775 }
776 
777 // PrepareToConnect() is the second stage of format negotiations that happens
778 // inside BMediaRoster::Connect().  At this point, the consumer's AcceptFormat()
779 // method has been called, and that node has potentially changed the proposed
780 // format.  It may also have left wildcards in the format.  PrepareToConnect()
781 // *must* fully specialize the format before returning!
782 
PrepareToConnect(const media_source & source,const media_destination & destination,media_format * pioFormat,media_source * poSource,char * poName)783 status_t FlangerNode::PrepareToConnect(
784 	const media_source& source,
785 	const media_destination& destination,
786 	media_format* pioFormat,
787 	media_source* poSource,
788 	char* poName) {
789 
790 	char formatStr[256];
791 	string_for_format(*pioFormat, formatStr, 255);
792 	PRINT(("FlangerNode::PrepareToConnect()\n"
793 		"\tproposed format: %s\n", formatStr));
794 
795 	if(source != m_output.source) {
796 		PRINT(("\tBad source.\n"));
797 		return B_MEDIA_BAD_SOURCE;
798 	}
799 	if(m_output.destination != media_destination::null) {
800 		PRINT(("\tAlready connected.\n"));
801 		return B_MEDIA_ALREADY_CONNECTED;
802 	}
803 
804 	if(pioFormat->type != B_MEDIA_RAW_AUDIO) {
805 		PRINT(("\tBad format type.\n"));
806 		return B_MEDIA_BAD_FORMAT;
807 	}
808 
809 	// do a final validity check:
810 	status_t err = validateProposedFormat(
811 		(m_format.u.raw_audio.format != media_raw_audio_format::wildcard.format) ?
812 			m_format :
813 			m_preferredFormat,
814 		*pioFormat);
815 
816 	if(err < B_OK) {
817 		// no go
818 		return err;
819 	}
820 
821 	// fill in wildcards
822 	specializeOutputFormat(*pioFormat);
823 
824 	// reserve the output
825 	m_output.destination = destination;
826 	m_output.format = *pioFormat;
827 
828 	// pass back source & output name
829 	*poSource = m_output.source;
830 	strncpy(poName, m_output.name, B_MEDIA_NAME_LENGTH);
831 
832 	return B_OK;
833 }
834 
SetBufferGroup(const media_source & source,BBufferGroup * pGroup)835 status_t FlangerNode::SetBufferGroup(
836 	const media_source& source,
837 	BBufferGroup* pGroup) {
838 
839 	PRINT(("FlangerNode::SetBufferGroup()\n"));
840 	if(source != m_output.source) {
841 		PRINT(("\tBad source.\n"));
842 		return B_MEDIA_BAD_SOURCE;
843 	}
844 
845 	if(m_input.source == media_source::null) {
846 		PRINT(("\tNo producer to send buffers to.\n"));
847 		return B_ERROR;
848 	}
849 
850 	// +++++ is this right?  buffer-group selection gets
851 	//       all asynchronous and weird...
852 	int32 changeTag;
853 	return SetOutputBuffersFor(
854 		m_input.source,
855 		m_input.destination,
856 		pGroup,
857 		0, &changeTag);
858 }
859 
SetPlayRate(int32 numerator,int32 denominator)860 status_t FlangerNode::SetPlayRate(
861 	int32 numerator,
862 	int32 denominator) {
863 	// not supported
864 	return B_ERROR;
865 }
866 
VideoClippingChanged(const media_source & source,int16 numShorts,int16 * pClipData,const media_video_display_info & display,int32 * poFromChangeTag)867 status_t FlangerNode::VideoClippingChanged(
868 	const media_source& source,
869 	int16 numShorts,
870 	int16* pClipData,
871 	const media_video_display_info& display,
872 	int32* poFromChangeTag) {
873 	// not sane
874 	return B_ERROR;
875 }
876 
877 // -------------------------------------------------------- //
878 // *** BControllable
879 // -------------------------------------------------------- //
880 
GetParameterValue(int32 id,bigtime_t * poLastChangeTime,void * poValue,size_t * pioSize)881 status_t FlangerNode::GetParameterValue(
882 	int32 id,
883 	bigtime_t* poLastChangeTime,
884 	void* poValue,
885 	size_t* pioSize) {
886 
887 //	PRINT(("FlangerNode::GetParameterValue()\n"));
888 
889 	// all parameters are floats
890 	if(*pioSize < sizeof(float)) {
891 		return B_NO_MEMORY;
892 	}
893 
894 	*pioSize = sizeof(float);
895 	switch(id) {
896 		case P_MIX_RATIO:
897 			*(float*)poValue = m_fMixRatio;
898 			*poLastChangeTime = m_tpMixRatioChanged;
899 			break;
900 
901 		case P_SWEEP_RATE:
902 			*(float*)poValue = m_fSweepRate;
903 			*poLastChangeTime = m_tpSweepRateChanged;
904 			break;
905 
906 		case P_DELAY:
907 			*(float*)poValue = m_fDelay;
908 			*poLastChangeTime = m_tpDelayChanged;
909 			break;
910 
911 		case P_DEPTH:
912 			*(float*)poValue = m_fDepth;
913 			*poLastChangeTime = m_tpDepthChanged;
914 			break;
915 
916 		case P_FEEDBACK:
917 			*(float*)poValue = m_fFeedback;
918 			*poLastChangeTime = m_tpFeedbackChanged;
919 			break;
920 
921 		default:
922 			return B_ERROR;
923 	}
924 
925 	return B_OK;
926 }
927 
SetParameterValue(int32 id,bigtime_t changeTime,const void * pValue,size_t size)928 void FlangerNode::SetParameterValue(
929 	int32 id,
930 	bigtime_t changeTime,
931 	const void* pValue,
932 	size_t size) {
933 
934 	switch(id) {
935 		case P_MIX_RATIO:
936 		case P_SWEEP_RATE:
937 		case P_DELAY:
938 		case P_DEPTH:
939 		case P_FEEDBACK: {
940 			if(size < sizeof(float))
941 				break;
942 
943 //      this is from ToneProducer.  it's fishy.
944 //			if(size > sizeof(float))
945 //				size = sizeof(float);
946 
947 			media_timed_event ev(
948 				changeTime,
949 				BTimedEventQueue::B_PARAMETER,
950 				0,
951 				BTimedEventQueue::B_NO_CLEANUP,
952 				size,
953 				id,
954 				(char*)pValue, size);
955 			EventQueue()->AddEvent(ev);
956 			break;
957 		}
958 	}
959 }
960 
961 // -------------------------------------------------------- //
962 // *** HandleEvent() impl
963 // -------------------------------------------------------- //
964 
handleParameterEvent(const media_timed_event * pEvent)965 void FlangerNode::handleParameterEvent(
966 	const media_timed_event* pEvent) {
967 
968 	float value = *(float*)pEvent->user_data;
969 	int32 id = pEvent->bigdata;
970 	size_t size = pEvent->data;
971 	bigtime_t now = TimeSource()->Now();
972 
973 	switch(id) {
974 		case P_MIX_RATIO:
975 			if(value == m_fMixRatio)
976 				break;
977 
978 			// set
979 			m_fMixRatio = value;
980 			m_tpMixRatioChanged = now;
981 			// broadcast
982 			BroadcastNewParameterValue(
983 				now,
984 				id,
985 				&m_fMixRatio,
986 				size);
987 			break;
988 
989 		case P_SWEEP_RATE:
990 			if(value == m_fSweepRate)
991 				break;
992 
993 			// set
994 			m_fSweepRate = value;
995 			m_tpSweepRateChanged = now;
996 
997 			if(m_output.destination != media_destination::null) {
998 				m_fThetaInc = calc_sweep_delta(
999 					m_format.u.raw_audio,
1000 					m_fSweepRate);
1001 			}
1002 
1003 			// broadcast
1004 			BroadcastNewParameterValue(
1005 				now,
1006 				id,
1007 				&m_fSweepRate,
1008 				size);
1009 			break;
1010 
1011 		case P_DELAY:
1012 			if(value == m_fDelay)
1013 				break;
1014 
1015 			// set
1016 			m_fDelay = value;
1017 			m_tpDelayChanged = now;
1018 
1019 			if(m_output.destination != media_destination::null) {
1020 				m_fSweepBase = calc_sweep_base(
1021 					m_format.u.raw_audio,
1022 					m_fDelay, m_fDepth);
1023 			}
1024 
1025 			// broadcast
1026 			BroadcastNewParameterValue(
1027 				now,
1028 				id,
1029 				&m_fDelay,
1030 				size);
1031 			break;
1032 
1033 		case P_DEPTH:
1034 			if(value == m_fDepth)
1035 				break;
1036 
1037 			// set
1038 			m_fDepth = value;
1039 			m_tpDepthChanged = now;
1040 
1041 			if(m_output.destination != media_destination::null) {
1042 				m_fSweepBase = calc_sweep_base(
1043 					m_format.u.raw_audio,
1044 					m_fDelay, m_fDepth);
1045 				m_fSweepFactor = calc_sweep_factor(
1046 					m_format.u.raw_audio,
1047 					m_fDepth);
1048 			}
1049 
1050 			// broadcast
1051 			BroadcastNewParameterValue(
1052 				now,
1053 				id,
1054 				&m_fDepth,
1055 				size);
1056 			break;
1057 
1058 		case P_FEEDBACK:
1059 			if(value == m_fFeedback)
1060 				break;
1061 
1062 			// set
1063 			m_fFeedback = value;
1064 			m_tpFeedbackChanged = now;
1065 			// broadcast
1066 			BroadcastNewParameterValue(
1067 				now,
1068 				id,
1069 				&m_fFeedback,
1070 				size);
1071 			break;
1072 	}
1073 }
1074 
handleStartEvent(const media_timed_event * pEvent)1075 void FlangerNode::handleStartEvent(
1076 	const media_timed_event* pEvent) {
1077 	PRINT(("FlangerNode::handleStartEvent\n"));
1078 
1079 	startFilter();
1080 }
1081 
handleStopEvent(const media_timed_event * pEvent)1082 void FlangerNode::handleStopEvent(
1083 	const media_timed_event* pEvent) {
1084 	PRINT(("FlangerNode::handleStopEvent\n"));
1085 
1086 	stopFilter();
1087 }
1088 
ignoreEvent(const media_timed_event * pEvent)1089 void FlangerNode::ignoreEvent(
1090 	const media_timed_event* pEvent) {
1091 	PRINT(("FlangerNode::ignoreEvent\n"));
1092 
1093 }
1094 
1095 
1096 // -------------------------------------------------------- //
1097 // *** internal operations
1098 // -------------------------------------------------------- //
1099 
1100 
1101 // figure the preferred format: any fields left as wildcards
1102 // are negotiable
getPreferredFormat(media_format & ioFormat)1103 void FlangerNode::getPreferredFormat(
1104 	media_format& ioFormat) {
1105 	ASSERT(ioFormat.type == B_MEDIA_RAW_AUDIO);
1106 
1107 	ioFormat.u.raw_audio = media_raw_audio_format::wildcard;
1108 	ioFormat.u.raw_audio.channel_count = 1;
1109 	ioFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT;
1110 
1111 //	ioFormat.u.raw_audio.frame_rate = 44100.0;
1112 //	ioFormat.u.raw_audio.buffer_size = 0x1000;
1113 }
1114 
1115 // test the given template format against a proposed format.
1116 // specialize wildcards for fields where the template contains
1117 // non-wildcard data; write required fields into proposed format
1118 // if they mismatch.
1119 // Returns B_OK if the proposed format doesn't conflict with the
1120 // template, or B_MEDIA_BAD_FORMAT otherwise.
1121 
validateProposedFormat(const media_format & preferredFormat,media_format & ioProposedFormat)1122 status_t FlangerNode::validateProposedFormat(
1123 	const media_format& preferredFormat,
1124 	media_format& ioProposedFormat) {
1125 
1126 	char formatStr[256];
1127 	PRINT(("FlangerNode::validateProposedFormat()\n"));
1128 
1129 	ASSERT(preferredFormat.type == B_MEDIA_RAW_AUDIO);
1130 
1131 	string_for_format(preferredFormat, formatStr, 255);
1132 	PRINT(("\ttemplate format: %s\n", formatStr));
1133 
1134 	string_for_format(ioProposedFormat, formatStr, 255);
1135 	PRINT(("\tproposed format: %s\n", formatStr));
1136 
1137 	status_t err = B_OK;
1138 
1139 	if(ioProposedFormat.type != B_MEDIA_RAW_AUDIO) {
1140 		// out of the ballpark
1141 		ioProposedFormat = preferredFormat;
1142 		return B_MEDIA_BAD_FORMAT;
1143 	}
1144 
1145 	// wildcard format
1146 	const media_raw_audio_format& wild = media_raw_audio_format::wildcard;
1147 	// proposed format
1148 	media_raw_audio_format& f = ioProposedFormat.u.raw_audio;
1149 	// template format
1150 	const media_raw_audio_format& pref = preferredFormat.u.raw_audio;
1151 
1152 	if(pref.frame_rate != wild.frame_rate) {
1153 		if(f.frame_rate != pref.frame_rate) {
1154 			if(f.frame_rate != wild.frame_rate)
1155 				err = B_MEDIA_BAD_FORMAT;
1156 			f.frame_rate = pref.frame_rate;
1157 		}
1158 	}
1159 
1160 	if(pref.channel_count != wild.channel_count) {
1161 		if(f.channel_count != pref.channel_count) {
1162 			if(f.channel_count != wild.channel_count)
1163 				err = B_MEDIA_BAD_FORMAT;
1164 			f.channel_count = pref.channel_count;
1165 		}
1166 	}
1167 
1168 	if(pref.format != wild.format) {
1169 		if(f.format != pref.format) {
1170 			if(f.format != wild.format)
1171 				err = B_MEDIA_BAD_FORMAT;
1172 			f.format = pref.format;
1173 		}
1174 	}
1175 
1176 	if(pref.byte_order != wild.byte_order) {
1177 		if(f.byte_order != pref.byte_order) {
1178 			if(f.byte_order != wild.byte_order)
1179 				err = B_MEDIA_BAD_FORMAT;
1180 			f.byte_order = pref.byte_order;
1181 		}
1182 	}
1183 
1184 	if(pref.buffer_size != wild.buffer_size) {
1185 		if(f.buffer_size != pref.buffer_size) {
1186 			if(f.buffer_size != wild.buffer_size)
1187 				err = B_MEDIA_BAD_FORMAT;
1188 			f.buffer_size = pref.buffer_size;
1189 		}
1190 	}
1191 
1192 	if(err != B_OK) {
1193 		string_for_format(ioProposedFormat, formatStr, 255);
1194 		PRINT((
1195 			"\tformat conflict; suggesting:\n\tformat %s\n", formatStr));
1196 	}
1197 
1198 	return err;
1199 }
1200 
1201 // fill in wildcards in the given format.
1202 // (assumes the format passes validateProposedFormat().)
specializeOutputFormat(media_format & ioFormat)1203 void FlangerNode::specializeOutputFormat(
1204 	media_format& ioFormat) {
1205 
1206 	char formatStr[256];
1207 	string_for_format(ioFormat, formatStr, 255);
1208 	PRINT(("FlangerNode::specializeOutputFormat()\n"
1209 		"\tinput format: %s\n", formatStr));
1210 
1211 	ASSERT(ioFormat.type == B_MEDIA_RAW_AUDIO);
1212 
1213 	// carpal_tunnel_paranoia
1214 	media_raw_audio_format& f = ioFormat.u.raw_audio;
1215 	const media_raw_audio_format& w = media_raw_audio_format::wildcard;
1216 
1217 	if (f.frame_rate == w.frame_rate)
1218 		f.frame_rate = 44100.0;
1219 	if (f.channel_count == w.channel_count) {
1220 		//+++++ tweaked 15sep99
1221 		if (m_input.source != media_source::null)
1222 			f.channel_count = m_input.format.u.raw_audio.channel_count;
1223 		else
1224 			f.channel_count = 1;
1225 	}
1226 	if (f.format == w.format)
1227 		f.format = media_raw_audio_format::B_AUDIO_FLOAT;
1228 	if (f.byte_order == w.byte_order)
1229 		f.byte_order = (B_HOST_IS_BENDIAN) ? B_MEDIA_BIG_ENDIAN : B_MEDIA_LITTLE_ENDIAN;
1230 	if (f.buffer_size == w.buffer_size)
1231 		f.buffer_size = 2048;
1232 
1233 	string_for_format(ioFormat, formatStr, 255);
1234 	PRINT(("\toutput format: %s\n", formatStr));
1235 }
1236 
1237 // set parameters to their default settings
initParameterValues()1238 void FlangerNode::initParameterValues() {
1239 	m_fMixRatio = 0.5;
1240 	m_tpMixRatioChanged = 0LL;
1241 
1242 	m_fSweepRate = 0.1;
1243 	m_tpSweepRateChanged = 0LL;
1244 
1245 	m_fDelay = 10.0;
1246 	m_tpDelayChanged = 0LL;
1247 
1248 	m_fDepth = 25.0;
1249 	m_tpDepthChanged = 0LL;
1250 
1251 	m_fFeedback = 0.1;
1252 	m_tpFeedbackChanged = 0LL;
1253 }
1254 
1255 // create and register a parameter web
initParameterWeb()1256 void FlangerNode::initParameterWeb() {
1257 	BParameterWeb* pWeb = new BParameterWeb();
1258 	BParameterGroup* pTopGroup = pWeb->MakeGroup(
1259 		B_TRANSLATE("FlangerNode parameters"));
1260 
1261 	BNullParameter* label;
1262 	BContinuousParameter* value;
1263 	BParameterGroup* g;
1264 
1265 	// mix ratio
1266 	g = pTopGroup->MakeGroup(B_TRANSLATE("Mix ratio"));
1267 	label = g->MakeNullParameter(
1268 		P_MIX_RATIO_LABEL,
1269 		B_MEDIA_NO_TYPE,
1270 		B_TRANSLATE("Mix ratio"),
1271 		B_GENERIC);
1272 
1273 	value = g->MakeContinuousParameter(
1274 		P_MIX_RATIO,
1275 		B_MEDIA_NO_TYPE,
1276 		"",
1277 		B_GAIN, "", 0.0, 1.0, 0.05);
1278 	label->AddOutput(value);
1279 	value->AddInput(label);
1280 
1281 	// sweep rate
1282 	g = pTopGroup->MakeGroup(B_TRANSLATE("Sweep rate"));
1283 	label = g->MakeNullParameter(
1284 		P_SWEEP_RATE_LABEL,
1285 		B_MEDIA_NO_TYPE,
1286 		B_TRANSLATE("Sweep rate"),
1287 		B_GENERIC);
1288 
1289 	value = g->MakeContinuousParameter(
1290 		P_SWEEP_RATE,
1291 		B_MEDIA_NO_TYPE,
1292 		"",
1293 		B_GAIN, "Hz", 0.01, 10.0, 0.01);
1294 	label->AddOutput(value);
1295 	value->AddInput(label);
1296 
1297 	// sweep range: minimum delay
1298 	g = pTopGroup->MakeGroup(B_TRANSLATE("Delay"));
1299 	label = g->MakeNullParameter(
1300 		P_DELAY_LABEL,
1301 		B_MEDIA_NO_TYPE,
1302 		B_TRANSLATE("Delay"),
1303 		B_GENERIC);
1304 
1305 	value = g->MakeContinuousParameter(
1306 		P_DELAY,
1307 		B_MEDIA_NO_TYPE,
1308 		"",
1309 		B_GAIN, "ms", 0.1, s_fMaxDelay/2.0, 0.1);
1310 	label->AddOutput(value);
1311 	value->AddInput(label);
1312 
1313 	// sweep range: maximum
1314 	g = pTopGroup->MakeGroup(B_TRANSLATE("Depth"));
1315 	label = g->MakeNullParameter(
1316 		P_DEPTH_LABEL,
1317 		B_MEDIA_NO_TYPE,
1318 		B_TRANSLATE("Depth"),
1319 		B_GENERIC);
1320 
1321 	value = g->MakeContinuousParameter(
1322 		P_DEPTH,
1323 		B_MEDIA_NO_TYPE,
1324 		"",
1325 		B_GAIN, "ms", 1.0, s_fMaxDelay/4.0, 0.1);
1326 	label->AddOutput(value);
1327 	value->AddInput(label);
1328 
1329 	// feedback
1330 	g = pTopGroup->MakeGroup(B_TRANSLATE("Feedback"));
1331 	label = g->MakeNullParameter(
1332 		P_FEEDBACK_LABEL,
1333 		B_MEDIA_NO_TYPE,
1334 		B_TRANSLATE("Feedback"),
1335 		B_GENERIC);
1336 
1337 	value = g->MakeContinuousParameter(
1338 		P_FEEDBACK,
1339 		B_MEDIA_NO_TYPE,
1340 		"",
1341 		B_GAIN, "", 0.0, 1.0, 0.01);
1342 	label->AddOutput(value);
1343 	value->AddInput(label);
1344 
1345 	// * Install parameter web
1346 	SetParameterWeb(pWeb);
1347 }
1348 
1349 // construct delay line if necessary, reset filter state
initFilter()1350 void FlangerNode::initFilter() {
1351 	PRINT(("FlangerNode::initFilter()\n"));
1352 	ASSERT(m_format.u.raw_audio.format != media_raw_audio_format::wildcard.format);
1353 
1354 	if(!m_pDelayBuffer) {
1355 		m_pDelayBuffer = new AudioBuffer(
1356 			m_format.u.raw_audio,
1357 			frames_for_duration(
1358 				m_format.u.raw_audio,
1359 				(bigtime_t)s_fMaxDelay*1000LL));
1360 		m_pDelayBuffer->zero();
1361 	}
1362 
1363 	m_framesSent = 0;
1364 	m_delayWriteFrame = 0;
1365 	m_fTheta = 0.0;
1366 	m_fThetaInc = calc_sweep_delta(m_format.u.raw_audio, m_fSweepRate);
1367 	m_fSweepBase = calc_sweep_base(m_format.u.raw_audio, m_fDelay, m_fDepth);
1368 	m_fSweepFactor = calc_sweep_factor(m_format.u.raw_audio, m_fDepth);
1369 
1370 //
1371 //	PRINT((
1372 //		"\tFrames       %ld\n"
1373 //		"\tDelay        %.2f\n"
1374 //		"\tDepth        %.2f\n"
1375 //		"\tSweepBase    %.2f\n"
1376 //		"\tSweepFactor  %.2f\n",
1377 //		m_pDelayBuffer->frames(),
1378 //		m_fDelay, m_fDepth, m_fSweepBase, m_fSweepFactor));
1379 }
1380 
startFilter()1381 void FlangerNode::startFilter() {
1382 	PRINT(("FlangerNode::startFilter()\n"));
1383 }
stopFilter()1384 void FlangerNode::stopFilter() {
1385 	PRINT(("FlangerNode::stopFilter()\n"));
1386 }
1387 
1388 // figure processing latency by doing 'dry runs' of filterBuffer()
calcProcessingLatency()1389 bigtime_t FlangerNode::calcProcessingLatency() {
1390 	PRINT(("FlangerNode::calcProcessingLatency()\n"));
1391 
1392 	if(m_output.destination == media_destination::null) {
1393 		PRINT(("\tNot connected.\n"));
1394 		return 0LL;
1395 	}
1396 
1397 	// allocate a temporary buffer group
1398 	BBufferGroup* pTestGroup = new BBufferGroup(
1399 		m_output.format.u.raw_audio.buffer_size, 1);
1400 
1401 	// fetch a buffer
1402 	BBuffer* pBuffer = pTestGroup->RequestBuffer(
1403 		m_output.format.u.raw_audio.buffer_size);
1404 	ASSERT(pBuffer);
1405 
1406 	pBuffer->Header()->type = B_MEDIA_RAW_AUDIO;
1407 	pBuffer->Header()->size_used = m_output.format.u.raw_audio.buffer_size;
1408 
1409 	// run the test
1410 	bigtime_t preTest = system_time();
1411 	filterBuffer(pBuffer);
1412 	bigtime_t elapsed = system_time()-preTest;
1413 
1414 	// clean up
1415 	pBuffer->Recycle();
1416 	delete pTestGroup;
1417 
1418 	// reset filter state
1419 	initFilter();
1420 
1421 	return elapsed;
1422 }
1423 
1424 // filter buffer data in place
1425 //
1426 // +++++ add 2-channel support 15sep991
1427 //
1428 
1429 const size_t MAX_CHANNELS = 2;
1430 
1431 struct _frame {
1432 	float channel[MAX_CHANNELS];
1433 };
1434 
filterBuffer(BBuffer * pBuffer)1435 void FlangerNode::filterBuffer(
1436 	BBuffer* pBuffer) {
1437 
1438 	if(!m_pDelayBuffer)
1439 		return;
1440 
1441 	// for each input frame:
1442 	// - fetch
1443 	// - write delay line(writeFrame)
1444 	// - read delay line(writeFrame-readOffset)  [interpolate]
1445 	// - mix (replace)
1446 	// - advance writeFrame
1447 	// - update readOffset
1448 
1449 	AudioBuffer input(m_format.u.raw_audio, pBuffer);
1450 
1451 	ASSERT(
1452 		m_format.u.raw_audio.channel_count == 1 ||
1453 		m_format.u.raw_audio.channel_count == 2);
1454 	uint32 channels = m_format.u.raw_audio.channel_count;
1455 	bool stereo = m_format.u.raw_audio.channel_count == 2;
1456 
1457 	uint32 samples = input.frames() * channels;
1458 	for(uint32 inPos = 0; inPos < samples; ++inPos) {
1459 
1460 		// read from input buffer
1461 		_frame inFrame = {};
1462 		inFrame.channel[0] = ((float*)input.data())[inPos];
1463 		if(stereo)
1464 			inFrame.channel[1] = ((float*)input.data())[inPos + 1];
1465 
1466 		// interpolate from delay buffer
1467 		float readOffset = m_fSweepBase + (m_fSweepFactor * sin(m_fTheta));
1468 		float fReadFrame = (float)m_delayWriteFrame - readOffset;
1469 		if(fReadFrame < 0.0)
1470 			fReadFrame += m_pDelayBuffer->frames();
1471 
1472 //		float delayed;
1473 
1474 
1475 		// read low-index (possibly only) frame
1476 		_frame delayedFrame = {};
1477 
1478 		int32 readFrameLo = (int32)floor(fReadFrame);
1479 		uint32 pos = readFrameLo * channels;
1480 		delayedFrame.channel[0] = ((float*)m_pDelayBuffer->data())[pos];
1481 		if(stereo)
1482 			delayedFrame.channel[1] = ((float*)m_pDelayBuffer->data())[pos+1];
1483 
1484 		if(readFrameLo != (int32)fReadFrame) {
1485 
1486 			// interpolate (A)
1487 			uint32 readFrameHi = (int32)ceil(fReadFrame);
1488 			delayedFrame.channel[0] *= ((float)readFrameHi - fReadFrame);
1489 			if(stereo)
1490 				delayedFrame.channel[1] *= ((float)readFrameHi - fReadFrame);
1491 
1492 			// read high-index frame
1493 			int32 hiWrap = (readFrameHi == m_pDelayBuffer->frames()) ? 0 : readFrameHi;
1494 			ASSERT(hiWrap >= 0);
1495 			pos = (uint32)hiWrap * channels;
1496 			_frame hiFrame;
1497 			hiFrame.channel[0] = ((float*)m_pDelayBuffer->data())[pos];
1498 			if(stereo)
1499 				hiFrame.channel[1] = ((float*)m_pDelayBuffer->data())[pos+1];
1500 
1501 			// interpolate (B)
1502 			delayedFrame.channel[0] +=
1503 				hiFrame.channel[0] * (fReadFrame - (float)readFrameLo);
1504 			if(stereo)
1505 				delayedFrame.channel[1] +=
1506 					hiFrame.channel[1] * (fReadFrame - (float)readFrameLo);
1507 		}
1508 
1509 		// mix back to output buffer
1510 		((float*)input.data())[inPos] =
1511 			(inFrame.channel[0] * (1.0-m_fMixRatio)) +
1512 			(delayedFrame.channel[0] * m_fMixRatio);
1513 		if(stereo)
1514 			((float*)input.data())[inPos+1] =
1515 				(inFrame.channel[1] * (1.0-m_fMixRatio)) +
1516 				(delayedFrame.channel[1] * m_fMixRatio);
1517 
1518 		// write to delay buffer
1519 		uint32 delayWritePos = m_delayWriteFrame * channels;
1520 		((float*)m_pDelayBuffer->data())[delayWritePos] =
1521 			inFrame.channel[0] +
1522 			(delayedFrame.channel[0] * m_fFeedback);
1523 		if(stereo)
1524 			((float*)m_pDelayBuffer->data())[delayWritePos+1] =
1525 				inFrame.channel[1] +
1526 				(delayedFrame.channel[1] * m_fFeedback);
1527 
1528 		// advance write position
1529 		if(++m_delayWriteFrame >= m_pDelayBuffer->frames())
1530 			m_delayWriteFrame = 0;
1531 
1532 		// advance read offset ('LFO')
1533 		m_fTheta += m_fThetaInc;
1534 		if(m_fTheta > 2 * M_PI)
1535 			m_fTheta -= 2 * M_PI;
1536 
1537 //		if(m_fDelayReadDelta < 0.0) {
1538 //			if(m_fDelayReadOffset < m_fDelay)
1539 //				m_fDelayReadDelta = -m_fDelayReadDelta;
1540 //		} else {
1541 //			if(m_fDelayReadOffset > m_fDepth)
1542 //				m_fDelayReadDelta = -m_fDelayReadDelta;
1543 //		}
1544 //		m_fDelayReadOffset += m_fDelayReadDelta;
1545 	}
1546 }
1547 
1548 
1549 /*!	Figure the rate at which the (radial) read offset changes,
1550 	based on the given sweep rate (in Hz)
1551 */
1552 float
calc_sweep_delta(const media_raw_audio_format & format,float fRate)1553 calc_sweep_delta(const media_raw_audio_format& format, float fRate)
1554 {
1555 	return 2 * M_PI * fRate / format.frame_rate;
1556 }
1557 
1558 /*!	Figure the base delay (in frames) based on the given
1559 	sweep delay/depth (in msec)
1560 */
1561 float
calc_sweep_base(const media_raw_audio_format & format,float delay,float depth)1562 calc_sweep_base(const media_raw_audio_format& format, float delay, float depth)
1563 {
1564 	return (format.frame_rate * (delay + depth)) / 1000.0;
1565 }
1566 
1567 
1568 float
calc_sweep_factor(const media_raw_audio_format & format,float depth)1569 calc_sweep_factor(const media_raw_audio_format& format, float depth)
1570 {
1571 	return (format.frame_rate * depth) / 1000.0;
1572 }
1573 
1574 
1575 // END -- FlangerNode.cpp --
1576