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	// init input
388	m_input.destination.port = ControlPort();
389	m_input.destination.id = ID_AUDIO_INPUT;
390	m_input.node = Node();
391	m_input.source = media_source::null;
392
393	m_input.format.type = B_MEDIA_RAW_AUDIO;
394	err = getRequiredInputFormat(m_input.format);
395	ASSERT(err == B_OK);
396
397	strncpy(m_input.name, "Audio Input", B_MEDIA_NAME_LENGTH);
398
399	// init output
400	m_output.source.port = ControlPort();
401	m_output.source.id = ID_AUDIO_MIX_OUTPUT;
402	m_output.node = Node();
403	m_output.destination = media_destination::null;
404
405	m_output.format.type = B_MEDIA_RAW_AUDIO;
406	err = getRequiredOutputFormat(m_output.format);
407	ASSERT(err == B_OK);
408
409	strncpy(m_output.name, "Audio Output", B_MEDIA_NAME_LENGTH);
410
411	// init parameters
412	initParameterWeb();
413
414	// Start the BMediaEventLooper thread
415	SetPriority(B_REAL_TIME_PRIORITY);
416	Run();
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 %" B_PRId32 "\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 %" B_PRId32 ", got %" B_PRId32
617				"\n", m_input.source.id, source.id));
618		return;
619	}
620	if(destination != m_input.destination) {
621		PRINT(("\tdestination mismatch: expected ID %" B_PRId32 ", got %"
622				B_PRId32 "\n", 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 %" B_PRIdBIGTIME "\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(%" B_PRId32 " at %"
711			B_PRIdBIGTIME ")\n", status, tpWhen));
712
713	// sanity check
714	if(destination != m_input.destination) {
715		PRINT(("\tbad destination\n"));
716	}
717
718	if(m_output.destination != media_destination::null) {
719		// pass status downstream
720		status_t err = SendDataStatus(
721			status,
722			m_output.destination,
723			tpWhen);
724		if(err < B_OK) {
725			PRINT(("\tSendDataStatus(): %s\n", strerror(err)));
726		}
727	}
728}
729
730// "This function is provided to aid in supporting media formats in which the
731//  outer encapsulation layer doesn't supply timing information. Producers will
732//  tag the buffers they generate with seek tags; these tags can be used to
733//  locate key frames in the media data."
734
735status_t AudioFilterNode::SeekTagRequested(
736	const media_destination&		destination,
737	bigtime_t										targetTime,
738	uint32											flags,
739	media_seek_tag*							outSeekTag,
740	bigtime_t*									outTaggedTime,
741	uint32*											outFlags) {
742
743	// +++++
744	PRINT(("AudioFilterNode::SeekTagRequested()\n"
745		"\tNot implemented.\n"));
746	return B_ERROR;
747}
748
749// -------------------------------------------------------- //
750// *** BBufferProducer
751// -------------------------------------------------------- //
752
753// "When a consumer calls BBufferConsumer::RequestAdditionalBuffer(), this
754//  function is called as a result. Its job is to call SendBuffer() to
755//  immediately send the next buffer to the consumer. The previousBufferID,
756//  previousTime, and previousTag arguments identify the last buffer the
757//  consumer received. Your node should respond by sending the next buffer
758//  after the one described.
759//
760//  The previousTag may be NULL.
761//  Return B_OK if all is well; otherwise return an appropriate error code."
762
763void AudioFilterNode::AdditionalBufferRequested(
764	const media_source&					source,
765	media_buffer_id							previousBufferID,
766	bigtime_t										previousTime,
767	const media_seek_tag*				previousTag) {
768
769	// +++++
770	PRINT(("AudioFilterNode::AdditionalBufferRequested\n"
771		"\tOffline mode not implemented."));
772}
773
774void AudioFilterNode::Connect(
775	status_t										status,
776	const media_source&					source,
777	const media_destination&		destination,
778	const media_format&					format,
779	char*												ioName) {
780
781	PRINT(("AudioFilterNode::Connect()\n"));
782	status_t err;
783
784#if DEBUG
785	char formatStr[256];
786	string_for_format(format, formatStr, 255);
787	PRINT(("\tformat: %s\n", formatStr));
788#endif
789
790	// connection failed?
791	if(status < B_OK) {
792		PRINT(("\tCONNECTION FAILED: Status '%s'\n", strerror(status)));
793		// 'unreserve' the output
794		m_output.destination = media_destination::null;
795		return;
796	}
797
798	// connection established:
799	strncpy(ioName, m_output.name, B_MEDIA_NAME_LENGTH);
800	m_output.destination = destination;
801
802	// figure downstream latency
803	media_node_id timeSource;
804	err = FindLatencyFor(m_output.destination, &m_downstreamLatency, &timeSource);
805	if(err < B_OK) {
806		PRINT(("\t!!! FindLatencyFor(): %s\n", strerror(err)));
807	}
808	PRINT(("\tdownstream latency = %" B_PRIdBIGTIME "\n", m_downstreamLatency));
809
810//	// prepare the filter
811//	initFilter();
812//
813//	// figure processing time
814//	m_processingLatency = calcProcessingLatency();
815//	PRINT(("\tprocessing latency = %Ld\n", m_processingLatency));
816//
817//	// store summed latency
818//	SetEventLatency(m_downstreamLatency + m_processingLatency);
819//
820//	if(m_input.source != media_source::null) {
821//		// pass new latency upstream
822//		err = SendLatencyChange(
823//			m_input.source,
824//			m_input.destination,
825//			EventLatency() + SchedulingLatency());
826//		if(err < B_OK)
827//			PRINT(("\t!!! SendLatencyChange(): %s\n", strerror(err)));
828//	}
829
830	// cache buffer duration
831	SetBufferDuration(
832		buffer_duration(
833			m_output.format.u.raw_audio));
834
835	// [re-]initialize operation
836	updateOperation();
837
838	// initialize buffer group if necessary
839	updateBufferGroup();
840}
841
842void AudioFilterNode::Disconnect(
843	const media_source&					source,
844	const media_destination&		destination) {
845
846	PRINT(("AudioFilterNode::Disconnect()\n"));
847
848	// sanity checks
849	if(source != m_output.source) {
850		PRINT(("\tbad source\n"));
851		return;
852	}
853	if(destination != m_output.destination) {
854		PRINT(("\tbad destination\n"));
855		return;
856	}
857
858	// clean up
859	m_output.destination = media_destination::null;
860
861#ifdef DEBUG
862	status_t err =
863#endif
864	getRequiredOutputFormat(m_output.format);
865	ASSERT(err == B_OK);
866
867	updateBufferGroup();
868
869	if(m_op) {
870		delete m_op;
871		m_op = 0;
872	}
873}
874
875status_t AudioFilterNode::DisposeOutputCookie(
876	int32												cookie) {
877	return B_OK;
878}
879
880void AudioFilterNode::EnableOutput(
881	const media_source&					source,
882	bool												enabled,
883	int32* _deprecated_) {
884
885	PRINT(("AudioFilterNode::EnableOutput()\n"));
886	if(source != m_output.source) {
887		PRINT(("\tbad source\n"));
888		return;
889	}
890
891	m_outputEnabled = enabled;
892}
893
894status_t AudioFilterNode::FormatChangeRequested(
895	const media_source&					source,
896	const media_destination&		destination,
897	media_format*								ioFormat,
898	int32* _deprecated_) {
899
900	// deny +++++ for now
901	PRINT(("AudioFilterNode::FormatChangeRequested()\n"
902		"\tNot supported.\n"));
903
904	return B_MEDIA_BAD_FORMAT;
905}
906
907status_t AudioFilterNode::FormatProposal(
908	const media_source&					source,
909	media_format*								ioFormat) {
910
911	PRINT(("AudioFilterNode::FormatProposal()\n"));
912	status_t err;
913
914	if(source != m_output.source) {
915		PRINT(("\tbad source\n"));
916		return B_MEDIA_BAD_SOURCE;
917	}
918
919	if(ioFormat->type != B_MEDIA_RAW_AUDIO) {
920		PRINT(("\tbad type\n"));
921		return B_MEDIA_BAD_FORMAT;
922	}
923
924	// validate against required format
925	media_format required;
926	required.type = B_MEDIA_RAW_AUDIO;
927	err = getRequiredOutputFormat(required);
928	ASSERT(err == B_OK);
929
930	err = validateProposedOutputFormat(
931		required,
932		*ioFormat);
933	if(err < B_OK)
934		return err;
935
936//	// specialize the format
937//	media_format testFormat = *ioFormat;
938//	specializeOutputFormat(testFormat);
939//
940//	// if the input is connected, ask the factory for a matching operation
941//	if(m_input.source != media_source::null) {
942//		ASSERT(m_opFactory);
943//		IAudioOp* op = m_opFactory->createOp(
944//			this,
945//			m_input.format.u.raw_audio,
946//			testFormat.u.raw_audio);
947//
948//		if(!op) {
949//			// format passed validation, but factory failed to provide a
950//			// capable operation object
951//			char fmt[256];
952//			string_for_format(*ioFormat, fmt, 255);
953//			PRINT((
954//				"*** FormatProposal(): format validated, but no operation found:\n"
955//				"    %s\n",
956//				fmt));
957//
958//			return B_MEDIA_BAD_FORMAT;
959//		}
960//		// clean up
961//		delete op;
962//	}
963
964	// format passed inspection
965	return B_OK;
966}
967
968status_t AudioFilterNode::FormatSuggestionRequested(
969	media_type									type,
970	int32												quality,
971	media_format*								outFormat) {
972
973	PRINT(("AudioFilterNode::FormatSuggestionRequested()\n"));
974	if(type != B_MEDIA_RAW_AUDIO) {
975		PRINT(("\tbad type\n"));
976		return B_MEDIA_BAD_FORMAT;
977	}
978
979	outFormat->type = type;
980	return getPreferredOutputFormat(*outFormat);
981}
982
983status_t AudioFilterNode::GetLatency(
984	bigtime_t*									outLatency) {
985
986	PRINT(("AudioFilterNode::GetLatency()\n"));
987	*outLatency = EventLatency() + SchedulingLatency();
988	PRINT(("\treturning %" B_PRIdBIGTIME "\n", *outLatency));
989
990	return B_OK;
991}
992
993status_t AudioFilterNode::GetNextOutput(
994	int32*											ioCookie,
995	media_output*								outOutput) {
996
997	if(*ioCookie)
998		return B_BAD_INDEX;
999
1000	++*ioCookie;
1001	*outOutput = m_output;
1002
1003	return B_OK;
1004}
1005
1006
1007// "This hook function is called when a BBufferConsumer that's receiving data
1008//  from you determines that its latency has changed. It will call its
1009//  BBufferConsumer::SendLatencyChange() function, and in response, the Media
1010//  Server will call your LatencyChanged() function.  The source argument
1011//  indicates your output that's involved in the connection, and destination
1012//  specifies the input on the consumer to which the connection is linked.
1013//  newLatency is the consumer's new latency. The flags are currently unused."
1014void AudioFilterNode::LatencyChanged(
1015	const media_source&					source,
1016	const media_destination&		destination,
1017	bigtime_t										newLatency,
1018	uint32											flags) {
1019
1020	PRINT(("AudioFilterNode::LatencyChanged()\n"));
1021
1022	if(source != m_output.source) {
1023		PRINT(("\tBad source.\n"));
1024		return;
1025	}
1026	if(destination != m_output.destination) {
1027		PRINT(("\tBad destination.\n"));
1028		return;
1029	}
1030
1031	m_downstreamLatency = newLatency;
1032	SetEventLatency(m_downstreamLatency + m_processingLatency);
1033
1034	if(m_input.source != media_source::null) {
1035		// pass new latency upstream
1036		status_t err = SendLatencyChange(
1037			m_input.source,
1038			m_input.destination,
1039			EventLatency() + SchedulingLatency());
1040		if(err < B_OK)
1041			PRINT(("\t!!! SendLatencyChange(): %s\n", strerror(err)));
1042	}
1043}
1044
1045void AudioFilterNode::LateNoticeReceived(
1046	const media_source&					source,
1047	bigtime_t										howLate,
1048	bigtime_t										tpWhen) {
1049
1050	PRINT(("AudioFilterNode::LateNoticeReceived()\n"
1051		"\thowLate == %" B_PRIdBIGTIME "\n"
1052		"\twhen    == %" B_PRIdBIGTIME "\n", howLate, tpWhen));
1053
1054	if(source != m_output.source) {
1055		PRINT(("\tBad source.\n"));
1056		return;
1057	}
1058
1059	if(m_input.source == media_source::null) {
1060		PRINT(("\t!!! No input to blame.\n"));
1061		return;
1062	}
1063
1064	// +++++ check run mode?
1065
1066	// pass the buck, since this node doesn't schedule buffer
1067	// production
1068	NotifyLateProducer(
1069		m_input.source,
1070		howLate,
1071		tpWhen);
1072}
1073
1074// PrepareToConnect() is the second stage of format negotiations that happens
1075// inside BMediaRoster::Connect().  At this point, the consumer's AcceptFormat()
1076// method has been called, and that node has potentially changed the proposed
1077// format.  It may also have left wildcards in the format.  PrepareToConnect()
1078// *must* fully specialize the format before returning!
1079
1080status_t AudioFilterNode::PrepareToConnect(
1081	const media_source&					source,
1082	const media_destination&		destination,
1083	media_format*								ioFormat,
1084	media_source*								outSource,
1085	char*												outName) {
1086
1087	status_t err;
1088	char formatStr[256];
1089	string_for_format(*ioFormat, formatStr, 255);
1090	PRINT(("AudioFilterNode::PrepareToConnect()\n"
1091		"\tproposed format: %s\n", formatStr));
1092
1093	if(source != m_output.source) {
1094		PRINT(("\tBad source.\n"));
1095		return B_MEDIA_BAD_SOURCE;
1096	}
1097	if(m_output.destination != media_destination::null) {
1098		PRINT(("\tAlready connected.\n"));
1099		return B_MEDIA_ALREADY_CONNECTED;
1100	}
1101
1102	if(ioFormat->type != B_MEDIA_RAW_AUDIO) {
1103		PRINT(("\tBad format type.\n"));
1104		return B_MEDIA_BAD_FORMAT;
1105	}
1106
1107	// do a final validity check:
1108	media_format required;
1109	required.type = B_MEDIA_RAW_AUDIO;
1110	err = getRequiredOutputFormat(required);
1111	ASSERT(err == B_OK);
1112
1113	err = validateProposedOutputFormat(
1114		required,	*ioFormat);
1115
1116	if(err < B_OK) {
1117		// no go
1118		return err;
1119	}
1120
1121	// fill in wildcards
1122	specializeOutputFormat(*ioFormat);
1123
1124	string_for_format(*ioFormat, formatStr, 255);
1125	PRINT(("FINAL FORMAT: %s\n", formatStr));
1126
1127	// reserve the output
1128	m_output.destination = destination;
1129	m_output.format = *ioFormat;
1130
1131	// pass back source & output name
1132	*outSource = m_output.source;
1133	strncpy(outName, m_output.name, B_MEDIA_NAME_LENGTH);
1134
1135	return B_OK;
1136}
1137
1138status_t AudioFilterNode::SetBufferGroup(
1139	const media_source&					source,
1140	BBufferGroup*								group) {
1141
1142	PRINT(("AudioFilterNode::SetBufferGroup()\n"));
1143	if(source != m_output.source) {
1144		PRINT(("\tBad source.\n"));
1145		return B_MEDIA_BAD_SOURCE;
1146	}
1147
1148//	if(m_input.source == media_source::null) {
1149//		PRINT(("\tNo producer to send buffers to.\n"));
1150//		return B_ERROR;
1151//	}
1152//
1153//	// +++++ is this right?  buffer-group selection gets
1154//	//       all asynchronous and weird...
1155//	int32 changeTag;
1156//	return SetOutputBuffersFor(
1157//		m_input.source,
1158//		m_input.destination,
1159//		group,
1160//		0, &changeTag);
1161
1162	// do it [8sep99]
1163	if(m_bufferGroup)
1164		delete m_bufferGroup;
1165	m_bufferGroup = group;
1166
1167	return B_OK;
1168}
1169
1170status_t AudioFilterNode::SetPlayRate(
1171	int32												numerator,
1172	int32												denominator) {
1173	// not supported
1174	return B_ERROR;
1175}
1176
1177status_t AudioFilterNode::VideoClippingChanged(
1178	const media_source&					source,
1179	int16												numShorts,
1180	int16*											clipData,
1181	const media_video_display_info& display,
1182	int32*											outFromChangeTag) {
1183	// not sane
1184	return B_ERROR;
1185}
1186
1187// -------------------------------------------------------- //
1188// *** BControllable
1189// -------------------------------------------------------- //
1190
1191status_t AudioFilterNode::GetParameterValue(
1192	int32												id,
1193	bigtime_t*									outLastChangeTime,
1194	void*												outValue,
1195	size_t*											ioSize) {
1196
1197	ASSERT(m_parameterSet);
1198	return m_parameterSet->getValue(
1199		id,
1200		outLastChangeTime,
1201		outValue,
1202		ioSize);
1203}
1204
1205void AudioFilterNode::SetParameterValue(
1206	int32												id,
1207	bigtime_t										changeTime,
1208	const void*									value,
1209	size_t											size) {
1210
1211	// not running? set parameter now
1212	if(RunState() != B_STARTED) {
1213		ASSERT(m_parameterSet);
1214		m_parameterSet->setValue(
1215			id,
1216			changeTime,
1217			value,
1218			size);
1219		return;
1220	}
1221
1222	// queue a parameter-change event
1223
1224	if(size > 64) { // +++++ hard-coded limitation in media_timed_event
1225		DEBUGGER((
1226			"!!! AudioFilterNode::SetParameterValue(): parameter data too large\n"));
1227	}
1228
1229	media_timed_event ev(
1230		changeTime,
1231		BTimedEventQueue::B_PARAMETER,
1232		0,
1233		BTimedEventQueue::B_NO_CLEANUP,
1234		size,
1235		id,
1236		(char*)value, size);
1237	EventQueue()->AddEvent(ev);
1238}
1239
1240// -------------------------------------------------------- //
1241// *** IAudioOpHost
1242// -------------------------------------------------------- //
1243
1244IParameterSet* AudioFilterNode::parameterSet() const {
1245	return m_parameterSet;
1246}
1247
1248// -------------------------------------------------------- //
1249// HandleEvent() impl.
1250// -------------------------------------------------------- //
1251
1252void AudioFilterNode::handleParameterEvent(
1253	const media_timed_event*		event) {
1254
1255	// retrieve encoded parameter data
1256	void* value = (void*)event->user_data;
1257	int32 id = event->bigdata;
1258	size_t size = event->data;
1259	bigtime_t changeTime = event->event_time;
1260	status_t err;
1261
1262	// hand to parameter set
1263	ASSERT(m_parameterSet);
1264	err = m_parameterSet->setValue(id, changeTime, value, size);
1265
1266	if(err < B_OK) {
1267		PRINT((
1268			"* AudioFilterNode::handleParameterEvent(): m_parameterSet->SetValue() failed:\n"
1269			"  %s\n", strerror(err)));
1270	}
1271}
1272
1273void AudioFilterNode::handleStartEvent(
1274	const media_timed_event*		event) {
1275	PRINT(("AudioFilterNode::handleStartEvent\n"));
1276
1277	// initialize the filter
1278	ASSERT(m_op);
1279	m_op->init();
1280}
1281
1282void AudioFilterNode::handleStopEvent(
1283	const media_timed_event*		event) {
1284
1285	PRINT(("AudioFilterNode::handleStopEvent\n"));
1286	// +++++
1287}
1288
1289void AudioFilterNode::ignoreEvent(
1290	const media_timed_event*		event) {
1291
1292	PRINT(("AudioFilterNode::ignoreEvent\n"));
1293}
1294
1295// -------------------------------------------------------- //
1296// *** internal operations
1297// -------------------------------------------------------- //
1298
1299status_t
1300AudioFilterNode::prepareFormatChange(const media_format &newFormat)
1301{
1302	media_format required;
1303	required.type = B_MEDIA_RAW_AUDIO;
1304	status_t err = getRequiredOutputFormat(required);
1305	ASSERT(err == B_OK);
1306
1307	media_format proposed = newFormat;
1308	err = validateProposedOutputFormat(
1309		required,
1310		proposed);
1311	return err;
1312}
1313
1314void
1315AudioFilterNode::doFormatChange(const media_format &newFormat)
1316{
1317	m_output.format = newFormat;
1318	updateOperation();
1319}
1320
1321
1322// create and register a parameter web
1323void AudioFilterNode::initParameterWeb() {
1324	ASSERT(m_parameterSet);
1325
1326	BParameterWeb* web = new BParameterWeb();
1327	BString groupName = Name();
1328	groupName << " Parameters";
1329	BParameterGroup* group = web->MakeGroup(groupName.String());
1330	m_parameterSet->populateGroup(group);
1331
1332	SetParameterWeb(web);
1333}
1334
1335// [re-]initialize operation if necessary
1336void AudioFilterNode::updateOperation() {
1337
1338	if(m_input.source == media_source::null ||
1339		m_output.destination == media_destination::null)
1340		// not fully connected; nothing to do
1341		return;
1342
1343	// ask the factory for an operation
1344	ASSERT(m_opFactory);
1345	IAudioOp* op = m_opFactory->createOp(
1346		this,
1347		m_input.format.u.raw_audio,
1348		m_output.format.u.raw_audio);
1349	if(!op) {
1350		PRINT((
1351			"!!! AudioFilterNode::updateOperation(): no operation created!\n"));
1352
1353		// clean up existing operation
1354		delete m_op;
1355		m_op = 0;
1356		return;
1357	}
1358
1359	// install new operation
1360	op->replace(m_op);
1361	m_op = op;
1362
1363	// do performance tests (what if I'm running? +++++)
1364
1365	m_processingLatency = calcProcessingLatency();
1366	PRINT(("\tprocessing latency = %" B_PRIdBIGTIME "\n", m_processingLatency));
1367
1368	// store summed latency
1369	SetEventLatency(m_downstreamLatency + m_processingLatency);
1370
1371	// pass new latency upstream
1372	status_t err = SendLatencyChange(
1373		m_input.source,
1374		m_input.destination,
1375		EventLatency() + SchedulingLatency());
1376	if(err < B_OK)
1377		PRINT(("\t!!! SendLatencyChange(): %s\n", strerror(err)));
1378}
1379
1380
1381// create or discard buffer group if necessary
1382void AudioFilterNode::updateBufferGroup() {
1383
1384	status_t err;
1385
1386	size_t inputSize = bytes_per_frame(m_input.format.u.raw_audio);
1387	size_t outputSize = bytes_per_frame(m_output.format.u.raw_audio);
1388
1389	if(m_input.source == media_source::null ||
1390		m_output.destination == media_destination::null ||
1391		inputSize >= outputSize) {
1392
1393		PRINT(("###### NO BUFFER GROUP NEEDED\n"));
1394
1395		// no internal buffer group needed
1396		if(m_bufferGroup) {
1397			// does this block? +++++
1398			delete m_bufferGroup;
1399			m_bufferGroup = 0;
1400		}
1401		return;
1402	}
1403
1404	int32 bufferCount = EventLatency() / BufferDuration() + 1 + 1;
1405
1406	// +++++
1407	// [e.moon 27sep99] this is a reasonable number of buffers,
1408	// but it fails with looped file-player node in BeOS 4.5.2.
1409	//
1410	if(bufferCount < 5)
1411		bufferCount = 5;
1412//	if(bufferCount < 3)
1413//		bufferCount = 3;
1414
1415	if(m_bufferGroup) {
1416
1417		// is the current group sufficient?
1418		int32 curBufferCount;
1419		err = m_bufferGroup->CountBuffers(&curBufferCount);
1420		if(err == B_OK && curBufferCount >= bufferCount) {
1421			BBuffer* buf = m_bufferGroup->RequestBuffer(
1422				outputSize, -1);
1423
1424			if(buf) {
1425				// yup
1426				buf->Recycle();
1427				return;
1428			}
1429		}
1430
1431		// nope, delete it to make way for the new one
1432		delete m_bufferGroup;
1433		m_bufferGroup = 0;
1434	}
1435
1436	// create buffer group
1437	PRINT((
1438		"##### AudioFilterNode::updateBufferGroup():\n"
1439		"##### creating %" B_PRId32 " buffers of size %" B_PRIuSIZE "\n",
1440		bufferCount, m_output.format.u.raw_audio.buffer_size));
1441
1442	m_bufferGroup = new BBufferGroup(
1443		m_output.format.u.raw_audio.buffer_size,
1444		bufferCount);
1445}
1446
1447
1448// figure processing latency by doing 'dry runs' of processBuffer()
1449bigtime_t AudioFilterNode::calcProcessingLatency() {
1450
1451	PRINT(("AudioFilterNode::calcProcessingLatency()\n"));
1452
1453	ASSERT(m_input.source != media_source::null);
1454	ASSERT(m_output.destination != media_destination::null);
1455	ASSERT(m_op);
1456
1457	// initialize filter
1458	m_op->init();
1459
1460	size_t maxSize = max_c(
1461		m_input.format.u.raw_audio.buffer_size,
1462		m_output.format.u.raw_audio.buffer_size);
1463
1464	// allocate a temporary buffer group
1465	BBufferGroup* testGroup = new BBufferGroup(
1466		maxSize, 1);
1467
1468	// fetch a buffer big enough for in-place processing
1469	BBuffer* buffer = testGroup->RequestBuffer(
1470		maxSize, -1);
1471	ASSERT(buffer);
1472
1473	buffer->Header()->type = B_MEDIA_RAW_AUDIO;
1474	buffer->Header()->size_used = m_input.format.u.raw_audio.buffer_size;
1475
1476	// run the test
1477	bigtime_t preTest = system_time();
1478	processBuffer(buffer, buffer);
1479	bigtime_t elapsed = system_time()-preTest;
1480
1481	// clean up
1482	buffer->Recycle();
1483	delete testGroup;
1484
1485	// reset filter state
1486	m_op->init();
1487
1488	return elapsed;// + 100000LL;
1489}
1490
1491// filter buffer data; inputBuffer and outputBuffer may be identical!
1492
1493void AudioFilterNode::processBuffer(
1494	BBuffer*										inputBuffer,
1495	BBuffer*										outputBuffer) {
1496
1497	ASSERT(inputBuffer);
1498	ASSERT(outputBuffer);
1499	ASSERT(m_op);
1500
1501	// create wrapper objects
1502	AudioBuffer input(m_input.format.u.raw_audio, inputBuffer);
1503	AudioBuffer output(m_output.format.u.raw_audio, outputBuffer);
1504
1505	double sourceOffset = 0.0;
1506	uint32 destinationOffset = 0L;
1507
1508	// when is the first frame due to be consumed?
1509	bigtime_t startTime = outputBuffer->Header()->start_time;
1510	// when is the next frame to be produced going to be consumed?
1511	bigtime_t targetTime = startTime;
1512	// when will the first frame of the next buffer be consumed?
1513	bigtime_t endTime = startTime + BufferDuration();
1514
1515	uint32 framesRemaining = input.frames();
1516	while(framesRemaining) {
1517
1518		// handle all events occurring before targetTime
1519		// +++++
1520
1521		bigtime_t nextEventTime = endTime;
1522
1523		// look for next event occurring before endTime
1524		// +++++
1525
1526		// process up to found event, if any, or to end of buffer
1527
1528		int64 toProcess = frames_for_duration(output.format(), nextEventTime - targetTime);
1529
1530		ASSERT(toProcess > 0);
1531
1532		uint32 processed = m_op->process(
1533			input, output, sourceOffset, destinationOffset, (uint32)toProcess, targetTime);
1534		if(processed < toProcess) {
1535			// +++++ in offline mode this will have to request additional buffer(s), right?
1536			PRINT((
1537				"*** AudioFilterNode::processBuffer(): insufficient frames filled\n"));
1538		}
1539
1540		if(toProcess > framesRemaining)
1541			framesRemaining = 0;
1542		else
1543			framesRemaining -= toProcess;
1544
1545		// advance target time
1546		targetTime = nextEventTime; // +++++ might this drift from the real frame offset?
1547	}
1548
1549	outputBuffer->Header()->size_used = input.frames() * bytes_per_frame(m_output.format.u.raw_audio);
1550//	PRINT(("### output size: %ld\n", outputBuffer->Header()->size_used));
1551}
1552
1553// END -- AudioFilterNode.cpp --
1554