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