xref: /haiku/src/add-ons/media/media-add-ons/mixer/MixerCore.cpp (revision d9cebac2b77547b7064f22497514eecd2d047160)
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 										format.valid_bits);
268 
269 	TRACE("MixerCore::OutputFormatChanged:\n");
270 	TRACE("  fMixBufferFrameRate %ld\n", fMixBufferFrameRate);
271 	TRACE("  fMixBufferFrameCount %ld\n", fMixBufferFrameCount);
272 	TRACE("  fMixBufferChannelCount %ld\n", fMixBufferChannelCount);
273 	for (int i = 0; i < fMixBufferChannelCount; i++)
274 		TRACE("  fMixBufferChannelTypes[%i] %ld\n", i, fMixBufferChannelTypes[i]);
275 
276 	MixerInput *input;
277 	for (int i = 0; (input = Input(i)); i++)
278 		input->SetMixBufferFormat(fMixBufferFrameRate, fMixBufferFrameCount);
279 }
280 
281 void
282 MixerCore::SetOutputBufferGroup(BBufferGroup *group)
283 {
284 	ASSERT_LOCKED();
285 	fBufferGroup = group;
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(),
300 							fDownstreamLatency);
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 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 void
360 MixerCore::StartMixThread()
361 {
362 	ASSERT(fOutputEnabled == true);
363 	ASSERT(fRunning == false);
364 	ASSERT(fOutput);
365 	fRunning = true;
366 	fMixThreadWaitSem = create_sem(0, "mix thread wait");
367 	fMixThread = spawn_thread(_mix_thread_, "Yeah baby, very shagadelic", 120, this);
368 	resume_thread(fMixThread);
369 }
370 
371 void
372 MixerCore::StopMixThread()
373 {
374 	ASSERT(fRunning == true);
375 	ASSERT(fMixThread > 0);
376 	ASSERT(fMixThreadWaitSem > 0);
377 
378 	status_t unused;
379 	delete_sem(fMixThreadWaitSem);
380 	wait_for_thread(fMixThread, &unused);
381 
382 	fMixThread = -1;
383 	fMixThreadWaitSem = -1;
384 	fRunning = false;
385 }
386 
387 int32
388 MixerCore::_mix_thread_(void *arg)
389 {
390 	static_cast<MixerCore *>(arg)->MixThread();
391 	return 0;
392 }
393 
394 struct chan_info {
395 	const char 	*base;
396 	uint32		sample_offset;
397 	float		gain;
398 };
399 
400 void
401 MixerCore::MixThread()
402 {
403 	bigtime_t 	event_time;
404 	bigtime_t 	time_base;
405 	bigtime_t 	latency;
406 	bigtime_t	start;
407 	bigtime_t	buffer_request_timeout;
408 	int64		frame_base;
409 	int64		frame_pos;
410 
411 	// The broken BeOS R5 multiaudio node starts with time 0,
412 	// then publishes negative times for about 50ms, publishes 0
413 	// again until it finally reaches time values > 0
414 	if (!LockFromMixThread())
415 		return;
416 	start = fTimeSource->Now();
417 	Unlock();
418 	while (start <= 0) {
419 		TRACE("MixerCore: delaying MixThread start, timesource is at %Ld\n", start);
420 		snooze(5000);
421 		if (!LockFromMixThread())
422 			return;
423 		start = fTimeSource->Now();
424 		Unlock();
425 	}
426 
427 	if (!LockFromMixThread())
428 		return;
429 	latency = max(3600LL, bigtime_t(0.4 * buffer_duration(fOutput->MediaOutput().format.u.raw_audio)));
430 
431 	// XXX when the format changes while running, everything is wrong!
432 	buffer_request_timeout = buffer_duration(fOutput->MediaOutput().format.u.raw_audio) / 2;
433 
434 	TRACE("MixerCore: starting MixThread at %Ld with latency %Ld and downstream latency %Ld, buffer_request_timeout %Ld\n", start, latency, fDownstreamLatency, buffer_request_timeout);
435 
436 	/* We must read from the input buffer at a position (pos) that is always a multiple of fMixBufferFrameCount.
437 	 */
438 	int64 temp = frames_for_duration(fMixBufferFrameRate, start	);
439 	frame_base = ((temp / fMixBufferFrameCount) + 1) * fMixBufferFrameCount;
440 	time_base = duration_for_frames(fMixBufferFrameRate, frame_base);
441 	Unlock();
442 
443 	TRACE("MixerCore: starting MixThread, start %Ld, time_base %Ld, frame_base %Ld\n", start, time_base, frame_base);
444 
445 	ASSERT(fMixBufferFrameCount > 0);
446 
447 #if DEBUG
448 	uint64 buffer_num = 0;
449 #endif
450 
451 	RtList<chan_info> InputChanInfos[MAX_CHANNEL_TYPES];
452 	RtList<chan_info> MixChanInfos[fMixBufferChannelCount]; // XXX this does not support changing output channel count
453 
454 	event_time = time_base;
455 	frame_pos = 0;
456 	for (;;) {
457 		bigtime_t wait_until;
458 		if (!LockFromMixThread())
459 			return;
460 		wait_until = fTimeSource->RealTimeFor(event_time, 0) - latency - fDownstreamLatency;
461 		Unlock();
462 		status_t rv;
463 		rv = acquire_sem_etc(fMixThreadWaitSem, 1, B_ABSOLUTE_TIMEOUT, wait_until);
464 		if (rv == B_INTERRUPTED)
465 			continue;
466 		if (rv != B_TIMED_OUT && rv < B_OK)
467 			return;
468 
469 		if (!LockWithTimeout(10000)) {
470 			ERROR("MixerCore: LockWithTimeout failed\n");
471 			continue;
472 		}
473 
474 		// no inputs or output muted, skip further processing and just send an empty buffer
475 		if (fInputs->IsEmpty() || fOutput->IsMuted()) {
476 			int size = fOutput->MediaOutput().format.u.raw_audio.buffer_size;
477 			BBuffer* buf = fBufferGroup->RequestBuffer(size, buffer_request_timeout);
478 			if (buf) {
479 				memset(buf->Data(), 0, size);
480 				// fill in the buffer header
481 				media_header* hdr = buf->Header();
482 				hdr->type = B_MEDIA_RAW_AUDIO;
483 				hdr->size_used = size;
484 				hdr->time_source = fTimeSource->ID();
485 				hdr->start_time = event_time;
486 				if (B_OK != fNode->SendBuffer(buf, fOutput->MediaOutput().destination)) {
487 #if DEBUG
488 					ERROR("MixerCore: SendBuffer failed for buffer %Ld\n", buffer_num);
489 #else
490 					ERROR("MixerCore: SendBuffer failed\n");
491 #endif
492 					buf->Recycle();
493 				}
494 			} else {
495 #if DEBUG
496 				ERROR("MixerCore: RequestBuffer failed for buffer %Ld\n", buffer_num);
497 #else
498 				ERROR("MixerCore: RequestBuffer failed\n");
499 #endif
500 			}
501 			goto schedule_next_event;
502 		}
503 
504 		int64 cur_framepos;
505 		cur_framepos = frame_base + frame_pos;
506 
507 		// mix all data from all inputs into the mix buffer
508 		ASSERT(cur_framepos % fMixBufferFrameCount == 0);
509 
510 		PRINT(4, "create new buffer event at %Ld, reading input frames at %Ld\n", event_time, cur_framepos);
511 
512 		MixerInput *input;
513 		for (int i = 0; (input = Input(i)) != 0; i++) {
514 			int count = input->GetMixerChannelCount();
515 			for (int chan = 0; chan < count; chan++) {
516 				int type;
517 				const float *base;
518 				uint32 sample_offset;
519 				float gain;
520 				if (!input->GetMixerChannelInfo(chan, cur_framepos, event_time, &base, &sample_offset, &type, &gain))
521 					continue;
522 				if (type < 0 || type >= MAX_CHANNEL_TYPES)
523 					continue;
524 				chan_info *info = InputChanInfos[type].Create();
525 				info->base = (const char *)base;
526 				info->sample_offset = sample_offset;
527 				info->gain = gain;
528 			}
529 		}
530 
531 		for (int chan = 0; chan < fMixBufferChannelCount; chan++) {
532 			int sourcecount = fOutput->GetOutputChannelSourceCount(chan);
533 			for (int i = 0; i < sourcecount; i++) {
534 				int type;
535 				float gain;
536 				fOutput->GetOutputChannelSourceInfoAt(chan, i, &type, &gain);
537 				if (type < 0 || type >= MAX_CHANNEL_TYPES)
538 					continue;
539 				int count = InputChanInfos[type].CountItems();
540 				for (int j = 0; j < count; j++) {
541 					chan_info *info = InputChanInfos[type].ItemAt(j);
542 					chan_info *newinfo = MixChanInfos[chan].Create();
543 					newinfo->base = info->base;
544 					newinfo->sample_offset = info->sample_offset;
545 					newinfo->gain = info->gain * gain;
546 				}
547 			}
548 		}
549 
550 		memset(fMixBuffer, 0, fMixBufferChannelCount * fMixBufferFrameCount * sizeof(float));
551 		for (int chan = 0; chan < fMixBufferChannelCount; chan++) {
552 			PRINT(5, "MixThread: chan %d has %d sources\n", chan, MixChanInfos[chan].CountItems());
553 			int count = MixChanInfos[chan].CountItems();
554 			for (int i = 0; i < count; i++) {
555 				chan_info *info = MixChanInfos[chan].ItemAt(i);
556 				PRINT(5, "MixThread:   base %p, sample-offset %2d, gain %.3f\n", info->base, info->sample_offset, info->gain);
557 				// This looks slightly ugly, but the current GCC will generate the fastest
558 				// code this way. fMixBufferFrameCount is always > 0.
559 				uint32 dst_sample_offset = fMixBufferChannelCount * sizeof(float);
560 				uint32 src_sample_offset = info->sample_offset;
561 				register char *dst = (char *)&fMixBuffer[chan];
562 				register char *src = (char *)info->base;
563 				register float gain = info->gain;
564 				register int j = fMixBufferFrameCount;
565 				do {
566 					*(float *)dst += *(const float *)src * gain;
567 					dst += dst_sample_offset;
568 					src += src_sample_offset;
569 				 } while (--j);
570 			}
571 		}
572 
573 		// request a buffer
574 		BBuffer	*buf;
575 		buf = fBufferGroup->RequestBuffer(
576 							fOutput->MediaOutput().format.u.raw_audio.buffer_size,
577 							buffer_request_timeout);
578 		if (buf) {
579 			// copy data from mix buffer into output buffer
580 			for (int i = 0; i < fMixBufferChannelCount; i++) {
581 				fResampler[i]->Resample(reinterpret_cast<char *>(fMixBuffer) + i *  sizeof(float),
582 										fMixBufferChannelCount * sizeof(float),
583 										fMixBufferFrameCount,
584 										reinterpret_cast<char *>(buf->Data()) + (i * bytes_per_sample(fOutput->MediaOutput().format.u.raw_audio)),
585 										bytes_per_frame(fOutput->MediaOutput().format.u.raw_audio),
586 										frames_per_buffer(fOutput->MediaOutput().format.u.raw_audio),
587 										fOutputGain * fOutput->GetOutputChannelGain(i));
588 			}
589 			PRINT(4, "send buffer, inframes %ld, outframes %ld\n", fMixBufferFrameCount, frames_per_buffer(fOutput->MediaOutput().format.u.raw_audio));
590 
591 			// fill in the buffer header
592 			media_header* hdr = buf->Header();
593 			hdr->type = B_MEDIA_RAW_AUDIO;
594 			hdr->size_used = fOutput->MediaOutput().format.u.raw_audio.buffer_size;
595 			hdr->time_source = fTimeSource->ID();
596 			hdr->start_time = event_time;
597 
598 			// swap byte order if necessary
599 			fOutput->AdjustByteOrder(buf);
600 
601 			// send the buffer
602 			status_t res = fNode->SendBuffer(buf, fOutput->MediaOutput().destination);
603 			if (B_OK != res) {
604 #if DEBUG
605 				ERROR("MixerCore: SendBuffer failed for buffer %Ld\n", buffer_num);
606 #else
607 				ERROR("MixerCore: SendBuffer failed\n");
608 #endif
609 				buf->Recycle();
610 			}
611 		} else {
612 #if DEBUG
613 			ERROR("MixerCore: RequestBuffer failed for buffer %Ld\n", buffer_num);
614 #else
615 			ERROR("MixerCore: RequestBuffer failed\n");
616 #endif
617 		}
618 
619 		// make all lists empty
620 		for (int i = 0; i < MAX_CHANNEL_TYPES; i++)
621 			InputChanInfos[i].MakeEmpty();
622 		for (int i = 0; i < fOutput->GetOutputChannelCount(); i++)
623 			MixChanInfos[i].MakeEmpty();
624 
625 schedule_next_event:
626 		// schedule next event
627 		frame_pos += fMixBufferFrameCount;
628 		event_time = time_base + bigtime_t((1000000LL * frame_pos) / fMixBufferFrameRate);
629 		Unlock();
630 #if DEBUG
631 		buffer_num++;
632 #endif
633 	}
634 }
635