xref: /haiku/src/add-ons/media/media-add-ons/mixer/MixerCore.cpp (revision c9ad965c81b08802fed0827fd1dd16f45297928a)
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("mixer core lock")),
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