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