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