xref: /haiku/src/add-ons/media/media-add-ons/mixer/AudioMixer.cpp (revision 220d04022750f40f8bac8f01fa551211e28d04f2)
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 %" B_PRIdBIGTIME
325 			" usec late\n", lateness);
326 		if (RunMode() == B_DROP_DATA || RunMode() == B_DECREASE_PRECISION
327 			|| RunMode() == B_INCREASE_LATENCY) {
328 			debug_printf("AudioMixer: sending notify\n");
329 
330 			// Build a media_source out of the header data
331 			media_source source = media_source::null;
332 			source.port = buffer->Header()->source_port;
333 			source.id = buffer->Header()->source;
334 
335 			NotifyLateProducer(source, lateness, TimeSource()->Now());
336 
337 			if (RunMode() == B_DROP_DATA) {
338 				debug_printf("AudioMixer: dropping buffer\n");
339 				return;
340 			}
341 		}
342 	}
343 	fLastLateness = lateness;
344 
345 	fCore->Lock();
346 	fCore->BufferReceived(buffer, lateness);
347 	fCore->Unlock();
348 }
349 
350 
351 void
352 AudioMixer::ProducerDataStatus(const media_destination& for_whom,
353 	int32 status, bigtime_t at_performance_time)
354 {
355 /*
356 	if (IsValidDest(for_whom))
357 	{
358 		media_timed_event event(at_performance_time, BTimedEventQueue::B_DATA_STATUS,
359 			(void *)(&for_whom), BTimedEventQueue::B_NO_CLEANUP, status, 0, NULL);
360 		EventQueue()->AddEvent(event);
361 
362 		// FIX_THIS
363 		// the for_whom destination is not being sent correctly - verify in HandleEvent loop
364 
365 	}
366 */
367 }
368 
369 
370 status_t
371 AudioMixer::GetLatencyFor(const media_destination &for_whom,
372 	bigtime_t *out_latency, media_node_id *out_timesource)
373 {
374 	// we have multiple inputs with different IDs, but
375 	// the port number must match our ControlPort()
376 	if (for_whom.port != ControlPort())
377 		return B_MEDIA_BAD_DESTINATION;
378 
379 	// return our event latency - this includes our internal + downstream
380 	// latency, but _not_ the scheduling latency
381 	*out_latency = EventLatency();
382 	*out_timesource = TimeSource()->ID();
383 
384 	printf("AudioMixer::GetLatencyFor %" B_PRIdBIGTIME ", timesource is %"
385 		B_PRId32 "\n", *out_latency, *out_timesource);
386 
387 	return B_OK;
388 }
389 
390 
391 status_t
392 AudioMixer::Connected(const media_source &producer,
393 	const media_destination &where, const media_format &with_format,
394 	media_input *out_input)
395 {
396 	PRINT_FORMAT("AudioMixer::Connected: ", with_format);
397 
398 	// workaround for a crashing bug in RealPlayer.  to be proper, RealPlayer's
399 	// BBufferProducer::PrepareToConnect() should have removed all wildcards.
400 	if (out_input->format.u.raw_audio.frame_rate == 0) {
401 		fprintf(stderr, "Audio Mixer Warning: "
402 			"Producer (port %" B_PRId32 ", id %" B_PRId32 ") connected with "
403 			"frame_rate=0\n", producer.port, producer.id);
404 		MixerOutput *output = fCore->Output();
405 		float frame_rate = output
406 			? output->MediaOutput().format.u.raw_audio.frame_rate : 44100.0f;
407 		out_input->format.u.raw_audio.frame_rate = frame_rate;
408 	}
409 
410 	// a BBufferProducer is connecting to our BBufferConsumer
411 
412 	// incoming connections should always have an incoming ID=0,
413 	// and the port number must match our ControlPort()
414 	if (where.id != 0 || where.port != ControlPort())
415 		return B_MEDIA_BAD_DESTINATION;
416 
417 	fCore->Lock();
418 
419 	// we assign a new id (!= 0) to the newly created input
420 	out_input->destination.id = fCore->CreateInputID();
421 
422 	// We need to make sure that the outInput's name field contains a valid
423 	// name, the name given the connection by the producer may still be an
424 	// empty string.
425 	// if the input has no name, assign one
426 	if (strlen(out_input->name) == 0) {
427 		sprintf(out_input->name, "Input %" B_PRId32,
428 			out_input->destination.id);
429 	}
430 
431 	// add a new input to the mixer engine
432 	MixerInput *input;
433 	input = fCore->AddInput(*out_input);
434 
435 	fCore->Settings()->LoadConnectionSettings(input);
436 
437 	fCore->Unlock();
438 
439 	// If we want the producer to use a specific BBufferGroup, we now need
440 	// to call BMediaRoster::SetOutputBuffersFor() here to set the producer's
441 	// buffer group.
442 	// But we have no special buffer requirements anyway...
443 
444 	UpdateParameterWeb();
445 
446 	return B_OK;
447 }
448 
449 
450 void
451 AudioMixer::Disconnected(const media_source &producer,
452 	const media_destination &where)
453 {
454 	// One of our inputs has been disconnected
455 
456 	// check if it is really belongs to us
457 	if (where.port != ControlPort()) {
458 		TRACE("AudioMixer::Disconnected wrong input port\n");
459 		return;
460 	}
461 
462 	fCore->Lock();
463 
464 	if (!fCore->RemoveInput(where.id)) {
465 		TRACE("AudioMixer::Disconnected can't remove input\n");
466 	}
467 
468 	fCore->Unlock();
469 	UpdateParameterWeb();
470 }
471 
472 
473 status_t
474 AudioMixer::FormatChanged(const media_source &producer,
475 	const media_destination &consumer, int32 change_tag,
476 	const media_format &format)
477 {
478 	// at some point in the future (indicated by change_tag and RequestCompleted()),
479 	// we will receive buffers in a different format
480 
481 	TRACE("AudioMixer::FormatChanged\n");
482 
483 	if (consumer.port != ControlPort() || consumer.id == 0)
484 		return B_MEDIA_BAD_DESTINATION;
485 
486 	if (fCore->Settings()->RefuseInputFormatChange()) {
487 		TRACE("AudioMixer::FormatChanged: input format change refused\n");
488 		return B_NOT_ALLOWED;
489 	}
490 
491 	// TODO: We should not apply the format change at this point
492 	// TODO: At the moment, this is not implemented at the moment and will just
493 	// crash the media_server.
494 	return B_NOT_SUPPORTED;
495 
496 	// tell core about format change
497 	fCore->Lock();
498 	fCore->InputFormatChanged(consumer.id, format.u.raw_audio);
499 	fCore->Unlock();
500 
501 	return B_OK;
502 }
503 
504 
505 //	#pragma mark - BBufferProducer methods
506 
507 
508 status_t
509 AudioMixer::FormatSuggestionRequested(media_type type, int32 quality,
510 	media_format *format)
511 {
512 	TRACE("AudioMixer::FormatSuggestionRequested\n");
513 
514 	// BBufferProducer function, a downstream consumer
515 	// is asking for our output format
516 
517 	if (type != B_MEDIA_RAW_AUDIO && type != B_MEDIA_UNKNOWN_TYPE)
518 		return B_MEDIA_BAD_FORMAT;
519 
520 	// we can produce any (wildcard) raw audio format
521 	memset(format, 0, sizeof(*format));
522 	format->type = B_MEDIA_RAW_AUDIO;
523 	return B_OK;
524 }
525 
526 
527 status_t
528 AudioMixer::FormatProposal(const media_source &output, media_format *ioFormat)
529 {
530 	// BBufferProducer function, we implement this function to verify that the
531 	// proposed media_format is suitable for the specified output. If any fields
532 	// in the format are wildcards, and we have a specific requirement, adjust
533 	// those fields to match our requirements before returning.
534 
535 	TRACE("AudioMixer::FormatProposal\n");
536 
537 	// we only have one output (id=0, port=ControlPort())
538 	if (output.id != 0 || output.port != ControlPort())
539 		return B_MEDIA_BAD_SOURCE;
540 
541 	// specialize to raw audio format if necessary
542 	if (ioFormat->type == B_MEDIA_UNKNOWN_TYPE)
543 		ioFormat->type = B_MEDIA_RAW_AUDIO;
544 
545 	// we require a raw audio format
546 	if (ioFormat->type != B_MEDIA_RAW_AUDIO)
547 		return B_MEDIA_BAD_FORMAT;
548 
549 	return B_OK;
550 }
551 
552 /*!	If the format isn't good, put a good format into *io_format and return error
553 	If format has wildcard, specialize to what you can do (and change).
554 	If you can change the format, return OK.
555 	The request comes from your destination sychronously, so you cannot ask it
556 	whether it likes it -- you should assume it will since it asked.
557 */
558 status_t
559 AudioMixer::FormatChangeRequested(const media_source &source,
560 	const media_destination &destination, media_format *io_format,
561 	int32 *_deprecated_)
562 {
563 	// the downstream consumer node (soundcard) requested that we produce
564 	// another format, we need to check if the format is acceptable and
565 	// remove any wildcards before returning OK.
566 
567 	TRACE("AudioMixer::FormatChangeRequested\n");
568 
569 	if (fCore->Settings()->RefuseOutputFormatChange()) {
570 		TRACE("AudioMixer::FormatChangeRequested: output format change refused\n");
571 		return B_ERROR;
572 	}
573 
574 	fCore->Lock();
575 
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 #if USE_MEDIA_FORMAT_WORKAROUND
605 	multi_audio_format_specialize(&io_format->u.raw_audio,
606 		&fDefaultFormat.u.raw_audio);
607 #else
608 	io_format->SpecializeTo(&fDefaultFormat);
609 #endif
610 
611 	media_node_id id;
612 	FindLatencyFor(destination, &fDownstreamLatency, &id);
613 	TRACE("AudioMixer: Downstream Latency is %" B_PRIdBIGTIME " usecs\n",
614 		fDownstreamLatency);
615 
616 	// SetDuration of one buffer
617 	SetBufferDuration(buffer_duration(io_format->u.raw_audio));
618 	TRACE("AudioMixer: buffer duration is %" B_PRIdBIGTIME " usecs\n",
619 		BufferDuration());
620 
621 	// Our internal latency is at least the length of a full output buffer
622 	fInternalLatency = BufferDuration()
623 		+ max((bigtime_t)4500, bigtime_t(0.5 * BufferDuration()));
624 	TRACE("AudioMixer: Internal latency is %" B_PRIdBIGTIME " usecs\n",
625 		fInternalLatency);
626 
627 	SetEventLatency(fDownstreamLatency + fInternalLatency);
628 
629 	// we need to inform all connected *inputs* about *our* change in latency
630 	PublishEventLatencyChange();
631 
632 	// TODO: we might need to create more buffers, to span a larger downstream
633 	// latency
634 
635 	// apply latency change
636 	fCore->SetTimingInfo(TimeSource(), fDownstreamLatency);
637 
638 	// apply format change
639 	fCore->OutputFormatChanged(io_format->u.raw_audio);
640 
641 	delete fBufferGroup;
642 	fBufferGroup = CreateBufferGroup();
643 	fCore->SetOutputBufferGroup(fBufferGroup);
644 
645 	fCore->Unlock();
646 	return B_OK;
647 
648 err:
649 	fCore->Unlock();
650 	return B_ERROR;
651 }
652 
653 
654 status_t
655 AudioMixer::GetNextOutput(int32 *cookie, media_output *out_output)
656 {
657 	TRACE("AudioMixer::GetNextOutput\n");
658 
659 	if (*cookie != 0)
660 		return B_BAD_INDEX;
661 
662 	fCore->Lock();
663 	MixerOutput *output = fCore->Output();
664 	if (output) {
665 		*out_output = output->MediaOutput();
666 	} else {
667 		out_output->node = Node();
668 		out_output->source.port = ControlPort();
669 		out_output->source.id = 0;
670 		out_output->destination = media_destination::null;
671 		memset(&out_output->format, 0, sizeof(out_output->format));
672 		out_output->format.type = B_MEDIA_RAW_AUDIO;
673 		strcpy(out_output->name, "Mixer Output");
674 	}
675 	fCore->Unlock();
676 
677 	*cookie += 1;
678 	return B_OK;
679 }
680 
681 
682 status_t
683 AudioMixer::DisposeOutputCookie(int32 cookie)
684 {
685 	// nothing to do
686 	return B_OK;
687 }
688 
689 
690 status_t
691 AudioMixer::SetBufferGroup(const media_source &for_source,
692 	BBufferGroup *newGroup)
693 {
694 	TRACE("AudioMixer::SetBufferGroup\n");
695 	// the downstream consumer (soundcard) node asks us to use another
696 	// BBufferGroup (might be NULL). We only have one output (id 0)
697 	if (for_source.port != ControlPort() || for_source.id != 0)
698 		return B_MEDIA_BAD_SOURCE;
699 
700 	if (newGroup == fBufferGroup) {
701 		// we're already using this buffergroup
702 		return B_OK;
703 	}
704 
705 	fCore->Lock();
706 	if (!newGroup)
707 		newGroup = CreateBufferGroup();
708 	fCore->SetOutputBufferGroup(newGroup);
709 	delete fBufferGroup;
710 	fBufferGroup = newGroup;
711 	fCore->Unlock();
712 
713 	return B_OK;
714 }
715 
716 
717 status_t
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 %Ld\n", *out_latency);
724 
725 	return B_OK;
726 }
727 
728 
729 void
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 %Ld to %Ld\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 %Ld 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
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 #if USE_MEDIA_FORMAT_WORKAROUND
856 	multi_audio_format_specialize(&format->u.raw_audio,
857 		&fDefaultFormat.u.raw_audio);
858 #else
859 	format->SpecializeTo(&fDefaultFormat);
860 #endif
861 
862 	PRINT_FORMAT("AudioMixer::PrepareToConnect: final format", *format);
863 
864 	/* add output to core */
865 	media_output output;
866 	output.node = Node();
867 	output.source = *out_source;
868 	output.destination = where;
869 	output.format = *format;
870 	strcpy(output.name, out_name);
871 
872 	fCore->EnableOutput(false);
873 	fCore->AddOutput(output);
874 
875 	fCore->Unlock();
876 	return B_OK;
877 }
878 
879 
880 void
881 AudioMixer::Connect(status_t error, const media_source &source,
882 	const media_destination &dest, const media_format &format, char *io_name)
883 {
884 	TRACE("AudioMixer::Connect\n");
885 
886 	fCore->Lock();
887 	// are we still connected?
888 	if (fCore->Output() == 0) {
889 		fCore->Unlock();
890 		ERROR("AudioMixer::Connect: no longer connected\n");
891 		return;
892 	}
893 	fCore->Unlock();
894 
895 	if (error != B_OK) {
896 		// if an error occured, remove output from core
897 		ERROR("AudioMixer::Connect failed with error 0x%08lX, removing "
898 			"connection\n", error);
899 		fCore->Lock();
900 		fCore->RemoveOutput();
901 		fCore->Unlock();
902 		return;
903 	}
904 
905 	// Switch our prefered format to have the same
906 	// frame_rate and channel count as the output.
907 	fDefaultFormat.u.raw_audio.frame_rate = format.u.raw_audio.frame_rate;
908 	fDefaultFormat.u.raw_audio.channel_count = format.u.raw_audio.channel_count;
909 
910 	// if the connection has no name, we set it now
911 	if (strlen(io_name) == 0)
912 		strcpy(io_name, "Mixer Output");
913 
914 	// Now that we're connected, we can determine our downstream latency.
915 	media_node_id id;
916 	FindLatencyFor(dest, &fDownstreamLatency, &id);
917 	TRACE("AudioMixer: Downstream Latency is %Ld usecs\n", fDownstreamLatency);
918 
919 	// SetDuration of one buffer
920 	SetBufferDuration(buffer_duration(format.u.raw_audio));
921 	TRACE("AudioMixer: buffer duration is %Ld usecs\n", BufferDuration());
922 
923 	// Our internal latency is at least the length of a full output buffer
924 	// plus mixing time, plus jitter
925 	fInternalLatency = BufferDuration()
926 		+ max(kMinMixingTime, bigtime_t(0.5 * BufferDuration())) + kMaxJitter;
927 	TRACE("AudioMixer: Internal latency is %Ld usecs\n", fInternalLatency);
928 
929 	SetEventLatency(fDownstreamLatency + fInternalLatency);
930 
931 	// we need to inform all connected *inputs* about *our* change in latency
932 	PublishEventLatencyChange();
933 
934 	// Set up the buffer group for our connection, as long as nobody handed
935 	// us a buffer group (via SetBufferGroup()) prior to this.  That can
936 	// happen, for example, if the consumer calls SetOutputBuffersFor() on
937 	// us from within its Connected() method.
938 	if (!fBufferGroup)
939 		fBufferGroup = CreateBufferGroup();
940 
941 	fCore->Lock();
942 
943 	ASSERT(fCore->Output() != 0);
944 
945 	// our source should still be valid, too
946 	ASSERT(fCore->Output()->MediaOutput().source.id == 0);
947 	ASSERT(fCore->Output()->MediaOutput().source.port == ControlPort());
948 
949 	// BBufferConsumer::Connected() may return a different input for the
950 	// newly created connection. The destination can have changed since
951 	// AudioMixer::PrepareToConnect() and we need to update it.
952 	fCore->Output()->MediaOutput().destination = dest;
953 
954 	fCore->EnableOutput(true);
955 	fCore->SetTimingInfo(TimeSource(), fDownstreamLatency);
956 	fCore->SetOutputBufferGroup(fBufferGroup);
957 
958 	fCore->Settings()->LoadConnectionSettings(fCore->Output());
959 
960 	fCore->Unlock();
961 	UpdateParameterWeb();
962 }
963 
964 
965 void
966 AudioMixer::Disconnect(const media_source& what, const media_destination& where)
967 {
968 	TRACE("AudioMixer::Disconnect\n");
969 	fCore->Lock();
970 
971 	// Make sure that our connection is the one being disconnected
972 	MixerOutput* output = fCore->Output();
973 	if (!output
974 		|| output->MediaOutput().node != Node()
975 		|| output->MediaOutput().source != what
976 		|| output->MediaOutput().destination != where) {
977 		ERROR("AudioMixer::Disconnect can't disconnect (wrong connection)\n");
978 		fCore->Unlock();
979 		return;
980 	}
981 
982 	// Switch our prefered format back to default
983 	// frame rate and channel count.
984 	fDefaultFormat.u.raw_audio.frame_rate = 96000;
985 	fDefaultFormat.u.raw_audio.channel_count = 2;
986 
987 	// force a stop
988 	fCore->Stop();
989 
990 	fCore->RemoveOutput();
991 
992 	// destroy buffer group
993 	delete fBufferGroup;
994 	fBufferGroup = 0;
995 	fCore->SetOutputBufferGroup(0);
996 
997 	fCore->Unlock();
998 	UpdateParameterWeb();
999 }
1000 
1001 
1002 void
1003 AudioMixer::LateNoticeReceived(const media_source& what, bigtime_t howMuch,
1004 	bigtime_t performanceTime)
1005 {
1006 	// We've produced some late buffers... Increase Latency
1007 	// is the only runmode in which we can do anything about this
1008 	// TODO: quality could be decreased, too
1009 
1010 	ERROR("AudioMixer::LateNoticeReceived, %Ld too late at %Ld\n", howMuch,
1011 		performanceTime);
1012 
1013 	if (what == fCore->Output()->MediaOutput().source
1014 		&& RunMode() == B_INCREASE_LATENCY) {
1015 		// We need to ignore subsequent notices whose arrival time here
1016 		// lies within the last lateness, because queued-up buffers will all be 'late'
1017 		if (performanceTime < fLastLateNotification)
1018 			return;
1019 
1020 		fInternalLatency += howMuch;
1021 
1022 		// At some point a too large latency can get annoying
1023 		// (actually more than annoying, as there won't be enough buffers long before this!)
1024 		if (fInternalLatency > kMaxLatency)
1025 			fInternalLatency = kMaxLatency;
1026 
1027 		fLastLateNotification = TimeSource()->Now() + howMuch;
1028 
1029 		debug_printf("AudioMixer: increasing internal latency to %"
1030 			B_PRIdBIGTIME " usec\n", fInternalLatency);
1031 		SetEventLatency(fDownstreamLatency + fInternalLatency);
1032 
1033 		PublishEventLatencyChange();
1034 	}
1035 }
1036 
1037 
1038 void
1039 AudioMixer::EnableOutput(const media_source& what, bool enabled,
1040 	int32 */*deprecated*/)
1041 {
1042 	// we only have one output
1043 	if (what.id != 0 || what.port != ControlPort())
1044 		return;
1045 
1046 	fCore->Lock();
1047 	fCore->EnableOutput(enabled);
1048 	fCore->Unlock();
1049 }
1050 
1051 
1052 //	#pragma mark - BMediaEventLooper methods
1053 
1054 
1055 void
1056 AudioMixer::NodeRegistered()
1057 {
1058 	Run();
1059 	SetPriority(120);
1060 	UpdateParameterWeb();
1061 }
1062 
1063 
1064 void
1065 AudioMixer::SetTimeSource(BTimeSource* timeSource)
1066 {
1067 	TRACE("AudioMixer::SetTimeSource: timesource is now %ld\n",
1068 		timeSource->ID());
1069 	fCore->Lock();
1070 	fCore->SetTimingInfo(timeSource, fDownstreamLatency);
1071 	fCore->Unlock();
1072 }
1073 
1074 
1075 void
1076 AudioMixer::HandleEvent(const media_timed_event *event, bigtime_t lateness,
1077 	bool realTimeEvent)
1078 {
1079 	switch (event->type) {
1080 		case BTimedEventQueue::B_HANDLE_BUFFER:
1081 		{
1082 			HandleInputBuffer((BBuffer *)event->pointer, lateness);
1083 			((BBuffer *)event->pointer)->Recycle();
1084 			break;
1085 		}
1086 
1087 		case BTimedEventQueue::B_START:
1088 		{
1089 			TRACE("AudioMixer::HandleEvent: B_START\n");
1090 			if (RunState() != B_STARTED) {
1091 				fCore->Lock();
1092 				fCore->Start();
1093 				fCore->Unlock();
1094 			}
1095 			break;
1096 		}
1097 
1098 		case BTimedEventQueue::B_STOP:
1099 		{
1100 			TRACE("AudioMixer::HandleEvent: B_STOP\n");
1101 			// stopped - don't process any more buffers, flush all buffers
1102 			// from event queue
1103 			EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true,
1104 				BTimedEventQueue::B_HANDLE_BUFFER);
1105 			fCore->Lock();
1106 			fCore->Stop();
1107 			fCore->Unlock();
1108 			break;
1109 		}
1110 
1111 		case BTimedEventQueue::B_DATA_STATUS:
1112 		{
1113 			ERROR("DataStatus message\n");
1114 			break;
1115 		}
1116 
1117 		default:
1118 			break;
1119 	}
1120 }
1121 
1122 
1123 //	#pragma mark - AudioMixer methods
1124 
1125 
1126 void
1127 AudioMixer::PublishEventLatencyChange()
1128 {
1129 	// our event (processing + downstream) latency has changed,
1130 	// and we need tell all inputs about this
1131 
1132 	TRACE("AudioMixer::PublishEventLatencyChange\n");
1133 
1134 	fCore->Lock();
1135 
1136 	MixerInput *input;
1137 	for (int i = 0; (input = fCore->Input(i)) != 0; i++) {
1138 		TRACE("AudioMixer::PublishEventLatencyChange: SendLatencyChange, "
1139 			"connection %ld/%ld to %ld/%ld event latency is now %Ld\n",
1140 			input->MediaInput().source.port, input->MediaInput().source.id,
1141 			input->MediaInput().destination.port,
1142 			input->MediaInput().destination.id, EventLatency());
1143 		SendLatencyChange(input->MediaInput().source,
1144 			input->MediaInput().destination, EventLatency());
1145 	}
1146 
1147 	fCore->Unlock();
1148 }
1149 
1150 
1151 BBufferGroup*
1152 AudioMixer::CreateBufferGroup()
1153 {
1154 	// allocate enough buffers to span our downstream latency
1155 	// (plus one for rounding up), plus one extra
1156 	int32 count = int32(fDownstreamLatency / BufferDuration()) + 2;
1157 
1158 	TRACE("AudioMixer::CreateBufferGroup: fDownstreamLatency %Ld, "
1159 		"BufferDuration %Ld, buffer count = %ld\n", fDownstreamLatency,
1160 		BufferDuration(), count);
1161 
1162 	if (count < 3)
1163 		count = 3;
1164 
1165 	fCore->Lock();
1166 	uint32 size = fCore->Output()->MediaOutput().format.u.raw_audio.buffer_size;
1167 	fCore->Unlock();
1168 
1169 	TRACE("AudioMixer: allocating %ld buffers of %ld bytes each\n",
1170 		count, size);
1171 	return new BBufferGroup(size, count);
1172 }
1173 
1174 
1175 status_t
1176 AudioMixer::SendBuffer(BBuffer* buffer, MixerOutput* output)
1177 {
1178 	return BBufferProducer::SendBuffer(buffer, output->MediaOutput().source,
1179 		output->MediaOutput().destination);
1180 }
1181 
1182 
1183 float
1184 AudioMixer::dB_to_Gain(float db)
1185 {
1186 	TRACE("dB_to_Gain: dB in: %01.2f ", db);
1187 	if (fCore->Settings()->NonLinearGainSlider()) {
1188 		if (db > 0) {
1189 			db = db * (pow(abs(DB_MAX), (1.0 / DB_EXPONENT_POSITIVE))
1190 				/ abs(DB_MAX));
1191 			db = pow(db, DB_EXPONENT_POSITIVE);
1192 		} else {
1193 			db = -db;
1194 			db = db * (pow(abs(DB_MIN), (1.0 / DB_EXPONENT_NEGATIVE))
1195 				/ abs(DB_MIN));
1196 			db = pow(db, DB_EXPONENT_NEGATIVE);
1197 			db = -db;
1198 		}
1199 	}
1200 	TRACE("dB out: %01.2f\n", db);
1201 	return pow(10.0, db / 20.0);
1202 }
1203 
1204 
1205 float
1206 AudioMixer::Gain_to_dB(float gain)
1207 {
1208 	float db;
1209 	db = 20.0 * log10(gain);
1210 	if (fCore->Settings()->NonLinearGainSlider()) {
1211 		if (db > 0) {
1212 			db = pow(db, (1.0 / DB_EXPONENT_POSITIVE));
1213 			db = db * (abs(DB_MAX) / pow(abs(DB_MAX),
1214 				(1.0 / DB_EXPONENT_POSITIVE)));
1215 		} else {
1216 			db = -db;
1217 			db = pow(db, (1.0 / DB_EXPONENT_NEGATIVE));
1218 			db = db * (abs(DB_MIN) / pow(abs(DB_MIN),
1219 				(1.0 / DB_EXPONENT_NEGATIVE)));
1220 			db = -db;
1221 		}
1222 	}
1223 	return db;
1224 }
1225 
1226 
1227 // #pragma mark - BControllable methods
1228 
1229 
1230 status_t
1231 AudioMixer::GetParameterValue(int32 id, bigtime_t *last_change, void *value,
1232 	size_t *ioSize)
1233 {
1234 	TRACE("GetParameterValue: id 0x%08lx, ioSize %ld\n", id, *ioSize);
1235 	int param = PARAM(id);
1236 	fCore->Lock();
1237 	if (PARAM_IS_ETC(id)) {
1238 		switch (ETC(id)) {
1239 			case 10:	// Attenuate mixer output by 3dB
1240 				*ioSize = sizeof(int32);
1241 				static_cast<int32 *>(value)[0] = fCore->Settings()->AttenuateOutput();
1242 				break;
1243 			case 20:	// Use non linear gain sliders
1244 				*ioSize = sizeof(int32);
1245 				static_cast<int32 *>(value)[0] = fCore->Settings()->NonLinearGainSlider();
1246 				break;
1247 			case 30:	// Display balance control for stereo connections
1248 				*ioSize = sizeof(int32);
1249 				static_cast<int32 *>(value)[0] = fCore->Settings()->UseBalanceControl();
1250 				break;
1251 			case 40:	// Allow output channel remapping
1252 				*ioSize = sizeof(int32);
1253 				static_cast<int32 *>(value)[0] = fCore->Settings()->AllowOutputChannelRemapping();
1254 				break;
1255 			case 50:	// Allow input channel remapping
1256 				*ioSize = sizeof(int32);
1257 				static_cast<int32 *>(value)[0] = fCore->Settings()->AllowInputChannelRemapping();
1258 				break;
1259 			case 60:	// Input gain controls
1260 				*ioSize = sizeof(int32);
1261 				static_cast<int32 *>(value)[0] = fCore->Settings()->InputGainControls();
1262 				break;
1263 			case 70:	// Resampling algorithm
1264 				*ioSize = sizeof(int32);
1265 				static_cast<int32 *>(value)[0] = fCore->Settings()->ResamplingAlgorithm();
1266 				break;
1267 			case 80:	// Refuse output format changes
1268 				*ioSize = sizeof(int32);
1269 				static_cast<int32 *>(value)[0] = fCore->Settings()->RefuseOutputFormatChange();
1270 				break;
1271 			case 90:	// Refuse input format changes
1272 				*ioSize = sizeof(int32);
1273 				static_cast<int32 *>(value)[0] = fCore->Settings()->RefuseInputFormatChange();
1274 				break;
1275 			default:
1276 				ERROR("unhandled ETC 0x%08lx\n", id);
1277 				break;
1278 		}
1279 	} else if (param == 0) {
1280 		MixerOutput *output = fCore->Output();
1281 		if (!output || (!PARAM_IS_MUTE(id) && !PARAM_IS_GAIN(id) && !PARAM_IS_SRC_ENABLE(id) && !PARAM_IS_SRC_GAIN(id) && !PARAM_IS_BALANCE(id)))
1282 			goto err;
1283 		if (PARAM_IS_MUTE(id)) {
1284 			// output mute control
1285 			if (*ioSize < sizeof(int32))
1286 				goto err;
1287 			*ioSize = sizeof(int32);
1288 			static_cast<int32 *>(value)[0] = output->IsMuted();
1289 		}
1290 		if (PARAM_IS_GAIN(id)) {
1291 			// output gain control
1292 			if (fCore->Settings()->UseBalanceControl() && output->GetOutputChannelCount() == 2 && 1 /*channel mask is stereo */) {
1293 				// single channel control + balance
1294 				if (*ioSize < sizeof(float))
1295 					goto err;
1296 				*ioSize = sizeof(float);
1297 				static_cast<float *>(value)[0] = GAIN_TO_DB((output->GetOutputChannelGain(0) + output->GetOutputChannelGain(1)) / 2);
1298 			} else {
1299 				// multi channel control
1300 				if (*ioSize == sizeof(float)) {
1301 					// get combined gain for all controls
1302 					float gain = 0;
1303 					for (int channel = 0;
1304 							channel < output->GetOutputChannelCount();
1305 							channel++) {
1306 						gain += GAIN_TO_DB(
1307 							output->GetOutputChannelGain(channel));
1308 					}
1309 					static_cast<float *>(value)[0] = gain
1310 						/ output->GetOutputChannelCount();
1311 				} else {
1312 					if (*ioSize < output->GetOutputChannelCount()
1313 							* sizeof(float))
1314 						goto err;
1315 
1316 					*ioSize = output->GetOutputChannelCount() * sizeof(float);
1317 
1318 					for (int channel = 0;
1319 							channel < output->GetOutputChannelCount();
1320 							channel++) {
1321 						static_cast<float *>(value)[channel]
1322 							= GAIN_TO_DB(output->GetOutputChannelGain(channel));
1323 					}
1324 				}
1325 			}
1326 		}
1327 		if (PARAM_IS_BALANCE(id)) {
1328 			float l = output->GetOutputChannelGain(0);
1329 			float r = output->GetOutputChannelGain(1);
1330 			float v = r / (l+r);
1331 			TRACE("balance l %1.3f, r %1.3f, v %1.3f\n",l,r,v);
1332 			if (*ioSize < sizeof(float))
1333 				goto err;
1334 			*ioSize = sizeof(float);
1335 			static_cast<float *>(value)[0] = v * 100;
1336 		}
1337 		if (PARAM_IS_SRC_ENABLE(id)) {
1338 			if (*ioSize < sizeof(int32))
1339 				goto err;
1340 			*ioSize = sizeof(int32);
1341 			static_cast<int32 *>(value)[0] = output->HasOutputChannelSource(PARAM_CHAN(id), PARAM_SRC(id));
1342 		}
1343 		if (PARAM_IS_SRC_GAIN(id)) {
1344 			if (*ioSize < sizeof(float))
1345 				goto err;
1346 			*ioSize = sizeof(float);
1347 			static_cast<float *>(value)[0] = GAIN_TO_PERCENT(output->GetOutputChannelSourceGain(PARAM_CHAN(id), PARAM_SRC(id)));
1348 		}
1349 	} else {
1350 		MixerInput *input;
1351 		for (int i = 0; (input = fCore->Input(i)); i++)
1352 			if (input->ID() == param)
1353 				break;
1354 		if (!input || (!PARAM_IS_MUTE(id) && !PARAM_IS_GAIN(id) && !PARAM_IS_DST_ENABLE(id) && !PARAM_IS_BALANCE(id)))
1355 			goto err;
1356 		if (PARAM_IS_MUTE(id)) {
1357 			// input mute control
1358 			if (*ioSize < sizeof(int32))
1359 				goto err;
1360 			*ioSize = sizeof(int32);
1361 			static_cast<int32 *>(value)[0] = !input->IsEnabled();
1362 		}
1363 		if (PARAM_IS_GAIN(id)) {
1364 			// input gain control
1365 			if (fCore->Settings()->InputGainControls() == 0) {
1366 				// Physical input channels
1367 				if (fCore->Settings()->UseBalanceControl() && input->GetInputChannelCount() == 2 && 1 /*channel mask is stereo */) {
1368 					// single channel control + balance
1369 					if (*ioSize < sizeof(float))
1370 						goto err;
1371 					*ioSize = sizeof(float);
1372 					static_cast<float *>(value)[0] = GAIN_TO_DB((input->GetInputChannelGain(0) + input->GetInputChannelGain(1)) / 2);
1373 				} else {
1374 					// multi channel control
1375 					if (*ioSize < input->GetInputChannelCount() * sizeof(float))
1376 						goto err;
1377 					*ioSize = input->GetInputChannelCount() * sizeof(float);
1378 					for (int chan = 0; chan < input->GetInputChannelCount(); chan++)
1379 						static_cast<float *>(value)[chan] = GAIN_TO_DB(input->GetInputChannelGain(chan));
1380 				}
1381 			} else {
1382 				// Virtual output channels
1383 				if (fCore->Settings()->UseBalanceControl() && input->GetMixerChannelCount() == 2 && 1 /*channel mask is stereo */) {
1384 					// single channel control + balance
1385 					if (*ioSize < sizeof(float))
1386 						goto err;
1387 					*ioSize = sizeof(float);
1388 					static_cast<float *>(value)[0] = GAIN_TO_DB((input->GetMixerChannelGain(0) + input->GetMixerChannelGain(1)) / 2);
1389 				} else {
1390 					// multi channel control
1391 					if (*ioSize < input->GetMixerChannelCount() * sizeof(float))
1392 						goto err;
1393 					*ioSize = input->GetMixerChannelCount() * sizeof(float);
1394 					for (int chan = 0; chan < input->GetMixerChannelCount(); chan++)
1395 						static_cast<float *>(value)[chan] = GAIN_TO_DB(input->GetMixerChannelGain(chan));
1396 				}
1397 			}
1398 		}
1399 		if (PARAM_IS_BALANCE(id)) {
1400 			if (fCore->Settings()->InputGainControls() == 0) {
1401 				// Physical input channels
1402 				float l = input->GetInputChannelGain(0);
1403 				float r = input->GetInputChannelGain(1);
1404 				float v = r / (l+r);
1405 				TRACE("balance l %1.3f, r %1.3f, v %1.3f\n",l,r,v);
1406 				if (*ioSize < sizeof(float))
1407 					goto err;
1408 				*ioSize = sizeof(float);
1409 				static_cast<float *>(value)[0] = v * 100;
1410 			} else {
1411 				// Virtual output channels
1412 				float l = input->GetMixerChannelGain(0);
1413 				float r = input->GetMixerChannelGain(1);
1414 				float v = r / (l+r);
1415 				TRACE("balance l %1.3f, r %1.3f, v %1.3f\n",l,r,v);
1416 				if (*ioSize < sizeof(float))
1417 					goto err;
1418 				*ioSize = sizeof(float);
1419 				static_cast<float *>(value)[0] = v * 100;
1420 			}
1421 		}
1422 		if (PARAM_IS_DST_ENABLE(id)) {
1423 			if (*ioSize < sizeof(int32))
1424 				goto err;
1425 			*ioSize = sizeof(int32);
1426 			static_cast<int32 *>(value)[0] = input->HasInputChannelDestination(PARAM_CHAN(id), PARAM_DST(id));
1427 		}
1428 	}
1429 	*last_change = TimeSource()->Now(); // XXX we could do better
1430 	fCore->Unlock();
1431 	return B_OK;
1432 err:
1433 	fCore->Unlock();
1434 	return B_ERROR;
1435 }
1436 
1437 
1438 void
1439 AudioMixer::SetParameterValue(int32 id, bigtime_t when, const void *value,
1440 	size_t size)
1441 {
1442 	TRACE("SetParameterValue: id 0x%08lx, size %ld\n", id, size);
1443 	bool update = false;
1444 	int param = PARAM(id);
1445 	fCore->Lock();
1446 	if (PARAM_IS_ETC(id)) {
1447 		switch (ETC(id)) {
1448 			case 10:	// Attenuate mixer output by 3dB
1449 				if (size != sizeof(int32))
1450 					goto err;
1451 				fCore->Settings()->SetAttenuateOutput(static_cast<const int32 *>(value)[0]);
1452 				// this value is special (see MixerCore.h) and we need to notify the core
1453 				fCore->SetOutputAttenuation((static_cast<const int32 *>(value)[0]) ? 0.708 : 1.0);
1454 				break;
1455 			case 20:	// Use non linear gain sliders
1456 				if (size != sizeof(int32))
1457 					goto err;
1458 				fCore->Settings()->SetNonLinearGainSlider(static_cast<const int32 *>(value)[0]);
1459 				update = true; // XXX should use BroadcastChangedParameter()
1460 				break;
1461 			case 30:	// Display balance control for stereo connections
1462 				if (size != sizeof(int32))
1463 					goto err;
1464 				fCore->Settings()->SetUseBalanceControl(static_cast<const int32 *>(value)[0]);
1465 				update = true;
1466 				break;
1467 			case 40:	// Allow output channel remapping
1468 				if (size != sizeof(int32))
1469 					goto err;
1470 				fCore->Settings()->SetAllowOutputChannelRemapping(static_cast<const int32 *>(value)[0]);
1471 				update = true;
1472 				break;
1473 			case 50:	// Allow input channel remapping
1474 				if (size != sizeof(int32))
1475 					goto err;
1476 				fCore->Settings()->SetAllowInputChannelRemapping(static_cast<const int32 *>(value)[0]);
1477 				update = true;
1478 				break;
1479 			case 60:	// Input gain controls represent
1480 						// (0, "Physical input channels")
1481 						// (1, "Virtual output channels")
1482 				if (size != sizeof(int32))
1483 					goto err;
1484 				fCore->Settings()->SetInputGainControls(static_cast<const int32 *>(value)[0]);
1485 				update = true; // XXX should use BroadcastChangedParameter()
1486 				break;
1487 			case 70:	// Resampling algorithm
1488 				if (size != sizeof(int32))
1489 					goto err;
1490 				fCore->Settings()->SetResamplingAlgorithm(static_cast<const int32 *>(value)[0]);
1491 				fCore->UpdateResamplingAlgorithm();
1492 				break;
1493 			case 80:	// Refuse output format changes
1494 				if (size != sizeof(int32))
1495 					goto err;
1496 				fCore->Settings()->SetRefuseOutputFormatChange(static_cast<const int32 *>(value)[0]);
1497 				break;
1498 			case 90:	// Refuse input format changes
1499 				if (size != sizeof(int32))
1500 					goto err;
1501 				fCore->Settings()->SetRefuseInputFormatChange(static_cast<const int32 *>(value)[0]);
1502 				break;
1503 			default:
1504 				ERROR("unhandled ETC 0x%08lx\n", id);
1505 				break;
1506 		}
1507 	} else if (param == 0) {
1508 		MixerOutput *output = fCore->Output();
1509 		if (!output || (!PARAM_IS_MUTE(id) && !PARAM_IS_GAIN(id) && !PARAM_IS_SRC_ENABLE(id) && !PARAM_IS_SRC_GAIN(id) && !PARAM_IS_BALANCE(id)))
1510 			goto err;
1511 		if (PARAM_IS_MUTE(id)) {
1512 			// output mute control
1513 			if (size != sizeof(int32))
1514 				goto err;
1515 			output->SetMuted(static_cast<const int32 *>(value)[0]);
1516 		}
1517 		if (PARAM_IS_GAIN(id)) {
1518 			// output gain control
1519 			if (fCore->Settings()->UseBalanceControl()
1520 				&& output->GetOutputChannelCount() == 2 && 1 /*channel mask is stereo */) {
1521 				// single channel control + balance
1522 				float l = output->GetOutputChannelGain(0);
1523 				float r = output->GetOutputChannelGain(1);
1524 				float m = (l + r) / 2;	// master volume
1525 				float v = DB_TO_GAIN(static_cast<const float *>(value)[0]);
1526 				float f = v / m;		// factor for both channels
1527 				TRACE("gain set l %1.3f, r %1.3f, m %1.3f, v %1.3f, f %1.3f\n",l,r,m,v,f);
1528 				output->SetOutputChannelGain(0, output->GetOutputChannelGain(0) * f);
1529 				output->SetOutputChannelGain(1, output->GetOutputChannelGain(1) * f);
1530 			} else {
1531 				// multi channel control
1532 				if (size == sizeof(float)) {
1533 					// set same volume for all channels
1534 					float gain = static_cast<const float *>(value)[0];
1535 					for (int channel = 0;
1536 							channel < output->GetOutputChannelCount();
1537 							channel++) {
1538 						output->SetOutputChannelGain(channel,
1539 							DB_TO_GAIN(gain));
1540 					}
1541 				} else {
1542 					if (size < output->GetOutputChannelCount() * sizeof(float))
1543 						goto err;
1544 					for (int channel = 0;
1545 							channel < output->GetOutputChannelCount();
1546 							channel++) {
1547 						output->SetOutputChannelGain(channel,
1548 							DB_TO_GAIN(static_cast<const float *>(
1549 								value)[channel]));
1550 					}
1551 				}
1552 			}
1553 		}
1554 		if (PARAM_IS_BALANCE(id)) {
1555 			float l = output->GetOutputChannelGain(0);
1556 			float r = output->GetOutputChannelGain(1);
1557 			float m = (l + r) / 2;	// master volume
1558 			float v = static_cast<const float *>(value)[0] / 100; // current balance value
1559 			float fl = 2 * (1 - v);	// left channel factor of master volume
1560 			float fr = 2 * v;		// right channel factor of master volume
1561 			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);
1562 			output->SetOutputChannelGain(0, m * fl);
1563 			output->SetOutputChannelGain(1, m * fr);
1564 		}
1565 		if (PARAM_IS_SRC_ENABLE(id)) {
1566 			if (size != sizeof(int32))
1567 				goto err;
1568 			if (static_cast<const int32 *>(value)[0]) {
1569 				output->AddOutputChannelSource(PARAM_CHAN(id), PARAM_SRC(id));
1570 			} else {
1571 				output->RemoveOutputChannelSource(PARAM_CHAN(id), PARAM_SRC(id));
1572 			}
1573 		}
1574 		if (PARAM_IS_SRC_GAIN(id)) {
1575 			if (size != sizeof(float))
1576 				goto err;
1577 			output->SetOutputChannelSourceGain(PARAM_CHAN(id), PARAM_SRC(id), PERCENT_TO_GAIN(static_cast<const float *>(value)[0]));
1578 		}
1579 		fCore->Settings()->SaveConnectionSettings(output);
1580 	} else {
1581 		MixerInput *input;
1582 		for (int i = 0; (input = fCore->Input(i)); i++)
1583 			if (input->ID() == param)
1584 				break;
1585 		if (!input || (!PARAM_IS_MUTE(id) && !PARAM_IS_GAIN(id) && !PARAM_IS_DST_ENABLE(id) && !PARAM_IS_BALANCE(id)))
1586 			goto err;
1587 		if (PARAM_IS_MUTE(id)) {
1588 			// input mute control
1589 			if (size != sizeof(int32))
1590 				goto err;
1591 			input->SetEnabled(!static_cast<const int32 *>(value)[0]);
1592 		}
1593 		if (PARAM_IS_GAIN(id)) {
1594 			// input gain control
1595 			if (fCore->Settings()->InputGainControls() == 0) {
1596 				// Physical input channels
1597 				if (fCore->Settings()->UseBalanceControl() && input->GetInputChannelCount() == 2 && 1 /*channel mask is stereo */) {
1598 					// single channel control + balance
1599 					float l = input->GetInputChannelGain(0);
1600 					float r = input->GetInputChannelGain(1);
1601 					float m = (l + r) / 2;	// master volume
1602 					float v = DB_TO_GAIN(static_cast<const float *>(value)[0]);
1603 					float f = v / m;		// factor for both channels
1604 					TRACE("gain set l %1.3f, r %1.3f, m %1.3f, v %1.3f, f %1.3f\n",l,r,m,v,f);
1605 					input->SetInputChannelGain(0, input->GetInputChannelGain(0) * f);
1606 					input->SetInputChannelGain(1, input->GetInputChannelGain(1) * f);
1607 				} else {
1608 					// multi channel control
1609 					if (size < input->GetInputChannelCount() * sizeof(float))
1610 						goto err;
1611 					for (int chan = 0; chan < input->GetInputChannelCount(); chan++)
1612 						input->SetInputChannelGain(chan, DB_TO_GAIN(static_cast<const float *>(value)[chan]));
1613 				}
1614 			} else {
1615 				// Virtual output channels
1616 				if (fCore->Settings()->UseBalanceControl() && input->GetMixerChannelCount() == 2 && 1 /*channel mask is stereo */) {
1617 					// single channel control + balance
1618 					float l = input->GetMixerChannelGain(0);
1619 					float r = input->GetMixerChannelGain(1);
1620 					float m = (l + r) / 2;	// master volume
1621 					float v = DB_TO_GAIN(static_cast<const float *>(value)[0]);
1622 					float f = v / m;		// factor for both channels
1623 					TRACE("gain set l %1.3f, r %1.3f, m %1.3f, v %1.3f, f %1.3f\n",l,r,m,v,f);
1624 					input->SetMixerChannelGain(0, input->GetMixerChannelGain(0) * f);
1625 					input->SetMixerChannelGain(1, input->GetMixerChannelGain(1) * f);
1626 				} else {
1627 					// multi channel control
1628 					if (size < input->GetMixerChannelCount() * sizeof(float))
1629 						goto err;
1630 					for (int chan = 0; chan < input->GetMixerChannelCount(); chan++)
1631 						input->SetMixerChannelGain(chan, DB_TO_GAIN(static_cast<const float *>(value)[chan]));
1632 				}
1633 			}
1634 		}
1635 		if (PARAM_IS_BALANCE(id)) {
1636 			if (fCore->Settings()->InputGainControls() == 0) {
1637 				// Physical input channels
1638 				float l = input->GetInputChannelGain(0);
1639 				float r = input->GetInputChannelGain(1);
1640 				float m = (l + r) / 2;	// master volume
1641 				float v = static_cast<const float *>(value)[0] / 100; // current balance value
1642 				float fl = 2 * (1 - v);	// left channel factor of master volume
1643 				float fr = 2 * v;		// right channel factor of master volume
1644 				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);
1645 				input->SetInputChannelGain(0, m * fl);
1646 				input->SetInputChannelGain(1, m * fr);
1647 			} else {
1648 				// Virtual output channels
1649 				float l = input->GetMixerChannelGain(0);
1650 				float r = input->GetMixerChannelGain(1);
1651 				float m = (l + r) / 2;	// master volume
1652 				float v = static_cast<const float *>(value)[0] / 100; // current balance value
1653 				float fl = 2 * (1 - v);	// left channel factor of master volume
1654 				float fr = 2 * v;		// right channel factor of master volume
1655 				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);
1656 				input->SetMixerChannelGain(0, m * fl);
1657 				input->SetMixerChannelGain(1, m * fr);
1658 			}
1659 		}
1660 		if (PARAM_IS_DST_ENABLE(id)) {
1661 			if (size != sizeof(int32))
1662 				goto err;
1663 			if (static_cast<const int32 *>(value)[0]) {
1664 				int oldchan = input->GetInputChannelForDestination(PARAM_DST(id));
1665 				if (oldchan != -1) {
1666 					input->RemoveInputChannelDestination(oldchan, PARAM_DST(id));
1667 					int32 null = 0;
1668 					BroadcastNewParameterValue(when, PARAM_DST_ENABLE(PARAM(id), oldchan, PARAM_DST(id)), &null, sizeof(null));
1669 				}
1670 				input->AddInputChannelDestination(PARAM_CHAN(id), PARAM_DST(id));
1671 			} else {
1672 				input->RemoveInputChannelDestination(PARAM_CHAN(id), PARAM_DST(id));
1673 			}
1674 			// TODO: this is really annoying
1675 			// The slider count of the gain control needs to be changed,
1676 			// but calling SetChannelCount(input->GetMixerChannelCount())
1677 			// on it has no effect on remote BParameterWebs in other apps.
1678 			// BroadcastChangedParameter() should be correct, but doesn't work
1679 			BroadcastChangedParameter(PARAM_GAIN(PARAM(id)));
1680 			// We trigger a complete ParameterWeb update as workaround
1681 			// but it will change the focus from tab 3 to tab 1
1682 			update = true;
1683 		}
1684 		fCore->Settings()->SaveConnectionSettings(input);
1685 	}
1686 
1687 	BroadcastNewParameterValue(when, id, const_cast<void *>(value), size);
1688 
1689 err:
1690 	fCore->Unlock();
1691 	if (update)
1692 		UpdateParameterWeb();
1693 }
1694 
1695 
1696 void
1697 AudioMixer::UpdateParameterWeb()
1698 {
1699 	fCore->Lock();
1700 	BParameterWeb *web = new BParameterWeb();
1701 	BParameterGroup *top;
1702 	BParameterGroup *outputchannels;
1703 	BParameterGroup *inputchannels;
1704 	BParameterGroup *group;
1705 	BParameterGroup *subgroup;
1706 	BParameterGroup *subsubgroup;
1707 	BDiscreteParameter *dp;
1708 	MixerInput *in;
1709 	MixerOutput *out;
1710 	char buf[50];
1711 
1712 	top = web->MakeGroup(B_TRANSLATE("Gain controls"));
1713 
1714 	out = fCore->Output();
1715 	group = top->MakeGroup("");
1716 	group->MakeNullParameter(PARAM_STR1(0), B_MEDIA_RAW_AUDIO,
1717 		B_TRANSLATE("Master output"), B_WEB_BUFFER_INPUT);
1718 	if (!out) {
1719 		group->MakeNullParameter(PARAM_STR2(0), B_MEDIA_RAW_AUDIO,
1720 			B_TRANSLATE("not connected"), B_GENERIC);
1721 	} else {
1722 		group->MakeNullParameter(PARAM_STR2(0), B_MEDIA_RAW_AUDIO,
1723 			StringForFormat(buf, out), B_GENERIC);
1724 		group->MakeDiscreteParameter(PARAM_MUTE(0), B_MEDIA_RAW_AUDIO,
1725 			B_TRANSLATE("Mute"), B_MUTE);
1726 		if (fCore->Settings()->UseBalanceControl()
1727 			&& out->GetOutputChannelCount() == 2 && 1
1728 			/*channel mask is stereo */) {
1729 			// single channel control + balance
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 			group->MakeContinuousParameter(PARAM_BALANCE(0), B_MEDIA_RAW_AUDIO,
1734 				"", B_BALANCE, "", 0, 100, 1);
1735 		} else {
1736 			// multi channel control
1737 			group->MakeContinuousParameter(PARAM_GAIN(0), B_MEDIA_RAW_AUDIO,
1738 				B_TRANSLATE("Gain"), B_MASTER_GAIN, B_TRANSLATE("dB"),
1739 				DB_MIN, DB_MAX, 0.1)
1740 				   ->SetChannelCount(out->GetOutputChannelCount());
1741 		}
1742 		group->MakeNullParameter(PARAM_STR3(0), B_MEDIA_RAW_AUDIO,
1743 			B_TRANSLATE("To output"), B_WEB_BUFFER_OUTPUT);
1744 	}
1745 
1746 	for (int i = 0; (in = fCore->Input(i)); i++) {
1747 		group = top->MakeGroup("");
1748 		group->MakeNullParameter(PARAM_STR1(in->ID()), B_MEDIA_RAW_AUDIO,
1749 			in->MediaInput().name, B_WEB_BUFFER_INPUT);
1750 		group->MakeNullParameter(PARAM_STR2(in->ID()), B_MEDIA_RAW_AUDIO,
1751 			StringForFormat(buf, in), B_GENERIC);
1752 		group->MakeDiscreteParameter(PARAM_MUTE(in->ID()), B_MEDIA_RAW_AUDIO,
1753 			B_TRANSLATE("Mute"), B_MUTE);
1754 		// XXX the gain control is ugly once you have more than two channels,
1755 		//     as you don't know what channel each slider controls. Tooltips might help...
1756 		if (fCore->Settings()->InputGainControls() == 0) {
1757 			// Physical input channels
1758 			if (fCore->Settings()->UseBalanceControl()
1759 				&& in->GetInputChannelCount() == 2 && 1
1760 				/*channel mask is stereo */) {
1761 				// single channel control + balance
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 				group->MakeContinuousParameter(PARAM_BALANCE(in->ID()),
1766 					B_MEDIA_RAW_AUDIO, "", B_BALANCE, "", 0, 100, 1);
1767 			} else {
1768 				// multi channel control
1769 				group->MakeContinuousParameter(PARAM_GAIN(in->ID()),
1770 					B_MEDIA_RAW_AUDIO, B_TRANSLATE("Gain"), B_GAIN,
1771 					B_TRANSLATE("dB"), DB_MIN, DB_MAX, 0.1)
1772 						->SetChannelCount(in->GetInputChannelCount());
1773 			}
1774 		} else {
1775 			// Virtual output channels
1776 			if (fCore->Settings()->UseBalanceControl()
1777 				&& in->GetMixerChannelCount() == 2 && 1
1778 				/*channel mask is stereo */) {
1779 				// single channel control + balance
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 				group->MakeContinuousParameter(PARAM_BALANCE(in->ID()),
1784 					B_MEDIA_RAW_AUDIO, "", B_BALANCE, "", 0, 100, 1);
1785 			} else {
1786 				// multi channel control
1787 				group->MakeContinuousParameter(PARAM_GAIN(in->ID()),
1788 					B_MEDIA_RAW_AUDIO, B_TRANSLATE("Gain"), B_GAIN,
1789 					B_TRANSLATE("dB"), DB_MIN, DB_MAX, 0.1)
1790 						->SetChannelCount(in->GetMixerChannelCount());
1791 			}
1792 		}
1793 		group->MakeNullParameter(PARAM_STR3(in->ID()), B_MEDIA_RAW_AUDIO,
1794 			B_TRANSLATE("To master"), B_WEB_BUFFER_OUTPUT);
1795 	}
1796 
1797 	if (fCore->Settings()->AllowOutputChannelRemapping()) {
1798 		top = web->MakeGroup(B_TRANSLATE("Output mapping")); // top level group
1799 		outputchannels = top->MakeGroup("");
1800 		outputchannels->MakeNullParameter(PARAM_STR4(0), B_MEDIA_RAW_AUDIO,
1801 			B_TRANSLATE("Output channel sources"), B_GENERIC);
1802 
1803 		group = outputchannels->MakeGroup("");
1804 		group->MakeNullParameter(PARAM_STR5(0), B_MEDIA_RAW_AUDIO,
1805 			B_TRANSLATE("Master output"), B_GENERIC);
1806 		group = group->MakeGroup("");
1807 		if (!out) {
1808 			group->MakeNullParameter(PARAM_STR6(0), B_MEDIA_RAW_AUDIO,
1809 				B_TRANSLATE("not connected"), B_GENERIC);
1810 		} else {
1811 			for (int chan = 0; chan < out->GetOutputChannelCount(); chan++) {
1812 				subgroup = group->MakeGroup("");
1813 				subgroup->MakeNullParameter(PARAM_SRC_STR(0, chan),
1814 					B_MEDIA_RAW_AUDIO, StringForChannelType(buf,
1815 						out->GetOutputChannelType(chan)), B_GENERIC);
1816 				for (int src = 0; src < MAX_CHANNEL_TYPES; src++) {
1817 					subsubgroup = subgroup->MakeGroup("");
1818 					subsubgroup->MakeDiscreteParameter(
1819 						PARAM_SRC_ENABLE(0, chan, src), B_MEDIA_RAW_AUDIO, "",
1820 						B_ENABLE);
1821 					subsubgroup->MakeContinuousParameter(
1822 						PARAM_SRC_GAIN(0, chan, src), B_MEDIA_RAW_AUDIO,
1823 						StringForChannelType(buf, src), B_GAIN, "%", 0.0,
1824 						100.0, 0.1);
1825 				}
1826 			}
1827 		}
1828 	}
1829 
1830 	if (fCore->Settings()->AllowInputChannelRemapping()) {
1831 		top = web->MakeGroup(B_TRANSLATE("Input mapping")); // top level group
1832 		inputchannels = top->MakeGroup("");
1833 		inputchannels->MakeNullParameter(PARAM_STR7(0), B_MEDIA_RAW_AUDIO,
1834 			B_TRANSLATE("Input channel destinations"), B_GENERIC);
1835 
1836 		for (int i = 0; (in = fCore->Input(i)); i++) {
1837 			group = inputchannels->MakeGroup("");
1838 			group->MakeNullParameter(PARAM_STR4(in->ID()), B_MEDIA_RAW_AUDIO,
1839 				in->MediaInput().name, B_GENERIC);
1840 			group = group->MakeGroup("");
1841 
1842 			for (int chan = 0; chan < in->GetInputChannelCount(); chan++) {
1843 				subgroup = group->MakeGroup("");
1844 				subgroup->MakeNullParameter(PARAM_DST_STR(in->ID(), chan),
1845 					B_MEDIA_RAW_AUDIO, StringForChannelType(buf,
1846 					in->GetInputChannelType(chan)), B_GENERIC);
1847 				for (int dst = 0; dst < MAX_CHANNEL_TYPES; dst++) {
1848 					subgroup->MakeDiscreteParameter(PARAM_DST_ENABLE(in->ID(),
1849 					chan, dst), B_MEDIA_RAW_AUDIO, StringForChannelType(buf, dst),
1850 					B_ENABLE);
1851 				}
1852 			}
1853 		}
1854 	}
1855 
1856 	top = web->MakeGroup(B_TRANSLATE("Setup")); // top level group
1857 	group = top->MakeGroup("");
1858 
1859 	group->MakeDiscreteParameter(PARAM_ETC(10), B_MEDIA_RAW_AUDIO,
1860 		B_TRANSLATE("Attenuate mixer output by 3dB (like BeOS R5)"), B_ENABLE);
1861 	group->MakeDiscreteParameter(PARAM_ETC(20), B_MEDIA_RAW_AUDIO,
1862 		B_TRANSLATE("Use non linear gain sliders (like BeOS R5)"), B_ENABLE);
1863 	group->MakeDiscreteParameter(PARAM_ETC(30), B_MEDIA_RAW_AUDIO,
1864 		B_TRANSLATE("Display balance control for stereo connections"),
1865 		B_ENABLE);
1866 
1867 	group->MakeDiscreteParameter(PARAM_ETC(40), B_MEDIA_RAW_AUDIO,
1868 		B_TRANSLATE("Allow output channel remapping"), B_ENABLE);
1869 	group->MakeDiscreteParameter(PARAM_ETC(50), B_MEDIA_RAW_AUDIO,
1870 		B_TRANSLATE("Allow input channel remapping"), B_ENABLE);
1871 
1872 	dp = group->MakeDiscreteParameter(PARAM_ETC(60), B_MEDIA_RAW_AUDIO,
1873 		B_TRANSLATE("Input gain controls represent"), B_INPUT_MUX);
1874 	dp->AddItem(0, B_TRANSLATE("Physical input channels"));
1875 	dp->AddItem(1, B_TRANSLATE("Virtual output channels"));
1876 
1877 	dp = group->MakeDiscreteParameter(PARAM_ETC(70), B_MEDIA_RAW_AUDIO,
1878 		B_TRANSLATE("Resampling algorithm"), B_INPUT_MUX);
1879 	dp->AddItem(0, B_TRANSLATE("Drop/repeat samples"));
1880 	dp->AddItem(2, B_TRANSLATE("Linear interpolation"));
1881 
1882 	// Note: The following code is outcommented on purpose
1883 	// and is about to be modified at a later point
1884 	/*
1885 	dp->AddItem(1, B_TRANSLATE("Drop/repeat samples (template based)"));
1886 	dp->AddItem(3, B_TRANSLATE("17th order filtering"));
1887 	*/
1888 	group->MakeDiscreteParameter(PARAM_ETC(80), B_MEDIA_RAW_AUDIO,
1889 		B_TRANSLATE("Refuse output format changes"), B_ENABLE);
1890 	group->MakeDiscreteParameter(PARAM_ETC(90), B_MEDIA_RAW_AUDIO,
1891 		B_TRANSLATE("Refuse input format changes"), B_ENABLE);
1892 
1893 	fCore->Unlock();
1894 	SetParameterWeb(web);
1895 }
1896 
1897 
1898 #if USE_MEDIA_FORMAT_WORKAROUND
1899 static void
1900 raw_audio_format_specialize(media_raw_audio_format *format,
1901 	const media_raw_audio_format *other)
1902 {
1903 	if (format->frame_rate == 0)
1904 		format->frame_rate = other->frame_rate;
1905 	if (format->channel_count == 0)
1906 		format->channel_count = other->channel_count;
1907 	if (format->format == 0)
1908 		format->format = other->format;
1909 	if (format->byte_order == 0)
1910 		format->byte_order = other->byte_order;
1911 	if (format->buffer_size == 0)
1912 		format->buffer_size = other->buffer_size;
1913 	if (format->frame_rate == 0)
1914 		format->frame_rate = other->frame_rate;
1915 }
1916 
1917 
1918 static void
1919 multi_audio_info_specialize(media_multi_audio_info *format,
1920 	const media_multi_audio_info *other)
1921 {
1922 	if (format->channel_mask == 0)
1923 		format->channel_mask = other->channel_mask;
1924 	if (format->valid_bits == 0)
1925 		format->valid_bits = other->valid_bits;
1926 	if (format->matrix_mask == 0)
1927 		format->matrix_mask = other->matrix_mask;
1928 }
1929 
1930 
1931 static void
1932 multi_audio_format_specialize(media_multi_audio_format *format,
1933 	const media_multi_audio_format *other)
1934 {
1935 	raw_audio_format_specialize(format, other);
1936 	multi_audio_info_specialize(format, other);
1937 }
1938 #endif
1939