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