2 * Copyright 2003-2016 Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
11 #include "MixerCore.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"
33 #define DOUBLE_RATE_MIXING 0
36 # define ASSERT_LOCKED() if (fLocker->IsLocked()) {} \
37 else debugger("core not locked, meltdown occurred")
39 # define ASSERT_LOCKED() ((void)0)
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
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,
64 MixerCore::MixerCore(AudioMixer
*node
)
66 fLocker(new BLocker("mixer core lock")),
75 fMixBufferFrameRate(0),
76 fMixBufferFrameCount(0),
77 fMixBufferChannelCount(0),
78 fMixBufferChannelTypes(0),
79 fDoubleRateMixing(DOUBLE_RATE_MIXING
),
80 fDownstreamLatency(1),
81 fSettings(new MixerSettings
),
86 fMixThreadWaitSem(-1),
93 MixerCore::~MixerCore()
100 ASSERT(fMixThreadWaitSem
== -1);
101 ASSERT(fMixThread
== -1);
104 rtm_free(fMixBuffer
);
107 fTimeSource
->Release();
110 for (int i
= 0; i
< fMixBufferChannelCount
; i
++)
111 delete fResampler
[i
];
115 delete fMixBufferChannelTypes
;
120 MixerCore::Settings()
127 MixerCore::UpdateResamplingAlgorithm()
131 _UpdateResamplers(fOutput
->MediaOutput().format
.u
.raw_audio
);
133 for (int32 i
= fInputs
->CountItems() - 1; i
>= 0; i
--) {
135 = reinterpret_cast<MixerInput
*>(fInputs
->ItemAtFast(i
));
136 input
->UpdateResamplingAlgorithm();
142 MixerCore::SetOutputAttenuation(float gain
)
150 MixerCore::AddInput(const media_input
& input
)
153 MixerInput
* in
= new MixerInput(this, input
, fMixBufferFrameRate
,
154 fMixBufferFrameCount
);
155 fInputs
->AddItem(in
);
161 MixerCore::AddOutput(const media_output
& output
)
165 ERROR("MixerCore::AddOutput: already connected\n");
168 fOutput
= new MixerOutput(this, output
);
169 // the output format might have been adjusted inside MixerOutput
170 _ApplyOutputFormat();
173 if (fStarted
&& fOutputEnabled
)
181 MixerCore::RemoveInput(int32 inputID
)
185 for (int i
= 0; (input
= Input(i
)) != 0; i
++) {
186 if (input
->ID() == inputID
) {
187 fInputs
->RemoveItem(i
);
197 MixerCore::RemoveOutput()
208 fOutputEnabled
= true;
214 MixerCore::CreateInputID()
217 return fNextInputID
++;
222 MixerCore::Input(int i
)
225 return (MixerInput
*)fInputs
->ItemAt(i
);
238 MixerCore::BufferReceived(BBuffer
*buffer
, bigtime_t lateness
)
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
);
249 ERROR("MixerCore::BufferReceived: received buffer for unknown id %ld\n",
255 MixerCore::InputFormatChanged(int32 inputID
,
256 const media_multi_audio_format
&format
)
259 ERROR("MixerCore::InputFormatChanged not handled\n");
264 MixerCore::OutputFormatChanged(const media_multi_audio_format
&format
)
267 bool was_started
= fStarted
;
272 fOutput
->ChangeFormat(format
);
273 _ApplyOutputFormat();
281 MixerCore::SetOutputBufferGroup(BBufferGroup
*group
)
284 fBufferGroup
= group
;
289 MixerCore::SetTimingInfo(BTimeSource
*ts
, bigtime_t downstream_latency
)
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
);
304 MixerCore::EnableOutput(bool enabled
)
307 TRACE("MixerCore::EnableOutput %d\n", enabled
);
308 fOutputEnabled
= enabled
;
310 if (fRunning
&& !fOutputEnabled
)
313 if (!fRunning
&& fOutput
&& fStarted
&& fOutputEnabled
)
319 MixerCore::OutputChannelCount()
321 return (fOutput
) ? fOutput
->GetOutputChannelCount() : 0;
329 TRACE("MixerCore::Start\n");
337 // only start the mix thread if we have an output
338 if (fOutput
&& fOutputEnabled
)
349 TRACE("MixerCore::Stop\n");
362 MixerCore::StartMixThread()
364 ASSERT(fOutputEnabled
== true);
365 ASSERT(fRunning
== false);
368 fMixThreadWaitSem
= create_sem(0, "mix thread wait");
369 fMixThread
= spawn_thread(_MixThreadEntry
, "Yeah baby, very shagadelic",
371 resume_thread(fMixThread
);
376 MixerCore::StopMixThread()
378 ASSERT(fRunning
== true);
379 ASSERT(fMixThread
> 0);
380 ASSERT(fMixThreadWaitSem
> 0);
384 delete_sem(fMixThreadWaitSem
);
385 wait_for_thread(fMixThread
, &unused
);
388 fMixThreadWaitSem
= -1;
392 // #pragma mark - private
396 MixerCore::_UpdateResamplers(const media_multi_audio_format
& format
)
400 if (fResampler
!= NULL
) {
401 for (int i
= 0; i
< fMixBufferChannelCount
; i
++)
402 delete fResampler
[i
];
406 fResampler
= new Resampler
*[fMixBufferChannelCount
];
407 for (int i
= 0; i
< fMixBufferChannelCount
; i
++) {
408 switch (Settings()->ResamplingAlgorithm()) {
410 fResampler
[i
] = new Interpolate(
411 media_raw_audio_format::B_AUDIO_FLOAT
, format
.format
);
414 fResampler
[i
] = new Resampler(
415 media_raw_audio_format::B_AUDIO_FLOAT
, format
.format
);
422 MixerCore::_ApplyOutputFormat()
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
]);
463 for (int i
= 0; (input
= Input(i
)); i
++)
464 input
->SetMixBufferFormat(fMixBufferFrameRate
, fMixBufferFrameCount
);
469 MixerCore::_MixThreadEntry(void* arg
)
471 static_cast<MixerCore
*>(arg
)->_MixThread();
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
484 bigtime_t start
= fTimeSource
->Now();
487 TRACE("MixerCore: delaying _MixThread start, timesource is at %Ld\n",
492 start
= fTimeSource
->Now();
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);
520 uint64 bufferIndex
= 0;
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
;
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
)
539 else if (ret
!= B_OK
)
544 if (!LockWithTimeout(10000)) {
545 ERROR("MixerCore: LockWithTimeout failed\n");
549 // no inputs or output muted, skip further processing and just send an
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
) {
565 ERROR("MixerCore: SendBuffer failed for buffer %Ld\n",
568 ERROR("MixerCore: SendBuffer failed\n");
574 ERROR("MixerCore: RequestBuffer failed for buffer %Ld\n",
577 ERROR("MixerCore: RequestBuffer failed\n");
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
++) {
600 if (!input
->GetMixerChannelInfo(channel
, currentFramePos
,
601 fEventTime
, &base
, &sampleOffset
, &type
, &gain
)) {
604 if (type
< 0 || type
>= MAX_CHANNEL_TYPES
)
606 chan_info
* info
= inputChanInfos
[type
].Create();
607 info
->base
= (const char*)base
;
608 info
->sample_offset
= sampleOffset
;
613 for (int channel
= 0; channel
< fMixBufferChannelCount
; channel
++) {
614 int sourceCount
= fOutput
->GetOutputChannelSourceCount(channel
);
615 for (int i
= 0; i
< sourceCount
; i
++) {
618 fOutput
->GetOutputChannelSourceInfoAt(channel
, i
, &type
,
620 if (type
< 0 || type
>= MAX_CHANNEL_TYPES
)
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
;
655 *(float*)dst
+= *(const float*)src
* gain
;
656 dst
+= dstSampleOffset
;
657 src
+= srcSampleOffset
;
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
),
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
;
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
);
698 status_t res
= fNode
->SendBuffer(buffer
, fOutput
);
701 ERROR("MixerCore: SendBuffer failed for buffer %Ld\n",
704 ERROR("MixerCore: SendBuffer failed\n");
710 ERROR("MixerCore: RequestBuffer failed for buffer %Ld\n",
713 ERROR("MixerCore: RequestBuffer failed\n");
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();
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
));
737 TRACE("MixerCore::_MixThread: can't write to owner port\n");