3rdparty/licenseReport: Add seperate LGPL checks
[haiku.git] / src / add-ons / media / media-add-ons / mixer / MixerCore.cpp
blobc0ce9e670e28dcc675cc68d21e6a6fcc5b4fb4f1
1 /*
2 * Copyright 2003-2016 Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Marcus Overhagen
7 * Dario Casalinuovo
8 */
11 #include "MixerCore.h"
13 #include <string.h>
15 #include <Buffer.h>
16 #include <BufferGroup.h>
17 #include <BufferProducer.h>
18 #include <MediaNode.h>
19 #include <RealtimeAlloc.h>
20 #include <StackOrHeapArray.h>
21 #include <StopWatch.h>
22 #include <TimeSource.h>
24 #include "AudioMixer.h"
25 #include "Interpolate.h"
26 #include "MixerInput.h"
27 #include "MixerOutput.h"
28 #include "MixerUtils.h"
29 #include "Resampler.h"
30 #include "RtList.h"
33 #define DOUBLE_RATE_MIXING 0
35 #if DEBUG > 1
36 # define ASSERT_LOCKED() if (fLocker->IsLocked()) {} \
37 else debugger("core not locked, meltdown occurred")
38 #else
39 # define ASSERT_LOCKED() ((void)0)
40 #endif
42 /*! Mixer channels are identified by a type number, each type number corresponds
43 to the one of the channel masks of enum media_multi_channels.
45 The mixer buffer uses either the same frame rate and same count of frames as
46 the output buffer, or the double frame rate and frame count.
48 All mixer input ring buffers must be an exact multiple of the mixer buffer
49 size, so that we do not get any buffer wrap around during reading from the
50 input buffers.
51 The mixer input is told by constructor (or after a format change by
52 SetMixBufferFormat() of the current mixer buffer propertys, and must
53 allocate a buffer that is an exact multiple,
57 struct chan_info {
58 const char *base;
59 uint32 sample_offset;
60 float gain;
64 MixerCore::MixerCore(AudioMixer *node)
66 fLocker(new BLocker("mixer core lock")),
67 fInputs(new BList),
68 fOutput(0),
69 fNextInputID(1),
70 fRunning(false),
71 fStarted(false),
72 fOutputEnabled(true),
73 fResampler(0),
74 fMixBuffer(0),
75 fMixBufferFrameRate(0),
76 fMixBufferFrameCount(0),
77 fMixBufferChannelCount(0),
78 fMixBufferChannelTypes(0),
79 fDoubleRateMixing(DOUBLE_RATE_MIXING),
80 fDownstreamLatency(1),
81 fSettings(new MixerSettings),
82 fNode(node),
83 fBufferGroup(0),
84 fTimeSource(0),
85 fMixThread(-1),
86 fMixThreadWaitSem(-1),
87 fHasEvent(false),
88 fOutputGain(1.0)
93 MixerCore::~MixerCore()
95 delete fSettings;
97 delete fLocker;
98 delete fInputs;
100 ASSERT(fMixThreadWaitSem == -1);
101 ASSERT(fMixThread == -1);
103 if (fMixBuffer)
104 rtm_free(fMixBuffer);
106 if (fTimeSource)
107 fTimeSource->Release();
109 if (fResampler) {
110 for (int i = 0; i < fMixBufferChannelCount; i++)
111 delete fResampler[i];
112 delete[] fResampler;
115 delete fMixBufferChannelTypes;
119 MixerSettings *
120 MixerCore::Settings()
122 return fSettings;
126 void
127 MixerCore::UpdateResamplingAlgorithm()
129 ASSERT_LOCKED();
131 _UpdateResamplers(fOutput->MediaOutput().format.u.raw_audio);
133 for (int32 i = fInputs->CountItems() - 1; i >= 0; i--) {
134 MixerInput* input
135 = reinterpret_cast<MixerInput*>(fInputs->ItemAtFast(i));
136 input->UpdateResamplingAlgorithm();
141 void
142 MixerCore::SetOutputAttenuation(float gain)
144 ASSERT_LOCKED();
145 fOutputGain = gain;
149 MixerInput*
150 MixerCore::AddInput(const media_input& input)
152 ASSERT_LOCKED();
153 MixerInput* in = new MixerInput(this, input, fMixBufferFrameRate,
154 fMixBufferFrameCount);
155 fInputs->AddItem(in);
156 return in;
160 MixerOutput*
161 MixerCore::AddOutput(const media_output& output)
163 ASSERT_LOCKED();
164 if (fOutput) {
165 ERROR("MixerCore::AddOutput: already connected\n");
166 return fOutput;
168 fOutput = new MixerOutput(this, output);
169 // the output format might have been adjusted inside MixerOutput
170 _ApplyOutputFormat();
172 ASSERT(!fRunning);
173 if (fStarted && fOutputEnabled)
174 StartMixThread();
176 return fOutput;
180 bool
181 MixerCore::RemoveInput(int32 inputID)
183 ASSERT_LOCKED();
184 MixerInput *input;
185 for (int i = 0; (input = Input(i)) != 0; i++) {
186 if (input->ID() == inputID) {
187 fInputs->RemoveItem(i);
188 delete input;
189 return true;
192 return false;
196 bool
197 MixerCore::RemoveOutput()
199 ASSERT_LOCKED();
200 if (!fOutput)
201 return false;
203 if (fStarted)
204 StopMixThread();
206 delete fOutput;
207 fOutput = 0;
208 fOutputEnabled = true;
209 return true;
213 int32
214 MixerCore::CreateInputID()
216 ASSERT_LOCKED();
217 return fNextInputID++;
221 MixerInput *
222 MixerCore::Input(int i)
224 ASSERT_LOCKED();
225 return (MixerInput *)fInputs->ItemAt(i);
229 MixerOutput *
230 MixerCore::Output()
232 ASSERT_LOCKED();
233 return fOutput;
237 void
238 MixerCore::BufferReceived(BBuffer *buffer, bigtime_t lateness)
240 ASSERT_LOCKED();
241 MixerInput *input;
242 int32 id = buffer->Header()->destination;
243 for (int i = 0; (input = Input(i)) != 0; i++) {
244 if (input->ID() == id) {
245 input->BufferReceived(buffer);
246 return;
249 ERROR("MixerCore::BufferReceived: received buffer for unknown id %ld\n",
250 id);
254 void
255 MixerCore::InputFormatChanged(int32 inputID,
256 const media_multi_audio_format &format)
258 ASSERT_LOCKED();
259 ERROR("MixerCore::InputFormatChanged not handled\n");
263 void
264 MixerCore::OutputFormatChanged(const media_multi_audio_format &format)
266 ASSERT_LOCKED();
267 bool was_started = fStarted;
269 if (was_started)
270 Stop();
272 fOutput->ChangeFormat(format);
273 _ApplyOutputFormat();
275 if (was_started)
276 Start();
280 void
281 MixerCore::SetOutputBufferGroup(BBufferGroup *group)
283 ASSERT_LOCKED();
284 fBufferGroup = group;
288 void
289 MixerCore::SetTimingInfo(BTimeSource *ts, bigtime_t downstream_latency)
291 ASSERT_LOCKED();
292 if (fTimeSource)
293 fTimeSource->Release();
295 fTimeSource = dynamic_cast<BTimeSource *>(ts->Acquire());
296 fDownstreamLatency = downstream_latency;
298 TRACE("MixerCore::SetTimingInfo, now = %Ld, downstream latency %Ld\n",
299 fTimeSource->Now(), fDownstreamLatency);
303 void
304 MixerCore::EnableOutput(bool enabled)
306 ASSERT_LOCKED();
307 TRACE("MixerCore::EnableOutput %d\n", enabled);
308 fOutputEnabled = enabled;
310 if (fRunning && !fOutputEnabled)
311 StopMixThread();
313 if (!fRunning && fOutput && fStarted && fOutputEnabled)
314 StartMixThread();
318 uint32
319 MixerCore::OutputChannelCount()
321 return (fOutput) ? fOutput->GetOutputChannelCount() : 0;
325 bool
326 MixerCore::Start()
328 ASSERT_LOCKED();
329 TRACE("MixerCore::Start\n");
330 if (fStarted)
331 return false;
333 fStarted = true;
335 ASSERT(!fRunning);
337 // only start the mix thread if we have an output
338 if (fOutput && fOutputEnabled)
339 StartMixThread();
341 return true;
345 bool
346 MixerCore::Stop()
348 ASSERT_LOCKED();
349 TRACE("MixerCore::Stop\n");
350 if (!fStarted)
351 return false;
353 if (fRunning)
354 StopMixThread();
356 fStarted = false;
357 return true;
361 void
362 MixerCore::StartMixThread()
364 ASSERT(fOutputEnabled == true);
365 ASSERT(fRunning == false);
366 ASSERT(fOutput);
367 fRunning = true;
368 fMixThreadWaitSem = create_sem(0, "mix thread wait");
369 fMixThread = spawn_thread(_MixThreadEntry, "Yeah baby, very shagadelic",
370 120, this);
371 resume_thread(fMixThread);
375 void
376 MixerCore::StopMixThread()
378 ASSERT(fRunning == true);
379 ASSERT(fMixThread > 0);
380 ASSERT(fMixThreadWaitSem > 0);
382 fRunning = false;
383 status_t unused;
384 delete_sem(fMixThreadWaitSem);
385 wait_for_thread(fMixThread, &unused);
387 fMixThread = -1;
388 fMixThreadWaitSem = -1;
392 // #pragma mark - private
395 void
396 MixerCore::_UpdateResamplers(const media_multi_audio_format& format)
398 ASSERT_LOCKED();
400 if (fResampler != NULL) {
401 for (int i = 0; i < fMixBufferChannelCount; i++)
402 delete fResampler[i];
403 delete[] fResampler;
406 fResampler = new Resampler*[fMixBufferChannelCount];
407 for (int i = 0; i < fMixBufferChannelCount; i++) {
408 switch (Settings()->ResamplingAlgorithm()) {
409 case 2:
410 fResampler[i] = new Interpolate(
411 media_raw_audio_format::B_AUDIO_FLOAT, format.format);
412 break;
413 default:
414 fResampler[i] = new Resampler(
415 media_raw_audio_format::B_AUDIO_FLOAT, format.format);
421 void
422 MixerCore::_ApplyOutputFormat()
424 ASSERT_LOCKED();
426 const media_multi_audio_format& format
427 = fOutput->MediaOutput().format.u.raw_audio;
429 if (fMixBuffer != NULL)
430 rtm_free(fMixBuffer);
432 delete fMixBufferChannelTypes;
434 fMixBufferFrameRate = (int32)(0.5 + format.frame_rate);
435 fMixBufferFrameCount = frames_per_buffer(format);
436 if (fDoubleRateMixing) {
437 fMixBufferFrameRate *= 2;
438 fMixBufferFrameCount *= 2;
440 fMixBufferChannelCount = format.channel_count;
441 ASSERT(fMixBufferChannelCount == fOutput->GetOutputChannelCount());
442 fMixBufferChannelTypes = new int32 [format.channel_count];
444 for (int i = 0; i < fMixBufferChannelCount; i++) {
445 fMixBufferChannelTypes[i]
446 = ChannelMaskToChannelType(GetChannelMask(i, format.channel_mask));
449 fMixBuffer = (float*)rtm_alloc(NULL, sizeof(float) * fMixBufferFrameCount
450 * fMixBufferChannelCount);
451 ASSERT(fMixBuffer != NULL);
453 _UpdateResamplers(format);
455 TRACE("MixerCore::OutputFormatChanged:\n");
456 TRACE(" fMixBufferFrameRate %ld\n", fMixBufferFrameRate);
457 TRACE(" fMixBufferFrameCount %ld\n", fMixBufferFrameCount);
458 TRACE(" fMixBufferChannelCount %ld\n", fMixBufferChannelCount);
459 for (int i = 0; i < fMixBufferChannelCount; i++)
460 TRACE(" fMixBufferChannelTypes[%i] %ld\n", i, fMixBufferChannelTypes[i]);
462 MixerInput *input;
463 for (int i = 0; (input = Input(i)); i++)
464 input->SetMixBufferFormat(fMixBufferFrameRate, fMixBufferFrameCount);
468 int32
469 MixerCore::_MixThreadEntry(void* arg)
471 static_cast<MixerCore*>(arg)->_MixThread();
472 return 0;
476 void
477 MixerCore::_MixThread()
479 // The broken BeOS R5 multiaudio node starts with time 0,
480 // then publishes negative times for about 50ms, publishes 0
481 // again until it finally reaches time values > 0
482 if (!Lock())
483 return;
484 bigtime_t start = fTimeSource->Now();
485 Unlock();
486 while (start <= 0) {
487 TRACE("MixerCore: delaying _MixThread start, timesource is at %Ld\n",
488 start);
489 snooze(5000);
490 if (!Lock())
491 return;
492 start = fTimeSource->Now();
493 Unlock();
496 fEventLatency = max((bigtime_t)3600, bigtime_t(0.4 * buffer_duration(
497 fOutput->MediaOutput().format.u.raw_audio)));
499 // TODO: when the format changes while running, everything is wrong!
500 bigtime_t bufferRequestTimeout = buffer_duration(
501 fOutput->MediaOutput().format.u.raw_audio) / 2;
503 TRACE("MixerCore: starting _MixThread at %Ld with latency %Ld and "
504 "downstream latency %Ld, bufferRequestTimeout %Ld\n", start, latency,
505 fDownstreamLatency, bufferRequestTimeout);
507 // We must read from the input buffer at a position (pos) that is always
508 // a multiple of fMixBufferFrameCount.
509 int64 temp = frames_for_duration(fMixBufferFrameRate, start);
510 int64 frameBase = ((temp / fMixBufferFrameCount) + 1)
511 * fMixBufferFrameCount;
512 bigtime_t timeBase = duration_for_frames(fMixBufferFrameRate, frameBase);
514 TRACE("MixerCore: starting _MixThread, start %Ld, timeBase %Ld, "
515 "frameBase %Ld\n", start, timeBase, frameBase);
517 ASSERT(fMixBufferFrameCount > 0);
519 #if DEBUG
520 uint64 bufferIndex = 0;
521 #endif
523 typedef RtList<chan_info> chan_info_list;
524 chan_info_list inputChanInfos[MAX_CHANNEL_TYPES];
525 BStackOrHeapArray<chan_info_list, 16> mixChanInfos(fMixBufferChannelCount);
526 // TODO: this does not support changing output channel count
528 fEventTime = timeBase;
529 int64 framePos = 0;
530 status_t ret = B_ERROR;
532 while(fRunning == true) {
533 if (fHasEvent == false)
534 goto schedule_next_event;
536 ret = acquire_sem(fMixThreadWaitSem);
537 if (ret == B_INTERRUPTED)
538 continue;
539 else if (ret != B_OK)
540 return;
542 fHasEvent = false;
544 if (!LockWithTimeout(10000)) {
545 ERROR("MixerCore: LockWithTimeout failed\n");
546 continue;
549 // no inputs or output muted, skip further processing and just send an
550 // empty buffer
551 if (fInputs->IsEmpty() || fOutput->IsMuted()) {
552 int size = fOutput->MediaOutput().format.u.raw_audio.buffer_size;
553 BBuffer* buffer = fBufferGroup->RequestBuffer(size,
554 bufferRequestTimeout);
555 if (buffer != NULL) {
556 memset(buffer->Data(), 0, size);
557 // fill in the buffer header
558 media_header* hdr = buffer->Header();
559 hdr->type = B_MEDIA_RAW_AUDIO;
560 hdr->size_used = size;
561 hdr->time_source = fTimeSource->ID();
562 hdr->start_time = fEventTime;
563 if (fNode->SendBuffer(buffer, fOutput) != B_OK) {
564 #if DEBUG
565 ERROR("MixerCore: SendBuffer failed for buffer %Ld\n",
566 bufferIndex);
567 #else
568 ERROR("MixerCore: SendBuffer failed\n");
569 #endif
570 buffer->Recycle();
572 } else {
573 #if DEBUG
574 ERROR("MixerCore: RequestBuffer failed for buffer %Ld\n",
575 bufferIndex);
576 #else
577 ERROR("MixerCore: RequestBuffer failed\n");
578 #endif
580 goto schedule_next_event;
583 int64 currentFramePos;
584 currentFramePos = frameBase + framePos;
586 // mix all data from all inputs into the mix buffer
587 ASSERT(currentFramePos % fMixBufferFrameCount == 0);
589 PRINT(4, "create new buffer event at %Ld, reading input frames at "
590 "%Ld\n", fEventTime, currentFramePos);
592 // Init the channel information for each MixerInput.
593 for (int i = 0; MixerInput* input = Input(i); i++) {
594 int count = input->GetMixerChannelCount();
595 for (int channel = 0; channel < count; channel++) {
596 int type;
597 const float* base;
598 uint32 sampleOffset;
599 float gain;
600 if (!input->GetMixerChannelInfo(channel, currentFramePos,
601 fEventTime, &base, &sampleOffset, &type, &gain)) {
602 continue;
604 if (type < 0 || type >= MAX_CHANNEL_TYPES)
605 continue;
606 chan_info* info = inputChanInfos[type].Create();
607 info->base = (const char*)base;
608 info->sample_offset = sampleOffset;
609 info->gain = gain;
613 for (int channel = 0; channel < fMixBufferChannelCount; channel++) {
614 int sourceCount = fOutput->GetOutputChannelSourceCount(channel);
615 for (int i = 0; i < sourceCount; i++) {
616 int type;
617 float gain;
618 fOutput->GetOutputChannelSourceInfoAt(channel, i, &type,
619 &gain);
620 if (type < 0 || type >= MAX_CHANNEL_TYPES)
621 continue;
622 int count = inputChanInfos[type].CountItems();
623 for (int j = 0; j < count; j++) {
624 chan_info* info = inputChanInfos[type].ItemAt(j);
625 chan_info* newInfo = mixChanInfos[channel].Create();
626 newInfo->base = info->base;
627 newInfo->sample_offset = info->sample_offset;
628 newInfo->gain = info->gain * gain;
633 memset(fMixBuffer, 0,
634 fMixBufferChannelCount * fMixBufferFrameCount * sizeof(float));
635 for (int channel = 0; channel < fMixBufferChannelCount; channel++) {
636 PRINT(5, "_MixThread: channel %d has %d sources\n", channel,
637 mixChanInfos[channel].CountItems());
639 int count = mixChanInfos[channel].CountItems();
640 for (int i = 0; i < count; i++) {
641 chan_info* info = mixChanInfos[channel].ItemAt(i);
642 PRINT(5, "_MixThread: base %p, sample-offset %2d, gain %.3f\n",
643 info->base, info->sample_offset, info->gain);
644 // This looks slightly ugly, but the current GCC will generate
645 // the fastest code this way.
646 // fMixBufferFrameCount is always > 0.
647 uint32 dstSampleOffset
648 = fMixBufferChannelCount * sizeof(float);
649 uint32 srcSampleOffset = info->sample_offset;
650 register char* dst = (char*)&fMixBuffer[channel];
651 register char* src = (char*)info->base;
652 register float gain = info->gain;
653 register int j = fMixBufferFrameCount;
654 do {
655 *(float*)dst += *(const float*)src * gain;
656 dst += dstSampleOffset;
657 src += srcSampleOffset;
658 } while (--j);
662 // request a buffer
663 BBuffer* buffer;
664 buffer = fBufferGroup->RequestBuffer(
665 fOutput->MediaOutput().format.u.raw_audio.buffer_size,
666 bufferRequestTimeout);
667 if (buffer != NULL) {
668 // copy data from mix buffer into output buffer
669 for (int i = 0; i < fMixBufferChannelCount; i++) {
670 fResampler[i]->Resample(
671 reinterpret_cast<char*>(fMixBuffer) + i * sizeof(float),
672 fMixBufferChannelCount * sizeof(float),
673 fMixBufferFrameCount,
674 reinterpret_cast<char*>(buffer->Data())
675 + (i * bytes_per_sample(
676 fOutput->MediaOutput().format.u.raw_audio)),
677 bytes_per_frame(fOutput->MediaOutput().format.u.raw_audio),
678 frames_per_buffer(
679 fOutput->MediaOutput().format.u.raw_audio),
680 fOutputGain * fOutput->GetOutputChannelGain(i));
682 PRINT(4, "send buffer, inframes %ld, outframes %ld\n",
683 fMixBufferFrameCount,
684 frames_per_buffer(fOutput->MediaOutput().format.u.raw_audio));
686 // fill in the buffer header
687 media_header* hdr = buffer->Header();
688 hdr->type = B_MEDIA_RAW_AUDIO;
689 hdr->size_used
690 = fOutput->MediaOutput().format.u.raw_audio.buffer_size;
691 hdr->time_source = fTimeSource->ID();
692 hdr->start_time = fEventTime;
694 // swap byte order if necessary
695 fOutput->AdjustByteOrder(buffer);
697 // send the buffer
698 status_t res = fNode->SendBuffer(buffer, fOutput);
699 if (res != B_OK) {
700 #if DEBUG
701 ERROR("MixerCore: SendBuffer failed for buffer %Ld\n",
702 bufferIndex);
703 #else
704 ERROR("MixerCore: SendBuffer failed\n");
705 #endif
706 buffer->Recycle();
708 } else {
709 #if DEBUG
710 ERROR("MixerCore: RequestBuffer failed for buffer %Ld\n",
711 bufferIndex);
712 #else
713 ERROR("MixerCore: RequestBuffer failed\n");
714 #endif
717 // make all lists empty
718 for (int i = 0; i < MAX_CHANNEL_TYPES; i++)
719 inputChanInfos[i].MakeEmpty();
720 for (int i = 0; i < fOutput->GetOutputChannelCount(); i++)
721 mixChanInfos[i].MakeEmpty();
723 schedule_next_event:
724 Unlock();
726 // schedule next event
727 framePos += fMixBufferFrameCount;
728 fEventTime = timeBase + bigtime_t((1000000LL * framePos)
729 / fMixBufferFrameRate);
731 media_timed_event mixerEvent(PickEvent(),
732 MIXER_PROCESS_EVENT, 0, BTimedEventQueue::B_NO_CLEANUP);
734 ret = write_port(fNode->ControlPort(), MIXER_SCHEDULE_EVENT,
735 &mixerEvent, sizeof(mixerEvent));
736 if (ret != B_OK)
737 TRACE("MixerCore::_MixThread: can't write to owner port\n");
739 fHasEvent = true;
741 #if DEBUG
742 bufferIndex++;
743 #endif