xref: /haiku/src/add-ons/media/media-add-ons/mixer/AudioMixer.cpp (revision 71f92c6439bddce17ccd7121d4ba7ff716617b1c)
1 /*
2  * Copyright 2002 David Shipman,
3  * Copyright 2003-2007 Marcus Overhagen
4  * Copyright 2007-2010 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_TRANSLATE_CONTEXT
33 #define B_TRANSLATE_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 {
118 	BMediaNode::AddNodeKind(B_SYSTEM_MIXER);
119 
120 	// this is the default format used for all wildcard format SpecializeTo()s
121 	fDefaultFormat.type = B_MEDIA_RAW_AUDIO;
122 	fDefaultFormat.u.raw_audio.frame_rate = 96000;
123 	fDefaultFormat.u.raw_audio.channel_count = 2;
124 	fDefaultFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT;
125 	fDefaultFormat.u.raw_audio.byte_order = B_MEDIA_HOST_ENDIAN;
126 	fDefaultFormat.u.raw_audio.buffer_size = 4096;
127 	fDefaultFormat.u.raw_audio.channel_mask = 0;
128 	fDefaultFormat.u.raw_audio.valid_bits = 0;
129 	fDefaultFormat.u.raw_audio.matrix_mask = 0;
130 
131 	if (isSystemMixer) {
132 		// to get persistent settings, assign a settings file
133 		BPath path;
134 		if (B_OK != find_directory (B_USER_SETTINGS_DIRECTORY, &path))
135 			path.SetTo("/boot/home/config/settings/");
136 		path.Append("System Audio Mixer");
137 		fCore->Settings()->SetSettingsFile(path.Path());
138 
139 		// disable stop on the auto started (system) mixer
140 		DisableNodeStop();
141 	}
142 
143 	ApplySettings();
144 }
145 
146 
147 AudioMixer::~AudioMixer()
148 {
149 	BMediaEventLooper::Quit();
150 	SetParameterWeb(NULL);
151 
152 	// stop the mixer
153 	fCore->Lock();
154 	fCore->Stop();
155 	fCore->Unlock();
156 
157 	// disconnect all nodes from the mixer
158 	// XXX todo
159 
160 	delete fCore;
161 	delete fBufferGroup;
162 
163 	DEBUG_ONLY(fCore = 0; fBufferGroup = 0; fWeb = 0);
164 }
165 
166 
167 void
168 AudioMixer::ApplySettings()
169 {
170 	fCore->Lock();
171 	fCore->SetOutputAttenuation(fCore->Settings()->AttenuateOutput() ? 0.708 : 1.0);
172 	fCore->Unlock();
173 }
174 
175 
176 void
177 AudioMixer::DisableNodeStop()
178 {
179 	fDisableStop = true;
180 }
181 
182 
183 //	#pragma mark - BMediaNode methods
184 
185 
186 void
187 AudioMixer::Stop(bigtime_t performance_time, bool immediate)
188 {
189 	if (fDisableStop) {
190 		TRACE("AudioMixer STOP is disabled\n");
191 		return;
192 	} else {
193 		BMediaEventLooper::Stop(performance_time, immediate);
194 	}
195 }
196 
197 
198 BMediaAddOn*
199 AudioMixer::AddOn(int32 *internal_id) const
200 {
201 	*internal_id = 0;
202 	return fAddOn;
203 }
204 
205 
206 //	#pragma mark - BBufferConsumer methods
207 
208 
209 status_t
210 AudioMixer::HandleMessage(int32 message, const void *data, size_t size)
211 {
212 	// since we're using a mediaeventlooper, there shouldn't be any messages
213 	return B_ERROR;
214 }
215 
216 
217 status_t
218 AudioMixer::AcceptFormat(const media_destination &dest, media_format *ioFormat)
219 {
220 	PRINT_FORMAT("AudioMixer::AcceptFormat: ", *ioFormat);
221 
222 	// check that the specified format is reasonable for the specified destination, and
223 	// fill in any wildcard fields for which our BBufferConsumer has specific requirements.
224 
225 	// we have multiple inputs with different IDs, but
226 	// the port number must match our ControlPort()
227 	if (dest.port != ControlPort())
228 		return B_MEDIA_BAD_DESTINATION;
229 
230 	// specialize to raw audio format if necessary
231 	if (ioFormat->type == B_MEDIA_UNKNOWN_TYPE)
232 		ioFormat->type = B_MEDIA_RAW_AUDIO;
233 
234 	// we require a raw audio format
235 	if (ioFormat->type != B_MEDIA_RAW_AUDIO)
236 		return B_MEDIA_BAD_FORMAT;
237 
238 	// We do not have special requirements, but just in case
239 	// another mixer is connecting to us and may need a hint
240 	// to create a connection at optimal frame rate and
241 	// channel count, we place this information in the user_data
242 	fCore->Lock();
243 	MixerOutput *output = fCore->Output();
244 	uint32 channel_count = output ? output->MediaOutput().format.u.raw_audio.channel_count : 0;
245 	float frame_rate = output ? output->MediaOutput().format.u.raw_audio.frame_rate : 0.0;
246 	fCore->Unlock();
247 	ioFormat->user_data_type = FORMAT_USER_DATA_TYPE;
248 	*(uint32 *)&ioFormat->user_data[0] = FORMAT_USER_DATA_MAGIC_1;
249 	*(uint32 *)&ioFormat->user_data[4] = channel_count;
250 	*(float *)&ioFormat->user_data[20] = frame_rate;
251 	*(uint32 *)&ioFormat->user_data[44] = FORMAT_USER_DATA_MAGIC_2;
252 
253 	return B_OK;
254 }
255 
256 
257 status_t
258 AudioMixer::GetNextInput(int32 *cookie, media_input *out_input)
259 {
260 	TRACE("AudioMixer::GetNextInput\n");
261 
262 	// our 0th input is always a wildcard and free one
263 	if (*cookie == 0) {
264 		out_input->node = Node();
265 		out_input->source = media_source::null;
266 		out_input->destination.port = ControlPort();
267 		out_input->destination.id = 0;
268 		memset(&out_input->format, 0, sizeof(out_input->format));
269 		out_input->format.type = B_MEDIA_RAW_AUDIO;
270 		strcpy(out_input->name, "Free Input");
271 		*cookie += 1;
272 		return B_OK;
273 	}
274 
275 	// the other inputs are the currently connected ones
276 	fCore->Lock();
277 	MixerInput *input;
278 	input = fCore->Input(*cookie - 1);
279 	if (!input) {
280 		fCore->Unlock();
281 		return B_BAD_INDEX;
282 	}
283 	*out_input = input->MediaInput();
284 	*cookie += 1;
285 	fCore->Unlock();
286 	return B_OK;
287 }
288 
289 
290 void
291 AudioMixer::DisposeInputCookie(int32 cookie)
292 {
293 	// nothing to do
294 }
295 
296 
297 void
298 AudioMixer::BufferReceived(BBuffer *buffer)
299 {
300 
301 	if (buffer->Header()->type == B_MEDIA_PARAMETERS) {
302 		TRACE("Control Buffer Received\n");
303 		ApplyParameterData(buffer->Data(), buffer->SizeUsed());
304 		buffer->Recycle();
305 		return;
306 	}
307 
308 	//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);
309 
310 	// to receive the buffer at the right time,
311 	// push it through the event looper
312 	media_timed_event event(buffer->Header()->start_time,
313 		BTimedEventQueue::B_HANDLE_BUFFER, buffer,
314 		BTimedEventQueue::B_RECYCLE_BUFFER);
315 	EventQueue()->AddEvent(event);
316 }
317 
318 
319 void
320 AudioMixer::HandleInputBuffer(BBuffer* buffer, bigtime_t lateness)
321 {
322 	if (lateness > kMaxJitter) {
323 		debug_printf("Received buffer %Ld usec late\n", lateness);
324 		if (RunMode() == B_DROP_DATA || RunMode() == B_DECREASE_PRECISION
325 			|| RunMode() == B_INCREASE_LATENCY) {
326 			debug_printf("sending notify\n");
327 
328 			// Build a media_source out of the header data
329 			media_source source = media_source::null;
330 			source.port = buffer->Header()->source_port;
331 			source.id = buffer->Header()->source;
332 
333 			NotifyLateProducer(source, lateness, TimeSource()->Now());
334 
335 			if (RunMode() == B_DROP_DATA) {
336 				debug_printf("dropping buffer\n");
337 				return;
338 			}
339 		}
340 	}
341 
342 	//	printf("Received buffer with lateness %Ld\n", 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 performance time
1010 		// lies before the performance time of the last notification
1011 		if (performanceTime < fLastLateNotification)
1012 			return;
1013 
1014 		fInternalLatency += howMuch;
1015 
1016 		// At some point a too large latency can get annoying
1017 		if (fInternalLatency > kMaxLatency)
1018 			fInternalLatency = kMaxLatency;
1019 
1020 		fLastLateNotification = TimeSource()->Now();
1021 
1022 		debug_printf("AudioMixer: increasing internal latency to %Ld usec\n", fInternalLatency);
1023 		SetEventLatency(fDownstreamLatency + fInternalLatency);
1024 
1025 		PublishEventLatencyChange();
1026 	}
1027 }
1028 
1029 
1030 void
1031 AudioMixer::EnableOutput(const media_source& what, bool enabled,
1032 	int32 */*deprecated*/)
1033 {
1034 	// we only have one output
1035 	if (what.id != 0 || what.port != ControlPort())
1036 		return;
1037 
1038 	fCore->Lock();
1039 	fCore->EnableOutput(enabled);
1040 	fCore->Unlock();
1041 }
1042 
1043 
1044 //	#pragma mark - BMediaEventLooper methods
1045 
1046 
1047 void
1048 AudioMixer::NodeRegistered()
1049 {
1050 	Run();
1051 	SetPriority(120);
1052 	UpdateParameterWeb();
1053 }
1054 
1055 
1056 void
1057 AudioMixer::SetTimeSource(BTimeSource* timeSource)
1058 {
1059 	TRACE("AudioMixer::SetTimeSource: timesource is now %ld\n",
1060 		timeSource->ID());
1061 	fCore->Lock();
1062 	fCore->SetTimingInfo(timeSource, fDownstreamLatency);
1063 	fCore->Unlock();
1064 }
1065 
1066 
1067 void
1068 AudioMixer::HandleEvent(const media_timed_event *event, bigtime_t lateness,
1069 	bool realTimeEvent)
1070 {
1071 	switch (event->type) {
1072 		case BTimedEventQueue::B_HANDLE_BUFFER:
1073 		{
1074 			HandleInputBuffer((BBuffer *)event->pointer, lateness);
1075 			((BBuffer *)event->pointer)->Recycle();
1076 			break;
1077 		}
1078 
1079 		case BTimedEventQueue::B_START:
1080 		{
1081 			TRACE("AudioMixer::HandleEvent: B_START\n");
1082 			if (RunState() != B_STARTED) {
1083 				fCore->Lock();
1084 				fCore->Start();
1085 				fCore->Unlock();
1086 			}
1087 			break;
1088 		}
1089 
1090 		case BTimedEventQueue::B_STOP:
1091 		{
1092 			TRACE("AudioMixer::HandleEvent: B_STOP\n");
1093 			// stopped - don't process any more buffers, flush all buffers
1094 			// from event queue
1095 			EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true,
1096 				BTimedEventQueue::B_HANDLE_BUFFER);
1097 			fCore->Lock();
1098 			fCore->Stop();
1099 			fCore->Unlock();
1100 			break;
1101 		}
1102 
1103 		case BTimedEventQueue::B_DATA_STATUS:
1104 		{
1105 			ERROR("DataStatus message\n");
1106 			break;
1107 		}
1108 
1109 		default:
1110 			break;
1111 	}
1112 }
1113 
1114 
1115 //	#pragma mark - AudioMixer methods
1116 
1117 
1118 void
1119 AudioMixer::PublishEventLatencyChange()
1120 {
1121 	// our event (processing + downstream) latency has changed,
1122 	// and we need tell all inputs about this
1123 
1124 	TRACE("AudioMixer::PublishEventLatencyChange\n");
1125 
1126 	fCore->Lock();
1127 
1128 	MixerInput *input;
1129 	for (int i = 0; (input = fCore->Input(i)) != 0; i++) {
1130 		TRACE("AudioMixer::PublishEventLatencyChange: SendLatencyChange, "
1131 			"connection %ld/%ld to %ld/%ld event latency is now %Ld\n",
1132 			input->MediaInput().source.port, input->MediaInput().source.id,
1133 			input->MediaInput().destination.port,
1134 			input->MediaInput().destination.id, EventLatency());
1135 		SendLatencyChange(input->MediaInput().source,
1136 			input->MediaInput().destination, EventLatency());
1137 	}
1138 
1139 	fCore->Unlock();
1140 }
1141 
1142 
1143 BBufferGroup*
1144 AudioMixer::CreateBufferGroup()
1145 {
1146 	// allocate enough buffers to span our downstream latency
1147 	// (plus one for rounding up), plus one extra
1148 	int32 count = int32(fDownstreamLatency / BufferDuration()) + 2;
1149 
1150 	TRACE("AudioMixer::CreateBufferGroup: fDownstreamLatency %Ld, "
1151 		"BufferDuration %Ld, buffer count = %ld\n", fDownstreamLatency,
1152 		BufferDuration(), count);
1153 
1154 	if (count < 3)
1155 		count = 3;
1156 
1157 	fCore->Lock();
1158 	uint32 size = fCore->Output()->MediaOutput().format.u.raw_audio.buffer_size;
1159 	fCore->Unlock();
1160 
1161 	TRACE("AudioMixer: allocating %ld buffers of %ld bytes each\n",
1162 		count, size);
1163 	return new BBufferGroup(size, count);
1164 }
1165 
1166 
1167 status_t
1168 AudioMixer::SendBuffer(BBuffer* buffer, MixerOutput* output)
1169 {
1170 	return BBufferProducer::SendBuffer(buffer, output->MediaOutput().source,
1171 		output->MediaOutput().destination);
1172 }
1173 
1174 
1175 float
1176 AudioMixer::dB_to_Gain(float db)
1177 {
1178 	TRACE("dB_to_Gain: dB in: %01.2f ", db);
1179 	if (fCore->Settings()->NonLinearGainSlider()) {
1180 		if (db > 0) {
1181 			db = db * (pow(abs(DB_MAX), (1.0 / DB_EXPONENT_POSITIVE))
1182 				/ abs(DB_MAX));
1183 			db = pow(db, DB_EXPONENT_POSITIVE);
1184 		} else {
1185 			db = -db;
1186 			db = db * (pow(abs(DB_MIN), (1.0 / DB_EXPONENT_NEGATIVE))
1187 				/ abs(DB_MIN));
1188 			db = pow(db, DB_EXPONENT_NEGATIVE);
1189 			db = -db;
1190 		}
1191 	}
1192 	TRACE("dB out: %01.2f\n", db);
1193 	return pow(10.0, db / 20.0);
1194 }
1195 
1196 
1197 float
1198 AudioMixer::Gain_to_dB(float gain)
1199 {
1200 	float db;
1201 	db = 20.0 * log10(gain);
1202 	if (fCore->Settings()->NonLinearGainSlider()) {
1203 		if (db > 0) {
1204 			db = pow(db, (1.0 / DB_EXPONENT_POSITIVE));
1205 			db = db * (abs(DB_MAX) / pow(abs(DB_MAX),
1206 				(1.0 / DB_EXPONENT_POSITIVE)));
1207 		} else {
1208 			db = -db;
1209 			db = pow(db, (1.0 / DB_EXPONENT_NEGATIVE));
1210 			db = db * (abs(DB_MIN) / pow(abs(DB_MIN),
1211 				(1.0 / DB_EXPONENT_NEGATIVE)));
1212 			db = -db;
1213 		}
1214 	}
1215 	return db;
1216 }
1217 
1218 
1219 // #pragma mark - BControllable methods
1220 
1221 
1222 status_t
1223 AudioMixer::GetParameterValue(int32 id, bigtime_t *last_change, void *value,
1224 	size_t *ioSize)
1225 {
1226 	TRACE("GetParameterValue: id 0x%08lx, ioSize %ld\n", id, *ioSize);
1227 	int param = PARAM(id);
1228 	fCore->Lock();
1229 	if (PARAM_IS_ETC(id)) {
1230 		switch (ETC(id)) {
1231 			case 10:	// Attenuate mixer output by 3dB
1232 				*ioSize = sizeof(int32);
1233 				static_cast<int32 *>(value)[0] = fCore->Settings()->AttenuateOutput();
1234 				break;
1235 			case 20:	// Use non linear gain sliders
1236 				*ioSize = sizeof(int32);
1237 				static_cast<int32 *>(value)[0] = fCore->Settings()->NonLinearGainSlider();
1238 				break;
1239 			case 30:	// Display balance control for stereo connections
1240 				*ioSize = sizeof(int32);
1241 				static_cast<int32 *>(value)[0] = fCore->Settings()->UseBalanceControl();
1242 				break;
1243 			case 40:	// Allow output channel remapping
1244 				*ioSize = sizeof(int32);
1245 				static_cast<int32 *>(value)[0] = fCore->Settings()->AllowOutputChannelRemapping();
1246 				break;
1247 			case 50:	// Allow input channel remapping
1248 				*ioSize = sizeof(int32);
1249 				static_cast<int32 *>(value)[0] = fCore->Settings()->AllowInputChannelRemapping();
1250 				break;
1251 			case 60:	// Input gain controls
1252 				*ioSize = sizeof(int32);
1253 				static_cast<int32 *>(value)[0] = fCore->Settings()->InputGainControls();
1254 				break;
1255 			case 70:	// Resampling algorithm
1256 				*ioSize = sizeof(int32);
1257 				static_cast<int32 *>(value)[0] = fCore->Settings()->ResamplingAlgorithm();
1258 				break;
1259 			case 80:	// Refuse output format changes
1260 				*ioSize = sizeof(int32);
1261 				static_cast<int32 *>(value)[0] = fCore->Settings()->RefuseOutputFormatChange();
1262 				break;
1263 			case 90:	// Refuse input format changes
1264 				*ioSize = sizeof(int32);
1265 				static_cast<int32 *>(value)[0] = fCore->Settings()->RefuseInputFormatChange();
1266 				break;
1267 			default:
1268 				ERROR("unhandled ETC 0x%08lx\n", id);
1269 				break;
1270 		}
1271 	} else if (param == 0) {
1272 		MixerOutput *output = fCore->Output();
1273 		if (!output || (!PARAM_IS_MUTE(id) && !PARAM_IS_GAIN(id) && !PARAM_IS_SRC_ENABLE(id) && !PARAM_IS_SRC_GAIN(id) && !PARAM_IS_BALANCE(id)))
1274 			goto err;
1275 		if (PARAM_IS_MUTE(id)) {
1276 			// output mute control
1277 			if (*ioSize < sizeof(int32))
1278 				goto err;
1279 			*ioSize = sizeof(int32);
1280 			static_cast<int32 *>(value)[0] = output->IsMuted();
1281 		}
1282 		if (PARAM_IS_GAIN(id)) {
1283 			// output gain control
1284 			if (fCore->Settings()->UseBalanceControl() && output->GetOutputChannelCount() == 2 && 1 /*channel mask is stereo */) {
1285 				// single channel control + balance
1286 				if (*ioSize < sizeof(float))
1287 					goto err;
1288 				*ioSize = sizeof(float);
1289 				static_cast<float *>(value)[0] = GAIN_TO_DB((output->GetOutputChannelGain(0) + output->GetOutputChannelGain(1)) / 2);
1290 			} else {
1291 				// multi channel control
1292 				if (*ioSize == sizeof(float)) {
1293 					// get combined gain for all controls
1294 					float gain = 0;
1295 					for (int channel = 0;
1296 							channel < output->GetOutputChannelCount();
1297 							channel++) {
1298 						gain += GAIN_TO_DB(
1299 							output->GetOutputChannelGain(channel));
1300 					}
1301 					static_cast<float *>(value)[0] = gain
1302 						/ output->GetOutputChannelCount();
1303 				} else {
1304 					if (*ioSize < output->GetOutputChannelCount()
1305 							* sizeof(float))
1306 						goto err;
1307 
1308 					*ioSize = output->GetOutputChannelCount() * sizeof(float);
1309 
1310 					for (int channel = 0;
1311 							channel < output->GetOutputChannelCount();
1312 							channel++) {
1313 						static_cast<float *>(value)[channel]
1314 							= GAIN_TO_DB(output->GetOutputChannelGain(channel));
1315 					}
1316 				}
1317 			}
1318 		}
1319 		if (PARAM_IS_BALANCE(id)) {
1320 			float l = output->GetOutputChannelGain(0);
1321 			float r = output->GetOutputChannelGain(1);
1322 			float v = r / (l+r);
1323 			TRACE("balance l %1.3f, r %1.3f, v %1.3f\n",l,r,v);
1324 			if (*ioSize < sizeof(float))
1325 				goto err;
1326 			*ioSize = sizeof(float);
1327 			static_cast<float *>(value)[0] = v * 100;
1328 		}
1329 		if (PARAM_IS_SRC_ENABLE(id)) {
1330 			if (*ioSize < sizeof(int32))
1331 				goto err;
1332 			*ioSize = sizeof(int32);
1333 			static_cast<int32 *>(value)[0] = output->HasOutputChannelSource(PARAM_CHAN(id), PARAM_SRC(id));
1334 		}
1335 		if (PARAM_IS_SRC_GAIN(id)) {
1336 			if (*ioSize < sizeof(float))
1337 				goto err;
1338 			*ioSize = sizeof(float);
1339 			static_cast<float *>(value)[0] = GAIN_TO_PERCENT(output->GetOutputChannelSourceGain(PARAM_CHAN(id), PARAM_SRC(id)));
1340 		}
1341 	} else {
1342 		MixerInput *input;
1343 		for (int i = 0; (input = fCore->Input(i)); i++)
1344 			if (input->ID() == param)
1345 				break;
1346 		if (!input || (!PARAM_IS_MUTE(id) && !PARAM_IS_GAIN(id) && !PARAM_IS_DST_ENABLE(id) && !PARAM_IS_BALANCE(id)))
1347 			goto err;
1348 		if (PARAM_IS_MUTE(id)) {
1349 			// input mute control
1350 			if (*ioSize < sizeof(int32))
1351 				goto err;
1352 			*ioSize = sizeof(int32);
1353 			static_cast<int32 *>(value)[0] = !input->IsEnabled();
1354 		}
1355 		if (PARAM_IS_GAIN(id)) {
1356 			// input gain control
1357 			if (fCore->Settings()->InputGainControls() == 0) {
1358 				// Physical input channels
1359 				if (fCore->Settings()->UseBalanceControl() && input->GetInputChannelCount() == 2 && 1 /*channel mask is stereo */) {
1360 					// single channel control + balance
1361 					if (*ioSize < sizeof(float))
1362 						goto err;
1363 					*ioSize = sizeof(float);
1364 					static_cast<float *>(value)[0] = GAIN_TO_DB((input->GetInputChannelGain(0) + input->GetInputChannelGain(1)) / 2);
1365 				} else {
1366 					// multi channel control
1367 					if (*ioSize < input->GetInputChannelCount() * sizeof(float))
1368 						goto err;
1369 					*ioSize = input->GetInputChannelCount() * sizeof(float);
1370 					for (int chan = 0; chan < input->GetInputChannelCount(); chan++)
1371 						static_cast<float *>(value)[chan] = GAIN_TO_DB(input->GetInputChannelGain(chan));
1372 				}
1373 			} else {
1374 				// Virtual output channels
1375 				if (fCore->Settings()->UseBalanceControl() && input->GetMixerChannelCount() == 2 && 1 /*channel mask is stereo */) {
1376 					// single channel control + balance
1377 					if (*ioSize < sizeof(float))
1378 						goto err;
1379 					*ioSize = sizeof(float);
1380 					static_cast<float *>(value)[0] = GAIN_TO_DB((input->GetMixerChannelGain(0) + input->GetMixerChannelGain(1)) / 2);
1381 				} else {
1382 					// multi channel control
1383 					if (*ioSize < input->GetMixerChannelCount() * sizeof(float))
1384 						goto err;
1385 					*ioSize = input->GetMixerChannelCount() * sizeof(float);
1386 					for (int chan = 0; chan < input->GetMixerChannelCount(); chan++)
1387 						static_cast<float *>(value)[chan] = GAIN_TO_DB(input->GetMixerChannelGain(chan));
1388 				}
1389 			}
1390 		}
1391 		if (PARAM_IS_BALANCE(id)) {
1392 			if (fCore->Settings()->InputGainControls() == 0) {
1393 				// Physical input channels
1394 				float l = input->GetInputChannelGain(0);
1395 				float r = input->GetInputChannelGain(1);
1396 				float v = r / (l+r);
1397 				TRACE("balance l %1.3f, r %1.3f, v %1.3f\n",l,r,v);
1398 				if (*ioSize < sizeof(float))
1399 					goto err;
1400 				*ioSize = sizeof(float);
1401 				static_cast<float *>(value)[0] = v * 100;
1402 			} else {
1403 				// Virtual output channels
1404 				float l = input->GetMixerChannelGain(0);
1405 				float r = input->GetMixerChannelGain(1);
1406 				float v = r / (l+r);
1407 				TRACE("balance l %1.3f, r %1.3f, v %1.3f\n",l,r,v);
1408 				if (*ioSize < sizeof(float))
1409 					goto err;
1410 				*ioSize = sizeof(float);
1411 				static_cast<float *>(value)[0] = v * 100;
1412 			}
1413 		}
1414 		if (PARAM_IS_DST_ENABLE(id)) {
1415 			if (*ioSize < sizeof(int32))
1416 				goto err;
1417 			*ioSize = sizeof(int32);
1418 			static_cast<int32 *>(value)[0] = input->HasInputChannelDestination(PARAM_CHAN(id), PARAM_DST(id));
1419 		}
1420 	}
1421 	*last_change = TimeSource()->Now(); // XXX we could do better
1422 	fCore->Unlock();
1423 	return B_OK;
1424 err:
1425 	fCore->Unlock();
1426 	return B_ERROR;
1427 }
1428 
1429 
1430 void
1431 AudioMixer::SetParameterValue(int32 id, bigtime_t when, const void *value,
1432 	size_t size)
1433 {
1434 	TRACE("SetParameterValue: id 0x%08lx, size %ld\n", id, size);
1435 	bool update = false;
1436 	int param = PARAM(id);
1437 	fCore->Lock();
1438 	if (PARAM_IS_ETC(id)) {
1439 		switch (ETC(id)) {
1440 			case 10:	// Attenuate mixer output by 3dB
1441 				if (size != sizeof(int32))
1442 					goto err;
1443 				fCore->Settings()->SetAttenuateOutput(static_cast<const int32 *>(value)[0]);
1444 				// this value is special (see MixerCore.h) and we need to notify the core
1445 				fCore->SetOutputAttenuation((static_cast<const int32 *>(value)[0]) ? 0.708 : 1.0);
1446 				break;
1447 			case 20:	// Use non linear gain sliders
1448 				if (size != sizeof(int32))
1449 					goto err;
1450 				fCore->Settings()->SetNonLinearGainSlider(static_cast<const int32 *>(value)[0]);
1451 				update = true; // XXX should use BroadcastChangedParameter()
1452 				break;
1453 			case 30:	// Display balance control for stereo connections
1454 				if (size != sizeof(int32))
1455 					goto err;
1456 				fCore->Settings()->SetUseBalanceControl(static_cast<const int32 *>(value)[0]);
1457 				update = true;
1458 				break;
1459 			case 40:	// Allow output channel remapping
1460 				if (size != sizeof(int32))
1461 					goto err;
1462 				fCore->Settings()->SetAllowOutputChannelRemapping(static_cast<const int32 *>(value)[0]);
1463 				update = true;
1464 				break;
1465 			case 50:	// Allow input channel remapping
1466 				if (size != sizeof(int32))
1467 					goto err;
1468 				fCore->Settings()->SetAllowInputChannelRemapping(static_cast<const int32 *>(value)[0]);
1469 				update = true;
1470 				break;
1471 			case 60:	// Input gain controls represent
1472 						// (0, "Physical input channels")
1473 						// (1, "Virtual output channels")
1474 				if (size != sizeof(int32))
1475 					goto err;
1476 				fCore->Settings()->SetInputGainControls(static_cast<const int32 *>(value)[0]);
1477 				update = true; // XXX should use BroadcastChangedParameter()
1478 				break;
1479 			case 70:	// Resampling algorithm
1480 				if (size != sizeof(int32))
1481 					goto err;
1482 				fCore->Settings()->SetResamplingAlgorithm(static_cast<const int32 *>(value)[0]);
1483 				fCore->UpdateResamplingAlgorithm();
1484 				break;
1485 			case 80:	// Refuse output format changes
1486 				if (size != sizeof(int32))
1487 					goto err;
1488 				fCore->Settings()->SetRefuseOutputFormatChange(static_cast<const int32 *>(value)[0]);
1489 				break;
1490 			case 90:	// Refuse input format changes
1491 				if (size != sizeof(int32))
1492 					goto err;
1493 				fCore->Settings()->SetRefuseInputFormatChange(static_cast<const int32 *>(value)[0]);
1494 				break;
1495 			default:
1496 				ERROR("unhandled ETC 0x%08lx\n", id);
1497 				break;
1498 		}
1499 	} else if (param == 0) {
1500 		MixerOutput *output = fCore->Output();
1501 		if (!output || (!PARAM_IS_MUTE(id) && !PARAM_IS_GAIN(id) && !PARAM_IS_SRC_ENABLE(id) && !PARAM_IS_SRC_GAIN(id) && !PARAM_IS_BALANCE(id)))
1502 			goto err;
1503 		if (PARAM_IS_MUTE(id)) {
1504 			// output mute control
1505 			if (size != sizeof(int32))
1506 				goto err;
1507 			output->SetMuted(static_cast<const int32 *>(value)[0]);
1508 		}
1509 		if (PARAM_IS_GAIN(id)) {
1510 			// output gain control
1511 			if (fCore->Settings()->UseBalanceControl()
1512 				&& output->GetOutputChannelCount() == 2 && 1 /*channel mask is stereo */) {
1513 				// single channel control + balance
1514 				float l = output->GetOutputChannelGain(0);
1515 				float r = output->GetOutputChannelGain(1);
1516 				float m = (l + r) / 2;	// master volume
1517 				float v = DB_TO_GAIN(static_cast<const float *>(value)[0]);
1518 				float f = v / m;		// factor for both channels
1519 				TRACE("gain set l %1.3f, r %1.3f, m %1.3f, v %1.3f, f %1.3f\n",l,r,m,v,f);
1520 				output->SetOutputChannelGain(0, output->GetOutputChannelGain(0) * f);
1521 				output->SetOutputChannelGain(1, output->GetOutputChannelGain(1) * f);
1522 			} else {
1523 				// multi channel control
1524 				if (size == sizeof(float)) {
1525 					// set same volume for all channels
1526 					float gain = static_cast<const float *>(value)[0];
1527 					for (int channel = 0;
1528 							channel < output->GetOutputChannelCount();
1529 							channel++) {
1530 						output->SetOutputChannelGain(channel,
1531 							DB_TO_GAIN(gain));
1532 					}
1533 				} else {
1534 					if (size < output->GetOutputChannelCount() * sizeof(float))
1535 						goto err;
1536 					for (int channel = 0;
1537 							channel < output->GetOutputChannelCount();
1538 							channel++) {
1539 						output->SetOutputChannelGain(channel,
1540 							DB_TO_GAIN(static_cast<const float *>(
1541 								value)[channel]));
1542 					}
1543 				}
1544 			}
1545 		}
1546 		if (PARAM_IS_BALANCE(id)) {
1547 			float l = output->GetOutputChannelGain(0);
1548 			float r = output->GetOutputChannelGain(1);
1549 			float m = (l + r) / 2;	// master volume
1550 			float v = static_cast<const float *>(value)[0] / 100; // current balance value
1551 			float fl = 2 * (1 - v);	// left channel factor of master volume
1552 			float fr = 2 * v;		// right channel factor of master volume
1553 			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);
1554 			output->SetOutputChannelGain(0, m * fl);
1555 			output->SetOutputChannelGain(1, m * fr);
1556 		}
1557 		if (PARAM_IS_SRC_ENABLE(id)) {
1558 			if (size != sizeof(int32))
1559 				goto err;
1560 			if (static_cast<const int32 *>(value)[0]) {
1561 				output->AddOutputChannelSource(PARAM_CHAN(id), PARAM_SRC(id));
1562 			} else {
1563 				output->RemoveOutputChannelSource(PARAM_CHAN(id), PARAM_SRC(id));
1564 			}
1565 		}
1566 		if (PARAM_IS_SRC_GAIN(id)) {
1567 			if (size != sizeof(float))
1568 				goto err;
1569 			output->SetOutputChannelSourceGain(PARAM_CHAN(id), PARAM_SRC(id), PERCENT_TO_GAIN(static_cast<const float *>(value)[0]));
1570 		}
1571 		fCore->Settings()->SaveConnectionSettings(output);
1572 	} else {
1573 		MixerInput *input;
1574 		for (int i = 0; (input = fCore->Input(i)); i++)
1575 			if (input->ID() == param)
1576 				break;
1577 		if (!input || (!PARAM_IS_MUTE(id) && !PARAM_IS_GAIN(id) && !PARAM_IS_DST_ENABLE(id) && !PARAM_IS_BALANCE(id)))
1578 			goto err;
1579 		if (PARAM_IS_MUTE(id)) {
1580 			// input mute control
1581 			if (size != sizeof(int32))
1582 				goto err;
1583 			input->SetEnabled(!static_cast<const int32 *>(value)[0]);
1584 		}
1585 		if (PARAM_IS_GAIN(id)) {
1586 			// input gain control
1587 			if (fCore->Settings()->InputGainControls() == 0) {
1588 				// Physical input channels
1589 				if (fCore->Settings()->UseBalanceControl() && input->GetInputChannelCount() == 2 && 1 /*channel mask is stereo */) {
1590 					// single channel control + balance
1591 					float l = input->GetInputChannelGain(0);
1592 					float r = input->GetInputChannelGain(1);
1593 					float m = (l + r) / 2;	// master volume
1594 					float v = DB_TO_GAIN(static_cast<const float *>(value)[0]);
1595 					float f = v / m;		// factor for both channels
1596 					TRACE("gain set l %1.3f, r %1.3f, m %1.3f, v %1.3f, f %1.3f\n",l,r,m,v,f);
1597 					input->SetInputChannelGain(0, input->GetInputChannelGain(0) * f);
1598 					input->SetInputChannelGain(1, input->GetInputChannelGain(1) * f);
1599 				} else {
1600 					// multi channel control
1601 					if (size < input->GetInputChannelCount() * sizeof(float))
1602 						goto err;
1603 					for (int chan = 0; chan < input->GetInputChannelCount(); chan++)
1604 						input->SetInputChannelGain(chan, DB_TO_GAIN(static_cast<const float *>(value)[chan]));
1605 				}
1606 			} else {
1607 				// Virtual output channels
1608 				if (fCore->Settings()->UseBalanceControl() && input->GetMixerChannelCount() == 2 && 1 /*channel mask is stereo */) {
1609 					// single channel control + balance
1610 					float l = input->GetMixerChannelGain(0);
1611 					float r = input->GetMixerChannelGain(1);
1612 					float m = (l + r) / 2;	// master volume
1613 					float v = DB_TO_GAIN(static_cast<const float *>(value)[0]);
1614 					float f = v / m;		// factor for both channels
1615 					TRACE("gain set l %1.3f, r %1.3f, m %1.3f, v %1.3f, f %1.3f\n",l,r,m,v,f);
1616 					input->SetMixerChannelGain(0, input->GetMixerChannelGain(0) * f);
1617 					input->SetMixerChannelGain(1, input->GetMixerChannelGain(1) * f);
1618 				} else {
1619 					// multi channel control
1620 					if (size < input->GetMixerChannelCount() * sizeof(float))
1621 						goto err;
1622 					for (int chan = 0; chan < input->GetMixerChannelCount(); chan++)
1623 						input->SetMixerChannelGain(chan, DB_TO_GAIN(static_cast<const float *>(value)[chan]));
1624 				}
1625 			}
1626 		}
1627 		if (PARAM_IS_BALANCE(id)) {
1628 			if (fCore->Settings()->InputGainControls() == 0) {
1629 				// Physical input channels
1630 				float l = input->GetInputChannelGain(0);
1631 				float r = input->GetInputChannelGain(1);
1632 				float m = (l + r) / 2;	// master volume
1633 				float v = static_cast<const float *>(value)[0] / 100; // current balance value
1634 				float fl = 2 * (1 - v);	// left channel factor of master volume
1635 				float fr = 2 * v;		// right channel factor of master volume
1636 				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);
1637 				input->SetInputChannelGain(0, m * fl);
1638 				input->SetInputChannelGain(1, m * fr);
1639 			} else {
1640 				// Virtual output channels
1641 				float l = input->GetMixerChannelGain(0);
1642 				float r = input->GetMixerChannelGain(1);
1643 				float m = (l + r) / 2;	// master volume
1644 				float v = static_cast<const float *>(value)[0] / 100; // current balance value
1645 				float fl = 2 * (1 - v);	// left channel factor of master volume
1646 				float fr = 2 * v;		// right channel factor of master volume
1647 				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);
1648 				input->SetMixerChannelGain(0, m * fl);
1649 				input->SetMixerChannelGain(1, m * fr);
1650 			}
1651 		}
1652 		if (PARAM_IS_DST_ENABLE(id)) {
1653 			if (size != sizeof(int32))
1654 				goto err;
1655 			if (static_cast<const int32 *>(value)[0]) {
1656 				int oldchan = input->GetInputChannelForDestination(PARAM_DST(id));
1657 				if (oldchan != -1) {
1658 					input->RemoveInputChannelDestination(oldchan, PARAM_DST(id));
1659 					int32 null = 0;
1660 					BroadcastNewParameterValue(when, PARAM_DST_ENABLE(PARAM(id), oldchan, PARAM_DST(id)), &null, sizeof(null));
1661 				}
1662 				input->AddInputChannelDestination(PARAM_CHAN(id), PARAM_DST(id));
1663 			} else {
1664 				input->RemoveInputChannelDestination(PARAM_CHAN(id), PARAM_DST(id));
1665 			}
1666 			// TODO: this is really annoying
1667 			// The slider count of the gain control needs to be changed,
1668 			// but calling SetChannelCount(input->GetMixerChannelCount())
1669 			// on it has no effect on remote BParameterWebs in other apps.
1670 			// BroadcastChangedParameter() should be correct, but doesn't work
1671 			BroadcastChangedParameter(PARAM_GAIN(PARAM(id)));
1672 			// We trigger a complete ParameterWeb update as workaround
1673 			// but it will change the focus from tab 3 to tab 1
1674 			update = true;
1675 		}
1676 		fCore->Settings()->SaveConnectionSettings(input);
1677 	}
1678 
1679 	BroadcastNewParameterValue(when, id, const_cast<void *>(value), size);
1680 
1681 err:
1682 	fCore->Unlock();
1683 	if (update)
1684 		UpdateParameterWeb();
1685 }
1686 
1687 
1688 void
1689 AudioMixer::UpdateParameterWeb()
1690 {
1691 	fCore->Lock();
1692 	BParameterWeb *web = new BParameterWeb();
1693 	BParameterGroup *top;
1694 	BParameterGroup *outputchannels;
1695 	BParameterGroup *inputchannels;
1696 	BParameterGroup *group;
1697 	BParameterGroup *subgroup;
1698 	BParameterGroup *subsubgroup;
1699 	BDiscreteParameter *dp;
1700 	MixerInput *in;
1701 	MixerOutput *out;
1702 	char buf[50];
1703 
1704 	top = web->MakeGroup(B_TRANSLATE("Gain controls"));
1705 
1706 	out = fCore->Output();
1707 	group = top->MakeGroup("");
1708 	group->MakeNullParameter(PARAM_STR1(0), B_MEDIA_RAW_AUDIO,
1709 		B_TRANSLATE("Master output"), B_WEB_BUFFER_INPUT);
1710 	if (!out) {
1711 		group->MakeNullParameter(PARAM_STR2(0), B_MEDIA_RAW_AUDIO,
1712 			B_TRANSLATE("not connected"), B_GENERIC);
1713 	} else {
1714 		group->MakeNullParameter(PARAM_STR2(0), B_MEDIA_RAW_AUDIO,
1715 			StringForFormat(buf, out), B_GENERIC);
1716 		group->MakeDiscreteParameter(PARAM_MUTE(0), B_MEDIA_RAW_AUDIO,
1717 			B_TRANSLATE("Mute"), B_MUTE);
1718 		if (fCore->Settings()->UseBalanceControl()
1719 			&& out->GetOutputChannelCount() == 2 && 1
1720 			/*channel mask is stereo */) {
1721 			// single channel control + balance
1722 			group->MakeContinuousParameter(PARAM_GAIN(0), B_MEDIA_RAW_AUDIO,
1723 				B_TRANSLATE("Gain"), B_MASTER_GAIN, B_TRANSLATE("dB"),
1724 				DB_MIN, DB_MAX, 0.1);
1725 			group->MakeContinuousParameter(PARAM_BALANCE(0), B_MEDIA_RAW_AUDIO,
1726 				"", B_BALANCE, "", 0, 100, 1);
1727 		} else {
1728 			// multi channel control
1729 			group->MakeContinuousParameter(PARAM_GAIN(0), B_MEDIA_RAW_AUDIO,
1730 				B_TRANSLATE("Gain"), B_MASTER_GAIN, B_TRANSLATE("dB"),
1731 				DB_MIN, DB_MAX, 0.1)
1732 				   ->SetChannelCount(out->GetOutputChannelCount());
1733 		}
1734 		group->MakeNullParameter(PARAM_STR3(0), B_MEDIA_RAW_AUDIO,
1735 			B_TRANSLATE("To output"), B_WEB_BUFFER_OUTPUT);
1736 	}
1737 
1738 	for (int i = 0; (in = fCore->Input(i)); i++) {
1739 		group = top->MakeGroup("");
1740 		group->MakeNullParameter(PARAM_STR1(in->ID()), B_MEDIA_RAW_AUDIO,
1741 			in->MediaInput().name, B_WEB_BUFFER_INPUT);
1742 		group->MakeNullParameter(PARAM_STR2(in->ID()), B_MEDIA_RAW_AUDIO,
1743 			StringForFormat(buf, in), B_GENERIC);
1744 		group->MakeDiscreteParameter(PARAM_MUTE(in->ID()), B_MEDIA_RAW_AUDIO,
1745 			B_TRANSLATE("Mute"), B_MUTE);
1746 		// XXX the gain control is ugly once you have more than two channels,
1747 		//     as you don't know what channel each slider controls. Tooltips might help...
1748 		if (fCore->Settings()->InputGainControls() == 0) {
1749 			// Physical input channels
1750 			if (fCore->Settings()->UseBalanceControl()
1751 				&& in->GetInputChannelCount() == 2 && 1
1752 				/*channel mask is stereo */) {
1753 				// single channel control + balance
1754 				group->MakeContinuousParameter(PARAM_GAIN(in->ID()),
1755 					B_MEDIA_RAW_AUDIO, B_TRANSLATE("Gain"), B_GAIN,
1756 					B_TRANSLATE("dB"), DB_MIN, DB_MAX, 0.1);
1757 				group->MakeContinuousParameter(PARAM_BALANCE(in->ID()),
1758 					B_MEDIA_RAW_AUDIO, "", B_BALANCE, "", 0, 100, 1);
1759 			} else {
1760 				// multi channel control
1761 				group->MakeContinuousParameter(PARAM_GAIN(in->ID()),
1762 					B_MEDIA_RAW_AUDIO, B_TRANSLATE("Gain"), B_GAIN,
1763 					B_TRANSLATE("dB"), DB_MIN, DB_MAX, 0.1)
1764 						->SetChannelCount(in->GetInputChannelCount());
1765 			}
1766 		} else {
1767 			// Virtual output channels
1768 			if (fCore->Settings()->UseBalanceControl()
1769 				&& in->GetMixerChannelCount() == 2 && 1
1770 				/*channel mask is stereo */) {
1771 				// single channel control + balance
1772 				group->MakeContinuousParameter(PARAM_GAIN(in->ID()),
1773 					B_MEDIA_RAW_AUDIO, B_TRANSLATE("Gain"), B_GAIN,
1774 					B_TRANSLATE("dB"), DB_MIN, DB_MAX, 0.1);
1775 				group->MakeContinuousParameter(PARAM_BALANCE(in->ID()),
1776 					B_MEDIA_RAW_AUDIO, "", B_BALANCE, "", 0, 100, 1);
1777 			} else {
1778 				// multi channel control
1779 				group->MakeContinuousParameter(PARAM_GAIN(in->ID()),
1780 					B_MEDIA_RAW_AUDIO, B_TRANSLATE("Gain"), B_GAIN,
1781 					B_TRANSLATE("dB"), DB_MIN, DB_MAX, 0.1)
1782 						->SetChannelCount(in->GetMixerChannelCount());
1783 			}
1784 		}
1785 		group->MakeNullParameter(PARAM_STR3(in->ID()), B_MEDIA_RAW_AUDIO,
1786 			B_TRANSLATE("To master"), B_WEB_BUFFER_OUTPUT);
1787 	}
1788 
1789 	if (fCore->Settings()->AllowOutputChannelRemapping()) {
1790 		top = web->MakeGroup(B_TRANSLATE("Output mapping")); // top level group
1791 		outputchannels = top->MakeGroup("");
1792 		outputchannels->MakeNullParameter(PARAM_STR4(0), B_MEDIA_RAW_AUDIO,
1793 			B_TRANSLATE("Output channel sources"), B_GENERIC);
1794 
1795 		group = outputchannels->MakeGroup("");
1796 		group->MakeNullParameter(PARAM_STR5(0), B_MEDIA_RAW_AUDIO,
1797 			B_TRANSLATE("Master output"), B_GENERIC);
1798 		group = group->MakeGroup("");
1799 		if (!out) {
1800 			group->MakeNullParameter(PARAM_STR6(0), B_MEDIA_RAW_AUDIO,
1801 				B_TRANSLATE("not connected"), B_GENERIC);
1802 		} else {
1803 			for (int chan = 0; chan < out->GetOutputChannelCount(); chan++) {
1804 				subgroup = group->MakeGroup("");
1805 				subgroup->MakeNullParameter(PARAM_SRC_STR(0, chan),
1806 					B_MEDIA_RAW_AUDIO, StringForChannelType(buf,
1807 						out->GetOutputChannelType(chan)), B_GENERIC);
1808 				for (int src = 0; src < MAX_CHANNEL_TYPES; src++) {
1809 					subsubgroup = subgroup->MakeGroup("");
1810 					subsubgroup->MakeDiscreteParameter(
1811 						PARAM_SRC_ENABLE(0, chan, src), B_MEDIA_RAW_AUDIO, "",
1812 						B_ENABLE);
1813 					subsubgroup->MakeContinuousParameter(
1814 						PARAM_SRC_GAIN(0, chan, src), B_MEDIA_RAW_AUDIO,
1815 						StringForChannelType(buf, src), B_GAIN, "%", 0.0,
1816 						100.0, 0.1);
1817 				}
1818 			}
1819 		}
1820 	}
1821 
1822 	if (fCore->Settings()->AllowInputChannelRemapping()) {
1823 		top = web->MakeGroup(B_TRANSLATE("Input mapping")); // top level group
1824 		inputchannels = top->MakeGroup("");
1825 		inputchannels->MakeNullParameter(PARAM_STR7(0), B_MEDIA_RAW_AUDIO,
1826 			B_TRANSLATE("Input channel destinations"), B_GENERIC);
1827 
1828 		for (int i = 0; (in = fCore->Input(i)); i++) {
1829 			group = inputchannels->MakeGroup("");
1830 			group->MakeNullParameter(PARAM_STR4(in->ID()), B_MEDIA_RAW_AUDIO,
1831 				in->MediaInput().name, B_GENERIC);
1832 			group = group->MakeGroup("");
1833 
1834 			for (int chan = 0; chan < in->GetInputChannelCount(); chan++) {
1835 				subgroup = group->MakeGroup("");
1836 				subgroup->MakeNullParameter(PARAM_DST_STR(in->ID(), chan),
1837 					B_MEDIA_RAW_AUDIO, StringForChannelType(buf,
1838 					in->GetInputChannelType(chan)), B_GENERIC);
1839 				for (int dst = 0; dst < MAX_CHANNEL_TYPES; dst++) {
1840 					subgroup->MakeDiscreteParameter(PARAM_DST_ENABLE(in->ID(),
1841 					chan, dst), B_MEDIA_RAW_AUDIO, StringForChannelType(buf, dst),
1842 					B_ENABLE);
1843 				}
1844 			}
1845 		}
1846 	}
1847 
1848 	top = web->MakeGroup(B_TRANSLATE("Setup")); // top level group
1849 	group = top->MakeGroup("");
1850 
1851 	group->MakeDiscreteParameter(PARAM_ETC(10), B_MEDIA_RAW_AUDIO,
1852 		B_TRANSLATE("Attenuate mixer output by 3dB (like BeOS R5)"), B_ENABLE);
1853 	group->MakeDiscreteParameter(PARAM_ETC(20), B_MEDIA_RAW_AUDIO,
1854 		B_TRANSLATE("Use non linear gain sliders (like BeOS R5)"), B_ENABLE);
1855 	group->MakeDiscreteParameter(PARAM_ETC(30), B_MEDIA_RAW_AUDIO,
1856 		B_TRANSLATE("Display balance control for stereo connections"),
1857 		B_ENABLE);
1858 
1859 	group->MakeDiscreteParameter(PARAM_ETC(40), B_MEDIA_RAW_AUDIO,
1860 		B_TRANSLATE("Allow output channel remapping"), B_ENABLE);
1861 	group->MakeDiscreteParameter(PARAM_ETC(50), B_MEDIA_RAW_AUDIO,
1862 		B_TRANSLATE("Allow input channel remapping"), B_ENABLE);
1863 
1864 	dp = group->MakeDiscreteParameter(PARAM_ETC(60), B_MEDIA_RAW_AUDIO,
1865 		B_TRANSLATE("Input gain controls represent"), B_INPUT_MUX);
1866 	dp->AddItem(0, B_TRANSLATE("Physical input channels"));
1867 	dp->AddItem(1, B_TRANSLATE("Virtual output channels"));
1868 
1869 	dp = group->MakeDiscreteParameter(PARAM_ETC(70), B_MEDIA_RAW_AUDIO,
1870 		B_TRANSLATE("Resampling algorithm"), B_INPUT_MUX);
1871 	dp->AddItem(0, B_TRANSLATE("Drop/repeat samples"));
1872 	dp->AddItem(2, B_TRANSLATE("Linear interpolation"));
1873 
1874 	// Note: The following code is outcommented on purpose
1875 	// and is about to be modified at a later point
1876 	/*
1877 	dp->AddItem(1, B_TRANSLATE("Drop/repeat samples (template based)"));
1878 	dp->AddItem(3, B_TRANSLATE("17th order filtering"));
1879 	*/
1880 	group->MakeDiscreteParameter(PARAM_ETC(80), B_MEDIA_RAW_AUDIO,
1881 		B_TRANSLATE("Refuse output format changes"), B_ENABLE);
1882 	group->MakeDiscreteParameter(PARAM_ETC(90), B_MEDIA_RAW_AUDIO,
1883 		B_TRANSLATE("Refuse input format changes"), B_ENABLE);
1884 
1885 	fCore->Unlock();
1886 	SetParameterWeb(web);
1887 }
1888 
1889 
1890 #if USE_MEDIA_FORMAT_WORKAROUND
1891 static void
1892 raw_audio_format_specialize(media_raw_audio_format *format,
1893 	const media_raw_audio_format *other)
1894 {
1895 	if (format->frame_rate == 0)
1896 		format->frame_rate = other->frame_rate;
1897 	if (format->channel_count == 0)
1898 		format->channel_count = other->channel_count;
1899 	if (format->format == 0)
1900 		format->format = other->format;
1901 	if (format->byte_order == 0)
1902 		format->byte_order = other->byte_order;
1903 	if (format->buffer_size == 0)
1904 		format->buffer_size = other->buffer_size;
1905 	if (format->frame_rate == 0)
1906 		format->frame_rate = other->frame_rate;
1907 }
1908 
1909 
1910 static void
1911 multi_audio_info_specialize(media_multi_audio_info *format,
1912 	const media_multi_audio_info *other)
1913 {
1914 	if (format->channel_mask == 0)
1915 		format->channel_mask = other->channel_mask;
1916 	if (format->valid_bits == 0)
1917 		format->valid_bits = other->valid_bits;
1918 	if (format->matrix_mask == 0)
1919 		format->matrix_mask = other->matrix_mask;
1920 }
1921 
1922 
1923 static void
1924 multi_audio_format_specialize(media_multi_audio_format *format,
1925 	const media_multi_audio_format *other)
1926 {
1927 	raw_audio_format_specialize(format, other);
1928 	multi_audio_info_specialize(format, other);
1929 }
1930 #endif
1931