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