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