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