xref: /haiku/src/add-ons/media/media-add-ons/mixer/MixerCore.cpp (revision 909af08f4328301fbdef1ffb41f566c3b5bec0c7)
1 /*
2  * Copyright 2003-2016 Haiku Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Marcus Overhagen
7  * 	Dario Casalinuovo
8  */
9 
10 
11 #include "MixerCore.h"
12 
13 #include <string.h>
14 
15 #include <Buffer.h>
16 #include <BufferGroup.h>
17 #include <BufferProducer.h>
18 #include <MediaNode.h>
19 #include <RealtimeAlloc.h>
20 #include <StackOrHeapArray.h>
21 #include <StopWatch.h>
22 #include <TimeSource.h>
23 
24 #include "AudioMixer.h"
25 #include "Interpolate.h"
26 #include "MixerInput.h"
27 #include "MixerOutput.h"
28 #include "MixerUtils.h"
29 #include "Resampler.h"
30 #include "RtList.h"
31 
32 
33 #define DOUBLE_RATE_MIXING 	0
34 
35 #if DEBUG > 1
36 #	define ASSERT_LOCKED()	if (fLocker->IsLocked()) {} \
37 	else debugger("core not locked, meltdown occurred")
38 #else
39 #	define ASSERT_LOCKED()	((void)0)
40 #endif
41 
42 /*!	Mixer channels are identified by a type number, each type number corresponds
43 	to the one of the channel masks of enum media_multi_channels.
44 
45 	The mixer buffer uses either the same frame rate and same count of frames as
46 	the output buffer, or the double frame rate and frame count.
47 
48 	All mixer input ring buffers must be an exact multiple of the mixer buffer
49 	size, so that we do not get any buffer wrap around during reading from the
50 	input buffers.
51 	The mixer input is told by constructor (or after a format change by
52 	SetMixBufferFormat() of the current mixer buffer propertys, and must
53 	allocate a buffer that is an exact multiple,
54 */
55 
56 
57 struct chan_info {
58 	const char 	*base;
59 	uint32		sample_offset;
60 	float		gain;
61 };
62 
63 
64 MixerCore::MixerCore(AudioMixer *node)
65 	:
66 	fLocker(new BLocker("mixer core lock")),
67 	fInputs(new BList),
68 	fOutput(0),
69 	fNextInputID(1),
70 	fRunning(false),
71 	fStarted(false),
72 	fOutputEnabled(true),
73 	fResampler(0),
74 	fMixBuffer(0),
75 	fMixBufferFrameRate(0),
76 	fMixBufferFrameCount(0),
77 	fMixBufferChannelCount(0),
78 	fMixBufferChannelTypes(0),
79 	fDoubleRateMixing(DOUBLE_RATE_MIXING),
80 	fDownstreamLatency(1),
81 	fSettings(new MixerSettings),
82 	fNode(node),
83 	fBufferGroup(0),
84 	fTimeSource(0),
85 	fMixThread(-1),
86 	fMixThreadWaitSem(-1),
87 	fOutputGain(1.0)
88 {
89 }
90 
91 
92 MixerCore::~MixerCore()
93 {
94 	delete fSettings;
95 
96 	delete fLocker;
97 	delete fInputs;
98 
99 	ASSERT(fMixThreadWaitSem == -1);
100 	ASSERT(fMixThread == -1);
101 
102 	if (fMixBuffer)
103 		rtm_free(fMixBuffer);
104 
105 	if (fTimeSource)
106 		fTimeSource->Release();
107 
108 	if (fResampler) {
109 		for (int i = 0; i < fMixBufferChannelCount; i++)
110 			delete fResampler[i];
111 		delete[] fResampler;
112 	}
113 
114 	delete[] fMixBufferChannelTypes;
115 }
116 
117 
118 MixerSettings *
119 MixerCore::Settings()
120 {
121 	return fSettings;
122 }
123 
124 
125 void
126 MixerCore::UpdateResamplingAlgorithm()
127 {
128 	ASSERT_LOCKED();
129 
130 	_UpdateResamplers(fOutput->MediaOutput().format.u.raw_audio);
131 
132 	for (int32 i = fInputs->CountItems() - 1; i >= 0; i--) {
133 		MixerInput* input
134 			= reinterpret_cast<MixerInput*>(fInputs->ItemAtFast(i));
135 		input->UpdateResamplingAlgorithm();
136 	}
137 }
138 
139 
140 void
141 MixerCore::SetOutputAttenuation(float gain)
142 {
143 	ASSERT_LOCKED();
144 	fOutputGain = gain;
145 }
146 
147 
148 MixerInput*
149 MixerCore::AddInput(const media_input& input)
150 {
151 	ASSERT_LOCKED();
152 	MixerInput* in = new MixerInput(this, input, fMixBufferFrameRate,
153 		fMixBufferFrameCount);
154 	fInputs->AddItem(in);
155 	return in;
156 }
157 
158 
159 MixerOutput*
160 MixerCore::AddOutput(const media_output& output)
161 {
162 	ASSERT_LOCKED();
163 	if (fOutput != NULL) {
164 		ERROR("MixerCore::AddOutput: already connected\n");
165 		return fOutput;
166 	}
167 
168 	fOutput = new MixerOutput(this, output);
169 	// the output format might have been adjusted inside MixerOutput
170 	_ApplyOutputFormat();
171 
172 	ASSERT(!fRunning);
173 	if (fStarted && fOutputEnabled)
174 		_StartMixThread();
175 
176 	return fOutput;
177 }
178 
179 
180 bool
181 MixerCore::RemoveInput(int32 inputID)
182 {
183 	ASSERT_LOCKED();
184 	MixerInput *input;
185 	for (int i = 0; (input = Input(i)) != 0; i++) {
186 		if (input->ID() == inputID) {
187 			fInputs->RemoveItem(i);
188 			delete input;
189 			return true;
190 		}
191 	}
192 	return false;
193 }
194 
195 
196 bool
197 MixerCore::RemoveOutput()
198 {
199 	ASSERT_LOCKED();
200 	if (fOutput == NULL)
201 		return false;
202 
203 	if (fRunning)
204 		_StopMixThread();
205 
206 	delete fOutput;
207 	fOutput = NULL;
208 	fOutputEnabled = true;
209 	return true;
210 }
211 
212 
213 int32
214 MixerCore::CreateInputID()
215 {
216 	ASSERT_LOCKED();
217 	return fNextInputID++;
218 }
219 
220 
221 MixerInput *
222 MixerCore::Input(int i)
223 {
224 	ASSERT_LOCKED();
225 	return (MixerInput *)fInputs->ItemAt(i);
226 }
227 
228 
229 MixerOutput *
230 MixerCore::Output()
231 {
232 	ASSERT_LOCKED();
233 	return fOutput;
234 }
235 
236 
237 void
238 MixerCore::BufferReceived(BBuffer *buffer, bigtime_t lateness)
239 {
240 	ASSERT_LOCKED();
241 	MixerInput *input;
242 	int32 id = buffer->Header()->destination;
243 	for (int i = 0; (input = Input(i)) != 0; i++) {
244 		if (input->ID() == id) {
245 			input->BufferReceived(buffer);
246 			return;
247 		}
248 	}
249 	ERROR("MixerCore::BufferReceived: received buffer for unknown id %ld\n",
250 		id);
251 }
252 
253 
254 void
255 MixerCore::InputFormatChanged(int32 inputID,
256 	const media_multi_audio_format &format)
257 {
258 	ASSERT_LOCKED();
259 	ERROR("MixerCore::InputFormatChanged not handled\n");
260 }
261 
262 
263 void
264 MixerCore::OutputFormatChanged(const media_multi_audio_format &format)
265 {
266 	ASSERT_LOCKED();
267 
268 	if (fRunning)
269 		_StopMixThread();
270 
271 	fOutput->ChangeFormat(format);
272 	_ApplyOutputFormat();
273 
274 	if (fStarted)
275 		_StartMixThread();
276 }
277 
278 
279 void
280 MixerCore::SetOutputBufferGroup(BBufferGroup *group)
281 {
282 	ASSERT_LOCKED();
283 	fBufferGroup = group;
284 }
285 
286 
287 void
288 MixerCore::SetTimingInfo(BTimeSource *ts, bigtime_t downstream_latency)
289 {
290 	ASSERT_LOCKED();
291 	if (fTimeSource)
292 		fTimeSource->Release();
293 
294 	fTimeSource = dynamic_cast<BTimeSource *>(ts->Acquire());
295 	fDownstreamLatency = downstream_latency;
296 
297 	TRACE("MixerCore::SetTimingInfo, now = %lld, downstream latency %lld\n",
298 		fTimeSource->Now(), fDownstreamLatency);
299 }
300 
301 
302 void
303 MixerCore::EnableOutput(bool enabled)
304 {
305 	ASSERT_LOCKED();
306 	TRACE("MixerCore::EnableOutput %d\n", enabled);
307 	fOutputEnabled = enabled;
308 
309 	if (fRunning && !fOutputEnabled)
310 		_StopMixThread();
311 
312 	if (!fRunning && fOutput != NULL && fStarted && fOutputEnabled)
313 		_StartMixThread();
314 }
315 
316 
317 uint32
318 MixerCore::OutputChannelCount()
319 {
320 	return (fOutput) ? fOutput->GetOutputChannelCount() : 0;
321 }
322 
323 
324 bool
325 MixerCore::Start()
326 {
327 	ASSERT_LOCKED();
328 	TRACE("MixerCore::Start\n");
329 	if (fStarted)
330 		return false;
331 
332 	fStarted = true;
333 
334 	ASSERT(!fRunning);
335 
336 	// only start the mix thread if we have an output
337 	if (fOutput != NULL && fOutputEnabled)
338 		_StartMixThread();
339 
340 	return true;
341 }
342 
343 
344 bool
345 MixerCore::Stop()
346 {
347 	ASSERT_LOCKED();
348 	TRACE("MixerCore::Stop\n");
349 	if (!fStarted)
350 		return false;
351 
352 	if (fRunning)
353 		_StopMixThread();
354 
355 	fStarted = false;
356 	return true;
357 }
358 
359 
360 void
361 MixerCore::_StartMixThread()
362 {
363 	ASSERT(fOutputEnabled);
364 	ASSERT(!fRunning);
365 	ASSERT(fOutput != NULL);
366 
367 	fRunning = true;
368 	fMixThreadWaitSem = create_sem(0, "mix thread wait");
369 	fMixThread = spawn_thread(_MixThreadEntry, "Yeah baby, very shagadelic",
370 		120, this);
371 	resume_thread(fMixThread);
372 }
373 
374 
375 void
376 MixerCore::_StopMixThread()
377 {
378 	ASSERT(fRunning);
379 	ASSERT(fMixThread > 0);
380 	ASSERT(fMixThreadWaitSem > 0);
381 
382 	fRunning = false;
383 	status_t unused;
384 	delete_sem(fMixThreadWaitSem);
385 	wait_for_thread(fMixThread, &unused);
386 
387 	fMixThread = -1;
388 	fMixThreadWaitSem = -1;
389 }
390 
391 
392 // #pragma mark - private
393 
394 
395 void
396 MixerCore::_UpdateResamplers(const media_multi_audio_format& format)
397 {
398 	ASSERT_LOCKED();
399 
400 	if (fResampler != NULL) {
401 		for (int i = 0; i < fMixBufferChannelCount; i++)
402 			delete fResampler[i];
403 		delete[] fResampler;
404 	}
405 
406 	fResampler = new Resampler*[fMixBufferChannelCount];
407 	for (int i = 0; i < fMixBufferChannelCount; i++) {
408 		switch (Settings()->ResamplingAlgorithm()) {
409 			case 2:
410 				fResampler[i] = new Interpolate(
411 					media_raw_audio_format::B_AUDIO_FLOAT, format.format);
412 				break;
413 			default:
414 				fResampler[i] = new Resampler(
415 					media_raw_audio_format::B_AUDIO_FLOAT, format.format);
416 		}
417 	}
418 }
419 
420 
421 void
422 MixerCore::_ApplyOutputFormat()
423 {
424 	ASSERT_LOCKED();
425 
426 	const media_multi_audio_format& format
427 		= fOutput->MediaOutput().format.u.raw_audio;
428 
429 	if (fMixBuffer != NULL)
430 		rtm_free(fMixBuffer);
431 
432 	delete[] fMixBufferChannelTypes;
433 
434 	fMixBufferFrameRate = (int32)(0.5 + format.frame_rate);
435 	fMixBufferFrameCount = frames_per_buffer(format);
436 	if (fDoubleRateMixing) {
437 		fMixBufferFrameRate *= 2;
438 		fMixBufferFrameCount *= 2;
439 	}
440 	fMixBufferChannelCount = format.channel_count;
441 	ASSERT(fMixBufferChannelCount == fOutput->GetOutputChannelCount());
442 	fMixBufferChannelTypes = new int32 [format.channel_count];
443 
444 	for (int i = 0; i < fMixBufferChannelCount; i++) {
445 		 fMixBufferChannelTypes[i]
446 		 	= ChannelMaskToChannelType(GetChannelMask(i, format.channel_mask));
447 	}
448 
449 	fMixBuffer = (float*)rtm_alloc(NULL, sizeof(float) * fMixBufferFrameCount
450 		* fMixBufferChannelCount);
451 	ASSERT(fMixBuffer != NULL);
452 
453 	_UpdateResamplers(format);
454 
455 	TRACE("MixerCore::OutputFormatChanged:\n");
456 	TRACE("  fMixBufferFrameRate %ld\n", fMixBufferFrameRate);
457 	TRACE("  fMixBufferFrameCount %ld\n", fMixBufferFrameCount);
458 	TRACE("  fMixBufferChannelCount %ld\n", fMixBufferChannelCount);
459 	for (int i = 0; i < fMixBufferChannelCount; i++)
460 		TRACE("  fMixBufferChannelTypes[%i] %ld\n", i, fMixBufferChannelTypes[i]);
461 
462 	MixerInput *input;
463 	for (int i = 0; (input = Input(i)); i++)
464 		input->SetMixBufferFormat(fMixBufferFrameRate, fMixBufferFrameCount);
465 }
466 
467 
468 int32
469 MixerCore::_MixThreadEntry(void* arg)
470 {
471 	static_cast<MixerCore*>(arg)->_MixThread();
472 	return 0;
473 }
474 
475 
476 void
477 MixerCore::_MixThread()
478 {
479 	if (!Lock())
480 		return;
481 	{
482 		// Delay starting the mixer until we have a valid time source.
483 		bigtime_t performanceTime, realTime;
484 		float drift;
485 		while (fTimeSource->GetTime(&performanceTime, &realTime, &drift) != B_OK
486 				|| performanceTime <= 0 || realTime <= 0) {
487 			TRACE("MixerCore: delaying _MixThread start, timesource is at %" B_PRIdBIGTIME "\n",
488 				performanceTime);
489 			Unlock();
490 			snooze(5000);
491 			while (!LockWithTimeout(10000)) {
492 				if (!fRunning)
493 					return;
494 			}
495 		}
496 	}
497 	Unlock();
498 
499 	const bigtime_t start = fTimeSource->Now();
500 
501 	bigtime_t eventLatency = max((bigtime_t)3600, bigtime_t(0.4 * buffer_duration(
502 		fOutput->MediaOutput().format.u.raw_audio)));
503 
504 	// TODO: when the format changes while running, everything is wrong!
505 	bigtime_t bufferRequestTimeout = buffer_duration(
506 		fOutput->MediaOutput().format.u.raw_audio) / 2;
507 
508 	TRACE("MixerCore: starting _MixThread at %lld with latency %lld and "
509 		"downstream latency %lld, bufferRequestTimeout %lld\n", start,
510 		eventLatency, fDownstreamLatency, bufferRequestTimeout);
511 
512 	// We must read from the input buffer at a position (pos) that is always
513 	// a multiple of fMixBufferFrameCount.
514 	int64 temp = frames_for_duration(fMixBufferFrameRate, start);
515 	const int64 frameBase = ((temp / fMixBufferFrameCount) + 1)
516 		* fMixBufferFrameCount;
517 	const bigtime_t timeBase = duration_for_frames(fMixBufferFrameRate, frameBase);
518 
519 	TRACE("MixerCore: starting _MixThread, start %lld, timeBase %lld, "
520 		"frameBase %lld\n", start, timeBase, frameBase);
521 
522 	ASSERT(fMixBufferFrameCount > 0);
523 
524 	typedef RtList<chan_info> chan_info_list;
525 	chan_info_list inputChanInfos[MAX_CHANNEL_TYPES];
526 	BStackOrHeapArray<chan_info_list, 16> mixChanInfos(fMixBufferChannelCount);
527 		// TODO: this does not support changing output channel count
528 	if (!mixChanInfos.IsValid()) {
529 		ERROR("MixerCore::_MixThread mixChanInfos allocation failed\n");
530 		return;
531 	}
532 
533 	int64 framePos = 0;
534 	uint64 bufferIndex = 0;
535 	bigtime_t eventTime = 0, nextRun = B_INFINITE_TIMEOUT;
536 	while (fRunning) {
537 		if (nextRun == B_INFINITE_TIMEOUT) {
538 			eventTime = timeBase + bigtime_t((1000000LL * framePos)
539 				/ fMixBufferFrameRate);
540 			nextRun = fTimeSource->RealTimeFor(eventTime, 0)
541 				- eventLatency - fDownstreamLatency;
542 		}
543 
544 		status_t status = acquire_sem_etc(fMixThreadWaitSem, 1,
545 			B_ABSOLUTE_TIMEOUT, nextRun);
546 		if (status != B_TIMED_OUT) {
547 			if (status == B_OK || status == B_INTERRUPTED)
548 				continue;
549 			return;
550 		}
551 		nextRun = B_INFINITE_TIMEOUT;
552 
553 		if (!LockWithTimeout(10000)) {
554 			ERROR("MixerCore: LockWithTimeout failed\n");
555 			continue;
556 		}
557 
558 		// no inputs or output muted, skip further processing and just send an
559 		// empty buffer
560 		if (fInputs->IsEmpty() || fOutput->IsMuted()) {
561 			int size = fOutput->MediaOutput().format.u.raw_audio.buffer_size;
562 			BBuffer* buffer = fBufferGroup->RequestBuffer(size,
563 				bufferRequestTimeout);
564 			if (buffer != NULL) {
565 				int middle = 0;
566 				if (fOutput->MediaOutput().format.u.raw_audio.format
567 						== media_raw_audio_format::B_AUDIO_UCHAR)
568 					middle = 128;
569 				memset(buffer->Data(), middle, size);
570 				// fill in the buffer header
571 				media_header* hdr = buffer->Header();
572 				hdr->type = B_MEDIA_RAW_AUDIO;
573 				hdr->size_used = size;
574 				hdr->time_source = fTimeSource->ID();
575 				hdr->start_time = eventTime;
576 				if (fNode->SendBuffer(buffer, fOutput) != B_OK) {
577 					ERROR("MixerCore: SendBuffer failed for buffer %lld\n",
578 						bufferIndex);
579 					buffer->Recycle();
580 				}
581 			} else {
582 				ERROR("MixerCore: RequestBuffer failed for buffer %lld\n",
583 					bufferIndex);
584 			}
585 
586 			bufferIndex++;
587 			framePos += fMixBufferFrameCount;
588 
589 			Unlock();
590 			continue;
591 		}
592 
593 		int64 currentFramePos;
594 		currentFramePos = frameBase + framePos;
595 
596 		// mix all data from all inputs into the mix buffer
597 		ASSERT(currentFramePos % fMixBufferFrameCount == 0);
598 
599 		PRINT(4, "create new buffer event at %lld, reading input frames at "
600 			"%lld\n", eventTime, currentFramePos);
601 
602 		// Init the channel information for each MixerInput.
603 		for (int i = 0; MixerInput* input = Input(i); i++) {
604 			int count = input->GetMixerChannelCount();
605 			for (int channel = 0; channel < count; channel++) {
606 				int type;
607 				const float* base;
608 				uint32 sampleOffset;
609 				float gain;
610 				if (!input->GetMixerChannelInfo(channel, currentFramePos,
611 						eventTime, &base, &sampleOffset, &type, &gain)) {
612 					continue;
613 				}
614 				if (type < 0 || type >= MAX_CHANNEL_TYPES)
615 					continue;
616 				chan_info* info = inputChanInfos[type].Create();
617 				info->base = (const char*)base;
618 				info->sample_offset = sampleOffset;
619 				info->gain = gain;
620 			}
621 		}
622 
623 		for (int channel = 0; channel < fMixBufferChannelCount; channel++) {
624 			int sourceCount = fOutput->GetOutputChannelSourceCount(channel);
625 			for (int i = 0; i < sourceCount; i++) {
626 				int type;
627 				float gain;
628 				fOutput->GetOutputChannelSourceInfoAt(channel, i, &type,
629 					&gain);
630 				if (type < 0 || type >= MAX_CHANNEL_TYPES)
631 					continue;
632 				int count = inputChanInfos[type].CountItems();
633 				for (int j = 0; j < count; j++) {
634 					chan_info* info = inputChanInfos[type].ItemAt(j);
635 					chan_info* newInfo = mixChanInfos[channel].Create();
636 					newInfo->base = info->base;
637 					newInfo->sample_offset = info->sample_offset;
638 					newInfo->gain = info->gain * gain;
639 				}
640 			}
641 		}
642 
643 		memset(fMixBuffer, 0,
644 			fMixBufferChannelCount * fMixBufferFrameCount * sizeof(float));
645 		for (int channel = 0; channel < fMixBufferChannelCount; channel++) {
646 			PRINT(5, "_MixThread: channel %d has %d sources\n", channel,
647 				mixChanInfos[channel].CountItems());
648 
649 			int count = mixChanInfos[channel].CountItems();
650 			for (int i = 0; i < count; i++) {
651 				chan_info* info = mixChanInfos[channel].ItemAt(i);
652 				PRINT(5, "_MixThread:   base %p, sample-offset %2d, gain %.3f\n",
653 					info->base, info->sample_offset, info->gain);
654 				// This looks slightly ugly, but the current GCC will generate
655 				// the fastest code this way.
656 				// fMixBufferFrameCount is always > 0.
657 				uint32 dstSampleOffset
658 					= fMixBufferChannelCount * sizeof(float);
659 				uint32 srcSampleOffset = info->sample_offset;
660 				char* dst = (char*)&fMixBuffer[channel];
661 				char* src = (char*)info->base;
662 				float gain = info->gain;
663 				int j = fMixBufferFrameCount;
664 				do {
665 					*(float*)dst += *(const float*)src * gain;
666 					dst += dstSampleOffset;
667 					src += srcSampleOffset;
668 				 } while (--j);
669 			}
670 		}
671 
672 		// request a buffer
673 		BBuffer* buffer;
674 		buffer = fBufferGroup->RequestBuffer(
675 			fOutput->MediaOutput().format.u.raw_audio.buffer_size,
676 			bufferRequestTimeout);
677 		if (buffer != NULL) {
678 			// copy data from mix buffer into output buffer
679 			for (int i = 0; i < fMixBufferChannelCount; i++) {
680 				fResampler[i]->Resample(
681 					reinterpret_cast<char*>(fMixBuffer) + i * sizeof(float),
682 					fMixBufferChannelCount * sizeof(float),
683 					fMixBufferFrameCount,
684 					reinterpret_cast<char*>(buffer->Data())
685 						+ (i * bytes_per_sample(
686 							fOutput->MediaOutput().format.u.raw_audio)),
687 					bytes_per_frame(fOutput->MediaOutput().format.u.raw_audio),
688 					frames_per_buffer(
689 						fOutput->MediaOutput().format.u.raw_audio),
690 					fOutputGain * fOutput->GetOutputChannelGain(i));
691 			}
692 			PRINT(4, "send buffer, inframes %ld, outframes %ld\n",
693 				fMixBufferFrameCount,
694 				frames_per_buffer(fOutput->MediaOutput().format.u.raw_audio));
695 
696 			// fill in the buffer header
697 			media_header* hdr = buffer->Header();
698 			hdr->type = B_MEDIA_RAW_AUDIO;
699 			hdr->size_used
700 				= fOutput->MediaOutput().format.u.raw_audio.buffer_size;
701 			hdr->time_source = fTimeSource->ID();
702 			hdr->start_time = eventTime;
703 
704 			// swap byte order if necessary
705 			fOutput->AdjustByteOrder(buffer);
706 
707 			// send the buffer
708 			status_t res = fNode->SendBuffer(buffer, fOutput);
709 			if (res != B_OK) {
710 				ERROR("MixerCore: SendBuffer failed for buffer %lld\n",
711 					bufferIndex);
712 				buffer->Recycle();
713 			}
714 		} else {
715 			ERROR("MixerCore: RequestBuffer failed for buffer %lld\n",
716 				bufferIndex);
717 		}
718 
719 		// make all lists empty
720 		for (int i = 0; i < MAX_CHANNEL_TYPES; i++)
721 			inputChanInfos[i].MakeEmpty();
722 		for (int i = 0; i < fOutput->GetOutputChannelCount(); i++)
723 			mixChanInfos[i].MakeEmpty();
724 
725 		bufferIndex++;
726 		framePos += fMixBufferFrameCount;
727 
728 		Unlock();
729 	}
730 }
731