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