1 /*
2 * Copyright 2002 David Shipman,
3 * Copyright 2003-2007 Marcus Overhagen
4 * Copyright 2007-2011 Haiku Inc. All rights reserved.
5 *
6 * Distributed under the terms of the MIT License.
7 */
8
9
10 #include "AudioMixer.h"
11
12 #include <math.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16
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>
26
27 #include "MixerCore.h"
28 #include "MixerInput.h"
29 #include "MixerOutput.h"
30 #include "MixerUtils.h"
31
32
33 #undef B_TRANSLATION_CONTEXT
34 #define B_TRANSLATION_CONTEXT "AudioMixer"
35
36
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
43
44 #define DB_TO_GAIN(db) dB_to_Gain((db))
45 #define GAIN_TO_DB(gain) Gain_to_dB((gain))
46 #define PERCENT_TO_GAIN(pct) ((pct) / 100.0)
47 #define GAIN_TO_PERCENT(gain) ((gain) * 100.0)
48
49 // the id is encoded with 16 bits
50 // then chan and src (or dst) are encoded with 6 bits
51 // the unique number with 4 bits
52 // the PARAM_ETC etc is encoded with 26 bits
53 #define PARAM_SRC_ENABLE(id, chan, src) (((id) << 16) | ((chan) << 10) | ((src) << 4) | 0x1)
54 #define PARAM_SRC_GAIN(id, chan, src) (((id) << 16) | ((chan) << 10) | ((src) << 4) | 0x2)
55 #define PARAM_DST_ENABLE(id, chan, dst) (((id) << 16) | ((chan) << 10) | ((dst) << 4) | 0x3)
56 #define PARAM_ETC(etc) (((etc) << 16) | 0x4)
57 #define PARAM_SRC_STR(id, chan) (((id) << 16) | ((chan) << 10) | 0x5)
58 #define PARAM_DST_STR(id, chan) (((id) << 16) | ((chan) << 10) | 0x6)
59 #define PARAM_MUTE(id) (((id) << 16) | 0x7)
60 #define PARAM_GAIN(id) (((id) << 16) | 0x8)
61 #define PARAM_BALANCE(id) (((id) << 16) | 0x9)
62 #define PARAM_STR1(id) (((id) << 16) | ((1) << 10) | 0xa)
63 #define PARAM_STR2(id) (((id) << 16) | ((2) << 10) | 0xa)
64 #define PARAM_STR3(id) (((id) << 16) | ((3) << 10) | 0xa)
65 #define PARAM_STR4(id) (((id) << 16) | ((4) << 10) | 0xa)
66 #define PARAM_STR5(id) (((id) << 16) | ((5) << 10) | 0xa)
67 #define PARAM_STR6(id) (((id) << 16) | ((6) << 10) | 0xa)
68 #define PARAM_STR7(id) (((id) << 16) | ((7) << 10) | 0xa)
69
70 #define PARAM(id) ((id) >> 16)
71 #define ETC(id) ((id) >> 16)
72 #define PARAM_CHAN(id) (((id) >> 10) & 0x3f)
73 #define PARAM_SRC(id) (((id) >> 4) & 0x3f)
74 #define PARAM_DST(id) (((id) >> 4) & 0x3f)
75 #define PARAM_IS_SRC_ENABLE(id) (((id) & 0xf) == 0x1)
76 #define PARAM_IS_SRC_GAIN(id) (((id) & 0xf) == 0x2)
77 #define PARAM_IS_DST_ENABLE(id) (((id) & 0xf) == 0x3)
78 #define PARAM_IS_ETC(id) (((id) & 0xf) == 0x4)
79 #define PARAM_IS_MUTE(id) (((id) & 0xf) == 0x7)
80 #define PARAM_IS_GAIN(id) (((id) & 0xf) == 0x8)
81 #define PARAM_IS_BALANCE(id) (((id) & 0xf) == 0x9)
82
83 #define FORMAT_USER_DATA_TYPE 0x7294a8f3
84 #define FORMAT_USER_DATA_MAGIC_1 0xc84173bd
85 #define FORMAT_USER_DATA_MAGIC_2 0x4af62b7d
86
87
88 const static bigtime_t kMaxLatency = 150000;
89 // 150 ms is the maximum latency we publish
90
91 const bigtime_t kMinMixingTime = 3500;
92 const bigtime_t kMaxJitter = 1500;
93
94
AudioMixer(BMediaAddOn * addOn,bool isSystemMixer)95 AudioMixer::AudioMixer(BMediaAddOn *addOn, bool isSystemMixer)
96 :
97 BMediaNode("Audio Mixer"),
98 BBufferConsumer(B_MEDIA_RAW_AUDIO),
99 BBufferProducer(B_MEDIA_RAW_AUDIO),
100 BControllable(),
101 BMediaEventLooper(),
102 fAddOn(addOn),
103 fCore(new MixerCore(this)),
104 fWeb(NULL),
105 fBufferGroup(NULL),
106 fDownstreamLatency(1),
107 fInternalLatency(1),
108 fDisableStop(false),
109 fLastLateNotification(0),
110 fLastLateness(0)
111 {
112 BMediaNode::AddNodeKind(B_SYSTEM_MIXER);
113
114 // this is the default format used for all wildcard format SpecializeTo()s
115 fDefaultFormat.type = B_MEDIA_RAW_AUDIO;
116 fDefaultFormat.u.raw_audio.frame_rate = 96000;
117 fDefaultFormat.u.raw_audio.channel_count = 2;
118 fDefaultFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT;
119 fDefaultFormat.u.raw_audio.byte_order = B_MEDIA_HOST_ENDIAN;
120 fDefaultFormat.u.raw_audio.buffer_size = 4096;
121 fDefaultFormat.u.raw_audio.channel_mask = 0;
122 fDefaultFormat.u.raw_audio.valid_bits = 0;
123 fDefaultFormat.u.raw_audio.matrix_mask = 0;
124
125 if (isSystemMixer) {
126 // to get persistent settings, assign a settings file
127 BPath path;
128 if (B_OK != find_directory (B_USER_SETTINGS_DIRECTORY, &path))
129 path.SetTo("/boot/home/config/settings/");
130 path.Append("System Audio Mixer");
131 fCore->Settings()->SetSettingsFile(path.Path());
132
133 // disable stop on the auto started (system) mixer
134 DisableNodeStop();
135 }
136
137 ApplySettings();
138 }
139
140
~AudioMixer()141 AudioMixer::~AudioMixer()
142 {
143 BMediaEventLooper::Quit();
144 SetParameterWeb(NULL);
145
146 // stop the mixer
147 fCore->Lock();
148 fCore->Stop();
149 fCore->Unlock();
150
151 // disconnect all nodes from the mixer
152 // XXX todo
153
154 delete fCore;
155 delete fBufferGroup;
156
157 DEBUG_ONLY(fCore = 0; fBufferGroup = 0; fWeb = 0);
158 }
159
160
161 void
ApplySettings()162 AudioMixer::ApplySettings()
163 {
164 fCore->Lock();
165 fCore->SetOutputAttenuation(fCore->Settings()->AttenuateOutput() ? 0.708 : 1.0);
166 fCore->Unlock();
167 }
168
169
170 void
DisableNodeStop()171 AudioMixer::DisableNodeStop()
172 {
173 fDisableStop = true;
174 }
175
176
177 // #pragma mark - BMediaNode methods
178
179
180 void
Stop(bigtime_t performance_time,bool immediate)181 AudioMixer::Stop(bigtime_t performance_time, bool immediate)
182 {
183 if (fDisableStop) {
184 TRACE("AudioMixer STOP is disabled\n");
185 return;
186 } else {
187 BMediaEventLooper::Stop(performance_time, immediate);
188 }
189 }
190
191
192 BMediaAddOn*
AddOn(int32 * internal_id) const193 AudioMixer::AddOn(int32 *internal_id) const
194 {
195 *internal_id = 0;
196 return fAddOn;
197 }
198
199
200 // #pragma mark - BBufferConsumer methods
201
202
203 status_t
HandleMessage(int32 message,const void * data,size_t size)204 AudioMixer::HandleMessage(int32 message, const void *data, size_t size)
205 {
206 // since we're using a mediaeventlooper, there shouldn't be any messages
207 return B_ERROR;
208 }
209
210
211 status_t
AcceptFormat(const media_destination & dest,media_format * ioFormat)212 AudioMixer::AcceptFormat(const media_destination &dest, media_format *ioFormat)
213 {
214 PRINT_FORMAT("AudioMixer::AcceptFormat: ", *ioFormat);
215
216 // check that the specified format is reasonable for the specified destination, and
217 // fill in any wildcard fields for which our BBufferConsumer has specific requirements.
218
219 // we have multiple inputs with different IDs, but
220 // the port number must match our ControlPort()
221 if (dest.port != ControlPort())
222 return B_MEDIA_BAD_DESTINATION;
223
224 // specialize to raw audio format if necessary
225 if (ioFormat->type == B_MEDIA_UNKNOWN_TYPE)
226 ioFormat->type = B_MEDIA_RAW_AUDIO;
227
228 // we require a raw audio format
229 if (ioFormat->type != B_MEDIA_RAW_AUDIO)
230 return B_MEDIA_BAD_FORMAT;
231
232 // We do not have special requirements, but just in case
233 // another mixer is connecting to us and may need a hint
234 // to create a connection at optimal frame rate and
235 // channel count, we place this information in the user_data
236 fCore->Lock();
237 MixerOutput *output = fCore->Output();
238 uint32 channel_count = output ? output->MediaOutput().format.u.raw_audio.channel_count : 0;
239 float frame_rate = output ? output->MediaOutput().format.u.raw_audio.frame_rate : 0.0;
240 fCore->Unlock();
241 ioFormat->user_data_type = FORMAT_USER_DATA_TYPE;
242 *(uint32 *)&ioFormat->user_data[0] = FORMAT_USER_DATA_MAGIC_1;
243 *(uint32 *)&ioFormat->user_data[4] = channel_count;
244 *(float *)&ioFormat->user_data[20] = frame_rate;
245 *(uint32 *)&ioFormat->user_data[44] = FORMAT_USER_DATA_MAGIC_2;
246
247 return B_OK;
248 }
249
250
251 status_t
GetNextInput(int32 * cookie,media_input * out_input)252 AudioMixer::GetNextInput(int32 *cookie, media_input *out_input)
253 {
254 TRACE("AudioMixer::GetNextInput\n");
255
256 // our 0th input is always a wildcard and free one
257 if (*cookie == 0) {
258 out_input->node = Node();
259 out_input->source = media_source::null;
260 out_input->destination.port = ControlPort();
261 out_input->destination.id = 0;
262 out_input->format.Clear();
263 out_input->format.type = B_MEDIA_RAW_AUDIO;
264 strcpy(out_input->name, "Free Input");
265 *cookie += 1;
266 return B_OK;
267 }
268
269 // the other inputs are the currently connected ones
270 fCore->Lock();
271 MixerInput *input;
272 input = fCore->Input(*cookie - 1);
273 if (!input) {
274 fCore->Unlock();
275 return B_BAD_INDEX;
276 }
277 *out_input = input->MediaInput();
278 *cookie += 1;
279 fCore->Unlock();
280 return B_OK;
281 }
282
283
284 void
DisposeInputCookie(int32 cookie)285 AudioMixer::DisposeInputCookie(int32 cookie)
286 {
287 // nothing to do
288 }
289
290
291 void
BufferReceived(BBuffer * buffer)292 AudioMixer::BufferReceived(BBuffer *buffer)
293 {
294
295 if (buffer->Header()->type == B_MEDIA_PARAMETERS) {
296 TRACE("Control Buffer Received\n");
297 ApplyParameterData(buffer->Data(), buffer->SizeUsed());
298 buffer->Recycle();
299 return;
300 }
301
302 //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);
303
304 // to receive the buffer at the right time,
305 // push it through the event looper
306 media_timed_event event(buffer->Header()->start_time,
307 BTimedEventQueue::B_HANDLE_BUFFER, buffer,
308 BTimedEventQueue::B_RECYCLE_BUFFER);
309 EventQueue()->AddEvent(event);
310 }
311
312
313 void
HandleInputBuffer(BBuffer * buffer,bigtime_t lateness)314 AudioMixer::HandleInputBuffer(BBuffer* buffer, bigtime_t lateness)
315 {
316 bigtime_t variation = 0;
317 if (lateness > fLastLateness)
318 variation = lateness-fLastLateness;
319
320 if (variation > kMaxJitter) {
321 TRACE("AudioMixer: Dequeued input buffer %" B_PRIdBIGTIME
322 " usec late\n", lateness);
323 if (RunMode() == B_DROP_DATA || RunMode() == B_DECREASE_PRECISION
324 || RunMode() == B_INCREASE_LATENCY) {
325 TRACE("AudioMixer: sending notify\n");
326
327 // Build a media_source out of the header data
328 media_source source = media_source::null;
329 source.port = buffer->Header()->source_port;
330 source.id = buffer->Header()->source;
331
332 NotifyLateProducer(source, variation, TimeSource()->Now());
333
334 if (RunMode() == B_DROP_DATA) {
335 TRACE("AudioMixer: dropping buffer\n");
336 return;
337 }
338 }
339 }
340
341 fLastLateness = lateness;
342
343 fCore->Lock();
344 fCore->BufferReceived(buffer, lateness);
345 fCore->Unlock();
346 }
347
348
349 void
ProducerDataStatus(const media_destination & for_whom,int32 status,bigtime_t at_performance_time)350 AudioMixer::ProducerDataStatus(const media_destination& for_whom,
351 int32 status, bigtime_t at_performance_time)
352 {
353 /*
354 if (IsValidDest(for_whom))
355 {
356 media_timed_event event(at_performance_time, BTimedEventQueue::B_DATA_STATUS,
357 (void *)(&for_whom), BTimedEventQueue::B_NO_CLEANUP, status, 0, NULL);
358 EventQueue()->AddEvent(event);
359
360 // FIX_THIS
361 // the for_whom destination is not being sent correctly - verify in HandleEvent loop
362
363 }
364 */
365 }
366
367
368 status_t
GetLatencyFor(const media_destination & for_whom,bigtime_t * out_latency,media_node_id * out_timesource)369 AudioMixer::GetLatencyFor(const media_destination &for_whom,
370 bigtime_t *out_latency, media_node_id *out_timesource)
371 {
372 // we have multiple inputs with different IDs, but
373 // the port number must match our ControlPort()
374 if (for_whom.port != ControlPort())
375 return B_MEDIA_BAD_DESTINATION;
376
377 // return our event latency - this includes our internal + downstream
378 // latency, but _not_ the scheduling latency
379 *out_latency = EventLatency();
380 *out_timesource = TimeSource()->ID();
381
382 printf("AudioMixer::GetLatencyFor %" B_PRIdBIGTIME ", timesource is %"
383 B_PRId32 "\n", *out_latency, *out_timesource);
384
385 return B_OK;
386 }
387
388
389 status_t
Connected(const media_source & producer,const media_destination & where,const media_format & with_format,media_input * out_input)390 AudioMixer::Connected(const media_source &producer,
391 const media_destination &where, const media_format &with_format,
392 media_input *out_input)
393 {
394 PRINT_FORMAT("AudioMixer::Connected: ", with_format);
395
396 // workaround for a crashing bug in RealPlayer. to be proper, RealPlayer's
397 // BBufferProducer::PrepareToConnect() should have removed all wildcards.
398 if (out_input->format.u.raw_audio.frame_rate == 0) {
399 fprintf(stderr, "Audio Mixer Warning: "
400 "Producer (port %" B_PRId32 ", id %" B_PRId32 ") connected with "
401 "frame_rate=0\n", producer.port, producer.id);
402 MixerOutput *output = fCore->Output();
403 float frame_rate = output
404 ? output->MediaOutput().format.u.raw_audio.frame_rate : 44100.0f;
405 out_input->format.u.raw_audio.frame_rate = frame_rate;
406 }
407
408 // a BBufferProducer is connecting to our BBufferConsumer
409
410 // incoming connections should always have an incoming ID=0,
411 // and the port number must match our ControlPort()
412 if (where.id != 0 || where.port != ControlPort())
413 return B_MEDIA_BAD_DESTINATION;
414
415 fCore->Lock();
416
417 // we assign a new id (!= 0) to the newly created input
418 out_input->destination.id = fCore->CreateInputID();
419
420 // We need to make sure that the outInput's name field contains a valid
421 // name, the name given the connection by the producer may still be an
422 // empty string.
423 // if the input has no name, assign one
424 if (strlen(out_input->name) == 0) {
425 sprintf(out_input->name, "Input %" B_PRId32,
426 out_input->destination.id);
427 }
428
429 // add a new input to the mixer engine
430 MixerInput *input;
431 input = fCore->AddInput(*out_input);
432
433 fCore->Settings()->LoadConnectionSettings(input);
434
435 fCore->Unlock();
436
437 // If we want the producer to use a specific BBufferGroup, we now need
438 // to call BMediaRoster::SetOutputBuffersFor() here to set the producer's
439 // buffer group.
440 // But we have no special buffer requirements anyway...
441
442 UpdateParameterWeb();
443
444 return B_OK;
445 }
446
447
448 void
Disconnected(const media_source & producer,const media_destination & where)449 AudioMixer::Disconnected(const media_source &producer,
450 const media_destination &where)
451 {
452 // One of our inputs has been disconnected
453
454 // check if it is really belongs to us
455 if (where.port != ControlPort()) {
456 TRACE("AudioMixer::Disconnected wrong input port\n");
457 return;
458 }
459
460 fCore->Lock();
461
462 if (!fCore->RemoveInput(where.id)) {
463 TRACE("AudioMixer::Disconnected can't remove input\n");
464 }
465
466 fCore->Unlock();
467 UpdateParameterWeb();
468 }
469
470
471 status_t
FormatChanged(const media_source & producer,const media_destination & consumer,int32 change_tag,const media_format & format)472 AudioMixer::FormatChanged(const media_source &producer,
473 const media_destination &consumer, int32 change_tag,
474 const media_format &format)
475 {
476 // at some point in the future (indicated by change_tag and RequestCompleted()),
477 // we will receive buffers in a different format
478
479 TRACE("AudioMixer::FormatChanged\n");
480
481 if (consumer.port != ControlPort() || consumer.id == 0)
482 return B_MEDIA_BAD_DESTINATION;
483
484 if (fCore->Settings()->RefuseInputFormatChange()) {
485 TRACE("AudioMixer::FormatChanged: input format change refused\n");
486 return B_NOT_ALLOWED;
487 }
488
489 // TODO: We should not apply the format change at this point
490 // TODO: At the moment, this is not implemented at the moment and will just
491 // crash the media_server.
492 return B_NOT_SUPPORTED;
493
494 // tell core about format change
495 fCore->Lock();
496 fCore->InputFormatChanged(consumer.id, format.u.raw_audio);
497 fCore->Unlock();
498
499 return B_OK;
500 }
501
502
503 // #pragma mark - BBufferProducer methods
504
505
506 status_t
FormatSuggestionRequested(media_type type,int32 quality,media_format * format)507 AudioMixer::FormatSuggestionRequested(media_type type, int32 quality,
508 media_format *format)
509 {
510 TRACE("AudioMixer::FormatSuggestionRequested\n");
511
512 // BBufferProducer function, a downstream consumer
513 // is asking for our output format
514
515 if (type != B_MEDIA_RAW_AUDIO && type != B_MEDIA_UNKNOWN_TYPE)
516 return B_MEDIA_BAD_FORMAT;
517
518 // we can produce any (wildcard) raw audio format
519 format->Clear();
520 format->type = B_MEDIA_RAW_AUDIO;
521 return B_OK;
522 }
523
524
525 status_t
FormatProposal(const media_source & output,media_format * ioFormat)526 AudioMixer::FormatProposal(const media_source &output, media_format *ioFormat)
527 {
528 // BBufferProducer function, we implement this function to verify that the
529 // proposed media_format is suitable for the specified output. If any fields
530 // in the format are wildcards, and we have a specific requirement, adjust
531 // those fields to match our requirements before returning.
532
533 TRACE("AudioMixer::FormatProposal\n");
534
535 // we only have one output (id=0, port=ControlPort())
536 if (output.id != 0 || output.port != ControlPort())
537 return B_MEDIA_BAD_SOURCE;
538
539 // specialize to raw audio format if necessary
540 if (ioFormat->type == B_MEDIA_UNKNOWN_TYPE)
541 ioFormat->type = B_MEDIA_RAW_AUDIO;
542
543 // we require a raw audio format
544 if (ioFormat->type != B_MEDIA_RAW_AUDIO)
545 return B_MEDIA_BAD_FORMAT;
546
547 return B_OK;
548 }
549
550 /*! If the format isn't good, put a good format into *io_format and return error
551 If format has wildcard, specialize to what you can do (and change).
552 If you can change the format, return OK.
553 The request comes from your destination sychronously, so you cannot ask it
554 whether it likes it -- you should assume it will since it asked.
555 */
556 status_t
FormatChangeRequested(const media_source & source,const media_destination & destination,media_format * io_format,int32 * _deprecated_)557 AudioMixer::FormatChangeRequested(const media_source &source,
558 const media_destination &destination, media_format *io_format,
559 int32 *_deprecated_)
560 {
561 // the downstream consumer node (soundcard) requested that we produce
562 // another format, we need to check if the format is acceptable and
563 // remove any wildcards before returning OK.
564
565 TRACE("AudioMixer::FormatChangeRequested\n");
566
567 if (fCore->Settings()->RefuseOutputFormatChange()) {
568 TRACE("AudioMixer::FormatChangeRequested: output format change refused\n");
569 return B_ERROR;
570 }
571
572 fCore->Lock();
573
574 status_t status = B_OK;
575 BBufferGroup *group = NULL;
576 MixerOutput *output = fCore->Output();
577 if (!output) {
578 ERROR("AudioMixer::FormatChangeRequested: no output\n");
579 goto err;
580 }
581 if (source != output->MediaOutput().source) {
582 ERROR("AudioMixer::FormatChangeRequested: wrong output source\n");
583 goto err;
584 }
585 if (destination != output->MediaOutput().destination) {
586 ERROR("AudioMixer::FormatChangeRequested: wrong output destination "
587 "(port %ld, id %ld), our is (port %ld, id %ld)\n", destination.port,
588 destination.id, output->MediaOutput().destination.port,
589 output->MediaOutput().destination.id);
590 if (destination.port == output->MediaOutput().destination.port
591 && destination.id == output->MediaOutput().destination.id + 1) {
592 ERROR("AudioMixer::FormatChangeRequested: this might be the broken "
593 "R5 multi audio add-on\n");
594 }
595 goto err;
596 }
597 if (io_format->type != B_MEDIA_RAW_AUDIO
598 && io_format->type != B_MEDIA_UNKNOWN_TYPE) {
599 ERROR("AudioMixer::FormatChangeRequested: wrong format type\n");
600 goto err;
601 }
602
603 /* remove wildcards */
604 io_format->SpecializeTo(&fDefaultFormat);
605
606 media_node_id id;
607 FindLatencyFor(destination, &fDownstreamLatency, &id);
608 TRACE("AudioMixer: Downstream Latency is %" B_PRIdBIGTIME " usecs\n",
609 fDownstreamLatency);
610
611 // SetDuration of one buffer
612 SetBufferDuration(buffer_duration(io_format->u.raw_audio));
613 TRACE("AudioMixer: buffer duration is %" B_PRIdBIGTIME " usecs\n",
614 BufferDuration());
615
616 // Our internal latency is at least the length of a full output buffer
617 fInternalLatency = BufferDuration()
618 + max((bigtime_t)4500, bigtime_t(0.5 * BufferDuration()));
619 TRACE("AudioMixer: Internal latency is %" B_PRIdBIGTIME " usecs\n",
620 fInternalLatency);
621
622 SetEventLatency(fDownstreamLatency + fInternalLatency);
623
624 // we need to inform all connected *inputs* about *our* change in latency
625 PublishEventLatencyChange();
626
627 // TODO: we might need to create more buffers, to span a larger downstream
628 // latency
629
630 // apply latency change
631 fCore->SetTimingInfo(TimeSource(), fDownstreamLatency);
632
633 // apply format change
634 fCore->OutputFormatChanged(io_format->u.raw_audio);
635
636 status = CreateBufferGroup(&group);
637 if (status != B_OK)
638 return status;
639 else {
640 delete fBufferGroup;
641 fBufferGroup = group;
642 fCore->SetOutputBufferGroup(fBufferGroup);
643 }
644
645 err:
646 fCore->Unlock();
647 return status;
648 }
649
650
651 status_t
GetNextOutput(int32 * cookie,media_output * out_output)652 AudioMixer::GetNextOutput(int32 *cookie, media_output *out_output)
653 {
654 TRACE("AudioMixer::GetNextOutput\n");
655
656 if (*cookie != 0)
657 return B_BAD_INDEX;
658
659 fCore->Lock();
660 MixerOutput *output = fCore->Output();
661 if (output) {
662 *out_output = output->MediaOutput();
663 } else {
664 out_output->node = Node();
665 out_output->source.port = ControlPort();
666 out_output->source.id = 0;
667 out_output->destination = media_destination::null;
668 out_output->format.Clear();
669 out_output->format.type = B_MEDIA_RAW_AUDIO;
670 strcpy(out_output->name, "Mixer Output");
671 }
672 fCore->Unlock();
673
674 *cookie += 1;
675 return B_OK;
676 }
677
678
679 status_t
DisposeOutputCookie(int32 cookie)680 AudioMixer::DisposeOutputCookie(int32 cookie)
681 {
682 // nothing to do
683 return B_OK;
684 }
685
686
687 status_t
SetBufferGroup(const media_source & for_source,BBufferGroup * newGroup)688 AudioMixer::SetBufferGroup(const media_source &for_source,
689 BBufferGroup *newGroup)
690 {
691 TRACE("AudioMixer::SetBufferGroup\n");
692 // the downstream consumer (soundcard) node asks us to use another
693 // BBufferGroup (might be NULL). We only have one output (id 0)
694 if (for_source.port != ControlPort() || for_source.id != 0)
695 return B_MEDIA_BAD_SOURCE;
696
697 if (newGroup == fBufferGroup) {
698 // we're already using this buffergroup
699 return B_OK;
700 }
701
702 fCore->Lock();
703 if (!newGroup) {
704 status_t status = CreateBufferGroup(&newGroup);
705 if (status != B_OK)
706 return status;
707 }
708 fCore->SetOutputBufferGroup(newGroup);
709 delete fBufferGroup;
710 fBufferGroup = newGroup;
711 fCore->Unlock();
712
713 return B_OK;
714 }
715
716
717 status_t
GetLatency(bigtime_t * out_latency)718 AudioMixer::GetLatency(bigtime_t *out_latency)
719 {
720 // report our *total* latency: internal plus downstream plus scheduling
721 *out_latency = EventLatency() + SchedulingLatency();
722
723 TRACE("AudioMixer::GetLatency %lld\n", *out_latency);
724
725 return B_OK;
726 }
727
728
729 void
LatencyChanged(const media_source & source,const media_destination & destination,bigtime_t new_latency,uint32 flags)730 AudioMixer::LatencyChanged(const media_source& source,
731 const media_destination& destination, bigtime_t new_latency, uint32 flags)
732 {
733 if (source.port != ControlPort() || source.id != 0) {
734 ERROR("AudioMixer::LatencyChanged: received but has wrong source "
735 "%ld/%ld\n", source.port, source.id);
736 return;
737 }
738
739 TRACE("AudioMixer::LatencyChanged: downstream latency from %ld/%ld to "
740 "%ld/%ld changed from %lld to %lld\n", source.port, source.id,
741 destination.port, destination.id, fDownstreamLatency, new_latency);
742
743 #if DEBUG
744 {
745 media_node_id id;
746 bigtime_t l;
747 FindLatencyFor(destination, &l, &id);
748 TRACE("AudioMixer: Reported downstream Latency is %lld usecs\n", l);
749 }
750 #endif
751
752 fDownstreamLatency = new_latency;
753 SetEventLatency(fDownstreamLatency + fInternalLatency);
754
755 // XXX we might need to create more buffers, to span a larger downstream
756 // latency
757
758 fCore->Lock();
759 fCore->SetTimingInfo(TimeSource(), fDownstreamLatency);
760 PublishEventLatencyChange();
761 fCore->Unlock();
762 }
763
764 status_t
PrepareToConnect(const media_source & what,const media_destination & where,media_format * format,media_source * out_source,char * out_name)765 AudioMixer::PrepareToConnect(const media_source &what,
766 const media_destination &where, media_format *format,
767 media_source *out_source, char *out_name)
768 {
769 TRACE("AudioMixer::PrepareToConnect\n");
770 // PrepareToConnect() is the second stage of format negotiations that
771 // happens inside BMediaRoster::Connect(). At this point, the consumer's
772 // AcceptFormat() method has been called, and that node has potentially
773 // changed the proposed format.
774 // It may also have left wildcards in the format. PrepareToConnect()
775 // *must* fully specialize the format before returning!
776 // we also create the new output connection and return it in out_source.
777
778 PRINT_FORMAT("AudioMixer::PrepareToConnect: suggested format", *format);
779
780 // avoid loop connections
781 if (where.port == ControlPort())
782 return B_MEDIA_BAD_SOURCE;
783
784 // is the source valid?
785 if (what.port != ControlPort() || what.id != 0)
786 return B_MEDIA_BAD_SOURCE;
787
788 // is the format acceptable?
789 if (format->type != B_MEDIA_RAW_AUDIO
790 && format->type != B_MEDIA_UNKNOWN_TYPE) {
791 PRINT_FORMAT("AudioMixer::PrepareToConnect: bad format", *format);
792 return B_MEDIA_BAD_FORMAT;
793 }
794
795 fCore->Lock();
796
797 // are we already connected?
798 if (fCore->Output() != 0) {
799 fCore->Unlock();
800 ERROR("AudioMixer::PrepareToConnect: already connected\n");
801 return B_MEDIA_ALREADY_CONNECTED;
802 }
803
804 // It is possible that another mixer is connecting.
805 // To avoid using the default format, we use one of
806 // a) the format that it indicated as hint in the user_data,
807 // b) the output format of the system audio mixer
808 // c) the input format of the system DAC device
809 // d) if everything failes, keep the wildcard
810 if (format->u.raw_audio.channel_count == 0
811 && format->u.raw_audio.frame_rate < 1
812 && format->user_data_type == FORMAT_USER_DATA_TYPE
813 && *(uint32 *)&format->user_data[0] == FORMAT_USER_DATA_MAGIC_1
814 && *(uint32 *)&format->user_data[44] == FORMAT_USER_DATA_MAGIC_2) {
815 // ok, a mixer is connecting
816 uint32 channel_count = *(uint32 *)&format->user_data[4];
817 float frame_rate = *(float *)&format->user_data[20];
818 if (channel_count > 0 && frame_rate > 0) {
819 // format is good, use it
820 format->u.raw_audio.channel_count = channel_count;
821 format->u.raw_audio.frame_rate = frame_rate;
822 } else {
823 // other mixer's output is probably not connected
824 media_node node;
825 BMediaRoster *roster = BMediaRoster::Roster();
826 media_output out;
827 media_input in;
828 int32 count;
829 if (roster->GetAudioMixer(&node) == B_OK
830 && roster->GetConnectedOutputsFor(node, &out, 1, &count)
831 == B_OK
832 && count == 1) {
833 // use mixer output format
834 format->u.raw_audio.channel_count
835 = out.format.u.raw_audio.channel_count;
836 format->u.raw_audio.frame_rate
837 = out.format.u.raw_audio.frame_rate;
838 } else if (roster->GetAudioOutput(&node) == B_OK
839 && roster->GetAllInputsFor(node, &in, 1, &count) == B_OK
840 && count == 1) {
841 // use DAC input format
842 format->u.raw_audio.channel_count
843 = in.format.u.raw_audio.channel_count;
844 format->u.raw_audio.frame_rate
845 = in.format.u.raw_audio.frame_rate;
846 }
847 }
848 }
849
850 /* set source and suggest a name */
851 *out_source = what;
852 strcpy(out_name, "Mixer Output");
853
854 /* remove wildcards */
855 format->SpecializeTo(&fDefaultFormat);
856
857 PRINT_FORMAT("AudioMixer::PrepareToConnect: final format", *format);
858
859 /* add output to core */
860 media_output output;
861 output.node = Node();
862 output.source = *out_source;
863 output.destination = where;
864 output.format = *format;
865 strcpy(output.name, out_name);
866
867 fCore->EnableOutput(false);
868 fCore->AddOutput(output);
869
870 fCore->Unlock();
871 return B_OK;
872 }
873
874
875 void
Connect(status_t error,const media_source & source,const media_destination & dest,const media_format & format,char * io_name)876 AudioMixer::Connect(status_t error, const media_source &source,
877 const media_destination &dest, const media_format &format, char *io_name)
878 {
879 TRACE("AudioMixer::Connect\n");
880
881 fCore->Lock();
882 // are we still connected?
883 if (fCore->Output() == 0) {
884 fCore->Unlock();
885 ERROR("AudioMixer::Connect: no longer connected\n");
886 return;
887 }
888 fCore->Unlock();
889
890 if (error != B_OK) {
891 // if an error occured, remove output from core
892 ERROR("AudioMixer::Connect failed with error 0x%08lX, removing "
893 "connection\n", error);
894 fCore->Lock();
895 fCore->RemoveOutput();
896 fCore->Unlock();
897 return;
898 }
899
900 // Switch our prefered format to have the same
901 // frame_rate and channel count as the output.
902 fDefaultFormat.u.raw_audio.frame_rate = format.u.raw_audio.frame_rate;
903 fDefaultFormat.u.raw_audio.channel_count = format.u.raw_audio.channel_count;
904
905 // if the connection has no name, we set it now
906 if (strlen(io_name) == 0)
907 strcpy(io_name, "Mixer Output");
908
909 // Now that we're connected, we can determine our downstream latency.
910 media_node_id id;
911 FindLatencyFor(dest, &fDownstreamLatency, &id);
912 TRACE("AudioMixer: Downstream Latency is %lld usecs\n", fDownstreamLatency);
913
914 // SetDuration of one buffer
915 SetBufferDuration(buffer_duration(format.u.raw_audio));
916 TRACE("AudioMixer: buffer duration is %lld usecs\n", BufferDuration());
917
918 // Our internal latency is at least the length of a full output buffer
919 // plus mixing time, plus jitter
920 fInternalLatency = BufferDuration()
921 + max(kMinMixingTime, bigtime_t(0.5 * BufferDuration())) + kMaxJitter;
922 TRACE("AudioMixer: Internal latency is %lld usecs\n", fInternalLatency);
923
924 SetEventLatency(fDownstreamLatency + fInternalLatency);
925
926 // we need to inform all connected *inputs* about *our* change in latency
927 PublishEventLatencyChange();
928
929 fCore->Lock();
930
931 // Set up the buffer group for our connection, as long as nobody handed
932 // us a buffer group (via SetBufferGroup()) prior to this. That can
933 // happen, for example, if the consumer calls SetOutputBuffersFor() on
934 // us from within its Connected() method.
935 if (!fBufferGroup) {
936 BBufferGroup *group = NULL;
937 if (CreateBufferGroup(&group) != B_OK)
938 return;
939 fBufferGroup = group;
940 }
941
942 ASSERT(fCore->Output() != 0);
943
944 // our source should still be valid, too
945 ASSERT(fCore->Output()->MediaOutput().source.id == 0);
946 ASSERT(fCore->Output()->MediaOutput().source.port == ControlPort());
947
948 // BBufferConsumer::Connected() may return a different input for the
949 // newly created connection. The destination can have changed since
950 // AudioMixer::PrepareToConnect() and we need to update it.
951 fCore->Output()->MediaOutput().destination = dest;
952
953 fCore->EnableOutput(true);
954 fCore->SetTimingInfo(TimeSource(), fDownstreamLatency);
955 fCore->SetOutputBufferGroup(fBufferGroup);
956
957 fCore->Settings()->LoadConnectionSettings(fCore->Output());
958
959 fCore->Unlock();
960 UpdateParameterWeb();
961 }
962
963
964 void
Disconnect(const media_source & what,const media_destination & where)965 AudioMixer::Disconnect(const media_source& what, const media_destination& where)
966 {
967 TRACE("AudioMixer::Disconnect\n");
968 fCore->Lock();
969
970 // Make sure that our connection is the one being disconnected
971 MixerOutput* output = fCore->Output();
972 if (!output
973 || output->MediaOutput().node != Node()
974 || output->MediaOutput().source != what
975 || output->MediaOutput().destination != where) {
976 ERROR("AudioMixer::Disconnect can't disconnect (wrong connection)\n");
977 fCore->Unlock();
978 return;
979 }
980
981 // Switch our prefered format back to default
982 // frame rate and channel count.
983 fDefaultFormat.u.raw_audio.frame_rate = 96000;
984 fDefaultFormat.u.raw_audio.channel_count = 2;
985
986 fCore->RemoveOutput();
987
988 // destroy buffer group
989 delete fBufferGroup;
990 fBufferGroup = NULL;
991 fCore->SetOutputBufferGroup(0);
992
993 fCore->Unlock();
994 UpdateParameterWeb();
995 }
996
997
998 void
LateNoticeReceived(const media_source & what,bigtime_t howMuch,bigtime_t performanceTime)999 AudioMixer::LateNoticeReceived(const media_source& what, bigtime_t howMuch,
1000 bigtime_t performanceTime)
1001 {
1002 // We've produced some late buffers... Increase Latency
1003 // is the only runmode in which we can do anything about this
1004 // TODO: quality could be decreased, too
1005
1006 ERROR("AudioMixer::LateNoticeReceived, %lld too late at %lld\n", howMuch,
1007 performanceTime);
1008
1009 if (what == fCore->Output()->MediaOutput().source
1010 && RunMode() == B_INCREASE_LATENCY) {
1011 // We need to ignore subsequent notices whose arrival time here
1012 // lies within the last lateness, because queued-up buffers will all be 'late'
1013 if (performanceTime < fLastLateNotification)
1014 return;
1015
1016 fInternalLatency += howMuch;
1017
1018 // At some point a too large latency can get annoying
1019 // (actually more than annoying, as there won't be enough buffers long before this!)
1020 if (fInternalLatency > kMaxLatency)
1021 fInternalLatency = kMaxLatency;
1022
1023 fLastLateNotification = TimeSource()->Now() + howMuch;
1024
1025 TRACE("AudioMixer: increasing internal latency to %"
1026 B_PRIdBIGTIME " usec\n", fInternalLatency);
1027 SetEventLatency(fDownstreamLatency + fInternalLatency);
1028
1029 PublishEventLatencyChange();
1030 }
1031 }
1032
1033
1034 void
EnableOutput(const media_source & what,bool enabled,int32 *)1035 AudioMixer::EnableOutput(const media_source& what, bool enabled,
1036 int32 */*deprecated*/)
1037 {
1038 // we only have one output
1039 if (what.id != 0 || what.port != ControlPort())
1040 return;
1041
1042 fCore->Lock();
1043 fCore->EnableOutput(enabled);
1044 fCore->Unlock();
1045 }
1046
1047
1048 // #pragma mark - BMediaEventLooper methods
1049
1050
1051 void
NodeRegistered()1052 AudioMixer::NodeRegistered()
1053 {
1054 UpdateParameterWeb();
1055 SetPriority(B_REAL_TIME_PRIORITY);
1056 Run();
1057 }
1058
1059
1060 void
SetTimeSource(BTimeSource * timeSource)1061 AudioMixer::SetTimeSource(BTimeSource* timeSource)
1062 {
1063 TRACE("AudioMixer::SetTimeSource: timesource is now %ld\n",
1064 timeSource->ID());
1065 fCore->Lock();
1066 fCore->SetTimingInfo(timeSource, fDownstreamLatency);
1067 fCore->Unlock();
1068 }
1069
1070
1071 void
HandleEvent(const media_timed_event * event,bigtime_t lateness,bool realTimeEvent)1072 AudioMixer::HandleEvent(const media_timed_event *event, bigtime_t lateness,
1073 bool realTimeEvent)
1074 {
1075 switch (event->type) {
1076 case BTimedEventQueue::B_HANDLE_BUFFER:
1077 {
1078 HandleInputBuffer((BBuffer *)event->pointer, lateness);
1079 ((BBuffer *)event->pointer)->Recycle();
1080 break;
1081 }
1082
1083 case BTimedEventQueue::B_START:
1084 {
1085 TRACE("AudioMixer::HandleEvent: B_START\n");
1086 if (RunState() != B_STARTED) {
1087 fCore->Lock();
1088 fCore->Start();
1089 fCore->Unlock();
1090 }
1091 break;
1092 }
1093
1094 case BTimedEventQueue::B_STOP:
1095 {
1096 TRACE("AudioMixer::HandleEvent: B_STOP\n");
1097 // stopped - don't process any more buffers, flush all buffers
1098 // from event queue
1099 EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true,
1100 BTimedEventQueue::B_HANDLE_BUFFER);
1101 fCore->Lock();
1102 fCore->Stop();
1103 fCore->Unlock();
1104 break;
1105 }
1106
1107 case BTimedEventQueue::B_DATA_STATUS:
1108 {
1109 ERROR("DataStatus message\n");
1110 break;
1111 }
1112
1113 default:
1114 break;
1115 }
1116 }
1117
1118
1119 // #pragma mark - AudioMixer methods
1120
1121
1122 void
PublishEventLatencyChange()1123 AudioMixer::PublishEventLatencyChange()
1124 {
1125 // our event (processing + downstream) latency has changed,
1126 // and we need tell all inputs about this
1127
1128 TRACE("AudioMixer::PublishEventLatencyChange\n");
1129
1130 fCore->Lock();
1131
1132 MixerInput *input;
1133 for (int i = 0; (input = fCore->Input(i)) != 0; i++) {
1134 TRACE("AudioMixer::PublishEventLatencyChange: SendLatencyChange, "
1135 "connection %ld/%ld to %ld/%ld event latency is now %lld\n",
1136 input->MediaInput().source.port, input->MediaInput().source.id,
1137 input->MediaInput().destination.port,
1138 input->MediaInput().destination.id, EventLatency());
1139 SendLatencyChange(input->MediaInput().source,
1140 input->MediaInput().destination, EventLatency());
1141 }
1142
1143 fCore->Unlock();
1144 }
1145
1146
1147 status_t
CreateBufferGroup(BBufferGroup ** buffer) const1148 AudioMixer::CreateBufferGroup(BBufferGroup** buffer) const
1149 {
1150 // allocate enough buffers to span our downstream latency
1151 // (plus one for rounding up), plus one extra
1152 int32 count = int32(fDownstreamLatency / BufferDuration()) + 2;
1153
1154 TRACE("AudioMixer::CreateBufferGroup: fDownstreamLatency %lld, "
1155 "BufferDuration %lld, buffer count = %ld\n", fDownstreamLatency,
1156 BufferDuration(), count);
1157
1158 if (count < 3)
1159 count = 3;
1160
1161 fCore->Lock();
1162 uint32 size = fCore->Output()->MediaOutput().format.u.raw_audio.buffer_size;
1163 fCore->Unlock();
1164
1165 TRACE("AudioMixer: allocating %ld buffers of %ld bytes each\n",
1166 count, size);
1167
1168 BBufferGroup* buf = new BBufferGroup(size, count);
1169 if (buf == NULL)
1170 return B_NO_MEMORY;
1171
1172 status_t status = buf->InitCheck();
1173 if (status != B_OK)
1174 delete buf;
1175 else
1176 *buffer = buf;
1177
1178 return status;
1179 }
1180
1181
1182 status_t
SendBuffer(BBuffer * buffer,MixerOutput * output)1183 AudioMixer::SendBuffer(BBuffer* buffer, MixerOutput* output)
1184 {
1185 return BBufferProducer::SendBuffer(buffer, output->MediaOutput().source,
1186 output->MediaOutput().destination);
1187 }
1188
1189
1190 float
dB_to_Gain(float db)1191 AudioMixer::dB_to_Gain(float db)
1192 {
1193 TRACE("dB_to_Gain: dB in: %01.2f ", db);
1194 if (db > 0) {
1195 db = db * (pow(abs(DB_MAX), (1.0 / DB_EXPONENT_POSITIVE))
1196 / abs(DB_MAX));
1197 db = pow(db, DB_EXPONENT_POSITIVE);
1198 } else {
1199 db = -db;
1200 db = db * (pow(abs(DB_MIN), (1.0 / DB_EXPONENT_NEGATIVE))
1201 / abs(DB_MIN));
1202 db = pow(db, DB_EXPONENT_NEGATIVE);
1203 db = -db;
1204 }
1205 TRACE("dB out: %01.2f\n", db);
1206 return pow(10.0, db / 20.0);
1207 }
1208
1209
1210 float
Gain_to_dB(float gain)1211 AudioMixer::Gain_to_dB(float gain)
1212 {
1213 float db;
1214 db = 20.0 * log10(gain);
1215 if (db > 0) {
1216 db = pow(db, (1.0 / DB_EXPONENT_POSITIVE));
1217 db = db * (abs(DB_MAX) / pow(abs(DB_MAX),
1218 (1.0 / DB_EXPONENT_POSITIVE)));
1219 } else {
1220 db = -db;
1221 db = pow(db, (1.0 / DB_EXPONENT_NEGATIVE));
1222 db = db * (abs(DB_MIN) / pow(abs(DB_MIN),
1223 (1.0 / DB_EXPONENT_NEGATIVE)));
1224 db = -db;
1225 }
1226 return db;
1227 }
1228
1229
1230 // #pragma mark - BControllable methods
1231
1232
1233 status_t
GetParameterValue(int32 id,bigtime_t * last_change,void * value,size_t * ioSize)1234 AudioMixer::GetParameterValue(int32 id, bigtime_t *last_change, void *value,
1235 size_t *ioSize)
1236 {
1237 TRACE("GetParameterValue: id 0x%08lx, ioSize %ld\n", id, *ioSize);
1238 int param = PARAM(id);
1239 fCore->Lock();
1240 if (PARAM_IS_ETC(id)) {
1241 switch (ETC(id)) {
1242 case 10: // Attenuate mixer output by 3dB
1243 *ioSize = sizeof(int32);
1244 static_cast<int32 *>(value)[0] = fCore->Settings()->AttenuateOutput();
1245 break;
1246 case 30: // Display balance control for stereo connections
1247 *ioSize = sizeof(int32);
1248 static_cast<int32 *>(value)[0] = fCore->Settings()->UseBalanceControl();
1249 break;
1250 case 40: // Allow output channel remapping
1251 *ioSize = sizeof(int32);
1252 static_cast<int32 *>(value)[0] = fCore->Settings()->AllowOutputChannelRemapping();
1253 break;
1254 case 50: // Allow input channel remapping
1255 *ioSize = sizeof(int32);
1256 static_cast<int32 *>(value)[0] = fCore->Settings()->AllowInputChannelRemapping();
1257 break;
1258 case 60: // Input gain controls
1259 *ioSize = sizeof(int32);
1260 static_cast<int32 *>(value)[0] = fCore->Settings()->InputGainControls();
1261 break;
1262 case 70: // Resampling algorithm
1263 *ioSize = sizeof(int32);
1264 static_cast<int32 *>(value)[0] = fCore->Settings()->ResamplingAlgorithm();
1265 break;
1266 case 80: // Refuse output format changes
1267 *ioSize = sizeof(int32);
1268 static_cast<int32 *>(value)[0] = fCore->Settings()->RefuseOutputFormatChange();
1269 break;
1270 case 90: // Refuse input format changes
1271 *ioSize = sizeof(int32);
1272 static_cast<int32 *>(value)[0] = fCore->Settings()->RefuseInputFormatChange();
1273 break;
1274 default:
1275 ERROR("unhandled ETC 0x%08lx\n", id);
1276 break;
1277 }
1278 } else if (param == 0) {
1279 MixerOutput *output = fCore->Output();
1280 if (!output || (!PARAM_IS_MUTE(id) && !PARAM_IS_GAIN(id) && !PARAM_IS_SRC_ENABLE(id) && !PARAM_IS_SRC_GAIN(id) && !PARAM_IS_BALANCE(id)))
1281 goto err;
1282 if (PARAM_IS_MUTE(id)) {
1283 // output mute control
1284 if (*ioSize < sizeof(int32))
1285 goto err;
1286 *ioSize = sizeof(int32);
1287 static_cast<int32 *>(value)[0] = output->IsMuted();
1288 }
1289 if (PARAM_IS_GAIN(id)) {
1290 // output gain control
1291 if (fCore->Settings()->UseBalanceControl() && output->GetOutputChannelCount() == 2 && 1 /*channel mask is stereo */) {
1292 // single channel control + balance
1293 if (*ioSize < sizeof(float))
1294 goto err;
1295 *ioSize = sizeof(float);
1296 static_cast<float *>(value)[0] = GAIN_TO_DB((output->GetOutputChannelGain(0) + output->GetOutputChannelGain(1)) / 2);
1297 } else {
1298 // multi channel control
1299 if (*ioSize == sizeof(float)) {
1300 // get combined gain for all controls
1301 float gain = 0;
1302 for (int channel = 0;
1303 channel < output->GetOutputChannelCount();
1304 channel++) {
1305 gain += GAIN_TO_DB(
1306 output->GetOutputChannelGain(channel));
1307 }
1308 static_cast<float *>(value)[0] = gain
1309 / output->GetOutputChannelCount();
1310 } else {
1311 if (*ioSize < output->GetOutputChannelCount()
1312 * sizeof(float))
1313 goto err;
1314
1315 *ioSize = output->GetOutputChannelCount() * sizeof(float);
1316
1317 for (int channel = 0;
1318 channel < output->GetOutputChannelCount();
1319 channel++) {
1320 static_cast<float *>(value)[channel]
1321 = GAIN_TO_DB(output->GetOutputChannelGain(channel));
1322 }
1323 }
1324 }
1325 }
1326 if (PARAM_IS_BALANCE(id)) {
1327 float l = output->GetOutputChannelGain(0);
1328 float r = output->GetOutputChannelGain(1);
1329 float v = r / (l+r);
1330 TRACE("balance l %1.3f, r %1.3f, v %1.3f\n",l,r,v);
1331 if (*ioSize < sizeof(float))
1332 goto err;
1333 *ioSize = sizeof(float);
1334 static_cast<float *>(value)[0] = v * 100;
1335 }
1336 if (PARAM_IS_SRC_ENABLE(id)) {
1337 if (*ioSize < sizeof(int32))
1338 goto err;
1339 *ioSize = sizeof(int32);
1340 static_cast<int32 *>(value)[0] = output->HasOutputChannelSource(PARAM_CHAN(id), PARAM_SRC(id));
1341 }
1342 if (PARAM_IS_SRC_GAIN(id)) {
1343 if (*ioSize < sizeof(float))
1344 goto err;
1345 *ioSize = sizeof(float);
1346 static_cast<float *>(value)[0] = GAIN_TO_PERCENT(output->GetOutputChannelSourceGain(PARAM_CHAN(id), PARAM_SRC(id)));
1347 }
1348 } else {
1349 MixerInput *input;
1350 for (int i = 0; (input = fCore->Input(i)); i++)
1351 if (input->ID() == param)
1352 break;
1353 if (!input || (!PARAM_IS_MUTE(id) && !PARAM_IS_GAIN(id) && !PARAM_IS_DST_ENABLE(id) && !PARAM_IS_BALANCE(id)))
1354 goto err;
1355 if (PARAM_IS_MUTE(id)) {
1356 // input mute control
1357 if (*ioSize < sizeof(int32))
1358 goto err;
1359 *ioSize = sizeof(int32);
1360 static_cast<int32 *>(value)[0] = !input->IsEnabled();
1361 }
1362 if (PARAM_IS_GAIN(id)) {
1363 // input gain control
1364 if (fCore->Settings()->InputGainControls() == 0) {
1365 // Physical input channels
1366 if (fCore->Settings()->UseBalanceControl() && input->GetInputChannelCount() == 2 && 1 /*channel mask is stereo */) {
1367 // single channel control + balance
1368 if (*ioSize < sizeof(float))
1369 goto err;
1370 *ioSize = sizeof(float);
1371 static_cast<float *>(value)[0] = GAIN_TO_DB((input->GetInputChannelGain(0) + input->GetInputChannelGain(1)) / 2);
1372 } else {
1373 // multi channel control
1374 if (*ioSize < input->GetInputChannelCount() * sizeof(float))
1375 goto err;
1376 *ioSize = input->GetInputChannelCount() * sizeof(float);
1377 for (int chan = 0; chan < input->GetInputChannelCount(); chan++)
1378 static_cast<float *>(value)[chan] = GAIN_TO_DB(input->GetInputChannelGain(chan));
1379 }
1380 } else {
1381 // Virtual output channels
1382 if (fCore->Settings()->UseBalanceControl() && input->GetMixerChannelCount() == 2 && 1 /*channel mask is stereo */) {
1383 // single channel control + balance
1384 if (*ioSize < sizeof(float))
1385 goto err;
1386 *ioSize = sizeof(float);
1387 static_cast<float *>(value)[0] = GAIN_TO_DB((input->GetMixerChannelGain(0) + input->GetMixerChannelGain(1)) / 2);
1388 } else {
1389 // multi channel control
1390 if (*ioSize < input->GetMixerChannelCount() * sizeof(float))
1391 goto err;
1392 *ioSize = input->GetMixerChannelCount() * sizeof(float);
1393 for (int chan = 0; chan < input->GetMixerChannelCount(); chan++)
1394 static_cast<float *>(value)[chan] = GAIN_TO_DB(input->GetMixerChannelGain(chan));
1395 }
1396 }
1397 }
1398 if (PARAM_IS_BALANCE(id)) {
1399 if (fCore->Settings()->InputGainControls() == 0) {
1400 // Physical input channels
1401 float l = input->GetInputChannelGain(0);
1402 float r = input->GetInputChannelGain(1);
1403 float v = r / (l+r);
1404 TRACE("balance l %1.3f, r %1.3f, v %1.3f\n",l,r,v);
1405 if (*ioSize < sizeof(float))
1406 goto err;
1407 *ioSize = sizeof(float);
1408 static_cast<float *>(value)[0] = v * 100;
1409 } else {
1410 // Virtual output channels
1411 float l = input->GetMixerChannelGain(0);
1412 float r = input->GetMixerChannelGain(1);
1413 float v = r / (l+r);
1414 TRACE("balance l %1.3f, r %1.3f, v %1.3f\n",l,r,v);
1415 if (*ioSize < sizeof(float))
1416 goto err;
1417 *ioSize = sizeof(float);
1418 static_cast<float *>(value)[0] = v * 100;
1419 }
1420 }
1421 if (PARAM_IS_DST_ENABLE(id)) {
1422 if (*ioSize < sizeof(int32))
1423 goto err;
1424 *ioSize = sizeof(int32);
1425 static_cast<int32 *>(value)[0] = input->HasInputChannelDestination(PARAM_CHAN(id), PARAM_DST(id));
1426 }
1427 }
1428 *last_change = TimeSource()->Now(); // XXX we could do better
1429 fCore->Unlock();
1430 return B_OK;
1431 err:
1432 fCore->Unlock();
1433 return B_ERROR;
1434 }
1435
1436
1437 void
SetParameterValue(int32 id,bigtime_t when,const void * value,size_t size)1438 AudioMixer::SetParameterValue(int32 id, bigtime_t when, const void *value,
1439 size_t size)
1440 {
1441 TRACE("SetParameterValue: id 0x%08lx, size %ld\n", id, size);
1442 bool update = false;
1443 int param = PARAM(id);
1444 fCore->Lock();
1445 if (PARAM_IS_ETC(id)) {
1446 switch (ETC(id)) {
1447 case 10: // Attenuate mixer output by 3dB
1448 if (size != sizeof(int32))
1449 goto err;
1450 fCore->Settings()->SetAttenuateOutput(static_cast<const int32 *>(value)[0]);
1451 // this value is special (see MixerCore.h) and we need to notify the core
1452 fCore->SetOutputAttenuation((static_cast<const int32 *>(value)[0]) ? 0.708 : 1.0);
1453 break;
1454 case 30: // Display balance control for stereo connections
1455 if (size != sizeof(int32))
1456 goto err;
1457 fCore->Settings()->SetUseBalanceControl(static_cast<const int32 *>(value)[0]);
1458 update = true;
1459 break;
1460 case 40: // Allow output channel remapping
1461 if (size != sizeof(int32))
1462 goto err;
1463 fCore->Settings()->SetAllowOutputChannelRemapping(static_cast<const int32 *>(value)[0]);
1464 update = true;
1465 break;
1466 case 50: // Allow input channel remapping
1467 if (size != sizeof(int32))
1468 goto err;
1469 fCore->Settings()->SetAllowInputChannelRemapping(static_cast<const int32 *>(value)[0]);
1470 update = true;
1471 break;
1472 case 60: // Input gain controls represent
1473 // (0, "Physical input channels")
1474 // (1, "Virtual output channels")
1475 if (size != sizeof(int32))
1476 goto err;
1477 fCore->Settings()->SetInputGainControls(static_cast<const int32 *>(value)[0]);
1478 update = true; // XXX should use BroadcastChangedParameter()
1479 break;
1480 case 70: // Resampling algorithm
1481 if (size != sizeof(int32))
1482 goto err;
1483 fCore->Settings()->SetResamplingAlgorithm(static_cast<const int32 *>(value)[0]);
1484 fCore->UpdateResamplingAlgorithm();
1485 break;
1486 case 80: // Refuse output format changes
1487 if (size != sizeof(int32))
1488 goto err;
1489 fCore->Settings()->SetRefuseOutputFormatChange(static_cast<const int32 *>(value)[0]);
1490 break;
1491 case 90: // Refuse input format changes
1492 if (size != sizeof(int32))
1493 goto err;
1494 fCore->Settings()->SetRefuseInputFormatChange(static_cast<const int32 *>(value)[0]);
1495 break;
1496 default:
1497 ERROR("unhandled ETC 0x%08lx\n", id);
1498 break;
1499 }
1500 } else if (param == 0) {
1501 MixerOutput *output = fCore->Output();
1502 if (!output || (!PARAM_IS_MUTE(id) && !PARAM_IS_GAIN(id) && !PARAM_IS_SRC_ENABLE(id) && !PARAM_IS_SRC_GAIN(id) && !PARAM_IS_BALANCE(id)))
1503 goto err;
1504 if (PARAM_IS_MUTE(id)) {
1505 // output mute control
1506 if (size != sizeof(int32))
1507 goto err;
1508 output->SetMuted(static_cast<const int32 *>(value)[0]);
1509 }
1510 if (PARAM_IS_GAIN(id)) {
1511 // output gain control
1512 if (fCore->Settings()->UseBalanceControl()
1513 && output->GetOutputChannelCount() == 2 && 1 /*channel mask is stereo */) {
1514 // single channel control + balance
1515 float l = output->GetOutputChannelGain(0);
1516 float r = output->GetOutputChannelGain(1);
1517 float m = (l + r) / 2; // master volume
1518 float v = DB_TO_GAIN(static_cast<const float *>(value)[0]);
1519 float f = v / m; // factor for both channels
1520 TRACE("gain set l %1.3f, r %1.3f, m %1.3f, v %1.3f, f %1.3f\n",l,r,m,v,f);
1521 output->SetOutputChannelGain(0, output->GetOutputChannelGain(0) * f);
1522 output->SetOutputChannelGain(1, output->GetOutputChannelGain(1) * f);
1523 } else {
1524 // multi channel control
1525 if (size == sizeof(float)) {
1526 // set same volume for all channels
1527 float gain = static_cast<const float *>(value)[0];
1528 for (int channel = 0;
1529 channel < output->GetOutputChannelCount();
1530 channel++) {
1531 output->SetOutputChannelGain(channel,
1532 DB_TO_GAIN(gain));
1533 }
1534 } else {
1535 if (size < output->GetOutputChannelCount() * sizeof(float))
1536 goto err;
1537 for (int channel = 0;
1538 channel < output->GetOutputChannelCount();
1539 channel++) {
1540 output->SetOutputChannelGain(channel,
1541 DB_TO_GAIN(static_cast<const float *>(
1542 value)[channel]));
1543 }
1544 }
1545 }
1546 }
1547 if (PARAM_IS_BALANCE(id)) {
1548 float l = output->GetOutputChannelGain(0);
1549 float r = output->GetOutputChannelGain(1);
1550 float m = (l + r) / 2; // master volume
1551 float v = static_cast<const float *>(value)[0] / 100; // current balance value
1552 float fl = 2 * (1 - v); // left channel factor of master volume
1553 float fr = 2 * v; // right channel factor of master volume
1554 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);
1555 output->SetOutputChannelGain(0, m * fl);
1556 output->SetOutputChannelGain(1, m * fr);
1557 }
1558 if (PARAM_IS_SRC_ENABLE(id)) {
1559 if (size != sizeof(int32))
1560 goto err;
1561 if (static_cast<const int32 *>(value)[0]) {
1562 output->AddOutputChannelSource(PARAM_CHAN(id), PARAM_SRC(id));
1563 } else {
1564 output->RemoveOutputChannelSource(PARAM_CHAN(id), PARAM_SRC(id));
1565 }
1566 }
1567 if (PARAM_IS_SRC_GAIN(id)) {
1568 if (size != sizeof(float))
1569 goto err;
1570 output->SetOutputChannelSourceGain(PARAM_CHAN(id), PARAM_SRC(id), PERCENT_TO_GAIN(static_cast<const float *>(value)[0]));
1571 }
1572 fCore->Settings()->SaveConnectionSettings(output);
1573 } else {
1574 MixerInput *input;
1575 for (int i = 0; (input = fCore->Input(i)); i++)
1576 if (input->ID() == param)
1577 break;
1578 if (!input || (!PARAM_IS_MUTE(id) && !PARAM_IS_GAIN(id) && !PARAM_IS_DST_ENABLE(id) && !PARAM_IS_BALANCE(id)))
1579 goto err;
1580 if (PARAM_IS_MUTE(id)) {
1581 // input mute control
1582 if (size != sizeof(int32))
1583 goto err;
1584 input->SetEnabled(!static_cast<const int32 *>(value)[0]);
1585 }
1586 if (PARAM_IS_GAIN(id)) {
1587 // input gain control
1588 if (fCore->Settings()->InputGainControls() == 0) {
1589 // Physical input channels
1590 if (fCore->Settings()->UseBalanceControl() && input->GetInputChannelCount() == 2 && 1 /*channel mask is stereo */) {
1591 // single channel control + balance
1592 float l = input->GetInputChannelGain(0);
1593 float r = input->GetInputChannelGain(1);
1594 float m = (l + r) / 2; // master volume
1595 float v = DB_TO_GAIN(static_cast<const float *>(value)[0]);
1596 float f = v / m; // factor for both channels
1597 TRACE("gain set l %1.3f, r %1.3f, m %1.3f, v %1.3f, f %1.3f\n",l,r,m,v,f);
1598 input->SetInputChannelGain(0, input->GetInputChannelGain(0) * f);
1599 input->SetInputChannelGain(1, input->GetInputChannelGain(1) * f);
1600 } else {
1601 // multi channel control
1602 if (size < input->GetInputChannelCount() * sizeof(float))
1603 goto err;
1604 for (int chan = 0; chan < input->GetInputChannelCount(); chan++)
1605 input->SetInputChannelGain(chan, DB_TO_GAIN(static_cast<const float *>(value)[chan]));
1606 }
1607 } else {
1608 // Virtual output channels
1609 if (fCore->Settings()->UseBalanceControl() && input->GetMixerChannelCount() == 2 && 1 /*channel mask is stereo */) {
1610 // single channel control + balance
1611 float l = input->GetMixerChannelGain(0);
1612 float r = input->GetMixerChannelGain(1);
1613 float m = (l + r) / 2; // master volume
1614 float v = DB_TO_GAIN(static_cast<const float *>(value)[0]);
1615 float f = v / m; // factor for both channels
1616 TRACE("gain set l %1.3f, r %1.3f, m %1.3f, v %1.3f, f %1.3f\n",l,r,m,v,f);
1617 input->SetMixerChannelGain(0, input->GetMixerChannelGain(0) * f);
1618 input->SetMixerChannelGain(1, input->GetMixerChannelGain(1) * f);
1619 } else {
1620 // multi channel control
1621 if (size < input->GetMixerChannelCount() * sizeof(float))
1622 goto err;
1623 for (int chan = 0; chan < input->GetMixerChannelCount(); chan++)
1624 input->SetMixerChannelGain(chan, DB_TO_GAIN(static_cast<const float *>(value)[chan]));
1625 }
1626 }
1627 }
1628 if (PARAM_IS_BALANCE(id)) {
1629 if (fCore->Settings()->InputGainControls() == 0) {
1630 // Physical input channels
1631 float l = input->GetInputChannelGain(0);
1632 float r = input->GetInputChannelGain(1);
1633 float m = (l + r) / 2; // master volume
1634 float v = static_cast<const float *>(value)[0] / 100; // current balance value
1635 float fl = 2 * (1 - v); // left channel factor of master volume
1636 float fr = 2 * v; // right channel factor of master volume
1637 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);
1638 input->SetInputChannelGain(0, m * fl);
1639 input->SetInputChannelGain(1, m * fr);
1640 } else {
1641 // Virtual output channels
1642 float l = input->GetMixerChannelGain(0);
1643 float r = input->GetMixerChannelGain(1);
1644 float m = (l + r) / 2; // master volume
1645 float v = static_cast<const float *>(value)[0] / 100; // current balance value
1646 float fl = 2 * (1 - v); // left channel factor of master volume
1647 float fr = 2 * v; // right channel factor of master volume
1648 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);
1649 input->SetMixerChannelGain(0, m * fl);
1650 input->SetMixerChannelGain(1, m * fr);
1651 }
1652 }
1653 if (PARAM_IS_DST_ENABLE(id)) {
1654 if (size != sizeof(int32))
1655 goto err;
1656 if (static_cast<const int32 *>(value)[0]) {
1657 int oldchan = input->GetInputChannelForDestination(PARAM_DST(id));
1658 if (oldchan != -1) {
1659 input->RemoveInputChannelDestination(oldchan, PARAM_DST(id));
1660 int32 null = 0;
1661 BroadcastNewParameterValue(when, PARAM_DST_ENABLE(PARAM(id), oldchan, PARAM_DST(id)), &null, sizeof(null));
1662 }
1663 input->AddInputChannelDestination(PARAM_CHAN(id), PARAM_DST(id));
1664 } else {
1665 input->RemoveInputChannelDestination(PARAM_CHAN(id), PARAM_DST(id));
1666 }
1667 // TODO: this is really annoying
1668 // The slider count of the gain control needs to be changed,
1669 // but calling SetChannelCount(input->GetMixerChannelCount())
1670 // on it has no effect on remote BParameterWebs in other apps.
1671 // BroadcastChangedParameter() should be correct, but doesn't work
1672 BroadcastChangedParameter(PARAM_GAIN(PARAM(id)));
1673 // We trigger a complete ParameterWeb update as workaround
1674 // but it will change the focus from tab 3 to tab 1
1675 update = true;
1676 }
1677 fCore->Settings()->SaveConnectionSettings(input);
1678 }
1679
1680 BroadcastNewParameterValue(when, id, const_cast<void *>(value), size);
1681
1682 err:
1683 fCore->Unlock();
1684 if (update)
1685 UpdateParameterWeb();
1686 }
1687
1688
1689 void
UpdateParameterWeb()1690 AudioMixer::UpdateParameterWeb()
1691 {
1692 fCore->Lock();
1693 BParameterWeb *web = new BParameterWeb();
1694 BParameterGroup *top;
1695 BParameterGroup *outputchannels;
1696 BParameterGroup *inputchannels;
1697 BParameterGroup *group;
1698 BParameterGroup *subgroup;
1699 BParameterGroup *subsubgroup;
1700 BDiscreteParameter *dp;
1701 MixerInput *in;
1702 MixerOutput *out;
1703 char buf[50];
1704
1705 top = web->MakeGroup(B_TRANSLATE("Gain controls"));
1706
1707 out = fCore->Output();
1708 group = top->MakeGroup("");
1709 group->MakeNullParameter(PARAM_STR1(0), B_MEDIA_RAW_AUDIO,
1710 B_TRANSLATE("Master output"), B_WEB_BUFFER_INPUT);
1711 if (!out) {
1712 group->MakeNullParameter(PARAM_STR2(0), B_MEDIA_RAW_AUDIO,
1713 B_TRANSLATE("not connected"), B_GENERIC);
1714 } else {
1715 group->MakeNullParameter(PARAM_STR2(0), B_MEDIA_RAW_AUDIO,
1716 StringForFormat(buf, out), B_GENERIC);
1717 group->MakeDiscreteParameter(PARAM_MUTE(0), B_MEDIA_RAW_AUDIO,
1718 B_TRANSLATE("Mute"), B_MUTE);
1719 if (fCore->Settings()->UseBalanceControl()
1720 && out->GetOutputChannelCount() == 2 && 1
1721 /*channel mask is stereo */) {
1722 // single channel control + balance
1723 group->MakeContinuousParameter(PARAM_GAIN(0), B_MEDIA_RAW_AUDIO,
1724 B_TRANSLATE("Gain"), B_MASTER_GAIN, B_TRANSLATE("dB"),
1725 DB_MIN, DB_MAX, 0.1);
1726 group->MakeContinuousParameter(PARAM_BALANCE(0), B_MEDIA_RAW_AUDIO,
1727 "", B_BALANCE, "", 0, 100, 1);
1728 } else {
1729 // multi channel control
1730 group->MakeContinuousParameter(PARAM_GAIN(0), B_MEDIA_RAW_AUDIO,
1731 B_TRANSLATE("Gain"), B_MASTER_GAIN, B_TRANSLATE("dB"),
1732 DB_MIN, DB_MAX, 0.1)
1733 ->SetChannelCount(out->GetOutputChannelCount());
1734 }
1735 group->MakeNullParameter(PARAM_STR3(0), B_MEDIA_RAW_AUDIO,
1736 B_TRANSLATE("To output"), B_WEB_BUFFER_OUTPUT);
1737 }
1738
1739 for (int i = 0; (in = fCore->Input(i)); i++) {
1740 group = top->MakeGroup("");
1741 group->MakeNullParameter(PARAM_STR1(in->ID()), B_MEDIA_RAW_AUDIO,
1742 in->MediaInput().name, B_WEB_BUFFER_INPUT);
1743 group->MakeNullParameter(PARAM_STR2(in->ID()), B_MEDIA_RAW_AUDIO,
1744 StringForFormat(buf, in), B_GENERIC);
1745 group->MakeDiscreteParameter(PARAM_MUTE(in->ID()), B_MEDIA_RAW_AUDIO,
1746 B_TRANSLATE("Mute"), B_MUTE);
1747 // XXX the gain control is ugly once you have more than two channels,
1748 // as you don't know what channel each slider controls. Tooltips might help...
1749 if (fCore->Settings()->InputGainControls() == 0) {
1750 // Physical input channels
1751 if (fCore->Settings()->UseBalanceControl()
1752 && in->GetInputChannelCount() == 2 && 1
1753 /*channel mask is stereo */) {
1754 // single channel control + balance
1755 group->MakeContinuousParameter(PARAM_GAIN(in->ID()),
1756 B_MEDIA_RAW_AUDIO, B_TRANSLATE("Gain"), B_GAIN,
1757 B_TRANSLATE("dB"), DB_MIN, DB_MAX, 0.1);
1758 group->MakeContinuousParameter(PARAM_BALANCE(in->ID()),
1759 B_MEDIA_RAW_AUDIO, "", B_BALANCE, "", 0, 100, 1);
1760 } else {
1761 // multi channel control
1762 group->MakeContinuousParameter(PARAM_GAIN(in->ID()),
1763 B_MEDIA_RAW_AUDIO, B_TRANSLATE("Gain"), B_GAIN,
1764 B_TRANSLATE("dB"), DB_MIN, DB_MAX, 0.1)
1765 ->SetChannelCount(in->GetInputChannelCount());
1766 }
1767 } else {
1768 // Virtual output channels
1769 if (fCore->Settings()->UseBalanceControl()
1770 && in->GetMixerChannelCount() == 2 && 1
1771 /*channel mask is stereo */) {
1772 // single channel control + balance
1773 group->MakeContinuousParameter(PARAM_GAIN(in->ID()),
1774 B_MEDIA_RAW_AUDIO, B_TRANSLATE("Gain"), B_GAIN,
1775 B_TRANSLATE("dB"), DB_MIN, DB_MAX, 0.1);
1776 group->MakeContinuousParameter(PARAM_BALANCE(in->ID()),
1777 B_MEDIA_RAW_AUDIO, "", B_BALANCE, "", 0, 100, 1);
1778 } else {
1779 // multi channel control
1780 group->MakeContinuousParameter(PARAM_GAIN(in->ID()),
1781 B_MEDIA_RAW_AUDIO, B_TRANSLATE("Gain"), B_GAIN,
1782 B_TRANSLATE("dB"), DB_MIN, DB_MAX, 0.1)
1783 ->SetChannelCount(in->GetMixerChannelCount());
1784 }
1785 }
1786 group->MakeNullParameter(PARAM_STR3(in->ID()), B_MEDIA_RAW_AUDIO,
1787 B_TRANSLATE("To master"), B_WEB_BUFFER_OUTPUT);
1788 }
1789
1790 if (fCore->Settings()->AllowOutputChannelRemapping()) {
1791 top = web->MakeGroup(B_TRANSLATE("Output mapping")); // top level group
1792 outputchannels = top->MakeGroup("");
1793 outputchannels->MakeNullParameter(PARAM_STR4(0), B_MEDIA_RAW_AUDIO,
1794 B_TRANSLATE("Output channel sources"), B_GENERIC);
1795
1796 group = outputchannels->MakeGroup("");
1797 group->MakeNullParameter(PARAM_STR5(0), B_MEDIA_RAW_AUDIO,
1798 B_TRANSLATE("Master output"), B_GENERIC);
1799 group = group->MakeGroup("");
1800 if (!out) {
1801 group->MakeNullParameter(PARAM_STR6(0), B_MEDIA_RAW_AUDIO,
1802 B_TRANSLATE("not connected"), B_GENERIC);
1803 } else {
1804 for (int chan = 0; chan < out->GetOutputChannelCount(); chan++) {
1805 subgroup = group->MakeGroup("");
1806 subgroup->MakeNullParameter(PARAM_SRC_STR(0, chan),
1807 B_MEDIA_RAW_AUDIO, StringForChannelType(buf,
1808 out->GetOutputChannelType(chan)), B_GENERIC);
1809 for (int src = 0; src < MAX_CHANNEL_TYPES; src++) {
1810 subsubgroup = subgroup->MakeGroup("");
1811 subsubgroup->MakeDiscreteParameter(
1812 PARAM_SRC_ENABLE(0, chan, src), B_MEDIA_RAW_AUDIO, "",
1813 B_ENABLE);
1814 subsubgroup->MakeContinuousParameter(
1815 PARAM_SRC_GAIN(0, chan, src), B_MEDIA_RAW_AUDIO,
1816 StringForChannelType(buf, src), B_GAIN, "%", 0.0,
1817 100.0, 0.1);
1818 }
1819 }
1820 }
1821 }
1822
1823 if (fCore->Settings()->AllowInputChannelRemapping()) {
1824 top = web->MakeGroup(B_TRANSLATE("Input mapping")); // top level group
1825 inputchannels = top->MakeGroup("");
1826 inputchannels->MakeNullParameter(PARAM_STR7(0), B_MEDIA_RAW_AUDIO,
1827 B_TRANSLATE("Input channel destinations"), B_GENERIC);
1828
1829 for (int i = 0; (in = fCore->Input(i)); i++) {
1830 group = inputchannels->MakeGroup("");
1831 group->MakeNullParameter(PARAM_STR4(in->ID()), B_MEDIA_RAW_AUDIO,
1832 in->MediaInput().name, B_GENERIC);
1833 group = group->MakeGroup("");
1834
1835 for (int chan = 0; chan < in->GetInputChannelCount(); chan++) {
1836 subgroup = group->MakeGroup("");
1837 subgroup->MakeNullParameter(PARAM_DST_STR(in->ID(), chan),
1838 B_MEDIA_RAW_AUDIO, StringForChannelType(buf,
1839 in->GetInputChannelType(chan)), B_GENERIC);
1840 for (int dst = 0; dst < MAX_CHANNEL_TYPES; dst++) {
1841 subgroup->MakeDiscreteParameter(PARAM_DST_ENABLE(in->ID(),
1842 chan, dst), B_MEDIA_RAW_AUDIO, StringForChannelType(buf, dst),
1843 B_ENABLE);
1844 }
1845 }
1846 }
1847 }
1848
1849 top = web->MakeGroup(B_TRANSLATE("Setup")); // top level group
1850 group = top->MakeGroup("");
1851
1852 group->MakeDiscreteParameter(PARAM_ETC(10), B_MEDIA_RAW_AUDIO,
1853 B_TRANSLATE("Attenuate mixer output by 3 dB"), B_ENABLE);
1854 group->MakeDiscreteParameter(PARAM_ETC(30), B_MEDIA_RAW_AUDIO,
1855 B_TRANSLATE("Display balance control for stereo connections"),
1856 B_ENABLE);
1857
1858 group->MakeDiscreteParameter(PARAM_ETC(40), B_MEDIA_RAW_AUDIO,
1859 B_TRANSLATE("Allow output channel remapping"), B_ENABLE);
1860 group->MakeDiscreteParameter(PARAM_ETC(50), B_MEDIA_RAW_AUDIO,
1861 B_TRANSLATE("Allow input channel remapping"), B_ENABLE);
1862
1863 dp = group->MakeDiscreteParameter(PARAM_ETC(60), B_MEDIA_RAW_AUDIO,
1864 B_TRANSLATE("Input gain controls represent:"), B_INPUT_MUX);
1865 dp->AddItem(0, B_TRANSLATE("Physical input channels"));
1866 dp->AddItem(1, B_TRANSLATE("Virtual output channels"));
1867
1868 dp = group->MakeDiscreteParameter(PARAM_ETC(70), B_MEDIA_RAW_AUDIO,
1869 B_TRANSLATE("Resampling algorithm:"), B_INPUT_MUX);
1870 dp->AddItem(0, B_TRANSLATE("Low quality (drop/repeat samples)"));
1871 dp->AddItem(2, B_TRANSLATE("High quality (linear interpolation)"));
1872
1873 // Note: The following code is outcommented on purpose
1874 // and is about to be modified at a later point
1875 /*
1876 dp->AddItem(1, B_TRANSLATE("Drop/repeat samples (template based)"));
1877 dp->AddItem(3, B_TRANSLATE("17th order filtering"));
1878 */
1879
1880 /* Remove those option from the GUI, but keep them in the settings
1881 group->MakeDiscreteParameter(PARAM_ETC(80), B_MEDIA_RAW_AUDIO,
1882 B_TRANSLATE("Refuse output format changes"), B_ENABLE);
1883 group->MakeDiscreteParameter(PARAM_ETC(90), B_MEDIA_RAW_AUDIO,
1884 B_TRANSLATE("Refuse input format changes"), B_ENABLE);
1885 */
1886 fCore->Unlock();
1887 SetParameterWeb(web);
1888 }
1889