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