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