xref: /haiku/src/add-ons/media/media-add-ons/mixer/AudioMixer.cpp (revision 820dca4df6c7bf955c46e8f6521b9408f50b2900)
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 <MediaRoster.h>
21 #include <ParameterWeb.h>
22 #include <Path.h>
23 #include <RealtimeAlloc.h>
24 #include <TimeSource.h>
25 
26 #include "MixerCore.h"
27 #include "MixerInput.h"
28 #include "MixerOutput.h"
29 #include "MixerUtils.h"
30 
31 
32 #undef B_TRANSLATION_CONTEXT
33 #define B_TRANSLATION_CONTEXT "AudioMixer"
34 
35 
36 // the range of the gain sliders (in dB)
37 #define DB_MAX	18.0
38 #define DB_MIN	-60.0
39 // when using non linear sliders, we use a power function with
40 #define DB_EXPONENT_POSITIVE 1.4	// for dB values > 0
41 #define DB_EXPONENT_NEGATIVE 1.8	// for dB values < 0
42 
43 #define USE_MEDIA_FORMAT_WORKAROUND 1
44 
45 #define DB_TO_GAIN(db)			dB_to_Gain((db))
46 #define GAIN_TO_DB(gain)		Gain_to_dB((gain))
47 #define PERCENT_TO_GAIN(pct)	((pct) / 100.0)
48 #define GAIN_TO_PERCENT(gain)	((gain)  * 100.0)
49 
50 // the id is encoded with 16 bits
51 // then chan and src (or dst) are encoded with 6 bits
52 // the unique number with 4 bits
53 // the PARAM_ETC etc is encoded with 26 bits
54 #define PARAM_SRC_ENABLE(id, chan, src)		(((id) << 16) | ((chan) << 10) | ((src) << 4) | 0x1)
55 #define PARAM_SRC_GAIN(id, chan, src)		(((id) << 16) | ((chan) << 10) | ((src) << 4) | 0x2)
56 #define PARAM_DST_ENABLE(id, chan, dst)		(((id) << 16) | ((chan) << 10) | ((dst) << 4) | 0x3)
57 #define PARAM_ETC(etc)						(((etc) << 16) | 0x4)
58 #define PARAM_SRC_STR(id, chan)				(((id) << 16) | ((chan) << 10) | 0x5)
59 #define PARAM_DST_STR(id, chan)				(((id) << 16) | ((chan) << 10) | 0x6)
60 #define PARAM_MUTE(id)						(((id) << 16) | 0x7)
61 #define PARAM_GAIN(id)						(((id) << 16) | 0x8)
62 #define PARAM_BALANCE(id)					(((id) << 16) | 0x9)
63 #define PARAM_STR1(id)						(((id) << 16) | ((1) << 10) | 0xa)
64 #define PARAM_STR2(id)						(((id) << 16) | ((2) << 10) | 0xa)
65 #define PARAM_STR3(id)						(((id) << 16) | ((3) << 10) | 0xa)
66 #define PARAM_STR4(id)						(((id) << 16) | ((4) << 10) | 0xa)
67 #define PARAM_STR5(id)						(((id) << 16) | ((5) << 10) | 0xa)
68 #define PARAM_STR6(id)						(((id) << 16) | ((6) << 10) | 0xa)
69 #define PARAM_STR7(id)						(((id) << 16) | ((7) << 10) | 0xa)
70 
71 #define PARAM(id)							((id) >> 16)
72 #define ETC(id)								((id) >> 16)
73 #define PARAM_CHAN(id)						(((id) >> 10) & 0x3f)
74 #define PARAM_SRC(id)						(((id) >> 4) & 0x3f)
75 #define PARAM_DST(id)						(((id) >> 4) & 0x3f)
76 #define PARAM_IS_SRC_ENABLE(id)				(((id) & 0xf) == 0x1)
77 #define PARAM_IS_SRC_GAIN(id)				(((id) & 0xf) == 0x2)
78 #define PARAM_IS_DST_ENABLE(id)				(((id) & 0xf) == 0x3)
79 #define PARAM_IS_ETC(id)					(((id) & 0xf) == 0x4)
80 #define PARAM_IS_MUTE(id)					(((id) & 0xf) == 0x7)
81 #define PARAM_IS_GAIN(id)					(((id) & 0xf) == 0x8)
82 #define PARAM_IS_BALANCE(id)				(((id) & 0xf) == 0x9)
83 
84 #if USE_MEDIA_FORMAT_WORKAROUND
85 static void
86 multi_audio_format_specialize(media_multi_audio_format *format,
87 	const media_multi_audio_format *other);
88 #endif
89 
90 #define FORMAT_USER_DATA_TYPE 		0x7294a8f3
91 #define FORMAT_USER_DATA_MAGIC_1	0xc84173bd
92 #define FORMAT_USER_DATA_MAGIC_2	0x4af62b7d
93 
94 
95 const static bigtime_t kMaxLatency = 150000;
96 	// 150 ms is the maximum latency we publish
97 
98 const bigtime_t kMinMixingTime = 3500;
99 const bigtime_t kMaxJitter = 1500;
100 
101 
102 AudioMixer::AudioMixer(BMediaAddOn *addOn, bool isSystemMixer)
103 	:
104 	BMediaNode("Audio Mixer"),
105 	BBufferConsumer(B_MEDIA_RAW_AUDIO),
106 	BBufferProducer(B_MEDIA_RAW_AUDIO),
107 	BControllable(),
108 	BMediaEventLooper(),
109 	fAddOn(addOn),
110 	fCore(new MixerCore(this)),
111 	fWeb(0),
112 	fBufferGroup(0),
113 	fDownstreamLatency(1),
114 	fInternalLatency(1),
115 	fDisableStop(false),
116 	fLastLateNotification(0),
117 	fLastLateness(0)
118 {
119 	BMediaNode::AddNodeKind(B_SYSTEM_MIXER);
120 
121 	// this is the default format used for all wildcard format SpecializeTo()s
122 	fDefaultFormat.type = B_MEDIA_RAW_AUDIO;
123 	fDefaultFormat.u.raw_audio.frame_rate = 96000;
124 	fDefaultFormat.u.raw_audio.channel_count = 2;
125 	fDefaultFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT;
126 	fDefaultFormat.u.raw_audio.byte_order = B_MEDIA_HOST_ENDIAN;
127 	fDefaultFormat.u.raw_audio.buffer_size = 4096;
128 	fDefaultFormat.u.raw_audio.channel_mask = 0;
129 	fDefaultFormat.u.raw_audio.valid_bits = 0;
130 	fDefaultFormat.u.raw_audio.matrix_mask = 0;
131 
132 	if (isSystemMixer) {
133 		// to get persistent settings, assign a settings file
134 		BPath path;
135 		if (B_OK != find_directory (B_USER_SETTINGS_DIRECTORY, &path))
136 			path.SetTo("/boot/home/config/settings/");
137 		path.Append("System Audio Mixer");
138 		fCore->Settings()->SetSettingsFile(path.Path());
139 
140 		// disable stop on the auto started (system) mixer
141 		DisableNodeStop();
142 	}
143 
144 	ApplySettings();
145 }
146 
147 
148 AudioMixer::~AudioMixer()
149 {
150 	BMediaEventLooper::Quit();
151 	SetParameterWeb(NULL);
152 
153 	// stop the mixer
154 	fCore->Lock();
155 	fCore->Stop();
156 	fCore->Unlock();
157 
158 	// disconnect all nodes from the mixer
159 	// XXX todo
160 
161 	delete fCore;
162 	delete fBufferGroup;
163 
164 	DEBUG_ONLY(fCore = 0; fBufferGroup = 0; fWeb = 0);
165 }
166 
167 
168 void
169 AudioMixer::ApplySettings()
170 {
171 	fCore->Lock();
172 	fCore->SetOutputAttenuation(fCore->Settings()->AttenuateOutput() ? 0.708 : 1.0);
173 	fCore->Unlock();
174 }
175 
176 
177 void
178 AudioMixer::DisableNodeStop()
179 {
180 	fDisableStop = true;
181 }
182 
183 
184 //	#pragma mark - BMediaNode methods
185 
186 
187 void
188 AudioMixer::Stop(bigtime_t performance_time, bool immediate)
189 {
190 	if (fDisableStop) {
191 		TRACE("AudioMixer STOP is disabled\n");
192 		return;
193 	} else {
194 		BMediaEventLooper::Stop(performance_time, immediate);
195 	}
196 }
197 
198 
199 BMediaAddOn*
200 AudioMixer::AddOn(int32 *internal_id) const
201 {
202 	*internal_id = 0;
203 	return fAddOn;
204 }
205 
206 
207 //	#pragma mark - BBufferConsumer methods
208 
209 
210 status_t
211 AudioMixer::HandleMessage(int32 message, const void *data, size_t size)
212 {
213 	// since we're using a mediaeventlooper, there shouldn't be any messages
214 	return B_ERROR;
215 }
216 
217 
218 status_t
219 AudioMixer::AcceptFormat(const media_destination &dest, media_format *ioFormat)
220 {
221 	PRINT_FORMAT("AudioMixer::AcceptFormat: ", *ioFormat);
222 
223 	// check that the specified format is reasonable for the specified destination, and
224 	// fill in any wildcard fields for which our BBufferConsumer has specific requirements.
225 
226 	// we have multiple inputs with different IDs, but
227 	// the port number must match our ControlPort()
228 	if (dest.port != ControlPort())
229 		return B_MEDIA_BAD_DESTINATION;
230 
231 	// specialize to raw audio format if necessary
232 	if (ioFormat->type == B_MEDIA_UNKNOWN_TYPE)
233 		ioFormat->type = B_MEDIA_RAW_AUDIO;
234 
235 	// we require a raw audio format
236 	if (ioFormat->type != B_MEDIA_RAW_AUDIO)
237 		return B_MEDIA_BAD_FORMAT;
238 
239 	// We do not have special requirements, but just in case
240 	// another mixer is connecting to us and may need a hint
241 	// to create a connection at optimal frame rate and
242 	// channel count, we place this information in the user_data
243 	fCore->Lock();
244 	MixerOutput *output = fCore->Output();
245 	uint32 channel_count = output ? output->MediaOutput().format.u.raw_audio.channel_count : 0;
246 	float frame_rate = output ? output->MediaOutput().format.u.raw_audio.frame_rate : 0.0;
247 	fCore->Unlock();
248 	ioFormat->user_data_type = FORMAT_USER_DATA_TYPE;
249 	*(uint32 *)&ioFormat->user_data[0] = FORMAT_USER_DATA_MAGIC_1;
250 	*(uint32 *)&ioFormat->user_data[4] = channel_count;
251 	*(float *)&ioFormat->user_data[20] = frame_rate;
252 	*(uint32 *)&ioFormat->user_data[44] = FORMAT_USER_DATA_MAGIC_2;
253 
254 	return B_OK;
255 }
256 
257 
258 status_t
259 AudioMixer::GetNextInput(int32 *cookie, media_input *out_input)
260 {
261 	TRACE("AudioMixer::GetNextInput\n");
262 
263 	// our 0th input is always a wildcard and free one
264 	if (*cookie == 0) {
265 		out_input->node = Node();
266 		out_input->source = media_source::null;
267 		out_input->destination.port = ControlPort();
268 		out_input->destination.id = 0;
269 		memset(&out_input->format, 0, sizeof(out_input->format));
270 		out_input->format.type = B_MEDIA_RAW_AUDIO;
271 		strcpy(out_input->name, "Free Input");
272 		*cookie += 1;
273 		return B_OK;
274 	}
275 
276 	// the other inputs are the currently connected ones
277 	fCore->Lock();
278 	MixerInput *input;
279 	input = fCore->Input(*cookie - 1);
280 	if (!input) {
281 		fCore->Unlock();
282 		return B_BAD_INDEX;
283 	}
284 	*out_input = input->MediaInput();
285 	*cookie += 1;
286 	fCore->Unlock();
287 	return B_OK;
288 }
289 
290 
291 void
292 AudioMixer::DisposeInputCookie(int32 cookie)
293 {
294 	// nothing to do
295 }
296 
297 
298 void
299 AudioMixer::BufferReceived(BBuffer *buffer)
300 {
301 
302 	if (buffer->Header()->type == B_MEDIA_PARAMETERS) {
303 		TRACE("Control Buffer Received\n");
304 		ApplyParameterData(buffer->Data(), buffer->SizeUsed());
305 		buffer->Recycle();
306 		return;
307 	}
308 
309 	//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);
310 
311 	// to receive the buffer at the right time,
312 	// push it through the event looper
313 	media_timed_event event(buffer->Header()->start_time,
314 		BTimedEventQueue::B_HANDLE_BUFFER, buffer,
315 		BTimedEventQueue::B_RECYCLE_BUFFER);
316 	EventQueue()->AddEvent(event);
317 }
318 
319 
320 void
321 AudioMixer::HandleInputBuffer(BBuffer* buffer, bigtime_t lateness)
322 {
323 	if (lateness > kMaxJitter && lateness > fLastLateness) {
324 		debug_printf("AudioMixer: Dequeued input buffer %Ld usec late\n", lateness);
325 		if (RunMode() == B_DROP_DATA || RunMode() == B_DECREASE_PRECISION
326 			|| RunMode() == B_INCREASE_LATENCY) {
327 			debug_printf("AudioMixer: sending notify\n");
328 
329 			// Build a media_source out of the header data
330 			media_source source = media_source::null;
331 			source.port = buffer->Header()->source_port;
332 			source.id = buffer->Header()->source;
333 
334 			NotifyLateProducer(source, lateness, TimeSource()->Now());
335 
336 			if (RunMode() == B_DROP_DATA) {
337 				debug_printf("AudioMixer: dropping buffer\n");
338 				return;
339 			}
340 		}
341 	}
342 	fLastLateness = lateness;
343 
344 	fCore->Lock();
345 	fCore->BufferReceived(buffer, lateness);
346 	fCore->Unlock();
347 }
348 
349 
350 void
351 AudioMixer::ProducerDataStatus(const media_destination& for_whom,
352 	int32 status, bigtime_t at_performance_time)
353 {
354 /*
355 	if (IsValidDest(for_whom))
356 	{
357 		media_timed_event event(at_performance_time, BTimedEventQueue::B_DATA_STATUS,
358 			(void *)(&for_whom), BTimedEventQueue::B_NO_CLEANUP, status, 0, NULL);
359 		EventQueue()->AddEvent(event);
360 
361 		// FIX_THIS
362 		// the for_whom destination is not being sent correctly - verify in HandleEvent loop
363 
364 	}
365 */
366 }
367 
368 
369 status_t
370 AudioMixer::GetLatencyFor(const media_destination &for_whom,
371 	bigtime_t *out_latency, media_node_id *out_timesource)
372 {
373 	// we have multiple inputs with different IDs, but
374 	// the port number must match our ControlPort()
375 	if (for_whom.port != ControlPort())
376 		return B_MEDIA_BAD_DESTINATION;
377 
378 	// return our event latency - this includes our internal + downstream
379 	// latency, but _not_ the scheduling latency
380 	*out_latency = EventLatency();
381 	*out_timesource = TimeSource()->ID();
382 
383 	printf("AudioMixer::GetLatencyFor %Ld, timesource is %ld\n",
384 		*out_latency, *out_timesource);
385 
386 	return B_OK;
387 }
388 
389 
390 status_t
391 AudioMixer::Connected(const media_source &producer,
392 	const media_destination &where, const media_format &with_format,
393 	media_input *out_input)
394 {
395 	PRINT_FORMAT("AudioMixer::Connected: ", with_format);
396 
397 	// workaround for a crashing bug in RealPlayer.  to be proper, RealPlayer's
398 	// BBufferProducer::PrepareToConnect() should have removed all wildcards.
399 	if (out_input->format.u.raw_audio.frame_rate == 0) {
400 		fprintf(stderr, "Audio Mixer Warning: "
401 			"Producer (port %ld, id %ld) connected with frame_rate=0\n",
402 			producer.port, producer.id);
403 		MixerOutput *output = fCore->Output();
404 		float frame_rate = output
405 			? output->MediaOutput().format.u.raw_audio.frame_rate : 44100.0f;
406 		out_input->format.u.raw_audio.frame_rate = frame_rate;
407 	}
408 
409 	// a BBufferProducer is connecting to our BBufferConsumer
410 
411 	// incoming connections should always have an incoming ID=0,
412 	// and the port number must match our ControlPort()
413 	if (where.id != 0 || where.port != ControlPort())
414 		return B_MEDIA_BAD_DESTINATION;
415 
416 	fCore->Lock();
417 
418 	// we assign a new id (!= 0) to the newly created input
419 	out_input->destination.id = fCore->CreateInputID();
420 
421 	// We need to make sure that the outInput's name field contains a valid
422 	// name, the name given the connection by the producer may still be an
423 	// empty string.
424 	// if the input has no name, assign one
425 	if (strlen(out_input->name) == 0)
426 		sprintf(out_input->name, "Input %ld", out_input->destination.id);
427 
428 	// add a new input to the mixer engine
429 	MixerInput *input;
430 	input = fCore->AddInput(*out_input);
431 
432 	fCore->Settings()->LoadConnectionSettings(input);
433 
434 	fCore->Unlock();
435 
436 	// If we want the producer to use a specific BBufferGroup, we now need
437 	// to call BMediaRoster::SetOutputBuffersFor() here to set the producer's
438 	// buffer group.
439 	// But we have no special buffer requirements anyway...
440 
441 	UpdateParameterWeb();
442 
443 	return B_OK;
444 }
445 
446 
447 void
448 AudioMixer::Disconnected(const media_source &producer,
449 	const media_destination &where)
450 {
451 	// One of our inputs has been disconnected
452 
453 	// check if it is really belongs to us
454 	if (where.port != ControlPort()) {
455 		TRACE("AudioMixer::Disconnected wrong input port\n");
456 		return;
457 	}
458 
459 	fCore->Lock();
460 
461 	if (!fCore->RemoveInput(where.id)) {
462 		TRACE("AudioMixer::Disconnected can't remove input\n");
463 	}
464 
465 	fCore->Unlock();
466 	UpdateParameterWeb();
467 }
468 
469 
470 status_t
471 AudioMixer::FormatChanged(const media_source &producer,
472 	const media_destination &consumer, int32 change_tag,
473 	const media_format &format)
474 {
475 	// at some point in the future (indicated by change_tag and RequestCompleted()),
476 	// we will receive buffers in a different format
477 
478 	TRACE("AudioMixer::FormatChanged\n");
479 
480 	if (consumer.port != ControlPort() || consumer.id == 0)
481 		return B_MEDIA_BAD_DESTINATION;
482 
483 	if (fCore->Settings()->RefuseInputFormatChange()) {
484 		TRACE("AudioMixer::FormatChanged: input format change refused\n");
485 		return B_NOT_ALLOWED;
486 	}
487 
488 	// TODO: We should not apply the format change at this point
489 	// TODO: At the moment, this is not implemented at the moment and will just
490 	// crash the media_server.
491 	return B_NOT_SUPPORTED;
492 
493 	// tell core about format change
494 	fCore->Lock();
495 	fCore->InputFormatChanged(consumer.id, format.u.raw_audio);
496 	fCore->Unlock();
497 
498 	return B_OK;
499 }
500 
501 
502 //	#pragma mark - BBufferProducer methods
503 
504 
505 status_t
506 AudioMixer::FormatSuggestionRequested(media_type type, int32 quality,
507 	media_format *format)
508 {
509 	TRACE("AudioMixer::FormatSuggestionRequested\n");
510 
511 	// BBufferProducer function, a downstream consumer
512 	// is asking for our output format
513 
514 	if (type != B_MEDIA_RAW_AUDIO && type != B_MEDIA_UNKNOWN_TYPE)
515 		return B_MEDIA_BAD_FORMAT;
516 
517 	// we can produce any (wildcard) raw audio format
518 	memset(format, 0, sizeof(*format));
519 	format->type = B_MEDIA_RAW_AUDIO;
520 	return B_OK;
521 }
522 
523 
524 status_t
525 AudioMixer::FormatProposal(const media_source &output, media_format *ioFormat)
526 {
527 	// BBufferProducer function, we implement this function to verify that the
528 	// proposed media_format is suitable for the specified output. If any fields
529 	// in the format are wildcards, and we have a specific requirement, adjust
530 	// those fields to match our requirements before returning.
531 
532 	TRACE("AudioMixer::FormatProposal\n");
533 
534 	// we only have one output (id=0, port=ControlPort())
535 	if (output.id != 0 || output.port != ControlPort())
536 		return B_MEDIA_BAD_SOURCE;
537 
538 	// specialize to raw audio format if necessary
539 	if (ioFormat->type == B_MEDIA_UNKNOWN_TYPE)
540 		ioFormat->type = B_MEDIA_RAW_AUDIO;
541 
542 	// we require a raw audio format
543 	if (ioFormat->type != B_MEDIA_RAW_AUDIO)
544 		return B_MEDIA_BAD_FORMAT;
545 
546 	return B_OK;
547 }
548 
549 /*!	If the format isn't good, put a good format into *io_format and return error
550 	If format has wildcard, specialize to what you can do (and change).
551 	If you can change the format, return OK.
552 	The request comes from your destination sychronously, so you cannot ask it
553 	whether it likes it -- you should assume it will since it asked.
554 */
555 status_t
556 AudioMixer::FormatChangeRequested(const media_source &source,
557 	const media_destination &destination, media_format *io_format,
558 	int32 *_deprecated_)
559 {
560 	// the downstream consumer node (soundcard) requested that we produce
561 	// another format, we need to check if the format is acceptable and
562 	// remove any wildcards before returning OK.
563 
564 	TRACE("AudioMixer::FormatChangeRequested\n");
565 
566 	if (fCore->Settings()->RefuseOutputFormatChange()) {
567 		TRACE("AudioMixer::FormatChangeRequested: output format change refused\n");
568 		return B_ERROR;
569 	}
570 
571 	fCore->Lock();
572 
573 	MixerOutput *output = fCore->Output();
574 	if (!output) {
575 		ERROR("AudioMixer::FormatChangeRequested: no output\n");
576 		goto err;
577 	}
578 	if (source != output->MediaOutput().source) {
579 		ERROR("AudioMixer::FormatChangeRequested: wrong output source\n");
580 		goto err;
581 	}
582 	if (destination != output->MediaOutput().destination) {
583 		ERROR("AudioMixer::FormatChangeRequested: wrong output destination "
584 			"(port %ld, id %ld), our is (port %ld, id %ld)\n", destination.port,
585 			destination.id, output->MediaOutput().destination.port,
586 			output->MediaOutput().destination.id);
587 		if (destination.port == output->MediaOutput().destination.port
588 			&& destination.id == output->MediaOutput().destination.id + 1) {
589 			ERROR("AudioMixer::FormatChangeRequested: this might be the broken "
590 				"R5 multi audio add-on\n");
591 		}
592 		goto err;
593 	}
594 	if (io_format->type != B_MEDIA_RAW_AUDIO
595 		&& io_format->type != B_MEDIA_UNKNOWN_TYPE) {
596 		ERROR("AudioMixer::FormatChangeRequested: wrong format type\n");
597 		goto err;
598 	}
599 
600 	/* remove wildcards */
601 #if USE_MEDIA_FORMAT_WORKAROUND
602 	multi_audio_format_specialize(&io_format->u.raw_audio,
603 		&fDefaultFormat.u.raw_audio);
604 #else
605 	io_format->SpecializeTo(&fDefaultFormat);
606 #endif
607 
608 	media_node_id id;
609 	FindLatencyFor(destination, &fDownstreamLatency, &id);
610 	TRACE("AudioMixer: Downstream Latency is %Ld usecs\n", fDownstreamLatency);
611 
612 	// SetDuration of one buffer
613 	SetBufferDuration(buffer_duration(io_format->u.raw_audio));
614 	TRACE("AudioMixer: buffer duration is %Ld usecs\n", BufferDuration());
615 
616 	// Our internal latency is at least the length of a full output buffer
617 	fInternalLatency = BufferDuration()
618 		+ max(4500LL, bigtime_t(0.5 * BufferDuration()));
619 	TRACE("AudioMixer: Internal latency is %Ld usecs\n", fInternalLatency);
620 
621 	SetEventLatency(fDownstreamLatency + fInternalLatency);
622 
623 	// we need to inform all connected *inputs* about *our* change in latency
624 	PublishEventLatencyChange();
625 
626 	// TODO: we might need to create more buffers, to span a larger downstream
627 	// latency
628 
629 	// apply latency change
630 	fCore->SetTimingInfo(TimeSource(), fDownstreamLatency);
631 
632 	// apply format change
633 	fCore->OutputFormatChanged(io_format->u.raw_audio);
634 
635 	delete fBufferGroup;
636 	fBufferGroup = CreateBufferGroup();
637 	fCore->SetOutputBufferGroup(fBufferGroup);
638 
639 	fCore->Unlock();
640 	return B_OK;
641 
642 err:
643 	fCore->Unlock();
644 	return B_ERROR;
645 }
646 
647 
648 status_t
649 AudioMixer::GetNextOutput(int32 *cookie, media_output *out_output)
650 {
651 	TRACE("AudioMixer::GetNextOutput\n");
652 
653 	if (*cookie != 0)
654 		return B_BAD_INDEX;
655 
656 	fCore->Lock();
657 	MixerOutput *output = fCore->Output();
658 	if (output) {
659 		*out_output = output->MediaOutput();
660 	} else {
661 		out_output->node = Node();
662 		out_output->source.port = ControlPort();
663 		out_output->source.id = 0;
664 		out_output->destination = media_destination::null;
665 		memset(&out_output->format, 0, sizeof(out_output->format));
666 		out_output->format.type = B_MEDIA_RAW_AUDIO;
667 		strcpy(out_output->name, "Mixer Output");
668 	}
669 	fCore->Unlock();
670 
671 	*cookie += 1;
672 	return B_OK;
673 }
674 
675 
676 status_t
677 AudioMixer::DisposeOutputCookie(int32 cookie)
678 {
679 	// nothing to do
680 	return B_OK;
681 }
682 
683 
684 status_t
685 AudioMixer::SetBufferGroup(const media_source &for_source,
686 	BBufferGroup *newGroup)
687 {
688 	TRACE("AudioMixer::SetBufferGroup\n");
689 	// the downstream consumer (soundcard) node asks us to use another
690 	// BBufferGroup (might be NULL). We only have one output (id 0)
691 	if (for_source.port != ControlPort() || for_source.id != 0)
692 		return B_MEDIA_BAD_SOURCE;
693 
694 	if (newGroup == fBufferGroup) {
695 		// we're already using this buffergroup
696 		return B_OK;
697 	}
698 
699 	fCore->Lock();
700 	if (!newGroup)
701 		newGroup = CreateBufferGroup();
702 	fCore->SetOutputBufferGroup(newGroup);
703 	delete fBufferGroup;
704 	fBufferGroup = newGroup;
705 	fCore->Unlock();
706 
707 	return B_OK;
708 }
709 
710 
711 status_t
712 AudioMixer::GetLatency(bigtime_t *out_latency)
713 {
714 	// report our *total* latency:  internal plus downstream plus scheduling
715 	*out_latency = EventLatency() + SchedulingLatency();
716 
717 	TRACE("AudioMixer::GetLatency %Ld\n", *out_latency);
718 
719 	return B_OK;
720 }
721 
722 
723 void
724 AudioMixer::LatencyChanged(const media_source& source,
725 	const media_destination& destination, bigtime_t new_latency, uint32 flags)
726 {
727 	if (source.port != ControlPort() || source.id != 0) {
728 		ERROR("AudioMixer::LatencyChanged: received but has wrong source "
729 			"%ld/%ld\n", source.port, source.id);
730 		return;
731 	}
732 
733 	TRACE("AudioMixer::LatencyChanged: downstream latency from %ld/%ld to "
734 		"%ld/%ld changed from %Ld to %Ld\n", source.port, source.id,
735 		destination.port, destination.id, fDownstreamLatency, new_latency);
736 
737 #if DEBUG
738 	{
739 		media_node_id id;
740 		bigtime_t l;
741 		FindLatencyFor(destination, &l, &id);
742 		TRACE("AudioMixer: Reported downstream Latency is %Ld usecs\n", l);
743 	}
744 #endif
745 
746 	fDownstreamLatency = new_latency;
747 	SetEventLatency(fDownstreamLatency + fInternalLatency);
748 
749 	// XXX we might need to create more buffers, to span a larger downstream
750 	// latency
751 
752 	fCore->Lock();
753 	fCore->SetTimingInfo(TimeSource(), fDownstreamLatency);
754 	PublishEventLatencyChange();
755 	fCore->Unlock();
756 }
757 
758 status_t
759 AudioMixer::PrepareToConnect(const media_source &what,
760 	const media_destination &where, media_format *format,
761 	media_source *out_source, char *out_name)
762 {
763 	TRACE("AudioMixer::PrepareToConnect\n");
764 	// PrepareToConnect() is the second stage of format negotiations that
765 	// happens inside BMediaRoster::Connect(). At this point, the consumer's
766 	// AcceptFormat() method has been called, and that node has potentially
767 	// changed the proposed format.
768 	// It may also have left wildcards in the format. PrepareToConnect()
769 	// *must* fully specialize the format before returning!
770 	// we also create the new output connection and return it in out_source.
771 
772 	PRINT_FORMAT("AudioMixer::PrepareToConnect: suggested format", *format);
773 
774 	// avoid loop connections
775 	if (where.port == ControlPort())
776 		return B_MEDIA_BAD_SOURCE;
777 
778 	// is the source valid?
779 	if (what.port != ControlPort() || what.id != 0)
780 		return B_MEDIA_BAD_SOURCE;
781 
782 	// is the format acceptable?
783 	if (format->type != B_MEDIA_RAW_AUDIO
784 		&& format->type != B_MEDIA_UNKNOWN_TYPE) {
785 		PRINT_FORMAT("AudioMixer::PrepareToConnect: bad format", *format);
786 		return B_MEDIA_BAD_FORMAT;
787 	}
788 
789 	fCore->Lock();
790 
791 	// are we already connected?
792 	if (fCore->Output() != 0) {
793 		fCore->Unlock();
794 		ERROR("AudioMixer::PrepareToConnect: already connected\n");
795 		return B_MEDIA_ALREADY_CONNECTED;
796 	}
797 
798 	// It is possible that another mixer is connecting.
799 	// To avoid using the default format, we use one of
800 	// a) the format that it indicated as hint in the user_data,
801 	// b) the output format of the system audio mixer
802 	// c) the input format of the system DAC device
803 	// d) if everything failes, keep the wildcard
804 	if (format->u.raw_audio.channel_count == 0
805 		&& format->u.raw_audio.frame_rate < 1
806 		&& format->user_data_type == FORMAT_USER_DATA_TYPE
807 		&& *(uint32 *)&format->user_data[0] == FORMAT_USER_DATA_MAGIC_1
808 		&& *(uint32 *)&format->user_data[44] == FORMAT_USER_DATA_MAGIC_2) {
809 		// ok, a mixer is connecting
810 		uint32 channel_count = *(uint32 *)&format->user_data[4];
811 		float frame_rate = *(float *)&format->user_data[20];
812 		if (channel_count > 0 && frame_rate > 0) {
813 			// format is good, use it
814 			format->u.raw_audio.channel_count = channel_count;
815 			format->u.raw_audio.frame_rate = frame_rate;
816 		} else {
817 			// other mixer's output is probably not connected
818 			media_node node;
819 			BMediaRoster *roster = BMediaRoster::Roster();
820 			media_output out;
821 			media_input in;
822 			int32 count;
823 			if (roster->GetAudioMixer(&node) == B_OK
824 				&& roster->GetConnectedOutputsFor(node, &out, 1, &count)
825 						== B_OK
826 				&& count == 1) {
827 				// use mixer output format
828 				format->u.raw_audio.channel_count
829 					= out.format.u.raw_audio.channel_count;
830 				format->u.raw_audio.frame_rate
831 					= out.format.u.raw_audio.frame_rate;
832 			} else if (roster->GetAudioOutput(&node) == B_OK
833 				&& roster->GetAllInputsFor(node, &in, 1, &count) == B_OK
834 				&& count == 1) {
835 				// use DAC input format
836 				format->u.raw_audio.channel_count
837 					= in.format.u.raw_audio.channel_count;
838 				format->u.raw_audio.frame_rate
839 					= in.format.u.raw_audio.frame_rate;
840 			}
841 		}
842 	}
843 
844 	/* set source and suggest a name */
845 	*out_source = what;
846 	strcpy(out_name, "Mixer Output");
847 
848 	/* remove wildcards */
849 #if USE_MEDIA_FORMAT_WORKAROUND
850 	multi_audio_format_specialize(&format->u.raw_audio,
851 		&fDefaultFormat.u.raw_audio);
852 #else
853 	format->SpecializeTo(&fDefaultFormat);
854 #endif
855 
856 	PRINT_FORMAT("AudioMixer::PrepareToConnect: final format", *format);
857 
858 	/* add output to core */
859 	media_output output;
860 	output.node = Node();
861 	output.source = *out_source;
862 	output.destination = where;
863 	output.format = *format;
864 	strcpy(output.name, out_name);
865 
866 	fCore->EnableOutput(false);
867 	fCore->AddOutput(output);
868 
869 	fCore->Unlock();
870 	return B_OK;
871 }
872 
873 
874 void
875 AudioMixer::Connect(status_t error, const media_source &source,
876 	const media_destination &dest, const media_format &format, char *io_name)
877 {
878 	TRACE("AudioMixer::Connect\n");
879 
880 	fCore->Lock();
881 	// are we still connected?
882 	if (fCore->Output() == 0) {
883 		fCore->Unlock();
884 		ERROR("AudioMixer::Connect: no longer connected\n");
885 		return;
886 	}
887 	fCore->Unlock();
888 
889 	if (error != B_OK) {
890 		// if an error occured, remove output from core
891 		ERROR("AudioMixer::Connect failed with error 0x%08lX, removing "
892 			"connection\n", error);
893 		fCore->Lock();
894 		fCore->RemoveOutput();
895 		fCore->Unlock();
896 		return;
897 	}
898 
899 	// Switch our prefered format to have the same
900 	// frame_rate and channel count as the output.
901 	fDefaultFormat.u.raw_audio.frame_rate = format.u.raw_audio.frame_rate;
902 	fDefaultFormat.u.raw_audio.channel_count = format.u.raw_audio.channel_count;
903 
904 	// if the connection has no name, we set it now
905 	if (strlen(io_name) == 0)
906 		strcpy(io_name, "Mixer Output");
907 
908 	// Now that we're connected, we can determine our downstream latency.
909 	media_node_id id;
910 	FindLatencyFor(dest, &fDownstreamLatency, &id);
911 	TRACE("AudioMixer: Downstream Latency is %Ld usecs\n", fDownstreamLatency);
912 
913 	// SetDuration of one buffer
914 	SetBufferDuration(buffer_duration(format.u.raw_audio));
915 	TRACE("AudioMixer: buffer duration is %Ld usecs\n", BufferDuration());
916 
917 	// Our internal latency is at least the length of a full output buffer
918 	// plus mixing time, plus jitter
919 	fInternalLatency = BufferDuration()
920 		+ max(kMinMixingTime, bigtime_t(0.5 * BufferDuration())) + kMaxJitter;
921 	TRACE("AudioMixer: Internal latency is %Ld usecs\n", fInternalLatency);
922 
923 	SetEventLatency(fDownstreamLatency + fInternalLatency);
924 
925 	// we need to inform all connected *inputs* about *our* change in latency
926 	PublishEventLatencyChange();
927 
928 	// Set up the buffer group for our connection, as long as nobody handed
929 	// us a buffer group (via SetBufferGroup()) prior to this.  That can
930 	// happen, for example, if the consumer calls SetOutputBuffersFor() on
931 	// us from within its Connected() method.
932 	if (!fBufferGroup)
933 		fBufferGroup = CreateBufferGroup();
934 
935 	fCore->Lock();
936 
937 	ASSERT(fCore->Output() != 0);
938 
939 	// our source should still be valid, too
940 	ASSERT(fCore->Output()->MediaOutput().source.id == 0);
941 	ASSERT(fCore->Output()->MediaOutput().source.port == ControlPort());
942 
943 	// BBufferConsumer::Connected() may return a different input for the
944 	// newly created connection. The destination can have changed since
945 	// AudioMixer::PrepareToConnect() and we need to update it.
946 	fCore->Output()->MediaOutput().destination = dest;
947 
948 	fCore->EnableOutput(true);
949 	fCore->SetTimingInfo(TimeSource(), fDownstreamLatency);
950 	fCore->SetOutputBufferGroup(fBufferGroup);
951 
952 	fCore->Settings()->LoadConnectionSettings(fCore->Output());
953 
954 	fCore->Unlock();
955 	UpdateParameterWeb();
956 }
957 
958 
959 void
960 AudioMixer::Disconnect(const media_source& what, const media_destination& where)
961 {
962 	TRACE("AudioMixer::Disconnect\n");
963 	fCore->Lock();
964 
965 	// Make sure that our connection is the one being disconnected
966 	MixerOutput* output = fCore->Output();
967 	if (!output
968 		|| output->MediaOutput().node != Node()
969 		|| output->MediaOutput().source != what
970 		|| output->MediaOutput().destination != where) {
971 		ERROR("AudioMixer::Disconnect can't disconnect (wrong connection)\n");
972 		fCore->Unlock();
973 		return;
974 	}
975 
976 	// Switch our prefered format back to default
977 	// frame rate and channel count.
978 	fDefaultFormat.u.raw_audio.frame_rate = 96000;
979 	fDefaultFormat.u.raw_audio.channel_count = 2;
980 
981 	// force a stop
982 	fCore->Stop();
983 
984 	fCore->RemoveOutput();
985 
986 	// destroy buffer group
987 	delete fBufferGroup;
988 	fBufferGroup = 0;
989 	fCore->SetOutputBufferGroup(0);
990 
991 	fCore->Unlock();
992 	UpdateParameterWeb();
993 }
994 
995 
996 void
997 AudioMixer::LateNoticeReceived(const media_source& what, bigtime_t howMuch,
998 	bigtime_t performanceTime)
999 {
1000 	// We've produced some late buffers... Increase Latency
1001 	// is the only runmode in which we can do anything about this
1002 	// TODO: quality could be decreased, too
1003 
1004 	ERROR("AudioMixer::LateNoticeReceived, %Ld too late at %Ld\n", howMuch,
1005 		performanceTime);
1006 
1007 	if (what == fCore->Output()->MediaOutput().source
1008 		&& RunMode() == B_INCREASE_LATENCY) {
1009 		// We need to ignore subsequent notices whose arrival time here
1010 		// lies within the last lateness, because queued-up buffers will all be 'late'
1011 		if (performanceTime < fLastLateNotification)
1012 			return;
1013 
1014 		fInternalLatency += howMuch;
1015 
1016 		// At some point a too large latency can get annoying
1017 		// (actually more than annoying, as there won't be enough buffers long before this!)
1018 		if (fInternalLatency > kMaxLatency)
1019 			fInternalLatency = kMaxLatency;
1020 
1021 		fLastLateNotification = TimeSource()->Now() + howMuch;
1022 
1023 		debug_printf("AudioMixer: increasing internal latency to %Ld usec\n", fInternalLatency);
1024 		SetEventLatency(fDownstreamLatency + fInternalLatency);
1025 
1026 		PublishEventLatencyChange();
1027 	}
1028 }
1029 
1030 
1031 void
1032 AudioMixer::EnableOutput(const media_source& what, bool enabled,
1033 	int32 */*deprecated*/)
1034 {
1035 	// we only have one output
1036 	if (what.id != 0 || what.port != ControlPort())
1037 		return;
1038 
1039 	fCore->Lock();
1040 	fCore->EnableOutput(enabled);
1041 	fCore->Unlock();
1042 }
1043 
1044 
1045 //	#pragma mark - BMediaEventLooper methods
1046 
1047 
1048 void
1049 AudioMixer::NodeRegistered()
1050 {
1051 	Run();
1052 	SetPriority(120);
1053 	UpdateParameterWeb();
1054 }
1055 
1056 
1057 void
1058 AudioMixer::SetTimeSource(BTimeSource* timeSource)
1059 {
1060 	TRACE("AudioMixer::SetTimeSource: timesource is now %ld\n",
1061 		timeSource->ID());
1062 	fCore->Lock();
1063 	fCore->SetTimingInfo(timeSource, fDownstreamLatency);
1064 	fCore->Unlock();
1065 }
1066 
1067 
1068 void
1069 AudioMixer::HandleEvent(const media_timed_event *event, bigtime_t lateness,
1070 	bool realTimeEvent)
1071 {
1072 	switch (event->type) {
1073 		case BTimedEventQueue::B_HANDLE_BUFFER:
1074 		{
1075 			HandleInputBuffer((BBuffer *)event->pointer, lateness);
1076 			((BBuffer *)event->pointer)->Recycle();
1077 			break;
1078 		}
1079 
1080 		case BTimedEventQueue::B_START:
1081 		{
1082 			TRACE("AudioMixer::HandleEvent: B_START\n");
1083 			if (RunState() != B_STARTED) {
1084 				fCore->Lock();
1085 				fCore->Start();
1086 				fCore->Unlock();
1087 			}
1088 			break;
1089 		}
1090 
1091 		case BTimedEventQueue::B_STOP:
1092 		{
1093 			TRACE("AudioMixer::HandleEvent: B_STOP\n");
1094 			// stopped - don't process any more buffers, flush all buffers
1095 			// from event queue
1096 			EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true,
1097 				BTimedEventQueue::B_HANDLE_BUFFER);
1098 			fCore->Lock();
1099 			fCore->Stop();
1100 			fCore->Unlock();
1101 			break;
1102 		}
1103 
1104 		case BTimedEventQueue::B_DATA_STATUS:
1105 		{
1106 			ERROR("DataStatus message\n");
1107 			break;
1108 		}
1109 
1110 		default:
1111 			break;
1112 	}
1113 }
1114 
1115 
1116 //	#pragma mark - AudioMixer methods
1117 
1118 
1119 void
1120 AudioMixer::PublishEventLatencyChange()
1121 {
1122 	// our event (processing + downstream) latency has changed,
1123 	// and we need tell all inputs about this
1124 
1125 	TRACE("AudioMixer::PublishEventLatencyChange\n");
1126 
1127 	fCore->Lock();
1128 
1129 	MixerInput *input;
1130 	for (int i = 0; (input = fCore->Input(i)) != 0; i++) {
1131 		TRACE("AudioMixer::PublishEventLatencyChange: SendLatencyChange, "
1132 			"connection %ld/%ld to %ld/%ld event latency is now %Ld\n",
1133 			input->MediaInput().source.port, input->MediaInput().source.id,
1134 			input->MediaInput().destination.port,
1135 			input->MediaInput().destination.id, EventLatency());
1136 		SendLatencyChange(input->MediaInput().source,
1137 			input->MediaInput().destination, EventLatency());
1138 	}
1139 
1140 	fCore->Unlock();
1141 }
1142 
1143 
1144 BBufferGroup*
1145 AudioMixer::CreateBufferGroup()
1146 {
1147 	// allocate enough buffers to span our downstream latency
1148 	// (plus one for rounding up), plus one extra
1149 	int32 count = int32(fDownstreamLatency / BufferDuration()) + 2;
1150 
1151 	TRACE("AudioMixer::CreateBufferGroup: fDownstreamLatency %Ld, "
1152 		"BufferDuration %Ld, buffer count = %ld\n", fDownstreamLatency,
1153 		BufferDuration(), count);
1154 
1155 	if (count < 3)
1156 		count = 3;
1157 
1158 	fCore->Lock();
1159 	uint32 size = fCore->Output()->MediaOutput().format.u.raw_audio.buffer_size;
1160 	fCore->Unlock();
1161 
1162 	TRACE("AudioMixer: allocating %ld buffers of %ld bytes each\n",
1163 		count, size);
1164 	return new BBufferGroup(size, count);
1165 }
1166 
1167 
1168 status_t
1169 AudioMixer::SendBuffer(BBuffer* buffer, MixerOutput* output)
1170 {
1171 	return BBufferProducer::SendBuffer(buffer, output->MediaOutput().source,
1172 		output->MediaOutput().destination);
1173 }
1174 
1175 
1176 float
1177 AudioMixer::dB_to_Gain(float db)
1178 {
1179 	TRACE("dB_to_Gain: dB in: %01.2f ", db);
1180 	if (fCore->Settings()->NonLinearGainSlider()) {
1181 		if (db > 0) {
1182 			db = db * (pow(abs(DB_MAX), (1.0 / DB_EXPONENT_POSITIVE))
1183 				/ abs(DB_MAX));
1184 			db = pow(db, DB_EXPONENT_POSITIVE);
1185 		} else {
1186 			db = -db;
1187 			db = db * (pow(abs(DB_MIN), (1.0 / DB_EXPONENT_NEGATIVE))
1188 				/ abs(DB_MIN));
1189 			db = pow(db, DB_EXPONENT_NEGATIVE);
1190 			db = -db;
1191 		}
1192 	}
1193 	TRACE("dB out: %01.2f\n", db);
1194 	return pow(10.0, db / 20.0);
1195 }
1196 
1197 
1198 float
1199 AudioMixer::Gain_to_dB(float gain)
1200 {
1201 	float db;
1202 	db = 20.0 * log10(gain);
1203 	if (fCore->Settings()->NonLinearGainSlider()) {
1204 		if (db > 0) {
1205 			db = pow(db, (1.0 / DB_EXPONENT_POSITIVE));
1206 			db = db * (abs(DB_MAX) / pow(abs(DB_MAX),
1207 				(1.0 / DB_EXPONENT_POSITIVE)));
1208 		} else {
1209 			db = -db;
1210 			db = pow(db, (1.0 / DB_EXPONENT_NEGATIVE));
1211 			db = db * (abs(DB_MIN) / pow(abs(DB_MIN),
1212 				(1.0 / DB_EXPONENT_NEGATIVE)));
1213 			db = -db;
1214 		}
1215 	}
1216 	return db;
1217 }
1218 
1219 
1220 // #pragma mark - BControllable methods
1221 
1222 
1223 status_t
1224 AudioMixer::GetParameterValue(int32 id, bigtime_t *last_change, void *value,
1225 	size_t *ioSize)
1226 {
1227 	TRACE("GetParameterValue: id 0x%08lx, ioSize %ld\n", id, *ioSize);
1228 	int param = PARAM(id);
1229 	fCore->Lock();
1230 	if (PARAM_IS_ETC(id)) {
1231 		switch (ETC(id)) {
1232 			case 10:	// Attenuate mixer output by 3dB
1233 				*ioSize = sizeof(int32);
1234 				static_cast<int32 *>(value)[0] = fCore->Settings()->AttenuateOutput();
1235 				break;
1236 			case 20:	// Use non linear gain sliders
1237 				*ioSize = sizeof(int32);
1238 				static_cast<int32 *>(value)[0] = fCore->Settings()->NonLinearGainSlider();
1239 				break;
1240 			case 30:	// Display balance control for stereo connections
1241 				*ioSize = sizeof(int32);
1242 				static_cast<int32 *>(value)[0] = fCore->Settings()->UseBalanceControl();
1243 				break;
1244 			case 40:	// Allow output channel remapping
1245 				*ioSize = sizeof(int32);
1246 				static_cast<int32 *>(value)[0] = fCore->Settings()->AllowOutputChannelRemapping();
1247 				break;
1248 			case 50:	// Allow input channel remapping
1249 				*ioSize = sizeof(int32);
1250 				static_cast<int32 *>(value)[0] = fCore->Settings()->AllowInputChannelRemapping();
1251 				break;
1252 			case 60:	// Input gain controls
1253 				*ioSize = sizeof(int32);
1254 				static_cast<int32 *>(value)[0] = fCore->Settings()->InputGainControls();
1255 				break;
1256 			case 70:	// Resampling algorithm
1257 				*ioSize = sizeof(int32);
1258 				static_cast<int32 *>(value)[0] = fCore->Settings()->ResamplingAlgorithm();
1259 				break;
1260 			case 80:	// Refuse output format changes
1261 				*ioSize = sizeof(int32);
1262 				static_cast<int32 *>(value)[0] = fCore->Settings()->RefuseOutputFormatChange();
1263 				break;
1264 			case 90:	// Refuse input format changes
1265 				*ioSize = sizeof(int32);
1266 				static_cast<int32 *>(value)[0] = fCore->Settings()->RefuseInputFormatChange();
1267 				break;
1268 			default:
1269 				ERROR("unhandled ETC 0x%08lx\n", id);
1270 				break;
1271 		}
1272 	} else if (param == 0) {
1273 		MixerOutput *output = fCore->Output();
1274 		if (!output || (!PARAM_IS_MUTE(id) && !PARAM_IS_GAIN(id) && !PARAM_IS_SRC_ENABLE(id) && !PARAM_IS_SRC_GAIN(id) && !PARAM_IS_BALANCE(id)))
1275 			goto err;
1276 		if (PARAM_IS_MUTE(id)) {
1277 			// output mute control
1278 			if (*ioSize < sizeof(int32))
1279 				goto err;
1280 			*ioSize = sizeof(int32);
1281 			static_cast<int32 *>(value)[0] = output->IsMuted();
1282 		}
1283 		if (PARAM_IS_GAIN(id)) {
1284 			// output gain control
1285 			if (fCore->Settings()->UseBalanceControl() && output->GetOutputChannelCount() == 2 && 1 /*channel mask is stereo */) {
1286 				// single channel control + balance
1287 				if (*ioSize < sizeof(float))
1288 					goto err;
1289 				*ioSize = sizeof(float);
1290 				static_cast<float *>(value)[0] = GAIN_TO_DB((output->GetOutputChannelGain(0) + output->GetOutputChannelGain(1)) / 2);
1291 			} else {
1292 				// multi channel control
1293 				if (*ioSize == sizeof(float)) {
1294 					// get combined gain for all controls
1295 					float gain = 0;
1296 					for (int channel = 0;
1297 							channel < output->GetOutputChannelCount();
1298 							channel++) {
1299 						gain += GAIN_TO_DB(
1300 							output->GetOutputChannelGain(channel));
1301 					}
1302 					static_cast<float *>(value)[0] = gain
1303 						/ output->GetOutputChannelCount();
1304 				} else {
1305 					if (*ioSize < output->GetOutputChannelCount()
1306 							* sizeof(float))
1307 						goto err;
1308 
1309 					*ioSize = output->GetOutputChannelCount() * sizeof(float);
1310 
1311 					for (int channel = 0;
1312 							channel < output->GetOutputChannelCount();
1313 							channel++) {
1314 						static_cast<float *>(value)[channel]
1315 							= GAIN_TO_DB(output->GetOutputChannelGain(channel));
1316 					}
1317 				}
1318 			}
1319 		}
1320 		if (PARAM_IS_BALANCE(id)) {
1321 			float l = output->GetOutputChannelGain(0);
1322 			float r = output->GetOutputChannelGain(1);
1323 			float v = r / (l+r);
1324 			TRACE("balance l %1.3f, r %1.3f, v %1.3f\n",l,r,v);
1325 			if (*ioSize < sizeof(float))
1326 				goto err;
1327 			*ioSize = sizeof(float);
1328 			static_cast<float *>(value)[0] = v * 100;
1329 		}
1330 		if (PARAM_IS_SRC_ENABLE(id)) {
1331 			if (*ioSize < sizeof(int32))
1332 				goto err;
1333 			*ioSize = sizeof(int32);
1334 			static_cast<int32 *>(value)[0] = output->HasOutputChannelSource(PARAM_CHAN(id), PARAM_SRC(id));
1335 		}
1336 		if (PARAM_IS_SRC_GAIN(id)) {
1337 			if (*ioSize < sizeof(float))
1338 				goto err;
1339 			*ioSize = sizeof(float);
1340 			static_cast<float *>(value)[0] = GAIN_TO_PERCENT(output->GetOutputChannelSourceGain(PARAM_CHAN(id), PARAM_SRC(id)));
1341 		}
1342 	} else {
1343 		MixerInput *input;
1344 		for (int i = 0; (input = fCore->Input(i)); i++)
1345 			if (input->ID() == param)
1346 				break;
1347 		if (!input || (!PARAM_IS_MUTE(id) && !PARAM_IS_GAIN(id) && !PARAM_IS_DST_ENABLE(id) && !PARAM_IS_BALANCE(id)))
1348 			goto err;
1349 		if (PARAM_IS_MUTE(id)) {
1350 			// input mute control
1351 			if (*ioSize < sizeof(int32))
1352 				goto err;
1353 			*ioSize = sizeof(int32);
1354 			static_cast<int32 *>(value)[0] = !input->IsEnabled();
1355 		}
1356 		if (PARAM_IS_GAIN(id)) {
1357 			// input gain control
1358 			if (fCore->Settings()->InputGainControls() == 0) {
1359 				// Physical input channels
1360 				if (fCore->Settings()->UseBalanceControl() && input->GetInputChannelCount() == 2 && 1 /*channel mask is stereo */) {
1361 					// single channel control + balance
1362 					if (*ioSize < sizeof(float))
1363 						goto err;
1364 					*ioSize = sizeof(float);
1365 					static_cast<float *>(value)[0] = GAIN_TO_DB((input->GetInputChannelGain(0) + input->GetInputChannelGain(1)) / 2);
1366 				} else {
1367 					// multi channel control
1368 					if (*ioSize < input->GetInputChannelCount() * sizeof(float))
1369 						goto err;
1370 					*ioSize = input->GetInputChannelCount() * sizeof(float);
1371 					for (int chan = 0; chan < input->GetInputChannelCount(); chan++)
1372 						static_cast<float *>(value)[chan] = GAIN_TO_DB(input->GetInputChannelGain(chan));
1373 				}
1374 			} else {
1375 				// Virtual output channels
1376 				if (fCore->Settings()->UseBalanceControl() && input->GetMixerChannelCount() == 2 && 1 /*channel mask is stereo */) {
1377 					// single channel control + balance
1378 					if (*ioSize < sizeof(float))
1379 						goto err;
1380 					*ioSize = sizeof(float);
1381 					static_cast<float *>(value)[0] = GAIN_TO_DB((input->GetMixerChannelGain(0) + input->GetMixerChannelGain(1)) / 2);
1382 				} else {
1383 					// multi channel control
1384 					if (*ioSize < input->GetMixerChannelCount() * sizeof(float))
1385 						goto err;
1386 					*ioSize = input->GetMixerChannelCount() * sizeof(float);
1387 					for (int chan = 0; chan < input->GetMixerChannelCount(); chan++)
1388 						static_cast<float *>(value)[chan] = GAIN_TO_DB(input->GetMixerChannelGain(chan));
1389 				}
1390 			}
1391 		}
1392 		if (PARAM_IS_BALANCE(id)) {
1393 			if (fCore->Settings()->InputGainControls() == 0) {
1394 				// Physical input channels
1395 				float l = input->GetInputChannelGain(0);
1396 				float r = input->GetInputChannelGain(1);
1397 				float v = r / (l+r);
1398 				TRACE("balance l %1.3f, r %1.3f, v %1.3f\n",l,r,v);
1399 				if (*ioSize < sizeof(float))
1400 					goto err;
1401 				*ioSize = sizeof(float);
1402 				static_cast<float *>(value)[0] = v * 100;
1403 			} else {
1404 				// Virtual output channels
1405 				float l = input->GetMixerChannelGain(0);
1406 				float r = input->GetMixerChannelGain(1);
1407 				float v = r / (l+r);
1408 				TRACE("balance l %1.3f, r %1.3f, v %1.3f\n",l,r,v);
1409 				if (*ioSize < sizeof(float))
1410 					goto err;
1411 				*ioSize = sizeof(float);
1412 				static_cast<float *>(value)[0] = v * 100;
1413 			}
1414 		}
1415 		if (PARAM_IS_DST_ENABLE(id)) {
1416 			if (*ioSize < sizeof(int32))
1417 				goto err;
1418 			*ioSize = sizeof(int32);
1419 			static_cast<int32 *>(value)[0] = input->HasInputChannelDestination(PARAM_CHAN(id), PARAM_DST(id));
1420 		}
1421 	}
1422 	*last_change = TimeSource()->Now(); // XXX we could do better
1423 	fCore->Unlock();
1424 	return B_OK;
1425 err:
1426 	fCore->Unlock();
1427 	return B_ERROR;
1428 }
1429 
1430 
1431 void
1432 AudioMixer::SetParameterValue(int32 id, bigtime_t when, const void *value,
1433 	size_t size)
1434 {
1435 	TRACE("SetParameterValue: id 0x%08lx, size %ld\n", id, size);
1436 	bool update = false;
1437 	int param = PARAM(id);
1438 	fCore->Lock();
1439 	if (PARAM_IS_ETC(id)) {
1440 		switch (ETC(id)) {
1441 			case 10:	// Attenuate mixer output by 3dB
1442 				if (size != sizeof(int32))
1443 					goto err;
1444 				fCore->Settings()->SetAttenuateOutput(static_cast<const int32 *>(value)[0]);
1445 				// this value is special (see MixerCore.h) and we need to notify the core
1446 				fCore->SetOutputAttenuation((static_cast<const int32 *>(value)[0]) ? 0.708 : 1.0);
1447 				break;
1448 			case 20:	// Use non linear gain sliders
1449 				if (size != sizeof(int32))
1450 					goto err;
1451 				fCore->Settings()->SetNonLinearGainSlider(static_cast<const int32 *>(value)[0]);
1452 				update = true; // XXX should use BroadcastChangedParameter()
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
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 3dB (like BeOS R5)"), B_ENABLE);
1854 	group->MakeDiscreteParameter(PARAM_ETC(20), B_MEDIA_RAW_AUDIO,
1855 		B_TRANSLATE("Use non linear gain sliders (like BeOS R5)"), B_ENABLE);
1856 	group->MakeDiscreteParameter(PARAM_ETC(30), B_MEDIA_RAW_AUDIO,
1857 		B_TRANSLATE("Display balance control for stereo connections"),
1858 		B_ENABLE);
1859 
1860 	group->MakeDiscreteParameter(PARAM_ETC(40), B_MEDIA_RAW_AUDIO,
1861 		B_TRANSLATE("Allow output channel remapping"), B_ENABLE);
1862 	group->MakeDiscreteParameter(PARAM_ETC(50), B_MEDIA_RAW_AUDIO,
1863 		B_TRANSLATE("Allow input channel remapping"), B_ENABLE);
1864 
1865 	dp = group->MakeDiscreteParameter(PARAM_ETC(60), B_MEDIA_RAW_AUDIO,
1866 		B_TRANSLATE("Input gain controls represent"), B_INPUT_MUX);
1867 	dp->AddItem(0, B_TRANSLATE("Physical input channels"));
1868 	dp->AddItem(1, B_TRANSLATE("Virtual output channels"));
1869 
1870 	dp = group->MakeDiscreteParameter(PARAM_ETC(70), B_MEDIA_RAW_AUDIO,
1871 		B_TRANSLATE("Resampling algorithm"), B_INPUT_MUX);
1872 	dp->AddItem(0, B_TRANSLATE("Drop/repeat samples"));
1873 	dp->AddItem(2, B_TRANSLATE("Linear interpolation"));
1874 
1875 	// Note: The following code is outcommented on purpose
1876 	// and is about to be modified at a later point
1877 	/*
1878 	dp->AddItem(1, B_TRANSLATE("Drop/repeat samples (template based)"));
1879 	dp->AddItem(3, B_TRANSLATE("17th order filtering"));
1880 	*/
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 
1890 
1891 #if USE_MEDIA_FORMAT_WORKAROUND
1892 static void
1893 raw_audio_format_specialize(media_raw_audio_format *format,
1894 	const media_raw_audio_format *other)
1895 {
1896 	if (format->frame_rate == 0)
1897 		format->frame_rate = other->frame_rate;
1898 	if (format->channel_count == 0)
1899 		format->channel_count = other->channel_count;
1900 	if (format->format == 0)
1901 		format->format = other->format;
1902 	if (format->byte_order == 0)
1903 		format->byte_order = other->byte_order;
1904 	if (format->buffer_size == 0)
1905 		format->buffer_size = other->buffer_size;
1906 	if (format->frame_rate == 0)
1907 		format->frame_rate = other->frame_rate;
1908 }
1909 
1910 
1911 static void
1912 multi_audio_info_specialize(media_multi_audio_info *format,
1913 	const media_multi_audio_info *other)
1914 {
1915 	if (format->channel_mask == 0)
1916 		format->channel_mask = other->channel_mask;
1917 	if (format->valid_bits == 0)
1918 		format->valid_bits = other->valid_bits;
1919 	if (format->matrix_mask == 0)
1920 		format->matrix_mask = other->matrix_mask;
1921 }
1922 
1923 
1924 static void
1925 multi_audio_format_specialize(media_multi_audio_format *format,
1926 	const media_multi_audio_format *other)
1927 {
1928 	raw_audio_format_specialize(format, other);
1929 	multi_audio_info_specialize(format, other);
1930 }
1931 #endif
1932