vfs: check userland buffers before reading them.
[haiku.git] / src / apps / cortex / addons / Flanger / FlangerNode.cpp
blobbb188a22baa8da6025e7152f318fb9726732a1dd
1 /*
2 * Copyright (c) 1999-2000, Eric Moon.
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions, and the following disclaimer.
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.
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.
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.
32 // FlangerNode.cpp
33 // e.moon 16jun99
35 #include "FlangerNode.h"
37 #include "AudioBuffer.h"
38 #include "SoundUtils.h"
40 #include <Buffer.h>
41 #include <BufferGroup.h>
42 #include <ByteOrder.h>
43 #include <Debug.h>
44 #include <ParameterWeb.h>
45 #include <TimeSource.h>
47 #include <cstdio>
48 #include <cstdlib>
49 #include <cstring>
50 #include <cmath>
52 // -------------------------------------------------------- //
53 // local helpers
54 // -------------------------------------------------------- //
56 float calc_sweep_delta(
57 const media_raw_audio_format& format,
58 float fRate);
60 float calc_sweep_base(
61 const media_raw_audio_format& format,
62 float fDelay, float fDepth);
64 float calc_sweep_factor(
65 const media_raw_audio_format& format,
66 float fDepth);
68 // -------------------------------------------------------- //
69 // constants
70 // -------------------------------------------------------- //
72 // input-ID symbols
73 enum input_id_t {
74 ID_AUDIO_INPUT
77 // output-ID symbols
78 enum output_id_t {
79 ID_AUDIO_MIX_OUTPUT
80 //ID_AUDIO_WET_OUTPUT ...
83 // parameter ID
84 enum param_id_t {
85 P_MIX_RATIO_LABEL = 100,
86 P_MIX_RATIO,
88 P_SWEEP_RATE_LABEL = 200,
89 P_SWEEP_RATE,
91 P_DELAY_LABEL = 300,
92 P_DELAY,
94 P_DEPTH_LABEL = 400,
95 P_DEPTH,
97 P_FEEDBACK_LABEL = 500,
98 P_FEEDBACK
101 const float FlangerNode::s_fMaxDelay = 100.0;
102 const char* const FlangerNode::s_nodeName = "FlangerNode";
105 // -------------------------------------------------------- //
106 // ctor/dtor
107 // -------------------------------------------------------- //
109 FlangerNode::~FlangerNode() {
110 // shut down
111 Quit();
113 // free delay buffer
114 if(m_pDelayBuffer)
115 delete m_pDelayBuffer;
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(),
126 // * init connection state
127 m_outputEnabled(true),
128 m_downstreamLatency(0),
129 m_processingLatency(0),
131 // * init filter state
132 m_pDelayBuffer(0),
134 // * init add-on stuff
135 m_pAddOn(pAddOn) {
137 // PRINT((
138 // "\n"
139 // "--*-- FlangerNode() [%s] --*--\n\n",
140 // __BUILD_DATE));
142 // the rest of the initialization happens in NodeRegistered().
146 // -------------------------------------------------------- //
147 // *** BMediaNode
148 // -------------------------------------------------------- //
150 status_t FlangerNode::HandleMessage(
151 int32 code,
152 const void* pData,
153 size_t size) {
155 // pass off to each base class
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);
163 // +++++ return error on bad message?
164 return B_OK;
167 BMediaAddOn* FlangerNode::AddOn(
168 int32* poID) const {
170 if(m_pAddOn)
171 *poID = 0;
172 return m_pAddOn;
175 void FlangerNode::SetRunMode(
176 run_mode mode) {
178 // disallow offline mode for now
179 // +++++
180 if(mode == B_OFFLINE)
181 ReportError(B_NODE_FAILED_SET_RUN_MODE);
183 // +++++ any other work to do?
185 // hand off
186 BMediaEventLooper::SetRunMode(mode);
189 // -------------------------------------------------------- //
190 // *** BMediaEventLooper
191 // -------------------------------------------------------- //
193 void FlangerNode::HandleEvent(
194 const media_timed_event* pEvent,
195 bigtime_t howLate,
196 bool realTimeEvent) {
198 ASSERT(pEvent);
200 switch(pEvent->type) {
201 case BTimedEventQueue::B_PARAMETER:
202 handleParameterEvent(pEvent);
203 break;
205 case BTimedEventQueue::B_START:
206 handleStartEvent(pEvent);
207 break;
209 case BTimedEventQueue::B_STOP:
210 handleStopEvent(pEvent);
211 break;
213 default:
214 ignoreEvent(pEvent);
215 break;
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."
226 void FlangerNode::NodeRegistered() {
228 PRINT(("FlangerNode::NodeRegistered()\n"));
230 // figure preferred ('template') format
231 m_preferredFormat.type = B_MEDIA_RAW_AUDIO;
232 getPreferredFormat(m_preferredFormat);
234 // initialize current format
235 m_format.type = B_MEDIA_RAW_AUDIO;
236 m_format.u.raw_audio = media_raw_audio_format::wildcard;
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);
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);
254 // init parameters
255 initParameterValues();
256 initParameterWeb();
258 // Start the BMediaEventLooper thread
259 SetPriority(B_REAL_TIME_PRIORITY);
260 Run();
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."
268 bigtime_t FlangerNode::OfflineTime() {
269 // +++++
270 return 0LL;
273 // -------------------------------------------------------- //
274 // *** BBufferConsumer
275 // -------------------------------------------------------- //
277 status_t FlangerNode::AcceptFormat(
278 const media_destination& destination,
279 media_format* pioFormat) {
281 PRINT(("FlangerNode::AcceptFormat()\n"));
283 // sanity checks
284 if(destination != m_input.destination) {
285 PRINT(("\tbad destination\n"));
286 return B_MEDIA_BAD_DESTINATION;
288 if(pioFormat->type != B_MEDIA_RAW_AUDIO) {
289 PRINT(("\tnot B_MEDIA_RAW_AUDIO\n"));
290 return B_MEDIA_BAD_FORMAT;
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;
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."
303 void FlangerNode::BufferReceived(
304 BBuffer* pBuffer) {
305 ASSERT(pBuffer);
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;
316 if(pBuffer->Header()->time_source != TimeSource()->ID()) {
317 PRINT(("* timesource mismatch\n"));
320 // check output
321 if(m_output.destination == media_destination::null ||
322 !m_outputEnabled) {
323 pBuffer->Recycle();
324 return;
327 // process and retransmit buffer
328 filterBuffer(pBuffer);
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();
336 // sent!
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().
343 status_t
344 FlangerNode::Connected(const media_source& source,
345 const media_destination& destination, const media_format& format,
346 media_input* poInput)
348 PRINT(("FlangerNode::Connected()\n"
349 "\tto source %" B_PRId32 "\n", source.id));
351 // sanity check
352 if(destination != m_input.destination) {
353 PRINT(("\tbad destination\n"));
354 return B_MEDIA_BAD_DESTINATION;
356 if(m_input.source != media_source::null) {
357 PRINT(("\talready connected\n"));
358 return B_MEDIA_ALREADY_CONNECTED;
361 // initialize input
362 m_input.source = source;
363 m_input.format = format;
364 *poInput = m_input;
366 // store format (this now constrains the output format)
367 m_format = format;
369 return B_OK;
372 void FlangerNode::Disconnected(
373 const media_source& source,
374 const media_destination& destination) {
376 PRINT(("FlangerNode::Disconnected()\n"));
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;
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;
390 // mark disconnected
391 m_input.source = media_source::null;
393 // no output? clear format:
394 if(m_output.destination == media_destination::null) {
395 m_format.u.raw_audio = media_raw_audio_format::wildcard;
398 m_input.format = m_format;
402 void FlangerNode::DisposeInputCookie(
403 int32 cookie) {}
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.
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.
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."
417 status_t FlangerNode::FormatChanged(
418 const media_source& source,
419 const media_destination& destination,
420 int32 changeTag,
421 const media_format& newFormat) {
423 // flat-out deny format changes
424 return B_MEDIA_BAD_FORMAT;
427 status_t FlangerNode::GetLatencyFor(
428 const media_destination& destination,
429 bigtime_t* poLatency,
430 media_node_id* poTimeSource) {
432 PRINT(("FlangerNode::GetLatencyFor()\n"));
434 // sanity check
435 if(destination != m_input.destination) {
436 PRINT(("\tbad destination\n"));
437 return B_MEDIA_BAD_DESTINATION;
440 *poLatency = m_downstreamLatency + m_processingLatency;
441 PRINT(("\treturning %" B_PRIdBIGTIME "\n", *poLatency));
442 *poTimeSource = TimeSource()->ID();
443 return B_OK;
446 status_t FlangerNode::GetNextInput(
447 int32* pioCookie,
448 media_input* poInput) {
450 if(*pioCookie)
451 return B_BAD_INDEX;
453 ++*pioCookie;
454 *poInput = m_input;
455 return B_OK;
458 void FlangerNode::ProducerDataStatus(
459 const media_destination& destination,
460 int32 status,
461 bigtime_t tpWhen) {
463 PRINT(("FlangerNode::ProducerDataStatus()\n"));
465 // sanity check
466 if(destination != m_input.destination) {
467 PRINT(("\tbad destination\n"));
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)));
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."
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) {
495 PRINT(("FlangerNode::SeekTagRequested()\n"
496 "\tNot implemented.\n"));
497 return B_ERROR;
500 // -------------------------------------------------------- //
501 // *** BBufferProducer
502 // -------------------------------------------------------- //
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.
511 // The previousTag may be NULL.
512 // Return B_OK if all is well; otherwise return an appropriate error code."
514 void FlangerNode::AdditionalBufferRequested(
515 const media_source& source,
516 media_buffer_id previousBufferID,
517 bigtime_t previousTime,
518 const media_seek_tag* pPreviousTag) {
520 PRINT(("FlangerNode::AdditionalBufferRequested\n"
521 "\tOffline mode not implemented."));
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) {
531 PRINT(("FlangerNode::Connect()\n"));
532 status_t err;
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;
542 // connection established:
543 strncpy(pioName, m_output.name, B_MEDIA_NAME_LENGTH);
544 m_output.destination = destination;
545 m_format = format;
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)));
553 PRINT(("\tdownstream latency = %" B_PRIdBIGTIME "\n", m_downstreamLatency));
555 // prepare the filter
556 initFilter();
558 // figure processing time
559 m_processingLatency = calcProcessingLatency();
560 PRINT(("\tprocessing latency = %" B_PRIdBIGTIME "\n", m_processingLatency));
562 // store summed latency
563 SetEventLatency(m_downstreamLatency + m_processingLatency);
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)));
575 // cache buffer duration
576 SetBufferDuration(
577 buffer_duration(
578 m_format.u.raw_audio));
581 void FlangerNode::Disconnect(
582 const media_source& source,
583 const media_destination& destination) {
585 PRINT(("FlangerNode::Disconnect()\n"));
587 // sanity checks
588 if(source != m_output.source) {
589 PRINT(("\tbad source\n"));
590 return;
592 if(destination != m_output.destination) {
593 PRINT(("\tbad destination\n"));
594 return;
597 // clean up
598 m_output.destination = media_destination::null;
600 // no input? clear format:
601 if(m_input.source == media_source::null) {
602 m_format.u.raw_audio = media_raw_audio_format::wildcard;
605 m_output.format = m_format;
607 // +++++ other cleanup goes here
610 status_t FlangerNode::DisposeOutputCookie(
611 int32 cookie) {
612 return B_OK;
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;
625 m_outputEnabled = enabled;
628 status_t FlangerNode::FormatChangeRequested(
629 const media_source& source,
630 const media_destination& destination,
631 media_format* pioFormat,
632 int32* _deprecated_) {
634 // deny
635 PRINT(("FlangerNode::FormatChangeRequested()\n"
636 "\tNot supported.\n"));
638 return B_MEDIA_BAD_FORMAT;
641 status_t FlangerNode::FormatProposal(
642 const media_source& source,
643 media_format* pioFormat) {
645 PRINT(("FlangerNode::FormatProposal()\n"));
647 if(source != m_output.source) {
648 PRINT(("\tbad source\n"));
649 return B_MEDIA_BAD_SOURCE;
652 if(pioFormat->type != B_MEDIA_RAW_AUDIO) {
653 PRINT(("\tbad type\n"));
654 return B_MEDIA_BAD_FORMAT;
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;
665 status_t FlangerNode::FormatSuggestionRequested(
666 media_type type,
667 int32 quality,
668 media_format* poFormat) {
670 PRINT(("FlangerNode::FormatSuggestionRequested()\n"));
671 if(type != B_MEDIA_RAW_AUDIO) {
672 PRINT(("\tbad type\n"));
673 return B_MEDIA_BAD_FORMAT;
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;
683 status_t FlangerNode::GetLatency(
684 bigtime_t* poLatency) {
686 PRINT(("FlangerNode::GetLatency()\n"));
687 *poLatency = EventLatency() + SchedulingLatency();
688 PRINT(("\treturning %" B_PRIdBIGTIME "\n", *poLatency));
690 return B_OK;
693 status_t FlangerNode::GetNextOutput(
694 int32* pioCookie,
695 media_output* poOutput) {
697 if(*pioCookie)
698 return B_BAD_INDEX;
700 ++*pioCookie;
701 *poOutput = m_output;
703 return B_OK;
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) {
719 PRINT(("FlangerNode::LatencyChanged()\n"));
721 if(source != m_output.source) {
722 PRINT(("\tBad source.\n"));
723 return;
725 if(destination != m_output.destination) {
726 PRINT(("\tBad destination.\n"));
727 return;
730 m_downstreamLatency = newLatency;
731 SetEventLatency(m_downstreamLatency + m_processingLatency);
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)));
744 void FlangerNode::LateNoticeReceived(
745 const media_source& source,
746 bigtime_t howLate,
747 bigtime_t tpWhen) {
749 PRINT(("FlangerNode::LateNoticeReceived()\n"
750 "\thowLate == %" B_PRIdBIGTIME "\n"
751 "\twhen == %" B_PRIdBIGTIME "\n", howLate, tpWhen));
753 if(source != m_output.source) {
754 PRINT(("\tBad source.\n"));
755 return;
758 if(m_input.source == media_source::null) {
759 PRINT(("\t!!! No input to blame.\n"));
760 return;
763 // +++++ check run mode?
765 // pass the buck, since this node doesn't schedule buffer
766 // production
767 NotifyLateProducer(
768 m_input.source,
769 howLate,
770 tpWhen);
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!
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) {
786 char formatStr[256];
787 string_for_format(*pioFormat, formatStr, 255);
788 PRINT(("FlangerNode::PrepareToConnect()\n"
789 "\tproposed format: %s\n", formatStr));
791 if(source != m_output.source) {
792 PRINT(("\tBad source.\n"));
793 return B_MEDIA_BAD_SOURCE;
795 if(m_output.destination != media_destination::null) {
796 PRINT(("\tAlready connected.\n"));
797 return B_MEDIA_ALREADY_CONNECTED;
800 if(pioFormat->type != B_MEDIA_RAW_AUDIO) {
801 PRINT(("\tBad format type.\n"));
802 return B_MEDIA_BAD_FORMAT;
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);
812 if(err < B_OK) {
813 // no go
814 return err;
817 // fill in wildcards
818 specializeOutputFormat(*pioFormat);
820 // reserve the output
821 m_output.destination = destination;
822 m_output.format = *pioFormat;
824 // pass back source & output name
825 *poSource = m_output.source;
826 strncpy(poName, m_output.name, B_MEDIA_NAME_LENGTH);
828 return B_OK;
831 status_t FlangerNode::SetBufferGroup(
832 const media_source& source,
833 BBufferGroup* pGroup) {
835 PRINT(("FlangerNode::SetBufferGroup()\n"));
836 if(source != m_output.source) {
837 PRINT(("\tBad source.\n"));
838 return B_MEDIA_BAD_SOURCE;
841 if(m_input.source == media_source::null) {
842 PRINT(("\tNo producer to send buffers to.\n"));
843 return B_ERROR;
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);
856 status_t FlangerNode::SetPlayRate(
857 int32 numerator,
858 int32 denominator) {
859 // not supported
860 return B_ERROR;
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;
873 // -------------------------------------------------------- //
874 // *** BControllable
875 // -------------------------------------------------------- //
877 status_t FlangerNode::GetParameterValue(
878 int32 id,
879 bigtime_t* poLastChangeTime,
880 void* poValue,
881 size_t* pioSize) {
883 // PRINT(("FlangerNode::GetParameterValue()\n"));
885 // all parameters are floats
886 if(*pioSize < sizeof(float)) {
887 return B_NO_MEMORY;
890 *pioSize = sizeof(float);
891 switch(id) {
892 case P_MIX_RATIO:
893 *(float*)poValue = m_fMixRatio;
894 *poLastChangeTime = m_tpMixRatioChanged;
895 break;
897 case P_SWEEP_RATE:
898 *(float*)poValue = m_fSweepRate;
899 *poLastChangeTime = m_tpSweepRateChanged;
900 break;
902 case P_DELAY:
903 *(float*)poValue = m_fDelay;
904 *poLastChangeTime = m_tpDelayChanged;
905 break;
907 case P_DEPTH:
908 *(float*)poValue = m_fDepth;
909 *poLastChangeTime = m_tpDepthChanged;
910 break;
912 case P_FEEDBACK:
913 *(float*)poValue = m_fFeedback;
914 *poLastChangeTime = m_tpFeedbackChanged;
915 break;
917 default:
918 return B_ERROR;
921 return B_OK;
924 void FlangerNode::SetParameterValue(
925 int32 id,
926 bigtime_t changeTime,
927 const void* pValue,
928 size_t size) {
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;
939 // this is from ToneProducer. it's fishy.
940 // if(size > sizeof(float))
941 // size = sizeof(float);
943 media_timed_event ev(
944 changeTime,
945 BTimedEventQueue::B_PARAMETER,
947 BTimedEventQueue::B_NO_CLEANUP,
948 size,
950 (char*)pValue, size);
951 EventQueue()->AddEvent(ev);
952 break;
957 // -------------------------------------------------------- //
958 // *** HandleEvent() impl
959 // -------------------------------------------------------- //
961 void FlangerNode::handleParameterEvent(
962 const media_timed_event* pEvent) {
964 float value = *(float*)pEvent->user_data;
965 int32 id = pEvent->bigdata;
966 size_t size = pEvent->data;
967 bigtime_t now = TimeSource()->Now();
969 switch(id) {
970 case P_MIX_RATIO:
971 if(value == m_fMixRatio)
972 break;
974 // set
975 m_fMixRatio = value;
976 m_tpMixRatioChanged = now;
977 // broadcast
978 BroadcastNewParameterValue(
979 now,
981 &m_fMixRatio,
982 size);
983 break;
985 case P_SWEEP_RATE:
986 if(value == m_fSweepRate)
987 break;
989 // set
990 m_fSweepRate = value;
991 m_tpSweepRateChanged = now;
993 if(m_output.destination != media_destination::null) {
994 m_fThetaInc = calc_sweep_delta(
995 m_format.u.raw_audio,
996 m_fSweepRate);
999 // broadcast
1000 BroadcastNewParameterValue(
1001 now,
1003 &m_fSweepRate,
1004 size);
1005 break;
1007 case P_DELAY:
1008 if(value == m_fDelay)
1009 break;
1011 // set
1012 m_fDelay = value;
1013 m_tpDelayChanged = now;
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);
1021 // broadcast
1022 BroadcastNewParameterValue(
1023 now,
1025 &m_fDelay,
1026 size);
1027 break;
1029 case P_DEPTH:
1030 if(value == m_fDepth)
1031 break;
1033 // set
1034 m_fDepth = value;
1035 m_tpDepthChanged = now;
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);
1046 // broadcast
1047 BroadcastNewParameterValue(
1048 now,
1050 &m_fDepth,
1051 size);
1052 break;
1054 case P_FEEDBACK:
1055 if(value == m_fFeedback)
1056 break;
1058 // set
1059 m_fFeedback = value;
1060 m_tpFeedbackChanged = now;
1061 // broadcast
1062 BroadcastNewParameterValue(
1063 now,
1065 &m_fFeedback,
1066 size);
1067 break;
1071 void FlangerNode::handleStartEvent(
1072 const media_timed_event* pEvent) {
1073 PRINT(("FlangerNode::handleStartEvent\n"));
1075 startFilter();
1078 void FlangerNode::handleStopEvent(
1079 const media_timed_event* pEvent) {
1080 PRINT(("FlangerNode::handleStopEvent\n"));
1082 stopFilter();
1085 void FlangerNode::ignoreEvent(
1086 const media_timed_event* pEvent) {
1087 PRINT(("FlangerNode::ignoreEvent\n"));
1092 // -------------------------------------------------------- //
1093 // *** internal operations
1094 // -------------------------------------------------------- //
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);
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;
1107 // ioFormat.u.raw_audio.frame_rate = 44100.0;
1108 // ioFormat.u.raw_audio.buffer_size = 0x1000;
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.
1118 status_t FlangerNode::validateProposedFormat(
1119 const media_format& preferredFormat,
1120 media_format& ioProposedFormat) {
1122 char formatStr[256];
1123 PRINT(("FlangerNode::validateProposedFormat()\n"));
1125 ASSERT(preferredFormat.type == B_MEDIA_RAW_AUDIO);
1127 string_for_format(preferredFormat, formatStr, 255);
1128 PRINT(("\ttemplate format: %s\n", formatStr));
1130 string_for_format(ioProposedFormat, formatStr, 255);
1131 PRINT(("\tproposed format: %s\n", formatStr));
1133 status_t err = B_OK;
1135 if(ioProposedFormat.type != B_MEDIA_RAW_AUDIO) {
1136 // out of the ballpark
1137 ioProposedFormat = preferredFormat;
1138 return B_MEDIA_BAD_FORMAT;
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;
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;
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;
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;
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;
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;
1188 if(err != B_OK) {
1189 string_for_format(ioProposedFormat, formatStr, 255);
1190 PRINT((
1191 "\tformat conflict; suggesting:\n\tformat %s\n", formatStr));
1194 return err;
1197 // fill in wildcards in the given format.
1198 // (assumes the format passes validateProposedFormat().)
1199 void FlangerNode::specializeOutputFormat(
1200 media_format& ioFormat) {
1202 char formatStr[256];
1203 string_for_format(ioFormat, formatStr, 255);
1204 PRINT(("FlangerNode::specializeOutputFormat()\n"
1205 "\tinput format: %s\n", formatStr));
1207 ASSERT(ioFormat.type == B_MEDIA_RAW_AUDIO);
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;
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;
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;
1229 string_for_format(ioFormat, formatStr, 255);
1230 PRINT(("\toutput format: %s\n", formatStr));
1233 // set parameters to their default settings
1234 void FlangerNode::initParameterValues() {
1235 m_fMixRatio = 0.5;
1236 m_tpMixRatioChanged = 0LL;
1238 m_fSweepRate = 0.1;
1239 m_tpSweepRateChanged = 0LL;
1241 m_fDelay = 10.0;
1242 m_tpDelayChanged = 0LL;
1244 m_fDepth = 25.0;
1245 m_tpDepthChanged = 0LL;
1247 m_fFeedback = 0.1;
1248 m_tpFeedbackChanged = 0LL;
1251 // create and register a parameter web
1252 void FlangerNode::initParameterWeb() {
1253 BParameterWeb* pWeb = new BParameterWeb();
1254 BParameterGroup* pTopGroup = pWeb->MakeGroup("FlangerNode Parameters");
1256 BNullParameter* label;
1257 BContinuousParameter* value;
1258 BParameterGroup* g;
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);
1268 value = g->MakeContinuousParameter(
1269 P_MIX_RATIO,
1270 B_MEDIA_NO_TYPE,
1272 B_GAIN, "", 0.0, 1.0, 0.05);
1273 label->AddOutput(value);
1274 value->AddInput(label);
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);
1284 value = g->MakeContinuousParameter(
1285 P_SWEEP_RATE,
1286 B_MEDIA_NO_TYPE,
1288 B_GAIN, "Hz", 0.01, 10.0, 0.01);
1289 label->AddOutput(value);
1290 value->AddInput(label);
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);
1300 value = g->MakeContinuousParameter(
1301 P_DELAY,
1302 B_MEDIA_NO_TYPE,
1304 B_GAIN, "ms", 0.1, s_fMaxDelay/2.0, 0.1);
1305 label->AddOutput(value);
1306 value->AddInput(label);
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);
1316 value = g->MakeContinuousParameter(
1317 P_DEPTH,
1318 B_MEDIA_NO_TYPE,
1320 B_GAIN, "ms", 1.0, s_fMaxDelay/4.0, 0.1);
1321 label->AddOutput(value);
1322 value->AddInput(label);
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);
1332 value = g->MakeContinuousParameter(
1333 P_FEEDBACK,
1334 B_MEDIA_NO_TYPE,
1336 B_GAIN, "", 0.0, 1.0, 0.01);
1337 label->AddOutput(value);
1338 value->AddInput(label);
1340 // * Install parameter web
1341 SetParameterWeb(pWeb);
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);
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();
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);
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));
1376 void FlangerNode::startFilter() {
1377 PRINT(("FlangerNode::startFilter()\n"));
1379 void FlangerNode::stopFilter() {
1380 PRINT(("FlangerNode::stopFilter()\n"));
1383 // figure processing latency by doing 'dry runs' of filterBuffer()
1384 bigtime_t FlangerNode::calcProcessingLatency() {
1385 PRINT(("FlangerNode::calcProcessingLatency()\n"));
1387 if(m_output.destination == media_destination::null) {
1388 PRINT(("\tNot connected.\n"));
1389 return 0LL;
1392 // allocate a temporary buffer group
1393 BBufferGroup* pTestGroup = new BBufferGroup(
1394 m_output.format.u.raw_audio.buffer_size, 1);
1396 // fetch a buffer
1397 BBuffer* pBuffer = pTestGroup->RequestBuffer(
1398 m_output.format.u.raw_audio.buffer_size);
1399 ASSERT(pBuffer);
1401 pBuffer->Header()->type = B_MEDIA_RAW_AUDIO;
1402 pBuffer->Header()->size_used = m_output.format.u.raw_audio.buffer_size;
1404 // run the test
1405 bigtime_t preTest = system_time();
1406 filterBuffer(pBuffer);
1407 bigtime_t elapsed = system_time()-preTest;
1409 // clean up
1410 pBuffer->Recycle();
1411 delete pTestGroup;
1413 // reset filter state
1414 initFilter();
1416 return elapsed;
1419 // filter buffer data in place
1421 // +++++ add 2-channel support 15sep991
1424 const size_t MAX_CHANNELS = 2;
1426 struct _frame {
1427 float channel[MAX_CHANNELS];
1430 void FlangerNode::filterBuffer(
1431 BBuffer* pBuffer) {
1433 if(!m_pDelayBuffer)
1434 return;
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
1444 AudioBuffer input(m_format.u.raw_audio, pBuffer);
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;
1452 uint32 samples = input.frames() * channels;
1453 for(uint32 inPos = 0; inPos < samples; ++inPos) {
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];
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();
1467 // float delayed;
1470 // read low-index (possibly only) frame
1471 _frame delayedFrame = {};
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];
1479 if(readFrameLo != (int32)fReadFrame) {
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);
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];
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);
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);
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);
1523 // advance write position
1524 if(++m_delayWriteFrame >= m_pDelayBuffer->frames())
1525 m_delayWriteFrame = 0;
1527 // advance read offset ('LFO')
1528 m_fTheta += m_fThetaInc;
1529 if(m_fTheta > 2 * M_PI)
1530 m_fTheta -= 2 * M_PI;
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;
1544 /*! Figure the rate at which the (radial) read offset changes,
1545 based on the given sweep rate (in Hz)
1547 float
1548 calc_sweep_delta(const media_raw_audio_format& format, float fRate)
1550 return 2 * M_PI * fRate / format.frame_rate;
1553 /*! Figure the base delay (in frames) based on the given
1554 sweep delay/depth (in msec)
1556 float
1557 calc_sweep_base(const media_raw_audio_format& format, float delay, float depth)
1559 return (format.frame_rate * (delay + depth)) / 1000.0;
1563 float
1564 calc_sweep_factor(const media_raw_audio_format& format, float depth)
1566 return (format.frame_rate * depth) / 1000.0;
1570 // END -- FlangerNode.cpp --