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