3rdparty/licenseReport: Add seperate LGPL checks
[haiku.git] / src / add-ons / media / media-add-ons / mixer / AudioMixer.cpp
blob4bf0978dacbcbe6ba3ec477591601de6282f585b
1 /*
2 * Copyright 2002 David Shipman,
3 * Copyright 2003-2007 Marcus Overhagen
4 * Copyright 2007-2011 Haiku Inc. All rights reserved.
6 * Distributed under the terms of the MIT License.
7 */
10 #include "AudioMixer.h"
12 #include <math.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
17 #include <Buffer.h>
18 #include <Catalog.h>
19 #include <FindDirectory.h>
20 #include <MediaDefs.h>
21 #include <MediaRoster.h>
22 #include <ParameterWeb.h>
23 #include <Path.h>
24 #include <RealtimeAlloc.h>
25 #include <TimeSource.h>
27 #include "MixerCore.h"
28 #include "MixerInput.h"
29 #include "MixerOutput.h"
30 #include "MixerUtils.h"
33 #undef B_TRANSLATION_CONTEXT
34 #define B_TRANSLATION_CONTEXT "AudioMixer"
37 // the range of the gain sliders (in dB)
38 #define DB_MAX 18.0
39 #define DB_MIN -60.0
40 // when using non linear sliders, we use a power function with
41 #define DB_EXPONENT_POSITIVE 1.4 // for dB values > 0
42 #define DB_EXPONENT_NEGATIVE 1.8 // for dB values < 0
44 #define USE_MEDIA_FORMAT_WORKAROUND 1
46 #define DB_TO_GAIN(db) dB_to_Gain((db))
47 #define GAIN_TO_DB(gain) Gain_to_dB((gain))
48 #define PERCENT_TO_GAIN(pct) ((pct) / 100.0)
49 #define GAIN_TO_PERCENT(gain) ((gain) * 100.0)
51 // the id is encoded with 16 bits
52 // then chan and src (or dst) are encoded with 6 bits
53 // the unique number with 4 bits
54 // the PARAM_ETC etc is encoded with 26 bits
55 #define PARAM_SRC_ENABLE(id, chan, src) (((id) << 16) | ((chan) << 10) | ((src) << 4) | 0x1)
56 #define PARAM_SRC_GAIN(id, chan, src) (((id) << 16) | ((chan) << 10) | ((src) << 4) | 0x2)
57 #define PARAM_DST_ENABLE(id, chan, dst) (((id) << 16) | ((chan) << 10) | ((dst) << 4) | 0x3)
58 #define PARAM_ETC(etc) (((etc) << 16) | 0x4)
59 #define PARAM_SRC_STR(id, chan) (((id) << 16) | ((chan) << 10) | 0x5)
60 #define PARAM_DST_STR(id, chan) (((id) << 16) | ((chan) << 10) | 0x6)
61 #define PARAM_MUTE(id) (((id) << 16) | 0x7)
62 #define PARAM_GAIN(id) (((id) << 16) | 0x8)
63 #define PARAM_BALANCE(id) (((id) << 16) | 0x9)
64 #define PARAM_STR1(id) (((id) << 16) | ((1) << 10) | 0xa)
65 #define PARAM_STR2(id) (((id) << 16) | ((2) << 10) | 0xa)
66 #define PARAM_STR3(id) (((id) << 16) | ((3) << 10) | 0xa)
67 #define PARAM_STR4(id) (((id) << 16) | ((4) << 10) | 0xa)
68 #define PARAM_STR5(id) (((id) << 16) | ((5) << 10) | 0xa)
69 #define PARAM_STR6(id) (((id) << 16) | ((6) << 10) | 0xa)
70 #define PARAM_STR7(id) (((id) << 16) | ((7) << 10) | 0xa)
72 #define PARAM(id) ((id) >> 16)
73 #define ETC(id) ((id) >> 16)
74 #define PARAM_CHAN(id) (((id) >> 10) & 0x3f)
75 #define PARAM_SRC(id) (((id) >> 4) & 0x3f)
76 #define PARAM_DST(id) (((id) >> 4) & 0x3f)
77 #define PARAM_IS_SRC_ENABLE(id) (((id) & 0xf) == 0x1)
78 #define PARAM_IS_SRC_GAIN(id) (((id) & 0xf) == 0x2)
79 #define PARAM_IS_DST_ENABLE(id) (((id) & 0xf) == 0x3)
80 #define PARAM_IS_ETC(id) (((id) & 0xf) == 0x4)
81 #define PARAM_IS_MUTE(id) (((id) & 0xf) == 0x7)
82 #define PARAM_IS_GAIN(id) (((id) & 0xf) == 0x8)
83 #define PARAM_IS_BALANCE(id) (((id) & 0xf) == 0x9)
85 #if USE_MEDIA_FORMAT_WORKAROUND
86 static void
87 multi_audio_format_specialize(media_multi_audio_format *format,
88 const media_multi_audio_format *other);
89 #endif
91 #define FORMAT_USER_DATA_TYPE 0x7294a8f3
92 #define FORMAT_USER_DATA_MAGIC_1 0xc84173bd
93 #define FORMAT_USER_DATA_MAGIC_2 0x4af62b7d
96 const static bigtime_t kMaxLatency = 150000;
97 // 150 ms is the maximum latency we publish
99 const bigtime_t kMinMixingTime = 3500;
100 const bigtime_t kMaxJitter = 1500;
103 AudioMixer::AudioMixer(BMediaAddOn *addOn, bool isSystemMixer)
105 BMediaNode("Audio Mixer"),
106 BBufferConsumer(B_MEDIA_RAW_AUDIO),
107 BBufferProducer(B_MEDIA_RAW_AUDIO),
108 BControllable(),
109 BMediaEventLooper(),
110 fAddOn(addOn),
111 fCore(new MixerCore(this)),
112 fWeb(NULL),
113 fBufferGroup(NULL),
114 fDownstreamLatency(1),
115 fInternalLatency(1),
116 fDisableStop(false),
117 fLastLateNotification(0),
118 fLastLateness(0)
120 BMediaNode::AddNodeKind(B_SYSTEM_MIXER);
122 // this is the default format used for all wildcard format SpecializeTo()s
123 fDefaultFormat.type = B_MEDIA_RAW_AUDIO;
124 fDefaultFormat.u.raw_audio.frame_rate = 96000;
125 fDefaultFormat.u.raw_audio.channel_count = 2;
126 fDefaultFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT;
127 fDefaultFormat.u.raw_audio.byte_order = B_MEDIA_HOST_ENDIAN;
128 fDefaultFormat.u.raw_audio.buffer_size = 4096;
129 fDefaultFormat.u.raw_audio.channel_mask = 0;
130 fDefaultFormat.u.raw_audio.valid_bits = 0;
131 fDefaultFormat.u.raw_audio.matrix_mask = 0;
133 if (isSystemMixer) {
134 // to get persistent settings, assign a settings file
135 BPath path;
136 if (B_OK != find_directory (B_USER_SETTINGS_DIRECTORY, &path))
137 path.SetTo("/boot/home/config/settings/");
138 path.Append("System Audio Mixer");
139 fCore->Settings()->SetSettingsFile(path.Path());
141 // disable stop on the auto started (system) mixer
142 DisableNodeStop();
145 ApplySettings();
149 AudioMixer::~AudioMixer()
151 BMediaEventLooper::Quit();
152 SetParameterWeb(NULL);
154 // stop the mixer
155 fCore->Lock();
156 fCore->Stop();
157 fCore->Unlock();
159 // disconnect all nodes from the mixer
160 // XXX todo
162 delete fCore;
163 delete fBufferGroup;
165 DEBUG_ONLY(fCore = 0; fBufferGroup = 0; fWeb = 0);
169 void
170 AudioMixer::ApplySettings()
172 fCore->Lock();
173 fCore->SetOutputAttenuation(fCore->Settings()->AttenuateOutput() ? 0.708 : 1.0);
174 fCore->Unlock();
178 void
179 AudioMixer::DisableNodeStop()
181 fDisableStop = true;
185 // #pragma mark - BMediaNode methods
188 void
189 AudioMixer::Stop(bigtime_t performance_time, bool immediate)
191 if (fDisableStop) {
192 TRACE("AudioMixer STOP is disabled\n");
193 return;
194 } else {
195 BMediaEventLooper::Stop(performance_time, immediate);
200 BMediaAddOn*
201 AudioMixer::AddOn(int32 *internal_id) const
203 *internal_id = 0;
204 return fAddOn;
208 // #pragma mark - BBufferConsumer methods
211 status_t
212 AudioMixer::HandleMessage(int32 message, const void *data, size_t size)
214 // since we're using a mediaeventlooper, there shouldn't be any messages
215 // except the message we are using to schedule output events for the
216 // process thread.
218 if (message == MIXER_SCHEDULE_EVENT) {
219 RealTimeQueue()->AddEvent(*(const media_timed_event*)data);
220 return B_OK;
223 return B_ERROR;
227 status_t
228 AudioMixer::AcceptFormat(const media_destination &dest, media_format *ioFormat)
230 PRINT_FORMAT("AudioMixer::AcceptFormat: ", *ioFormat);
232 // check that the specified format is reasonable for the specified destination, and
233 // fill in any wildcard fields for which our BBufferConsumer has specific requirements.
235 // we have multiple inputs with different IDs, but
236 // the port number must match our ControlPort()
237 if (dest.port != ControlPort())
238 return B_MEDIA_BAD_DESTINATION;
240 // specialize to raw audio format if necessary
241 if (ioFormat->type == B_MEDIA_UNKNOWN_TYPE)
242 ioFormat->type = B_MEDIA_RAW_AUDIO;
244 // we require a raw audio format
245 if (ioFormat->type != B_MEDIA_RAW_AUDIO)
246 return B_MEDIA_BAD_FORMAT;
248 // We do not have special requirements, but just in case
249 // another mixer is connecting to us and may need a hint
250 // to create a connection at optimal frame rate and
251 // channel count, we place this information in the user_data
252 fCore->Lock();
253 MixerOutput *output = fCore->Output();
254 uint32 channel_count = output ? output->MediaOutput().format.u.raw_audio.channel_count : 0;
255 float frame_rate = output ? output->MediaOutput().format.u.raw_audio.frame_rate : 0.0;
256 fCore->Unlock();
257 ioFormat->user_data_type = FORMAT_USER_DATA_TYPE;
258 *(uint32 *)&ioFormat->user_data[0] = FORMAT_USER_DATA_MAGIC_1;
259 *(uint32 *)&ioFormat->user_data[4] = channel_count;
260 *(float *)&ioFormat->user_data[20] = frame_rate;
261 *(uint32 *)&ioFormat->user_data[44] = FORMAT_USER_DATA_MAGIC_2;
263 return B_OK;
267 status_t
268 AudioMixer::GetNextInput(int32 *cookie, media_input *out_input)
270 TRACE("AudioMixer::GetNextInput\n");
272 // our 0th input is always a wildcard and free one
273 if (*cookie == 0) {
274 out_input->node = Node();
275 out_input->source = media_source::null;
276 out_input->destination.port = ControlPort();
277 out_input->destination.id = 0;
278 memset(&out_input->format, 0, sizeof(out_input->format));
279 out_input->format.type = B_MEDIA_RAW_AUDIO;
280 strcpy(out_input->name, "Free Input");
281 *cookie += 1;
282 return B_OK;
285 // the other inputs are the currently connected ones
286 fCore->Lock();
287 MixerInput *input;
288 input = fCore->Input(*cookie - 1);
289 if (!input) {
290 fCore->Unlock();
291 return B_BAD_INDEX;
293 *out_input = input->MediaInput();
294 *cookie += 1;
295 fCore->Unlock();
296 return B_OK;
300 void
301 AudioMixer::DisposeInputCookie(int32 cookie)
303 // nothing to do
307 void
308 AudioMixer::BufferReceived(BBuffer *buffer)
311 if (buffer->Header()->type == B_MEDIA_PARAMETERS) {
312 TRACE("Control Buffer Received\n");
313 ApplyParameterData(buffer->Data(), buffer->SizeUsed());
314 buffer->Recycle();
315 return;
318 //PRINT(4, "buffer received at %12Ld, should arrive at %12Ld, delta %12Ld\n", TimeSource()->Now(), buffer->Header()->start_time, TimeSource()->Now() - buffer->Header()->start_time);
320 // to receive the buffer at the right time,
321 // push it through the event looper
322 media_timed_event event(buffer->Header()->start_time,
323 BTimedEventQueue::B_HANDLE_BUFFER, buffer,
324 BTimedEventQueue::B_RECYCLE_BUFFER);
325 EventQueue()->AddEvent(event);
329 void
330 AudioMixer::HandleInputBuffer(BBuffer* buffer, bigtime_t lateness)
332 bigtime_t variation = 0;
333 if (lateness > fLastLateness)
334 variation = lateness-fLastLateness;
336 if (variation > kMaxJitter) {
337 TRACE("AudioMixer: Dequeued input buffer %" B_PRIdBIGTIME
338 " usec late\n", lateness);
339 if (RunMode() == B_DROP_DATA || RunMode() == B_DECREASE_PRECISION
340 || RunMode() == B_INCREASE_LATENCY) {
341 TRACE("AudioMixer: sending notify\n");
343 // Build a media_source out of the header data
344 media_source source = media_source::null;
345 source.port = buffer->Header()->source_port;
346 source.id = buffer->Header()->source;
348 NotifyLateProducer(source, variation, TimeSource()->Now());
350 if (RunMode() == B_DROP_DATA) {
351 TRACE("AudioMixer: dropping buffer\n");
352 return;
357 fLastLateness = lateness;
359 fCore->Lock();
360 fCore->BufferReceived(buffer, lateness);
361 fCore->Unlock();
365 void
366 AudioMixer::ProducerDataStatus(const media_destination& for_whom,
367 int32 status, bigtime_t at_performance_time)
370 if (IsValidDest(for_whom))
372 media_timed_event event(at_performance_time, BTimedEventQueue::B_DATA_STATUS,
373 (void *)(&for_whom), BTimedEventQueue::B_NO_CLEANUP, status, 0, NULL);
374 EventQueue()->AddEvent(event);
376 // FIX_THIS
377 // the for_whom destination is not being sent correctly - verify in HandleEvent loop
384 status_t
385 AudioMixer::GetLatencyFor(const media_destination &for_whom,
386 bigtime_t *out_latency, media_node_id *out_timesource)
388 // we have multiple inputs with different IDs, but
389 // the port number must match our ControlPort()
390 if (for_whom.port != ControlPort())
391 return B_MEDIA_BAD_DESTINATION;
393 // return our event latency - this includes our internal + downstream
394 // latency, but _not_ the scheduling latency
395 *out_latency = EventLatency();
396 *out_timesource = TimeSource()->ID();
398 printf("AudioMixer::GetLatencyFor %" B_PRIdBIGTIME ", timesource is %"
399 B_PRId32 "\n", *out_latency, *out_timesource);
401 return B_OK;
405 status_t
406 AudioMixer::Connected(const media_source &producer,
407 const media_destination &where, const media_format &with_format,
408 media_input *out_input)
410 PRINT_FORMAT("AudioMixer::Connected: ", with_format);
412 // workaround for a crashing bug in RealPlayer. to be proper, RealPlayer's
413 // BBufferProducer::PrepareToConnect() should have removed all wildcards.
414 if (out_input->format.u.raw_audio.frame_rate == 0) {
415 fprintf(stderr, "Audio Mixer Warning: "
416 "Producer (port %" B_PRId32 ", id %" B_PRId32 ") connected with "
417 "frame_rate=0\n", producer.port, producer.id);
418 MixerOutput *output = fCore->Output();
419 float frame_rate = output
420 ? output->MediaOutput().format.u.raw_audio.frame_rate : 44100.0f;
421 out_input->format.u.raw_audio.frame_rate = frame_rate;
424 // a BBufferProducer is connecting to our BBufferConsumer
426 // incoming connections should always have an incoming ID=0,
427 // and the port number must match our ControlPort()
428 if (where.id != 0 || where.port != ControlPort())
429 return B_MEDIA_BAD_DESTINATION;
431 fCore->Lock();
433 // we assign a new id (!= 0) to the newly created input
434 out_input->destination.id = fCore->CreateInputID();
436 // We need to make sure that the outInput's name field contains a valid
437 // name, the name given the connection by the producer may still be an
438 // empty string.
439 // if the input has no name, assign one
440 if (strlen(out_input->name) == 0) {
441 sprintf(out_input->name, "Input %" B_PRId32,
442 out_input->destination.id);
445 // add a new input to the mixer engine
446 MixerInput *input;
447 input = fCore->AddInput(*out_input);
449 fCore->Settings()->LoadConnectionSettings(input);
451 fCore->Unlock();
453 // If we want the producer to use a specific BBufferGroup, we now need
454 // to call BMediaRoster::SetOutputBuffersFor() here to set the producer's
455 // buffer group.
456 // But we have no special buffer requirements anyway...
458 UpdateParameterWeb();
460 return B_OK;
464 void
465 AudioMixer::Disconnected(const media_source &producer,
466 const media_destination &where)
468 // One of our inputs has been disconnected
470 // check if it is really belongs to us
471 if (where.port != ControlPort()) {
472 TRACE("AudioMixer::Disconnected wrong input port\n");
473 return;
476 fCore->Lock();
478 if (!fCore->RemoveInput(where.id)) {
479 TRACE("AudioMixer::Disconnected can't remove input\n");
482 fCore->Unlock();
483 UpdateParameterWeb();
487 status_t
488 AudioMixer::FormatChanged(const media_source &producer,
489 const media_destination &consumer, int32 change_tag,
490 const media_format &format)
492 // at some point in the future (indicated by change_tag and RequestCompleted()),
493 // we will receive buffers in a different format
495 TRACE("AudioMixer::FormatChanged\n");
497 if (consumer.port != ControlPort() || consumer.id == 0)
498 return B_MEDIA_BAD_DESTINATION;
500 if (fCore->Settings()->RefuseInputFormatChange()) {
501 TRACE("AudioMixer::FormatChanged: input format change refused\n");
502 return B_NOT_ALLOWED;
505 // TODO: We should not apply the format change at this point
506 // TODO: At the moment, this is not implemented at the moment and will just
507 // crash the media_server.
508 return B_NOT_SUPPORTED;
510 // tell core about format change
511 fCore->Lock();
512 fCore->InputFormatChanged(consumer.id, format.u.raw_audio);
513 fCore->Unlock();
515 return B_OK;
519 // #pragma mark - BBufferProducer methods
522 status_t
523 AudioMixer::FormatSuggestionRequested(media_type type, int32 quality,
524 media_format *format)
526 TRACE("AudioMixer::FormatSuggestionRequested\n");
528 // BBufferProducer function, a downstream consumer
529 // is asking for our output format
531 if (type != B_MEDIA_RAW_AUDIO && type != B_MEDIA_UNKNOWN_TYPE)
532 return B_MEDIA_BAD_FORMAT;
534 // we can produce any (wildcard) raw audio format
535 memset(format, 0, sizeof(*format));
536 format->type = B_MEDIA_RAW_AUDIO;
537 return B_OK;
541 status_t
542 AudioMixer::FormatProposal(const media_source &output, media_format *ioFormat)
544 // BBufferProducer function, we implement this function to verify that the
545 // proposed media_format is suitable for the specified output. If any fields
546 // in the format are wildcards, and we have a specific requirement, adjust
547 // those fields to match our requirements before returning.
549 TRACE("AudioMixer::FormatProposal\n");
551 // we only have one output (id=0, port=ControlPort())
552 if (output.id != 0 || output.port != ControlPort())
553 return B_MEDIA_BAD_SOURCE;
555 // specialize to raw audio format if necessary
556 if (ioFormat->type == B_MEDIA_UNKNOWN_TYPE)
557 ioFormat->type = B_MEDIA_RAW_AUDIO;
559 // we require a raw audio format
560 if (ioFormat->type != B_MEDIA_RAW_AUDIO)
561 return B_MEDIA_BAD_FORMAT;
563 return B_OK;
566 /*! If the format isn't good, put a good format into *io_format and return error
567 If format has wildcard, specialize to what you can do (and change).
568 If you can change the format, return OK.
569 The request comes from your destination sychronously, so you cannot ask it
570 whether it likes it -- you should assume it will since it asked.
572 status_t
573 AudioMixer::FormatChangeRequested(const media_source &source,
574 const media_destination &destination, media_format *io_format,
575 int32 *_deprecated_)
577 // the downstream consumer node (soundcard) requested that we produce
578 // another format, we need to check if the format is acceptable and
579 // remove any wildcards before returning OK.
581 TRACE("AudioMixer::FormatChangeRequested\n");
583 if (fCore->Settings()->RefuseOutputFormatChange()) {
584 TRACE("AudioMixer::FormatChangeRequested: output format change refused\n");
585 return B_ERROR;
588 fCore->Lock();
590 status_t status = B_OK;
591 BBufferGroup *group = NULL;
592 MixerOutput *output = fCore->Output();
593 if (!output) {
594 ERROR("AudioMixer::FormatChangeRequested: no output\n");
595 goto err;
597 if (source != output->MediaOutput().source) {
598 ERROR("AudioMixer::FormatChangeRequested: wrong output source\n");
599 goto err;
601 if (destination != output->MediaOutput().destination) {
602 ERROR("AudioMixer::FormatChangeRequested: wrong output destination "
603 "(port %ld, id %ld), our is (port %ld, id %ld)\n", destination.port,
604 destination.id, output->MediaOutput().destination.port,
605 output->MediaOutput().destination.id);
606 if (destination.port == output->MediaOutput().destination.port
607 && destination.id == output->MediaOutput().destination.id + 1) {
608 ERROR("AudioMixer::FormatChangeRequested: this might be the broken "
609 "R5 multi audio add-on\n");
611 goto err;
613 if (io_format->type != B_MEDIA_RAW_AUDIO
614 && io_format->type != B_MEDIA_UNKNOWN_TYPE) {
615 ERROR("AudioMixer::FormatChangeRequested: wrong format type\n");
616 goto err;
619 /* remove wildcards */
620 #if USE_MEDIA_FORMAT_WORKAROUND
621 multi_audio_format_specialize(&io_format->u.raw_audio,
622 &fDefaultFormat.u.raw_audio);
623 #else
624 io_format->SpecializeTo(&fDefaultFormat);
625 #endif
627 media_node_id id;
628 FindLatencyFor(destination, &fDownstreamLatency, &id);
629 TRACE("AudioMixer: Downstream Latency is %" B_PRIdBIGTIME " usecs\n",
630 fDownstreamLatency);
632 // SetDuration of one buffer
633 SetBufferDuration(buffer_duration(io_format->u.raw_audio));
634 TRACE("AudioMixer: buffer duration is %" B_PRIdBIGTIME " usecs\n",
635 BufferDuration());
637 // Our internal latency is at least the length of a full output buffer
638 fInternalLatency = BufferDuration()
639 + max((bigtime_t)4500, bigtime_t(0.5 * BufferDuration()));
640 TRACE("AudioMixer: Internal latency is %" B_PRIdBIGTIME " usecs\n",
641 fInternalLatency);
643 SetEventLatency(fDownstreamLatency + fInternalLatency);
645 // we need to inform all connected *inputs* about *our* change in latency
646 PublishEventLatencyChange();
648 // TODO: we might need to create more buffers, to span a larger downstream
649 // latency
651 // apply latency change
652 fCore->SetTimingInfo(TimeSource(), fDownstreamLatency);
654 // apply format change
655 fCore->OutputFormatChanged(io_format->u.raw_audio);
657 status = CreateBufferGroup(&group);
658 if (status != B_OK)
659 return status;
660 else {
661 delete fBufferGroup;
662 fBufferGroup = group;
663 fCore->SetOutputBufferGroup(fBufferGroup);
666 err:
667 fCore->Unlock();
668 return status;
672 status_t
673 AudioMixer::GetNextOutput(int32 *cookie, media_output *out_output)
675 TRACE("AudioMixer::GetNextOutput\n");
677 if (*cookie != 0)
678 return B_BAD_INDEX;
680 fCore->Lock();
681 MixerOutput *output = fCore->Output();
682 if (output) {
683 *out_output = output->MediaOutput();
684 } else {
685 out_output->node = Node();
686 out_output->source.port = ControlPort();
687 out_output->source.id = 0;
688 out_output->destination = media_destination::null;
689 memset(&out_output->format, 0, sizeof(out_output->format));
690 out_output->format.type = B_MEDIA_RAW_AUDIO;
691 strcpy(out_output->name, "Mixer Output");
693 fCore->Unlock();
695 *cookie += 1;
696 return B_OK;
700 status_t
701 AudioMixer::DisposeOutputCookie(int32 cookie)
703 // nothing to do
704 return B_OK;
708 status_t
709 AudioMixer::SetBufferGroup(const media_source &for_source,
710 BBufferGroup *newGroup)
712 TRACE("AudioMixer::SetBufferGroup\n");
713 // the downstream consumer (soundcard) node asks us to use another
714 // BBufferGroup (might be NULL). We only have one output (id 0)
715 if (for_source.port != ControlPort() || for_source.id != 0)
716 return B_MEDIA_BAD_SOURCE;
718 if (newGroup == fBufferGroup) {
719 // we're already using this buffergroup
720 return B_OK;
723 fCore->Lock();
724 if (!newGroup) {
725 status_t status = CreateBufferGroup(&newGroup);
726 if (status != B_OK)
727 return status;
729 fCore->SetOutputBufferGroup(newGroup);
730 delete fBufferGroup;
731 fBufferGroup = newGroup;
732 fCore->Unlock();
734 return B_OK;
738 status_t
739 AudioMixer::GetLatency(bigtime_t *out_latency)
741 // report our *total* latency: internal plus downstream plus scheduling
742 *out_latency = EventLatency() + SchedulingLatency();
744 TRACE("AudioMixer::GetLatency %Ld\n", *out_latency);
746 return B_OK;
750 void
751 AudioMixer::LatencyChanged(const media_source& source,
752 const media_destination& destination, bigtime_t new_latency, uint32 flags)
754 if (source.port != ControlPort() || source.id != 0) {
755 ERROR("AudioMixer::LatencyChanged: received but has wrong source "
756 "%ld/%ld\n", source.port, source.id);
757 return;
760 TRACE("AudioMixer::LatencyChanged: downstream latency from %ld/%ld to "
761 "%ld/%ld changed from %Ld to %Ld\n", source.port, source.id,
762 destination.port, destination.id, fDownstreamLatency, new_latency);
764 #if DEBUG
766 media_node_id id;
767 bigtime_t l;
768 FindLatencyFor(destination, &l, &id);
769 TRACE("AudioMixer: Reported downstream Latency is %Ld usecs\n", l);
771 #endif
773 fDownstreamLatency = new_latency;
774 SetEventLatency(fDownstreamLatency + fInternalLatency);
776 // XXX we might need to create more buffers, to span a larger downstream
777 // latency
779 fCore->Lock();
780 fCore->SetTimingInfo(TimeSource(), fDownstreamLatency);
781 PublishEventLatencyChange();
782 fCore->Unlock();
785 status_t
786 AudioMixer::PrepareToConnect(const media_source &what,
787 const media_destination &where, media_format *format,
788 media_source *out_source, char *out_name)
790 TRACE("AudioMixer::PrepareToConnect\n");
791 // PrepareToConnect() is the second stage of format negotiations that
792 // happens inside BMediaRoster::Connect(). At this point, the consumer's
793 // AcceptFormat() method has been called, and that node has potentially
794 // changed the proposed format.
795 // It may also have left wildcards in the format. PrepareToConnect()
796 // *must* fully specialize the format before returning!
797 // we also create the new output connection and return it in out_source.
799 PRINT_FORMAT("AudioMixer::PrepareToConnect: suggested format", *format);
801 // avoid loop connections
802 if (where.port == ControlPort())
803 return B_MEDIA_BAD_SOURCE;
805 // is the source valid?
806 if (what.port != ControlPort() || what.id != 0)
807 return B_MEDIA_BAD_SOURCE;
809 // is the format acceptable?
810 if (format->type != B_MEDIA_RAW_AUDIO
811 && format->type != B_MEDIA_UNKNOWN_TYPE) {
812 PRINT_FORMAT("AudioMixer::PrepareToConnect: bad format", *format);
813 return B_MEDIA_BAD_FORMAT;
816 fCore->Lock();
818 // are we already connected?
819 if (fCore->Output() != 0) {
820 fCore->Unlock();
821 ERROR("AudioMixer::PrepareToConnect: already connected\n");
822 return B_MEDIA_ALREADY_CONNECTED;
825 // It is possible that another mixer is connecting.
826 // To avoid using the default format, we use one of
827 // a) the format that it indicated as hint in the user_data,
828 // b) the output format of the system audio mixer
829 // c) the input format of the system DAC device
830 // d) if everything failes, keep the wildcard
831 if (format->u.raw_audio.channel_count == 0
832 && format->u.raw_audio.frame_rate < 1
833 && format->user_data_type == FORMAT_USER_DATA_TYPE
834 && *(uint32 *)&format->user_data[0] == FORMAT_USER_DATA_MAGIC_1
835 && *(uint32 *)&format->user_data[44] == FORMAT_USER_DATA_MAGIC_2) {
836 // ok, a mixer is connecting
837 uint32 channel_count = *(uint32 *)&format->user_data[4];
838 float frame_rate = *(float *)&format->user_data[20];
839 if (channel_count > 0 && frame_rate > 0) {
840 // format is good, use it
841 format->u.raw_audio.channel_count = channel_count;
842 format->u.raw_audio.frame_rate = frame_rate;
843 } else {
844 // other mixer's output is probably not connected
845 media_node node;
846 BMediaRoster *roster = BMediaRoster::Roster();
847 media_output out;
848 media_input in;
849 int32 count;
850 if (roster->GetAudioMixer(&node) == B_OK
851 && roster->GetConnectedOutputsFor(node, &out, 1, &count)
852 == B_OK
853 && count == 1) {
854 // use mixer output format
855 format->u.raw_audio.channel_count
856 = out.format.u.raw_audio.channel_count;
857 format->u.raw_audio.frame_rate
858 = out.format.u.raw_audio.frame_rate;
859 } else if (roster->GetAudioOutput(&node) == B_OK
860 && roster->GetAllInputsFor(node, &in, 1, &count) == B_OK
861 && count == 1) {
862 // use DAC input format
863 format->u.raw_audio.channel_count
864 = in.format.u.raw_audio.channel_count;
865 format->u.raw_audio.frame_rate
866 = in.format.u.raw_audio.frame_rate;
871 /* set source and suggest a name */
872 *out_source = what;
873 strcpy(out_name, "Mixer Output");
875 /* remove wildcards */
876 #if USE_MEDIA_FORMAT_WORKAROUND
877 multi_audio_format_specialize(&format->u.raw_audio,
878 &fDefaultFormat.u.raw_audio);
879 #else
880 format->SpecializeTo(&fDefaultFormat);
881 #endif
883 PRINT_FORMAT("AudioMixer::PrepareToConnect: final format", *format);
885 /* add output to core */
886 media_output output;
887 output.node = Node();
888 output.source = *out_source;
889 output.destination = where;
890 output.format = *format;
891 strcpy(output.name, out_name);
893 fCore->EnableOutput(false);
894 fCore->AddOutput(output);
896 fCore->Unlock();
897 return B_OK;
901 void
902 AudioMixer::Connect(status_t error, const media_source &source,
903 const media_destination &dest, const media_format &format, char *io_name)
905 TRACE("AudioMixer::Connect\n");
907 fCore->Lock();
908 // are we still connected?
909 if (fCore->Output() == 0) {
910 fCore->Unlock();
911 ERROR("AudioMixer::Connect: no longer connected\n");
912 return;
914 fCore->Unlock();
916 if (error != B_OK) {
917 // if an error occured, remove output from core
918 ERROR("AudioMixer::Connect failed with error 0x%08lX, removing "
919 "connection\n", error);
920 fCore->Lock();
921 fCore->RemoveOutput();
922 fCore->Unlock();
923 return;
926 // Switch our prefered format to have the same
927 // frame_rate and channel count as the output.
928 fDefaultFormat.u.raw_audio.frame_rate = format.u.raw_audio.frame_rate;
929 fDefaultFormat.u.raw_audio.channel_count = format.u.raw_audio.channel_count;
931 // if the connection has no name, we set it now
932 if (strlen(io_name) == 0)
933 strcpy(io_name, "Mixer Output");
935 // Now that we're connected, we can determine our downstream latency.
936 media_node_id id;
937 FindLatencyFor(dest, &fDownstreamLatency, &id);
938 TRACE("AudioMixer: Downstream Latency is %Ld usecs\n", fDownstreamLatency);
940 // SetDuration of one buffer
941 SetBufferDuration(buffer_duration(format.u.raw_audio));
942 TRACE("AudioMixer: buffer duration is %Ld usecs\n", BufferDuration());
944 // Our internal latency is at least the length of a full output buffer
945 // plus mixing time, plus jitter
946 fInternalLatency = BufferDuration()
947 + max(kMinMixingTime, bigtime_t(0.5 * BufferDuration())) + kMaxJitter;
948 TRACE("AudioMixer: Internal latency is %Ld usecs\n", fInternalLatency);
950 SetEventLatency(fDownstreamLatency + fInternalLatency);
952 // we need to inform all connected *inputs* about *our* change in latency
953 PublishEventLatencyChange();
955 fCore->Lock();
957 // Set up the buffer group for our connection, as long as nobody handed
958 // us a buffer group (via SetBufferGroup()) prior to this. That can
959 // happen, for example, if the consumer calls SetOutputBuffersFor() on
960 // us from within its Connected() method.
961 if (!fBufferGroup) {
962 BBufferGroup *group = NULL;
963 if (CreateBufferGroup(&group) != B_OK)
964 return;
965 fBufferGroup = group;
968 ASSERT(fCore->Output() != 0);
970 // our source should still be valid, too
971 ASSERT(fCore->Output()->MediaOutput().source.id == 0);
972 ASSERT(fCore->Output()->MediaOutput().source.port == ControlPort());
974 // BBufferConsumer::Connected() may return a different input for the
975 // newly created connection. The destination can have changed since
976 // AudioMixer::PrepareToConnect() and we need to update it.
977 fCore->Output()->MediaOutput().destination = dest;
979 fCore->EnableOutput(true);
980 fCore->SetTimingInfo(TimeSource(), fDownstreamLatency);
981 fCore->SetOutputBufferGroup(fBufferGroup);
983 fCore->Settings()->LoadConnectionSettings(fCore->Output());
985 fCore->Unlock();
986 UpdateParameterWeb();
990 void
991 AudioMixer::Disconnect(const media_source& what, const media_destination& where)
993 TRACE("AudioMixer::Disconnect\n");
994 fCore->Lock();
996 // Make sure that our connection is the one being disconnected
997 MixerOutput* output = fCore->Output();
998 if (!output
999 || output->MediaOutput().node != Node()
1000 || output->MediaOutput().source != what
1001 || output->MediaOutput().destination != where) {
1002 ERROR("AudioMixer::Disconnect can't disconnect (wrong connection)\n");
1003 fCore->Unlock();
1004 return;
1007 // Switch our prefered format back to default
1008 // frame rate and channel count.
1009 fDefaultFormat.u.raw_audio.frame_rate = 96000;
1010 fDefaultFormat.u.raw_audio.channel_count = 2;
1012 // force a stop
1013 fCore->Stop();
1015 fCore->RemoveOutput();
1017 // destroy buffer group
1018 delete fBufferGroup;
1019 fBufferGroup = NULL;
1020 fCore->SetOutputBufferGroup(0);
1022 fCore->Unlock();
1023 UpdateParameterWeb();
1027 void
1028 AudioMixer::LateNoticeReceived(const media_source& what, bigtime_t howMuch,
1029 bigtime_t performanceTime)
1031 // We've produced some late buffers... Increase Latency
1032 // is the only runmode in which we can do anything about this
1033 // TODO: quality could be decreased, too
1035 ERROR("AudioMixer::LateNoticeReceived, %Ld too late at %Ld\n", howMuch,
1036 performanceTime);
1038 if (what == fCore->Output()->MediaOutput().source
1039 && RunMode() == B_INCREASE_LATENCY) {
1040 // We need to ignore subsequent notices whose arrival time here
1041 // lies within the last lateness, because queued-up buffers will all be 'late'
1042 if (performanceTime < fLastLateNotification)
1043 return;
1045 fInternalLatency += howMuch;
1047 // At some point a too large latency can get annoying
1048 // (actually more than annoying, as there won't be enough buffers long before this!)
1049 if (fInternalLatency > kMaxLatency)
1050 fInternalLatency = kMaxLatency;
1052 fLastLateNotification = TimeSource()->Now() + howMuch;
1054 TRACE("AudioMixer: increasing internal latency to %"
1055 B_PRIdBIGTIME " usec\n", fInternalLatency);
1056 SetEventLatency(fDownstreamLatency + fInternalLatency);
1058 PublishEventLatencyChange();
1063 void
1064 AudioMixer::EnableOutput(const media_source& what, bool enabled,
1065 int32 */*deprecated*/)
1067 // we only have one output
1068 if (what.id != 0 || what.port != ControlPort())
1069 return;
1071 fCore->Lock();
1072 fCore->EnableOutput(enabled);
1073 fCore->Unlock();
1077 // #pragma mark - BMediaEventLooper methods
1080 void
1081 AudioMixer::NodeRegistered()
1083 UpdateParameterWeb();
1084 SetPriority(B_REAL_TIME_PRIORITY);
1085 Run();
1089 void
1090 AudioMixer::SetTimeSource(BTimeSource* timeSource)
1092 TRACE("AudioMixer::SetTimeSource: timesource is now %ld\n",
1093 timeSource->ID());
1094 fCore->Lock();
1095 fCore->SetTimingInfo(timeSource, fDownstreamLatency);
1096 fCore->Unlock();
1100 void
1101 AudioMixer::HandleEvent(const media_timed_event *event, bigtime_t lateness,
1102 bool realTimeEvent)
1104 switch (event->type) {
1105 case BTimedEventQueue::B_HANDLE_BUFFER:
1107 HandleInputBuffer((BBuffer *)event->pointer, lateness);
1108 ((BBuffer *)event->pointer)->Recycle();
1109 break;
1112 case BTimedEventQueue::B_START:
1114 TRACE("AudioMixer::HandleEvent: B_START\n");
1115 if (RunState() != B_STARTED) {
1116 fCore->Lock();
1117 fCore->Start();
1118 fCore->Unlock();
1120 break;
1123 case BTimedEventQueue::B_STOP:
1125 TRACE("AudioMixer::HandleEvent: B_STOP\n");
1126 // stopped - don't process any more buffers, flush all buffers
1127 // from event queue
1128 EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true,
1129 BTimedEventQueue::B_HANDLE_BUFFER);
1130 fCore->Lock();
1131 fCore->Stop();
1132 fCore->Unlock();
1133 break;
1136 case BTimedEventQueue::B_DATA_STATUS:
1138 ERROR("DataStatus message\n");
1139 break;
1142 case MIXER_PROCESS_EVENT:
1143 fCore->Process();
1144 break;
1146 default:
1147 break;
1152 // #pragma mark - AudioMixer methods
1155 void
1156 AudioMixer::PublishEventLatencyChange()
1158 // our event (processing + downstream) latency has changed,
1159 // and we need tell all inputs about this
1161 TRACE("AudioMixer::PublishEventLatencyChange\n");
1163 fCore->Lock();
1165 MixerInput *input;
1166 for (int i = 0; (input = fCore->Input(i)) != 0; i++) {
1167 TRACE("AudioMixer::PublishEventLatencyChange: SendLatencyChange, "
1168 "connection %ld/%ld to %ld/%ld event latency is now %Ld\n",
1169 input->MediaInput().source.port, input->MediaInput().source.id,
1170 input->MediaInput().destination.port,
1171 input->MediaInput().destination.id, EventLatency());
1172 SendLatencyChange(input->MediaInput().source,
1173 input->MediaInput().destination, EventLatency());
1176 fCore->Unlock();
1180 status_t
1181 AudioMixer::CreateBufferGroup(BBufferGroup** buffer) const
1183 // allocate enough buffers to span our downstream latency
1184 // (plus one for rounding up), plus one extra
1185 int32 count = int32(fDownstreamLatency / BufferDuration()) + 2;
1187 TRACE("AudioMixer::CreateBufferGroup: fDownstreamLatency %Ld, "
1188 "BufferDuration %Ld, buffer count = %ld\n", fDownstreamLatency,
1189 BufferDuration(), count);
1191 if (count < 3)
1192 count = 3;
1194 fCore->Lock();
1195 uint32 size = fCore->Output()->MediaOutput().format.u.raw_audio.buffer_size;
1196 fCore->Unlock();
1198 TRACE("AudioMixer: allocating %ld buffers of %ld bytes each\n",
1199 count, size);
1201 BBufferGroup* buf = new BBufferGroup(size, count);
1202 if (buf == NULL)
1203 return B_NO_MEMORY;
1205 status_t status = buf->InitCheck();
1206 if (status != B_OK)
1207 delete buf;
1208 else
1209 *buffer = buf;
1211 return status;
1215 status_t
1216 AudioMixer::SendBuffer(BBuffer* buffer, MixerOutput* output)
1218 return BBufferProducer::SendBuffer(buffer, output->MediaOutput().source,
1219 output->MediaOutput().destination);
1223 float
1224 AudioMixer::dB_to_Gain(float db)
1226 TRACE("dB_to_Gain: dB in: %01.2f ", db);
1227 if (fCore->Settings()->NonLinearGainSlider()) {
1228 if (db > 0) {
1229 db = db * (pow(abs(DB_MAX), (1.0 / DB_EXPONENT_POSITIVE))
1230 / abs(DB_MAX));
1231 db = pow(db, DB_EXPONENT_POSITIVE);
1232 } else {
1233 db = -db;
1234 db = db * (pow(abs(DB_MIN), (1.0 / DB_EXPONENT_NEGATIVE))
1235 / abs(DB_MIN));
1236 db = pow(db, DB_EXPONENT_NEGATIVE);
1237 db = -db;
1240 TRACE("dB out: %01.2f\n", db);
1241 return pow(10.0, db / 20.0);
1245 float
1246 AudioMixer::Gain_to_dB(float gain)
1248 float db;
1249 db = 20.0 * log10(gain);
1250 if (fCore->Settings()->NonLinearGainSlider()) {
1251 if (db > 0) {
1252 db = pow(db, (1.0 / DB_EXPONENT_POSITIVE));
1253 db = db * (abs(DB_MAX) / pow(abs(DB_MAX),
1254 (1.0 / DB_EXPONENT_POSITIVE)));
1255 } else {
1256 db = -db;
1257 db = pow(db, (1.0 / DB_EXPONENT_NEGATIVE));
1258 db = db * (abs(DB_MIN) / pow(abs(DB_MIN),
1259 (1.0 / DB_EXPONENT_NEGATIVE)));
1260 db = -db;
1263 return db;
1267 // #pragma mark - BControllable methods
1270 status_t
1271 AudioMixer::GetParameterValue(int32 id, bigtime_t *last_change, void *value,
1272 size_t *ioSize)
1274 TRACE("GetParameterValue: id 0x%08lx, ioSize %ld\n", id, *ioSize);
1275 int param = PARAM(id);
1276 fCore->Lock();
1277 if (PARAM_IS_ETC(id)) {
1278 switch (ETC(id)) {
1279 case 10: // Attenuate mixer output by 3dB
1280 *ioSize = sizeof(int32);
1281 static_cast<int32 *>(value)[0] = fCore->Settings()->AttenuateOutput();
1282 break;
1283 case 20: // Use non linear gain sliders
1284 *ioSize = sizeof(int32);
1285 static_cast<int32 *>(value)[0] = fCore->Settings()->NonLinearGainSlider();
1286 break;
1287 case 30: // Display balance control for stereo connections
1288 *ioSize = sizeof(int32);
1289 static_cast<int32 *>(value)[0] = fCore->Settings()->UseBalanceControl();
1290 break;
1291 case 40: // Allow output channel remapping
1292 *ioSize = sizeof(int32);
1293 static_cast<int32 *>(value)[0] = fCore->Settings()->AllowOutputChannelRemapping();
1294 break;
1295 case 50: // Allow input channel remapping
1296 *ioSize = sizeof(int32);
1297 static_cast<int32 *>(value)[0] = fCore->Settings()->AllowInputChannelRemapping();
1298 break;
1299 case 60: // Input gain controls
1300 *ioSize = sizeof(int32);
1301 static_cast<int32 *>(value)[0] = fCore->Settings()->InputGainControls();
1302 break;
1303 case 70: // Resampling algorithm
1304 *ioSize = sizeof(int32);
1305 static_cast<int32 *>(value)[0] = fCore->Settings()->ResamplingAlgorithm();
1306 break;
1307 case 80: // Refuse output format changes
1308 *ioSize = sizeof(int32);
1309 static_cast<int32 *>(value)[0] = fCore->Settings()->RefuseOutputFormatChange();
1310 break;
1311 case 90: // Refuse input format changes
1312 *ioSize = sizeof(int32);
1313 static_cast<int32 *>(value)[0] = fCore->Settings()->RefuseInputFormatChange();
1314 break;
1315 default:
1316 ERROR("unhandled ETC 0x%08lx\n", id);
1317 break;
1319 } else if (param == 0) {
1320 MixerOutput *output = fCore->Output();
1321 if (!output || (!PARAM_IS_MUTE(id) && !PARAM_IS_GAIN(id) && !PARAM_IS_SRC_ENABLE(id) && !PARAM_IS_SRC_GAIN(id) && !PARAM_IS_BALANCE(id)))
1322 goto err;
1323 if (PARAM_IS_MUTE(id)) {
1324 // output mute control
1325 if (*ioSize < sizeof(int32))
1326 goto err;
1327 *ioSize = sizeof(int32);
1328 static_cast<int32 *>(value)[0] = output->IsMuted();
1330 if (PARAM_IS_GAIN(id)) {
1331 // output gain control
1332 if (fCore->Settings()->UseBalanceControl() && output->GetOutputChannelCount() == 2 && 1 /*channel mask is stereo */) {
1333 // single channel control + balance
1334 if (*ioSize < sizeof(float))
1335 goto err;
1336 *ioSize = sizeof(float);
1337 static_cast<float *>(value)[0] = GAIN_TO_DB((output->GetOutputChannelGain(0) + output->GetOutputChannelGain(1)) / 2);
1338 } else {
1339 // multi channel control
1340 if (*ioSize == sizeof(float)) {
1341 // get combined gain for all controls
1342 float gain = 0;
1343 for (int channel = 0;
1344 channel < output->GetOutputChannelCount();
1345 channel++) {
1346 gain += GAIN_TO_DB(
1347 output->GetOutputChannelGain(channel));
1349 static_cast<float *>(value)[0] = gain
1350 / output->GetOutputChannelCount();
1351 } else {
1352 if (*ioSize < output->GetOutputChannelCount()
1353 * sizeof(float))
1354 goto err;
1356 *ioSize = output->GetOutputChannelCount() * sizeof(float);
1358 for (int channel = 0;
1359 channel < output->GetOutputChannelCount();
1360 channel++) {
1361 static_cast<float *>(value)[channel]
1362 = GAIN_TO_DB(output->GetOutputChannelGain(channel));
1367 if (PARAM_IS_BALANCE(id)) {
1368 float l = output->GetOutputChannelGain(0);
1369 float r = output->GetOutputChannelGain(1);
1370 float v = r / (l+r);
1371 TRACE("balance l %1.3f, r %1.3f, v %1.3f\n",l,r,v);
1372 if (*ioSize < sizeof(float))
1373 goto err;
1374 *ioSize = sizeof(float);
1375 static_cast<float *>(value)[0] = v * 100;
1377 if (PARAM_IS_SRC_ENABLE(id)) {
1378 if (*ioSize < sizeof(int32))
1379 goto err;
1380 *ioSize = sizeof(int32);
1381 static_cast<int32 *>(value)[0] = output->HasOutputChannelSource(PARAM_CHAN(id), PARAM_SRC(id));
1383 if (PARAM_IS_SRC_GAIN(id)) {
1384 if (*ioSize < sizeof(float))
1385 goto err;
1386 *ioSize = sizeof(float);
1387 static_cast<float *>(value)[0] = GAIN_TO_PERCENT(output->GetOutputChannelSourceGain(PARAM_CHAN(id), PARAM_SRC(id)));
1389 } else {
1390 MixerInput *input;
1391 for (int i = 0; (input = fCore->Input(i)); i++)
1392 if (input->ID() == param)
1393 break;
1394 if (!input || (!PARAM_IS_MUTE(id) && !PARAM_IS_GAIN(id) && !PARAM_IS_DST_ENABLE(id) && !PARAM_IS_BALANCE(id)))
1395 goto err;
1396 if (PARAM_IS_MUTE(id)) {
1397 // input mute control
1398 if (*ioSize < sizeof(int32))
1399 goto err;
1400 *ioSize = sizeof(int32);
1401 static_cast<int32 *>(value)[0] = !input->IsEnabled();
1403 if (PARAM_IS_GAIN(id)) {
1404 // input gain control
1405 if (fCore->Settings()->InputGainControls() == 0) {
1406 // Physical input channels
1407 if (fCore->Settings()->UseBalanceControl() && input->GetInputChannelCount() == 2 && 1 /*channel mask is stereo */) {
1408 // single channel control + balance
1409 if (*ioSize < sizeof(float))
1410 goto err;
1411 *ioSize = sizeof(float);
1412 static_cast<float *>(value)[0] = GAIN_TO_DB((input->GetInputChannelGain(0) + input->GetInputChannelGain(1)) / 2);
1413 } else {
1414 // multi channel control
1415 if (*ioSize < input->GetInputChannelCount() * sizeof(float))
1416 goto err;
1417 *ioSize = input->GetInputChannelCount() * sizeof(float);
1418 for (int chan = 0; chan < input->GetInputChannelCount(); chan++)
1419 static_cast<float *>(value)[chan] = GAIN_TO_DB(input->GetInputChannelGain(chan));
1421 } else {
1422 // Virtual output channels
1423 if (fCore->Settings()->UseBalanceControl() && input->GetMixerChannelCount() == 2 && 1 /*channel mask is stereo */) {
1424 // single channel control + balance
1425 if (*ioSize < sizeof(float))
1426 goto err;
1427 *ioSize = sizeof(float);
1428 static_cast<float *>(value)[0] = GAIN_TO_DB((input->GetMixerChannelGain(0) + input->GetMixerChannelGain(1)) / 2);
1429 } else {
1430 // multi channel control
1431 if (*ioSize < input->GetMixerChannelCount() * sizeof(float))
1432 goto err;
1433 *ioSize = input->GetMixerChannelCount() * sizeof(float);
1434 for (int chan = 0; chan < input->GetMixerChannelCount(); chan++)
1435 static_cast<float *>(value)[chan] = GAIN_TO_DB(input->GetMixerChannelGain(chan));
1439 if (PARAM_IS_BALANCE(id)) {
1440 if (fCore->Settings()->InputGainControls() == 0) {
1441 // Physical input channels
1442 float l = input->GetInputChannelGain(0);
1443 float r = input->GetInputChannelGain(1);
1444 float v = r / (l+r);
1445 TRACE("balance l %1.3f, r %1.3f, v %1.3f\n",l,r,v);
1446 if (*ioSize < sizeof(float))
1447 goto err;
1448 *ioSize = sizeof(float);
1449 static_cast<float *>(value)[0] = v * 100;
1450 } else {
1451 // Virtual output channels
1452 float l = input->GetMixerChannelGain(0);
1453 float r = input->GetMixerChannelGain(1);
1454 float v = r / (l+r);
1455 TRACE("balance l %1.3f, r %1.3f, v %1.3f\n",l,r,v);
1456 if (*ioSize < sizeof(float))
1457 goto err;
1458 *ioSize = sizeof(float);
1459 static_cast<float *>(value)[0] = v * 100;
1462 if (PARAM_IS_DST_ENABLE(id)) {
1463 if (*ioSize < sizeof(int32))
1464 goto err;
1465 *ioSize = sizeof(int32);
1466 static_cast<int32 *>(value)[0] = input->HasInputChannelDestination(PARAM_CHAN(id), PARAM_DST(id));
1469 *last_change = TimeSource()->Now(); // XXX we could do better
1470 fCore->Unlock();
1471 return B_OK;
1472 err:
1473 fCore->Unlock();
1474 return B_ERROR;
1478 void
1479 AudioMixer::SetParameterValue(int32 id, bigtime_t when, const void *value,
1480 size_t size)
1482 TRACE("SetParameterValue: id 0x%08lx, size %ld\n", id, size);
1483 bool update = false;
1484 int param = PARAM(id);
1485 fCore->Lock();
1486 if (PARAM_IS_ETC(id)) {
1487 switch (ETC(id)) {
1488 case 10: // Attenuate mixer output by 3dB
1489 if (size != sizeof(int32))
1490 goto err;
1491 fCore->Settings()->SetAttenuateOutput(static_cast<const int32 *>(value)[0]);
1492 // this value is special (see MixerCore.h) and we need to notify the core
1493 fCore->SetOutputAttenuation((static_cast<const int32 *>(value)[0]) ? 0.708 : 1.0);
1494 break;
1495 case 20: // Use non linear gain sliders
1496 if (size != sizeof(int32))
1497 goto err;
1498 fCore->Settings()->SetNonLinearGainSlider(static_cast<const int32 *>(value)[0]);
1499 update = true; // XXX should use BroadcastChangedParameter()
1500 break;
1501 case 30: // Display balance control for stereo connections
1502 if (size != sizeof(int32))
1503 goto err;
1504 fCore->Settings()->SetUseBalanceControl(static_cast<const int32 *>(value)[0]);
1505 update = true;
1506 break;
1507 case 40: // Allow output channel remapping
1508 if (size != sizeof(int32))
1509 goto err;
1510 fCore->Settings()->SetAllowOutputChannelRemapping(static_cast<const int32 *>(value)[0]);
1511 update = true;
1512 break;
1513 case 50: // Allow input channel remapping
1514 if (size != sizeof(int32))
1515 goto err;
1516 fCore->Settings()->SetAllowInputChannelRemapping(static_cast<const int32 *>(value)[0]);
1517 update = true;
1518 break;
1519 case 60: // Input gain controls represent
1520 // (0, "Physical input channels")
1521 // (1, "Virtual output channels")
1522 if (size != sizeof(int32))
1523 goto err;
1524 fCore->Settings()->SetInputGainControls(static_cast<const int32 *>(value)[0]);
1525 update = true; // XXX should use BroadcastChangedParameter()
1526 break;
1527 case 70: // Resampling algorithm
1528 if (size != sizeof(int32))
1529 goto err;
1530 fCore->Settings()->SetResamplingAlgorithm(static_cast<const int32 *>(value)[0]);
1531 fCore->UpdateResamplingAlgorithm();
1532 break;
1533 case 80: // Refuse output format changes
1534 if (size != sizeof(int32))
1535 goto err;
1536 fCore->Settings()->SetRefuseOutputFormatChange(static_cast<const int32 *>(value)[0]);
1537 break;
1538 case 90: // Refuse input format changes
1539 if (size != sizeof(int32))
1540 goto err;
1541 fCore->Settings()->SetRefuseInputFormatChange(static_cast<const int32 *>(value)[0]);
1542 break;
1543 default:
1544 ERROR("unhandled ETC 0x%08lx\n", id);
1545 break;
1547 } else if (param == 0) {
1548 MixerOutput *output = fCore->Output();
1549 if (!output || (!PARAM_IS_MUTE(id) && !PARAM_IS_GAIN(id) && !PARAM_IS_SRC_ENABLE(id) && !PARAM_IS_SRC_GAIN(id) && !PARAM_IS_BALANCE(id)))
1550 goto err;
1551 if (PARAM_IS_MUTE(id)) {
1552 // output mute control
1553 if (size != sizeof(int32))
1554 goto err;
1555 output->SetMuted(static_cast<const int32 *>(value)[0]);
1557 if (PARAM_IS_GAIN(id)) {
1558 // output gain control
1559 if (fCore->Settings()->UseBalanceControl()
1560 && output->GetOutputChannelCount() == 2 && 1 /*channel mask is stereo */) {
1561 // single channel control + balance
1562 float l = output->GetOutputChannelGain(0);
1563 float r = output->GetOutputChannelGain(1);
1564 float m = (l + r) / 2; // master volume
1565 float v = DB_TO_GAIN(static_cast<const float *>(value)[0]);
1566 float f = v / m; // factor for both channels
1567 TRACE("gain set l %1.3f, r %1.3f, m %1.3f, v %1.3f, f %1.3f\n",l,r,m,v,f);
1568 output->SetOutputChannelGain(0, output->GetOutputChannelGain(0) * f);
1569 output->SetOutputChannelGain(1, output->GetOutputChannelGain(1) * f);
1570 } else {
1571 // multi channel control
1572 if (size == sizeof(float)) {
1573 // set same volume for all channels
1574 float gain = static_cast<const float *>(value)[0];
1575 for (int channel = 0;
1576 channel < output->GetOutputChannelCount();
1577 channel++) {
1578 output->SetOutputChannelGain(channel,
1579 DB_TO_GAIN(gain));
1581 } else {
1582 if (size < output->GetOutputChannelCount() * sizeof(float))
1583 goto err;
1584 for (int channel = 0;
1585 channel < output->GetOutputChannelCount();
1586 channel++) {
1587 output->SetOutputChannelGain(channel,
1588 DB_TO_GAIN(static_cast<const float *>(
1589 value)[channel]));
1594 if (PARAM_IS_BALANCE(id)) {
1595 float l = output->GetOutputChannelGain(0);
1596 float r = output->GetOutputChannelGain(1);
1597 float m = (l + r) / 2; // master volume
1598 float v = static_cast<const float *>(value)[0] / 100; // current balance value
1599 float fl = 2 * (1 - v); // left channel factor of master volume
1600 float fr = 2 * v; // right channel factor of master volume
1601 TRACE("balance set l %1.3f, r %1.3f, m %1.3f, v %1.3f, fl %1.3f, fr %1.3f\n",l,r,m,v,fl,fr);
1602 output->SetOutputChannelGain(0, m * fl);
1603 output->SetOutputChannelGain(1, m * fr);
1605 if (PARAM_IS_SRC_ENABLE(id)) {
1606 if (size != sizeof(int32))
1607 goto err;
1608 if (static_cast<const int32 *>(value)[0]) {
1609 output->AddOutputChannelSource(PARAM_CHAN(id), PARAM_SRC(id));
1610 } else {
1611 output->RemoveOutputChannelSource(PARAM_CHAN(id), PARAM_SRC(id));
1614 if (PARAM_IS_SRC_GAIN(id)) {
1615 if (size != sizeof(float))
1616 goto err;
1617 output->SetOutputChannelSourceGain(PARAM_CHAN(id), PARAM_SRC(id), PERCENT_TO_GAIN(static_cast<const float *>(value)[0]));
1619 fCore->Settings()->SaveConnectionSettings(output);
1620 } else {
1621 MixerInput *input;
1622 for (int i = 0; (input = fCore->Input(i)); i++)
1623 if (input->ID() == param)
1624 break;
1625 if (!input || (!PARAM_IS_MUTE(id) && !PARAM_IS_GAIN(id) && !PARAM_IS_DST_ENABLE(id) && !PARAM_IS_BALANCE(id)))
1626 goto err;
1627 if (PARAM_IS_MUTE(id)) {
1628 // input mute control
1629 if (size != sizeof(int32))
1630 goto err;
1631 input->SetEnabled(!static_cast<const int32 *>(value)[0]);
1633 if (PARAM_IS_GAIN(id)) {
1634 // input gain control
1635 if (fCore->Settings()->InputGainControls() == 0) {
1636 // Physical input channels
1637 if (fCore->Settings()->UseBalanceControl() && input->GetInputChannelCount() == 2 && 1 /*channel mask is stereo */) {
1638 // single channel control + balance
1639 float l = input->GetInputChannelGain(0);
1640 float r = input->GetInputChannelGain(1);
1641 float m = (l + r) / 2; // master volume
1642 float v = DB_TO_GAIN(static_cast<const float *>(value)[0]);
1643 float f = v / m; // factor for both channels
1644 TRACE("gain set l %1.3f, r %1.3f, m %1.3f, v %1.3f, f %1.3f\n",l,r,m,v,f);
1645 input->SetInputChannelGain(0, input->GetInputChannelGain(0) * f);
1646 input->SetInputChannelGain(1, input->GetInputChannelGain(1) * f);
1647 } else {
1648 // multi channel control
1649 if (size < input->GetInputChannelCount() * sizeof(float))
1650 goto err;
1651 for (int chan = 0; chan < input->GetInputChannelCount(); chan++)
1652 input->SetInputChannelGain(chan, DB_TO_GAIN(static_cast<const float *>(value)[chan]));
1654 } else {
1655 // Virtual output channels
1656 if (fCore->Settings()->UseBalanceControl() && input->GetMixerChannelCount() == 2 && 1 /*channel mask is stereo */) {
1657 // single channel control + balance
1658 float l = input->GetMixerChannelGain(0);
1659 float r = input->GetMixerChannelGain(1);
1660 float m = (l + r) / 2; // master volume
1661 float v = DB_TO_GAIN(static_cast<const float *>(value)[0]);
1662 float f = v / m; // factor for both channels
1663 TRACE("gain set l %1.3f, r %1.3f, m %1.3f, v %1.3f, f %1.3f\n",l,r,m,v,f);
1664 input->SetMixerChannelGain(0, input->GetMixerChannelGain(0) * f);
1665 input->SetMixerChannelGain(1, input->GetMixerChannelGain(1) * f);
1666 } else {
1667 // multi channel control
1668 if (size < input->GetMixerChannelCount() * sizeof(float))
1669 goto err;
1670 for (int chan = 0; chan < input->GetMixerChannelCount(); chan++)
1671 input->SetMixerChannelGain(chan, DB_TO_GAIN(static_cast<const float *>(value)[chan]));
1675 if (PARAM_IS_BALANCE(id)) {
1676 if (fCore->Settings()->InputGainControls() == 0) {
1677 // Physical input channels
1678 float l = input->GetInputChannelGain(0);
1679 float r = input->GetInputChannelGain(1);
1680 float m = (l + r) / 2; // master volume
1681 float v = static_cast<const float *>(value)[0] / 100; // current balance value
1682 float fl = 2 * (1 - v); // left channel factor of master volume
1683 float fr = 2 * v; // right channel factor of master volume
1684 TRACE("balance set l %1.3f, r %1.3f, m %1.3f, v %1.3f, fl %1.3f, fr %1.3f\n",l,r,m,v,fl,fr);
1685 input->SetInputChannelGain(0, m * fl);
1686 input->SetInputChannelGain(1, m * fr);
1687 } else {
1688 // Virtual output channels
1689 float l = input->GetMixerChannelGain(0);
1690 float r = input->GetMixerChannelGain(1);
1691 float m = (l + r) / 2; // master volume
1692 float v = static_cast<const float *>(value)[0] / 100; // current balance value
1693 float fl = 2 * (1 - v); // left channel factor of master volume
1694 float fr = 2 * v; // right channel factor of master volume
1695 TRACE("balance set l %1.3f, r %1.3f, m %1.3f, v %1.3f, fl %1.3f, fr %1.3f\n",l,r,m,v,fl,fr);
1696 input->SetMixerChannelGain(0, m * fl);
1697 input->SetMixerChannelGain(1, m * fr);
1700 if (PARAM_IS_DST_ENABLE(id)) {
1701 if (size != sizeof(int32))
1702 goto err;
1703 if (static_cast<const int32 *>(value)[0]) {
1704 int oldchan = input->GetInputChannelForDestination(PARAM_DST(id));
1705 if (oldchan != -1) {
1706 input->RemoveInputChannelDestination(oldchan, PARAM_DST(id));
1707 int32 null = 0;
1708 BroadcastNewParameterValue(when, PARAM_DST_ENABLE(PARAM(id), oldchan, PARAM_DST(id)), &null, sizeof(null));
1710 input->AddInputChannelDestination(PARAM_CHAN(id), PARAM_DST(id));
1711 } else {
1712 input->RemoveInputChannelDestination(PARAM_CHAN(id), PARAM_DST(id));
1714 // TODO: this is really annoying
1715 // The slider count of the gain control needs to be changed,
1716 // but calling SetChannelCount(input->GetMixerChannelCount())
1717 // on it has no effect on remote BParameterWebs in other apps.
1718 // BroadcastChangedParameter() should be correct, but doesn't work
1719 BroadcastChangedParameter(PARAM_GAIN(PARAM(id)));
1720 // We trigger a complete ParameterWeb update as workaround
1721 // but it will change the focus from tab 3 to tab 1
1722 update = true;
1724 fCore->Settings()->SaveConnectionSettings(input);
1727 BroadcastNewParameterValue(when, id, const_cast<void *>(value), size);
1729 err:
1730 fCore->Unlock();
1731 if (update)
1732 UpdateParameterWeb();
1736 void
1737 AudioMixer::UpdateParameterWeb()
1739 fCore->Lock();
1740 BParameterWeb *web = new BParameterWeb();
1741 BParameterGroup *top;
1742 BParameterGroup *outputchannels;
1743 BParameterGroup *inputchannels;
1744 BParameterGroup *group;
1745 BParameterGroup *subgroup;
1746 BParameterGroup *subsubgroup;
1747 BDiscreteParameter *dp;
1748 MixerInput *in;
1749 MixerOutput *out;
1750 char buf[50];
1752 top = web->MakeGroup(B_TRANSLATE("Gain controls"));
1754 out = fCore->Output();
1755 group = top->MakeGroup("");
1756 group->MakeNullParameter(PARAM_STR1(0), B_MEDIA_RAW_AUDIO,
1757 B_TRANSLATE("Master output"), B_WEB_BUFFER_INPUT);
1758 if (!out) {
1759 group->MakeNullParameter(PARAM_STR2(0), B_MEDIA_RAW_AUDIO,
1760 B_TRANSLATE("not connected"), B_GENERIC);
1761 } else {
1762 group->MakeNullParameter(PARAM_STR2(0), B_MEDIA_RAW_AUDIO,
1763 StringForFormat(buf, out), B_GENERIC);
1764 group->MakeDiscreteParameter(PARAM_MUTE(0), B_MEDIA_RAW_AUDIO,
1765 B_TRANSLATE("Mute"), B_MUTE);
1766 if (fCore->Settings()->UseBalanceControl()
1767 && out->GetOutputChannelCount() == 2 && 1
1768 /*channel mask is stereo */) {
1769 // single channel control + balance
1770 group->MakeContinuousParameter(PARAM_GAIN(0), B_MEDIA_RAW_AUDIO,
1771 B_TRANSLATE("Gain"), B_MASTER_GAIN, B_TRANSLATE("dB"),
1772 DB_MIN, DB_MAX, 0.1);
1773 group->MakeContinuousParameter(PARAM_BALANCE(0), B_MEDIA_RAW_AUDIO,
1774 "", B_BALANCE, "", 0, 100, 1);
1775 } else {
1776 // multi channel control
1777 group->MakeContinuousParameter(PARAM_GAIN(0), B_MEDIA_RAW_AUDIO,
1778 B_TRANSLATE("Gain"), B_MASTER_GAIN, B_TRANSLATE("dB"),
1779 DB_MIN, DB_MAX, 0.1)
1780 ->SetChannelCount(out->GetOutputChannelCount());
1782 group->MakeNullParameter(PARAM_STR3(0), B_MEDIA_RAW_AUDIO,
1783 B_TRANSLATE("To output"), B_WEB_BUFFER_OUTPUT);
1786 for (int i = 0; (in = fCore->Input(i)); i++) {
1787 group = top->MakeGroup("");
1788 group->MakeNullParameter(PARAM_STR1(in->ID()), B_MEDIA_RAW_AUDIO,
1789 in->MediaInput().name, B_WEB_BUFFER_INPUT);
1790 group->MakeNullParameter(PARAM_STR2(in->ID()), B_MEDIA_RAW_AUDIO,
1791 StringForFormat(buf, in), B_GENERIC);
1792 group->MakeDiscreteParameter(PARAM_MUTE(in->ID()), B_MEDIA_RAW_AUDIO,
1793 B_TRANSLATE("Mute"), B_MUTE);
1794 // XXX the gain control is ugly once you have more than two channels,
1795 // as you don't know what channel each slider controls. Tooltips might help...
1796 if (fCore->Settings()->InputGainControls() == 0) {
1797 // Physical input channels
1798 if (fCore->Settings()->UseBalanceControl()
1799 && in->GetInputChannelCount() == 2 && 1
1800 /*channel mask is stereo */) {
1801 // single channel control + balance
1802 group->MakeContinuousParameter(PARAM_GAIN(in->ID()),
1803 B_MEDIA_RAW_AUDIO, B_TRANSLATE("Gain"), B_GAIN,
1804 B_TRANSLATE("dB"), DB_MIN, DB_MAX, 0.1);
1805 group->MakeContinuousParameter(PARAM_BALANCE(in->ID()),
1806 B_MEDIA_RAW_AUDIO, "", B_BALANCE, "", 0, 100, 1);
1807 } else {
1808 // multi channel control
1809 group->MakeContinuousParameter(PARAM_GAIN(in->ID()),
1810 B_MEDIA_RAW_AUDIO, B_TRANSLATE("Gain"), B_GAIN,
1811 B_TRANSLATE("dB"), DB_MIN, DB_MAX, 0.1)
1812 ->SetChannelCount(in->GetInputChannelCount());
1814 } else {
1815 // Virtual output channels
1816 if (fCore->Settings()->UseBalanceControl()
1817 && in->GetMixerChannelCount() == 2 && 1
1818 /*channel mask is stereo */) {
1819 // single channel control + balance
1820 group->MakeContinuousParameter(PARAM_GAIN(in->ID()),
1821 B_MEDIA_RAW_AUDIO, B_TRANSLATE("Gain"), B_GAIN,
1822 B_TRANSLATE("dB"), DB_MIN, DB_MAX, 0.1);
1823 group->MakeContinuousParameter(PARAM_BALANCE(in->ID()),
1824 B_MEDIA_RAW_AUDIO, "", B_BALANCE, "", 0, 100, 1);
1825 } else {
1826 // multi channel control
1827 group->MakeContinuousParameter(PARAM_GAIN(in->ID()),
1828 B_MEDIA_RAW_AUDIO, B_TRANSLATE("Gain"), B_GAIN,
1829 B_TRANSLATE("dB"), DB_MIN, DB_MAX, 0.1)
1830 ->SetChannelCount(in->GetMixerChannelCount());
1833 group->MakeNullParameter(PARAM_STR3(in->ID()), B_MEDIA_RAW_AUDIO,
1834 B_TRANSLATE("To master"), B_WEB_BUFFER_OUTPUT);
1837 if (fCore->Settings()->AllowOutputChannelRemapping()) {
1838 top = web->MakeGroup(B_TRANSLATE("Output mapping")); // top level group
1839 outputchannels = top->MakeGroup("");
1840 outputchannels->MakeNullParameter(PARAM_STR4(0), B_MEDIA_RAW_AUDIO,
1841 B_TRANSLATE("Output channel sources"), B_GENERIC);
1843 group = outputchannels->MakeGroup("");
1844 group->MakeNullParameter(PARAM_STR5(0), B_MEDIA_RAW_AUDIO,
1845 B_TRANSLATE("Master output"), B_GENERIC);
1846 group = group->MakeGroup("");
1847 if (!out) {
1848 group->MakeNullParameter(PARAM_STR6(0), B_MEDIA_RAW_AUDIO,
1849 B_TRANSLATE("not connected"), B_GENERIC);
1850 } else {
1851 for (int chan = 0; chan < out->GetOutputChannelCount(); chan++) {
1852 subgroup = group->MakeGroup("");
1853 subgroup->MakeNullParameter(PARAM_SRC_STR(0, chan),
1854 B_MEDIA_RAW_AUDIO, StringForChannelType(buf,
1855 out->GetOutputChannelType(chan)), B_GENERIC);
1856 for (int src = 0; src < MAX_CHANNEL_TYPES; src++) {
1857 subsubgroup = subgroup->MakeGroup("");
1858 subsubgroup->MakeDiscreteParameter(
1859 PARAM_SRC_ENABLE(0, chan, src), B_MEDIA_RAW_AUDIO, "",
1860 B_ENABLE);
1861 subsubgroup->MakeContinuousParameter(
1862 PARAM_SRC_GAIN(0, chan, src), B_MEDIA_RAW_AUDIO,
1863 StringForChannelType(buf, src), B_GAIN, "%", 0.0,
1864 100.0, 0.1);
1870 if (fCore->Settings()->AllowInputChannelRemapping()) {
1871 top = web->MakeGroup(B_TRANSLATE("Input mapping")); // top level group
1872 inputchannels = top->MakeGroup("");
1873 inputchannels->MakeNullParameter(PARAM_STR7(0), B_MEDIA_RAW_AUDIO,
1874 B_TRANSLATE("Input channel destinations"), B_GENERIC);
1876 for (int i = 0; (in = fCore->Input(i)); i++) {
1877 group = inputchannels->MakeGroup("");
1878 group->MakeNullParameter(PARAM_STR4(in->ID()), B_MEDIA_RAW_AUDIO,
1879 in->MediaInput().name, B_GENERIC);
1880 group = group->MakeGroup("");
1882 for (int chan = 0; chan < in->GetInputChannelCount(); chan++) {
1883 subgroup = group->MakeGroup("");
1884 subgroup->MakeNullParameter(PARAM_DST_STR(in->ID(), chan),
1885 B_MEDIA_RAW_AUDIO, StringForChannelType(buf,
1886 in->GetInputChannelType(chan)), B_GENERIC);
1887 for (int dst = 0; dst < MAX_CHANNEL_TYPES; dst++) {
1888 subgroup->MakeDiscreteParameter(PARAM_DST_ENABLE(in->ID(),
1889 chan, dst), B_MEDIA_RAW_AUDIO, StringForChannelType(buf, dst),
1890 B_ENABLE);
1896 top = web->MakeGroup(B_TRANSLATE("Setup")); // top level group
1897 group = top->MakeGroup("");
1899 group->MakeDiscreteParameter(PARAM_ETC(10), B_MEDIA_RAW_AUDIO,
1900 B_TRANSLATE("Attenuate mixer output by 3dB (like BeOS R5)"), B_ENABLE);
1901 group->MakeDiscreteParameter(PARAM_ETC(20), B_MEDIA_RAW_AUDIO,
1902 B_TRANSLATE("Use non linear gain sliders (like BeOS R5)"), B_ENABLE);
1903 group->MakeDiscreteParameter(PARAM_ETC(30), B_MEDIA_RAW_AUDIO,
1904 B_TRANSLATE("Display balance control for stereo connections"),
1905 B_ENABLE);
1907 group->MakeDiscreteParameter(PARAM_ETC(40), B_MEDIA_RAW_AUDIO,
1908 B_TRANSLATE("Allow output channel remapping"), B_ENABLE);
1909 group->MakeDiscreteParameter(PARAM_ETC(50), B_MEDIA_RAW_AUDIO,
1910 B_TRANSLATE("Allow input channel remapping"), B_ENABLE);
1912 dp = group->MakeDiscreteParameter(PARAM_ETC(60), B_MEDIA_RAW_AUDIO,
1913 B_TRANSLATE("Input gain controls represent"), B_INPUT_MUX);
1914 dp->AddItem(0, B_TRANSLATE("Physical input channels"));
1915 dp->AddItem(1, B_TRANSLATE("Virtual output channels"));
1917 dp = group->MakeDiscreteParameter(PARAM_ETC(70), B_MEDIA_RAW_AUDIO,
1918 B_TRANSLATE("Resampling algorithm"), B_INPUT_MUX);
1919 dp->AddItem(0, B_TRANSLATE("Drop/repeat samples"));
1920 dp->AddItem(2, B_TRANSLATE("Linear interpolation"));
1922 // Note: The following code is outcommented on purpose
1923 // and is about to be modified at a later point
1925 dp->AddItem(1, B_TRANSLATE("Drop/repeat samples (template based)"));
1926 dp->AddItem(3, B_TRANSLATE("17th order filtering"));
1928 group->MakeDiscreteParameter(PARAM_ETC(80), B_MEDIA_RAW_AUDIO,
1929 B_TRANSLATE("Refuse output format changes"), B_ENABLE);
1930 group->MakeDiscreteParameter(PARAM_ETC(90), B_MEDIA_RAW_AUDIO,
1931 B_TRANSLATE("Refuse input format changes"), B_ENABLE);
1933 fCore->Unlock();
1934 SetParameterWeb(web);
1938 #if USE_MEDIA_FORMAT_WORKAROUND
1939 static void
1940 raw_audio_format_specialize(media_raw_audio_format *format,
1941 const media_raw_audio_format *other)
1943 if (format->frame_rate == 0)
1944 format->frame_rate = other->frame_rate;
1945 if (format->channel_count == 0)
1946 format->channel_count = other->channel_count;
1947 if (format->format == 0)
1948 format->format = other->format;
1949 if (format->byte_order == 0)
1950 format->byte_order = other->byte_order;
1951 if (format->buffer_size == 0)
1952 format->buffer_size = other->buffer_size;
1953 if (format->frame_rate == 0)
1954 format->frame_rate = other->frame_rate;
1958 static void
1959 multi_audio_info_specialize(media_multi_audio_info *format,
1960 const media_multi_audio_info *other)
1962 if (format->channel_mask == 0)
1963 format->channel_mask = other->channel_mask;
1964 if (format->valid_bits == 0)
1965 format->valid_bits = other->valid_bits;
1966 if (format->matrix_mask == 0)
1967 format->matrix_mask = other->matrix_mask;
1971 static void
1972 multi_audio_format_specialize(media_multi_audio_format *format,
1973 const media_multi_audio_format *other)
1975 raw_audio_format_specialize(format, other);
1976 multi_audio_info_specialize(format, other);
1978 #endif