xref: /haiku/src/add-ons/media/media-add-ons/mixer/MixerCore.cpp (revision 1a3518cf757c2da8006753f83962da5935bbc82b)
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 	fHasEvent(false),
88 	fOutputGain(1.0)
89 {
90 }
91 
92 
93 MixerCore::~MixerCore()
94 {
95 	delete fSettings;
96 
97 	delete fLocker;
98 	delete fInputs;
99 
100 	ASSERT(fMixThreadWaitSem == -1);
101 	ASSERT(fMixThread == -1);
102 
103 	if (fMixBuffer)
104 		rtm_free(fMixBuffer);
105 
106 	if (fTimeSource)
107 		fTimeSource->Release();
108 
109 	if (fResampler) {
110 		for (int i = 0; i < fMixBufferChannelCount; i++)
111 			delete fResampler[i];
112 		delete[] fResampler;
113 	}
114 
115 	delete[] fMixBufferChannelTypes;
116 }
117 
118 
119 MixerSettings *
120 MixerCore::Settings()
121 {
122 	return fSettings;
123 }
124 
125 
126 void
127 MixerCore::UpdateResamplingAlgorithm()
128 {
129 	ASSERT_LOCKED();
130 
131 	_UpdateResamplers(fOutput->MediaOutput().format.u.raw_audio);
132 
133 	for (int32 i = fInputs->CountItems() - 1; i >= 0; i--) {
134 		MixerInput* input
135 			= reinterpret_cast<MixerInput*>(fInputs->ItemAtFast(i));
136 		input->UpdateResamplingAlgorithm();
137 	}
138 }
139 
140 
141 void
142 MixerCore::SetOutputAttenuation(float gain)
143 {
144 	ASSERT_LOCKED();
145 	fOutputGain = gain;
146 }
147 
148 
149 MixerInput*
150 MixerCore::AddInput(const media_input& input)
151 {
152 	ASSERT_LOCKED();
153 	MixerInput* in = new MixerInput(this, input, fMixBufferFrameRate,
154 		fMixBufferFrameCount);
155 	fInputs->AddItem(in);
156 	return in;
157 }
158 
159 
160 MixerOutput*
161 MixerCore::AddOutput(const media_output& output)
162 {
163 	ASSERT_LOCKED();
164 	if (fOutput) {
165 		ERROR("MixerCore::AddOutput: already connected\n");
166 		return fOutput;
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)
201 		return false;
202 
203 	if (fStarted)
204 		StopMixThread();
205 
206 	delete fOutput;
207 	fOutput = 0;
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 	bool was_started = fStarted;
268 
269 	if (was_started)
270 		Stop();
271 
272 	fOutput->ChangeFormat(format);
273 	_ApplyOutputFormat();
274 
275 	if (was_started)
276 		Start();
277 }
278 
279 
280 void
281 MixerCore::SetOutputBufferGroup(BBufferGroup *group)
282 {
283 	ASSERT_LOCKED();
284 	fBufferGroup = group;
285 }
286 
287 
288 void
289 MixerCore::SetTimingInfo(BTimeSource *ts, bigtime_t downstream_latency)
290 {
291 	ASSERT_LOCKED();
292 	if (fTimeSource)
293 		fTimeSource->Release();
294 
295 	fTimeSource = dynamic_cast<BTimeSource *>(ts->Acquire());
296 	fDownstreamLatency = downstream_latency;
297 
298 	TRACE("MixerCore::SetTimingInfo, now = %Ld, downstream latency %Ld\n",
299 		fTimeSource->Now(), fDownstreamLatency);
300 }
301 
302 
303 void
304 MixerCore::EnableOutput(bool enabled)
305 {
306 	ASSERT_LOCKED();
307 	TRACE("MixerCore::EnableOutput %d\n", enabled);
308 	fOutputEnabled = enabled;
309 
310 	if (fRunning && !fOutputEnabled)
311 		StopMixThread();
312 
313 	if (!fRunning && fOutput && fStarted && fOutputEnabled)
314 		StartMixThread();
315 }
316 
317 
318 uint32
319 MixerCore::OutputChannelCount()
320 {
321 	return (fOutput) ? fOutput->GetOutputChannelCount() : 0;
322 }
323 
324 
325 bool
326 MixerCore::Start()
327 {
328 	ASSERT_LOCKED();
329 	TRACE("MixerCore::Start\n");
330 	if (fStarted)
331 		return false;
332 
333 	fStarted = true;
334 
335 	ASSERT(!fRunning);
336 
337 	// only start the mix thread if we have an output
338 	if (fOutput && fOutputEnabled)
339 		StartMixThread();
340 
341 	return true;
342 }
343 
344 
345 bool
346 MixerCore::Stop()
347 {
348 	ASSERT_LOCKED();
349 	TRACE("MixerCore::Stop\n");
350 	if (!fStarted)
351 		return false;
352 
353 	if (fRunning)
354 		StopMixThread();
355 
356 	fStarted = false;
357 	return true;
358 }
359 
360 
361 void
362 MixerCore::StartMixThread()
363 {
364 	ASSERT(fOutputEnabled == true);
365 	ASSERT(fRunning == false);
366 	ASSERT(fOutput);
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 == true);
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 	// The broken BeOS R5 multiaudio node starts with time 0,
480 	// then publishes negative times for about 50ms, publishes 0
481 	// again until it finally reaches time values > 0
482 	if (!Lock())
483 		return;
484 	bigtime_t start = fTimeSource->Now();
485 	Unlock();
486 	while (start <= 0) {
487 		TRACE("MixerCore: delaying _MixThread start, timesource is at %Ld\n",
488 			start);
489 		snooze(5000);
490 		if (!Lock())
491 			return;
492 		start = fTimeSource->Now();
493 		Unlock();
494 	}
495 
496 	fEventLatency = max((bigtime_t)3600, bigtime_t(0.4 * buffer_duration(
497 		fOutput->MediaOutput().format.u.raw_audio)));
498 
499 	// TODO: when the format changes while running, everything is wrong!
500 	bigtime_t bufferRequestTimeout = buffer_duration(
501 		fOutput->MediaOutput().format.u.raw_audio) / 2;
502 
503 	TRACE("MixerCore: starting _MixThread at %Ld with latency %Ld and "
504 		"downstream latency %Ld, bufferRequestTimeout %Ld\n", start, latency,
505 		fDownstreamLatency, bufferRequestTimeout);
506 
507 	// We must read from the input buffer at a position (pos) that is always
508 	// a multiple of fMixBufferFrameCount.
509 	int64 temp = frames_for_duration(fMixBufferFrameRate, start);
510 	int64 frameBase = ((temp / fMixBufferFrameCount) + 1)
511 		* fMixBufferFrameCount;
512 	bigtime_t timeBase = duration_for_frames(fMixBufferFrameRate, frameBase);
513 
514 	TRACE("MixerCore: starting _MixThread, start %Ld, timeBase %Ld, "
515 		"frameBase %Ld\n", start, timeBase, frameBase);
516 
517 	ASSERT(fMixBufferFrameCount > 0);
518 
519 #if DEBUG
520 	uint64 bufferIndex = 0;
521 #endif
522 
523 	typedef RtList<chan_info> chan_info_list;
524 	chan_info_list inputChanInfos[MAX_CHANNEL_TYPES];
525 	BStackOrHeapArray<chan_info_list, 16> mixChanInfos(fMixBufferChannelCount);
526 		// TODO: this does not support changing output channel count
527 	if (!mixChanInfos.IsValid()) {
528 		ERROR("MixerCore::_MixThread mixChanInfos allocation failed\n");
529 		return;
530 	}
531 
532 	fEventTime = timeBase;
533 	int64 framePos = 0;
534 	status_t ret = B_ERROR;
535 
536 	while(fRunning == true) {
537 		if (fHasEvent == false)
538 			goto schedule_next_event;
539 
540 		ret = acquire_sem(fMixThreadWaitSem);
541 		if (ret == B_INTERRUPTED)
542 			continue;
543 		else if (ret != B_OK)
544 			return;
545 
546 		fHasEvent = false;
547 
548 		if (!LockWithTimeout(10000)) {
549 			ERROR("MixerCore: LockWithTimeout failed\n");
550 			continue;
551 		}
552 
553 		// no inputs or output muted, skip further processing and just send an
554 		// empty buffer
555 		if (fInputs->IsEmpty() || fOutput->IsMuted()) {
556 			int size = fOutput->MediaOutput().format.u.raw_audio.buffer_size;
557 			BBuffer* buffer = fBufferGroup->RequestBuffer(size,
558 				bufferRequestTimeout);
559 			if (buffer != NULL) {
560 				memset(buffer->Data(), 0, size);
561 				// fill in the buffer header
562 				media_header* hdr = buffer->Header();
563 				hdr->type = B_MEDIA_RAW_AUDIO;
564 				hdr->size_used = size;
565 				hdr->time_source = fTimeSource->ID();
566 				hdr->start_time = fEventTime;
567 				if (fNode->SendBuffer(buffer, fOutput) != B_OK) {
568 #if DEBUG
569 					ERROR("MixerCore: SendBuffer failed for buffer %Ld\n",
570 						bufferIndex);
571 #else
572 					ERROR("MixerCore: SendBuffer failed\n");
573 #endif
574 					buffer->Recycle();
575 				}
576 			} else {
577 #if DEBUG
578 				ERROR("MixerCore: RequestBuffer failed for buffer %Ld\n",
579 					bufferIndex);
580 #else
581 				ERROR("MixerCore: RequestBuffer failed\n");
582 #endif
583 			}
584 			goto schedule_next_event;
585 		}
586 
587 		int64 currentFramePos;
588 		currentFramePos = frameBase + framePos;
589 
590 		// mix all data from all inputs into the mix buffer
591 		ASSERT(currentFramePos % fMixBufferFrameCount == 0);
592 
593 		PRINT(4, "create new buffer event at %Ld, reading input frames at "
594 			"%Ld\n", fEventTime, currentFramePos);
595 
596 		// Init the channel information for each MixerInput.
597 		for (int i = 0; MixerInput* input = Input(i); i++) {
598 			int count = input->GetMixerChannelCount();
599 			for (int channel = 0; channel < count; channel++) {
600 				int type;
601 				const float* base;
602 				uint32 sampleOffset;
603 				float gain;
604 				if (!input->GetMixerChannelInfo(channel, currentFramePos,
605 						fEventTime, &base, &sampleOffset, &type, &gain)) {
606 					continue;
607 				}
608 				if (type < 0 || type >= MAX_CHANNEL_TYPES)
609 					continue;
610 				chan_info* info = inputChanInfos[type].Create();
611 				info->base = (const char*)base;
612 				info->sample_offset = sampleOffset;
613 				info->gain = gain;
614 			}
615 		}
616 
617 		for (int channel = 0; channel < fMixBufferChannelCount; channel++) {
618 			int sourceCount = fOutput->GetOutputChannelSourceCount(channel);
619 			for (int i = 0; i < sourceCount; i++) {
620 				int type;
621 				float gain;
622 				fOutput->GetOutputChannelSourceInfoAt(channel, i, &type,
623 					&gain);
624 				if (type < 0 || type >= MAX_CHANNEL_TYPES)
625 					continue;
626 				int count = inputChanInfos[type].CountItems();
627 				for (int j = 0; j < count; j++) {
628 					chan_info* info = inputChanInfos[type].ItemAt(j);
629 					chan_info* newInfo = mixChanInfos[channel].Create();
630 					newInfo->base = info->base;
631 					newInfo->sample_offset = info->sample_offset;
632 					newInfo->gain = info->gain * gain;
633 				}
634 			}
635 		}
636 
637 		memset(fMixBuffer, 0,
638 			fMixBufferChannelCount * fMixBufferFrameCount * sizeof(float));
639 		for (int channel = 0; channel < fMixBufferChannelCount; channel++) {
640 			PRINT(5, "_MixThread: channel %d has %d sources\n", channel,
641 				mixChanInfos[channel].CountItems());
642 
643 			int count = mixChanInfos[channel].CountItems();
644 			for (int i = 0; i < count; i++) {
645 				chan_info* info = mixChanInfos[channel].ItemAt(i);
646 				PRINT(5, "_MixThread:   base %p, sample-offset %2d, gain %.3f\n",
647 					info->base, info->sample_offset, info->gain);
648 				// This looks slightly ugly, but the current GCC will generate
649 				// the fastest code this way.
650 				// fMixBufferFrameCount is always > 0.
651 				uint32 dstSampleOffset
652 					= fMixBufferChannelCount * sizeof(float);
653 				uint32 srcSampleOffset = info->sample_offset;
654 				register char* dst = (char*)&fMixBuffer[channel];
655 				register char* src = (char*)info->base;
656 				register float gain = info->gain;
657 				register int j = fMixBufferFrameCount;
658 				do {
659 					*(float*)dst += *(const float*)src * gain;
660 					dst += dstSampleOffset;
661 					src += srcSampleOffset;
662 				 } while (--j);
663 			}
664 		}
665 
666 		// request a buffer
667 		BBuffer* buffer;
668 		buffer = fBufferGroup->RequestBuffer(
669 			fOutput->MediaOutput().format.u.raw_audio.buffer_size,
670 			bufferRequestTimeout);
671 		if (buffer != NULL) {
672 			// copy data from mix buffer into output buffer
673 			for (int i = 0; i < fMixBufferChannelCount; i++) {
674 				fResampler[i]->Resample(
675 					reinterpret_cast<char*>(fMixBuffer) + i * sizeof(float),
676 					fMixBufferChannelCount * sizeof(float),
677 					fMixBufferFrameCount,
678 					reinterpret_cast<char*>(buffer->Data())
679 						+ (i * bytes_per_sample(
680 							fOutput->MediaOutput().format.u.raw_audio)),
681 					bytes_per_frame(fOutput->MediaOutput().format.u.raw_audio),
682 					frames_per_buffer(
683 						fOutput->MediaOutput().format.u.raw_audio),
684 					fOutputGain * fOutput->GetOutputChannelGain(i));
685 			}
686 			PRINT(4, "send buffer, inframes %ld, outframes %ld\n",
687 				fMixBufferFrameCount,
688 				frames_per_buffer(fOutput->MediaOutput().format.u.raw_audio));
689 
690 			// fill in the buffer header
691 			media_header* hdr = buffer->Header();
692 			hdr->type = B_MEDIA_RAW_AUDIO;
693 			hdr->size_used
694 				= fOutput->MediaOutput().format.u.raw_audio.buffer_size;
695 			hdr->time_source = fTimeSource->ID();
696 			hdr->start_time = fEventTime;
697 
698 			// swap byte order if necessary
699 			fOutput->AdjustByteOrder(buffer);
700 
701 			// send the buffer
702 			status_t res = fNode->SendBuffer(buffer, fOutput);
703 			if (res != B_OK) {
704 #if DEBUG
705 				ERROR("MixerCore: SendBuffer failed for buffer %Ld\n",
706 					bufferIndex);
707 #else
708 				ERROR("MixerCore: SendBuffer failed\n");
709 #endif
710 				buffer->Recycle();
711 			}
712 		} else {
713 #if DEBUG
714 			ERROR("MixerCore: RequestBuffer failed for buffer %Ld\n",
715 				bufferIndex);
716 #else
717 			ERROR("MixerCore: RequestBuffer failed\n");
718 #endif
719 		}
720 
721 		// make all lists empty
722 		for (int i = 0; i < MAX_CHANNEL_TYPES; i++)
723 			inputChanInfos[i].MakeEmpty();
724 		for (int i = 0; i < fOutput->GetOutputChannelCount(); i++)
725 			mixChanInfos[i].MakeEmpty();
726 
727 schedule_next_event:
728 		Unlock();
729 
730 		// schedule next event
731 		framePos += fMixBufferFrameCount;
732 		fEventTime = timeBase + bigtime_t((1000000LL * framePos)
733 			/ fMixBufferFrameRate);
734 
735 		media_timed_event mixerEvent(PickEvent(),
736 			MIXER_PROCESS_EVENT, 0, BTimedEventQueue::B_NO_CLEANUP);
737 
738 		ret = write_port(fNode->ControlPort(), MIXER_SCHEDULE_EVENT,
739 			&mixerEvent, sizeof(mixerEvent));
740 		if (ret != B_OK)
741 			TRACE("MixerCore::_MixThread: can't write to owner port\n");
742 
743 		fHasEvent = true;
744 
745 #if DEBUG
746 		bufferIndex++;
747 #endif
748 	}
749 }
750