xref: /haiku/src/add-ons/media/media-add-ons/mixer/AudioMixer.cpp (revision 02354704729d38c3b078c696adc1bbbd33cbcf72)
1 /*
2  * Copyright 2002 David Shipman,
3  * Copyright 2003-2007 Marcus Overhagen
4  * Copyright 2007-2011 Haiku Inc. All rights reserved.
5  *
6  * Distributed under the terms of the MIT License.
7  */
8 
9 
10 #include "AudioMixer.h"
11 
12 #include <math.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 
17 #include <Buffer.h>
18 #include <Catalog.h>
19 #include <FindDirectory.h>
20 #include <MediaDefs.h>
21 #include <MediaRoster.h>
22 #include <ParameterWeb.h>
23 #include <Path.h>
24 #include <RealtimeAlloc.h>
25 #include <TimeSource.h>
26 
27 #include "MixerCore.h"
28 #include "MixerInput.h"
29 #include "MixerOutput.h"
30 #include "MixerUtils.h"
31 
32 
33 #undef B_TRANSLATION_CONTEXT
34 #define B_TRANSLATION_CONTEXT "AudioMixer"
35 
36 
37 // the range of the gain sliders (in dB)
38 #define DB_MAX	18.0
39 #define DB_MIN	-60.0
40 // when using non linear sliders, we use a power function with
41 #define DB_EXPONENT_POSITIVE 1.4	// for dB values > 0
42 #define DB_EXPONENT_NEGATIVE 1.8	// for dB values < 0
43 
44 #define DB_TO_GAIN(db)			dB_to_Gain((db))
45 #define GAIN_TO_DB(gain)		Gain_to_dB((gain))
46 #define PERCENT_TO_GAIN(pct)	((pct) / 100.0)
47 #define GAIN_TO_PERCENT(gain)	((gain)  * 100.0)
48 
49 // the id is encoded with 16 bits
50 // then chan and src (or dst) are encoded with 6 bits
51 // the unique number with 4 bits
52 // the PARAM_ETC etc is encoded with 26 bits
53 #define PARAM_SRC_ENABLE(id, chan, src)		(((id) << 16) | ((chan) << 10) | ((src) << 4) | 0x1)
54 #define PARAM_SRC_GAIN(id, chan, src)		(((id) << 16) | ((chan) << 10) | ((src) << 4) | 0x2)
55 #define PARAM_DST_ENABLE(id, chan, dst)		(((id) << 16) | ((chan) << 10) | ((dst) << 4) | 0x3)
56 #define PARAM_ETC(etc)						(((etc) << 16) | 0x4)
57 #define PARAM_SRC_STR(id, chan)				(((id) << 16) | ((chan) << 10) | 0x5)
58 #define PARAM_DST_STR(id, chan)				(((id) << 16) | ((chan) << 10) | 0x6)
59 #define PARAM_MUTE(id)						(((id) << 16) | 0x7)
60 #define PARAM_GAIN(id)						(((id) << 16) | 0x8)
61 #define PARAM_BALANCE(id)					(((id) << 16) | 0x9)
62 #define PARAM_STR1(id)						(((id) << 16) | ((1) << 10) | 0xa)
63 #define PARAM_STR2(id)						(((id) << 16) | ((2) << 10) | 0xa)
64 #define PARAM_STR3(id)						(((id) << 16) | ((3) << 10) | 0xa)
65 #define PARAM_STR4(id)						(((id) << 16) | ((4) << 10) | 0xa)
66 #define PARAM_STR5(id)						(((id) << 16) | ((5) << 10) | 0xa)
67 #define PARAM_STR6(id)						(((id) << 16) | ((6) << 10) | 0xa)
68 #define PARAM_STR7(id)						(((id) << 16) | ((7) << 10) | 0xa)
69 
70 #define PARAM(id)							((id) >> 16)
71 #define ETC(id)								((id) >> 16)
72 #define PARAM_CHAN(id)						(((id) >> 10) & 0x3f)
73 #define PARAM_SRC(id)						(((id) >> 4) & 0x3f)
74 #define PARAM_DST(id)						(((id) >> 4) & 0x3f)
75 #define PARAM_IS_SRC_ENABLE(id)				(((id) & 0xf) == 0x1)
76 #define PARAM_IS_SRC_GAIN(id)				(((id) & 0xf) == 0x2)
77 #define PARAM_IS_DST_ENABLE(id)				(((id) & 0xf) == 0x3)
78 #define PARAM_IS_ETC(id)					(((id) & 0xf) == 0x4)
79 #define PARAM_IS_MUTE(id)					(((id) & 0xf) == 0x7)
80 #define PARAM_IS_GAIN(id)					(((id) & 0xf) == 0x8)
81 #define PARAM_IS_BALANCE(id)				(((id) & 0xf) == 0x9)
82 
83 #define FORMAT_USER_DATA_TYPE 		0x7294a8f3
84 #define FORMAT_USER_DATA_MAGIC_1	0xc84173bd
85 #define FORMAT_USER_DATA_MAGIC_2	0x4af62b7d
86 
87 
88 const static bigtime_t kMaxLatency = 150000;
89 	// 150 ms is the maximum latency we publish
90 
91 const bigtime_t kMinMixingTime = 3500;
92 const bigtime_t kMaxJitter = 1500;
93 
94 
95 AudioMixer::AudioMixer(BMediaAddOn *addOn, bool isSystemMixer)
96 	:
97 	BMediaNode("Audio Mixer"),
98 	BBufferConsumer(B_MEDIA_RAW_AUDIO),
99 	BBufferProducer(B_MEDIA_RAW_AUDIO),
100 	BControllable(),
101 	BMediaEventLooper(),
102 	fAddOn(addOn),
103 	fCore(new MixerCore(this)),
104 	fWeb(NULL),
105 	fBufferGroup(NULL),
106 	fDownstreamLatency(1),
107 	fInternalLatency(1),
108 	fDisableStop(false),
109 	fLastLateNotification(0),
110 	fLastLateness(0)
111 {
112 	BMediaNode::AddNodeKind(B_SYSTEM_MIXER);
113 
114 	// this is the default format used for all wildcard format SpecializeTo()s
115 	fDefaultFormat.type = B_MEDIA_RAW_AUDIO;
116 	fDefaultFormat.u.raw_audio.frame_rate = 96000;
117 	fDefaultFormat.u.raw_audio.channel_count = 2;
118 	fDefaultFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT;
119 	fDefaultFormat.u.raw_audio.byte_order = B_MEDIA_HOST_ENDIAN;
120 	fDefaultFormat.u.raw_audio.buffer_size = 4096;
121 	fDefaultFormat.u.raw_audio.channel_mask = 0;
122 	fDefaultFormat.u.raw_audio.valid_bits = 0;
123 	fDefaultFormat.u.raw_audio.matrix_mask = 0;
124 
125 	if (isSystemMixer) {
126 		// to get persistent settings, assign a settings file
127 		BPath path;
128 		if (B_OK != find_directory (B_USER_SETTINGS_DIRECTORY, &path))
129 			path.SetTo("/boot/home/config/settings/");
130 		path.Append("System Audio Mixer");
131 		fCore->Settings()->SetSettingsFile(path.Path());
132 
133 		// disable stop on the auto started (system) mixer
134 		DisableNodeStop();
135 	}
136 
137 	ApplySettings();
138 }
139 
140 
141 AudioMixer::~AudioMixer()
142 {
143 	BMediaEventLooper::Quit();
144 	SetParameterWeb(NULL);
145 
146 	// stop the mixer
147 	fCore->Lock();
148 	fCore->Stop();
149 	fCore->Unlock();
150 
151 	// disconnect all nodes from the mixer
152 	// XXX todo
153 
154 	delete fCore;
155 	delete fBufferGroup;
156 
157 	DEBUG_ONLY(fCore = 0; fBufferGroup = 0; fWeb = 0);
158 }
159 
160 
161 void
162 AudioMixer::ApplySettings()
163 {
164 	fCore->Lock();
165 	fCore->SetOutputAttenuation(fCore->Settings()->AttenuateOutput() ? 0.708 : 1.0);
166 	fCore->Unlock();
167 }
168 
169 
170 void
171 AudioMixer::DisableNodeStop()
172 {
173 	fDisableStop = true;
174 }
175 
176 
177 //	#pragma mark - BMediaNode methods
178 
179 
180 void
181 AudioMixer::Stop(bigtime_t performance_time, bool immediate)
182 {
183 	if (fDisableStop) {
184 		TRACE("AudioMixer STOP is disabled\n");
185 		return;
186 	} else {
187 		BMediaEventLooper::Stop(performance_time, immediate);
188 	}
189 }
190 
191 
192 BMediaAddOn*
193 AudioMixer::AddOn(int32 *internal_id) const
194 {
195 	*internal_id = 0;
196 	return fAddOn;
197 }
198 
199 
200 //	#pragma mark - BBufferConsumer methods
201 
202 
203 status_t
204 AudioMixer::HandleMessage(int32 message, const void *data, size_t size)
205 {
206 	// since we're using a mediaeventlooper, there shouldn't be any messages
207 	// except the message we are using to schedule output events for the
208 	// process thread.
209 
210 	if (message == MIXER_SCHEDULE_EVENT) {
211 		RealTimeQueue()->AddEvent(*(const media_timed_event*)data);
212 		return B_OK;
213 	}
214 
215 	return B_ERROR;
216 }
217 
218 
219 status_t
220 AudioMixer::AcceptFormat(const media_destination &dest, media_format *ioFormat)
221 {
222 	PRINT_FORMAT("AudioMixer::AcceptFormat: ", *ioFormat);
223 
224 	// check that the specified format is reasonable for the specified destination, and
225 	// fill in any wildcard fields for which our BBufferConsumer has specific requirements.
226 
227 	// we have multiple inputs with different IDs, but
228 	// the port number must match our ControlPort()
229 	if (dest.port != ControlPort())
230 		return B_MEDIA_BAD_DESTINATION;
231 
232 	// specialize to raw audio format if necessary
233 	if (ioFormat->type == B_MEDIA_UNKNOWN_TYPE)
234 		ioFormat->type = B_MEDIA_RAW_AUDIO;
235 
236 	// we require a raw audio format
237 	if (ioFormat->type != B_MEDIA_RAW_AUDIO)
238 		return B_MEDIA_BAD_FORMAT;
239 
240 	// We do not have special requirements, but just in case
241 	// another mixer is connecting to us and may need a hint
242 	// to create a connection at optimal frame rate and
243 	// channel count, we place this information in the user_data
244 	fCore->Lock();
245 	MixerOutput *output = fCore->Output();
246 	uint32 channel_count = output ? output->MediaOutput().format.u.raw_audio.channel_count : 0;
247 	float frame_rate = output ? output->MediaOutput().format.u.raw_audio.frame_rate : 0.0;
248 	fCore->Unlock();
249 	ioFormat->user_data_type = FORMAT_USER_DATA_TYPE;
250 	*(uint32 *)&ioFormat->user_data[0] = FORMAT_USER_DATA_MAGIC_1;
251 	*(uint32 *)&ioFormat->user_data[4] = channel_count;
252 	*(float *)&ioFormat->user_data[20] = frame_rate;
253 	*(uint32 *)&ioFormat->user_data[44] = FORMAT_USER_DATA_MAGIC_2;
254 
255 	return B_OK;
256 }
257 
258 
259 status_t
260 AudioMixer::GetNextInput(int32 *cookie, media_input *out_input)
261 {
262 	TRACE("AudioMixer::GetNextInput\n");
263 
264 	// our 0th input is always a wildcard and free one
265 	if (*cookie == 0) {
266 		out_input->node = Node();
267 		out_input->source = media_source::null;
268 		out_input->destination.port = ControlPort();
269 		out_input->destination.id = 0;
270 		out_input->format.Clear();
271 		out_input->format.type = B_MEDIA_RAW_AUDIO;
272 		strcpy(out_input->name, "Free Input");
273 		*cookie += 1;
274 		return B_OK;
275 	}
276 
277 	// the other inputs are the currently connected ones
278 	fCore->Lock();
279 	MixerInput *input;
280 	input = fCore->Input(*cookie - 1);
281 	if (!input) {
282 		fCore->Unlock();
283 		return B_BAD_INDEX;
284 	}
285 	*out_input = input->MediaInput();
286 	*cookie += 1;
287 	fCore->Unlock();
288 	return B_OK;
289 }
290 
291 
292 void
293 AudioMixer::DisposeInputCookie(int32 cookie)
294 {
295 	// nothing to do
296 }
297 
298 
299 void
300 AudioMixer::BufferReceived(BBuffer *buffer)
301 {
302 
303 	if (buffer->Header()->type == B_MEDIA_PARAMETERS) {
304 		TRACE("Control Buffer Received\n");
305 		ApplyParameterData(buffer->Data(), buffer->SizeUsed());
306 		buffer->Recycle();
307 		return;
308 	}
309 
310 	//PRINT(4, "buffer received at %12Ld, should arrive at %12Ld, delta %12Ld\n", TimeSource()->Now(), buffer->Header()->start_time, TimeSource()->Now() - buffer->Header()->start_time);
311 
312 	// to receive the buffer at the right time,
313 	// push it through the event looper
314 	media_timed_event event(buffer->Header()->start_time,
315 		BTimedEventQueue::B_HANDLE_BUFFER, buffer,
316 		BTimedEventQueue::B_RECYCLE_BUFFER);
317 	EventQueue()->AddEvent(event);
318 }
319 
320 
321 void
322 AudioMixer::HandleInputBuffer(BBuffer* buffer, bigtime_t lateness)
323 {
324 	bigtime_t variation = 0;
325 	if (lateness > fLastLateness)
326 		variation = lateness-fLastLateness;
327 
328 	if (variation > kMaxJitter) {
329 		TRACE("AudioMixer: Dequeued input buffer %" B_PRIdBIGTIME
330 			" usec late\n", lateness);
331 		if (RunMode() == B_DROP_DATA || RunMode() == B_DECREASE_PRECISION
332 			|| RunMode() == B_INCREASE_LATENCY) {
333 			TRACE("AudioMixer: sending notify\n");
334 
335 			// Build a media_source out of the header data
336 			media_source source = media_source::null;
337 			source.port = buffer->Header()->source_port;
338 			source.id = buffer->Header()->source;
339 
340 			NotifyLateProducer(source, variation, TimeSource()->Now());
341 
342 			if (RunMode() == B_DROP_DATA) {
343 				TRACE("AudioMixer: dropping buffer\n");
344 				return;
345 			}
346 		}
347 	}
348 
349 	fLastLateness = lateness;
350 
351 	fCore->Lock();
352 	fCore->BufferReceived(buffer, lateness);
353 	fCore->Unlock();
354 }
355 
356 
357 void
358 AudioMixer::ProducerDataStatus(const media_destination& for_whom,
359 	int32 status, bigtime_t at_performance_time)
360 {
361 /*
362 	if (IsValidDest(for_whom))
363 	{
364 		media_timed_event event(at_performance_time, BTimedEventQueue::B_DATA_STATUS,
365 			(void *)(&for_whom), BTimedEventQueue::B_NO_CLEANUP, status, 0, NULL);
366 		EventQueue()->AddEvent(event);
367 
368 		// FIX_THIS
369 		// the for_whom destination is not being sent correctly - verify in HandleEvent loop
370 
371 	}
372 */
373 }
374 
375 
376 status_t
377 AudioMixer::GetLatencyFor(const media_destination &for_whom,
378 	bigtime_t *out_latency, media_node_id *out_timesource)
379 {
380 	// we have multiple inputs with different IDs, but
381 	// the port number must match our ControlPort()
382 	if (for_whom.port != ControlPort())
383 		return B_MEDIA_BAD_DESTINATION;
384 
385 	// return our event latency - this includes our internal + downstream
386 	// latency, but _not_ the scheduling latency
387 	*out_latency = EventLatency();
388 	*out_timesource = TimeSource()->ID();
389 
390 	printf("AudioMixer::GetLatencyFor %" B_PRIdBIGTIME ", timesource is %"
391 		B_PRId32 "\n", *out_latency, *out_timesource);
392 
393 	return B_OK;
394 }
395 
396 
397 status_t
398 AudioMixer::Connected(const media_source &producer,
399 	const media_destination &where, const media_format &with_format,
400 	media_input *out_input)
401 {
402 	PRINT_FORMAT("AudioMixer::Connected: ", with_format);
403 
404 	// workaround for a crashing bug in RealPlayer.  to be proper, RealPlayer's
405 	// BBufferProducer::PrepareToConnect() should have removed all wildcards.
406 	if (out_input->format.u.raw_audio.frame_rate == 0) {
407 		fprintf(stderr, "Audio Mixer Warning: "
408 			"Producer (port %" B_PRId32 ", id %" B_PRId32 ") connected with "
409 			"frame_rate=0\n", producer.port, producer.id);
410 		MixerOutput *output = fCore->Output();
411 		float frame_rate = output
412 			? output->MediaOutput().format.u.raw_audio.frame_rate : 44100.0f;
413 		out_input->format.u.raw_audio.frame_rate = frame_rate;
414 	}
415 
416 	// a BBufferProducer is connecting to our BBufferConsumer
417 
418 	// incoming connections should always have an incoming ID=0,
419 	// and the port number must match our ControlPort()
420 	if (where.id != 0 || where.port != ControlPort())
421 		return B_MEDIA_BAD_DESTINATION;
422 
423 	fCore->Lock();
424 
425 	// we assign a new id (!= 0) to the newly created input
426 	out_input->destination.id = fCore->CreateInputID();
427 
428 	// We need to make sure that the outInput's name field contains a valid
429 	// name, the name given the connection by the producer may still be an
430 	// empty string.
431 	// if the input has no name, assign one
432 	if (strlen(out_input->name) == 0) {
433 		sprintf(out_input->name, "Input %" B_PRId32,
434 			out_input->destination.id);
435 	}
436 
437 	// add a new input to the mixer engine
438 	MixerInput *input;
439 	input = fCore->AddInput(*out_input);
440 
441 	fCore->Settings()->LoadConnectionSettings(input);
442 
443 	fCore->Unlock();
444 
445 	// If we want the producer to use a specific BBufferGroup, we now need
446 	// to call BMediaRoster::SetOutputBuffersFor() here to set the producer's
447 	// buffer group.
448 	// But we have no special buffer requirements anyway...
449 
450 	UpdateParameterWeb();
451 
452 	return B_OK;
453 }
454 
455 
456 void
457 AudioMixer::Disconnected(const media_source &producer,
458 	const media_destination &where)
459 {
460 	// One of our inputs has been disconnected
461 
462 	// check if it is really belongs to us
463 	if (where.port != ControlPort()) {
464 		TRACE("AudioMixer::Disconnected wrong input port\n");
465 		return;
466 	}
467 
468 	fCore->Lock();
469 
470 	if (!fCore->RemoveInput(where.id)) {
471 		TRACE("AudioMixer::Disconnected can't remove input\n");
472 	}
473 
474 	fCore->Unlock();
475 	UpdateParameterWeb();
476 }
477 
478 
479 status_t
480 AudioMixer::FormatChanged(const media_source &producer,
481 	const media_destination &consumer, int32 change_tag,
482 	const media_format &format)
483 {
484 	// at some point in the future (indicated by change_tag and RequestCompleted()),
485 	// we will receive buffers in a different format
486 
487 	TRACE("AudioMixer::FormatChanged\n");
488 
489 	if (consumer.port != ControlPort() || consumer.id == 0)
490 		return B_MEDIA_BAD_DESTINATION;
491 
492 	if (fCore->Settings()->RefuseInputFormatChange()) {
493 		TRACE("AudioMixer::FormatChanged: input format change refused\n");
494 		return B_NOT_ALLOWED;
495 	}
496 
497 	// TODO: We should not apply the format change at this point
498 	// TODO: At the moment, this is not implemented at the moment and will just
499 	// crash the media_server.
500 	return B_NOT_SUPPORTED;
501 
502 	// tell core about format change
503 	fCore->Lock();
504 	fCore->InputFormatChanged(consumer.id, format.u.raw_audio);
505 	fCore->Unlock();
506 
507 	return B_OK;
508 }
509 
510 
511 //	#pragma mark - BBufferProducer methods
512 
513 
514 status_t
515 AudioMixer::FormatSuggestionRequested(media_type type, int32 quality,
516 	media_format *format)
517 {
518 	TRACE("AudioMixer::FormatSuggestionRequested\n");
519 
520 	// BBufferProducer function, a downstream consumer
521 	// is asking for our output format
522 
523 	if (type != B_MEDIA_RAW_AUDIO && type != B_MEDIA_UNKNOWN_TYPE)
524 		return B_MEDIA_BAD_FORMAT;
525 
526 	// we can produce any (wildcard) raw audio format
527 	format->Clear();
528 	format->type = B_MEDIA_RAW_AUDIO;
529 	return B_OK;
530 }
531 
532 
533 status_t
534 AudioMixer::FormatProposal(const media_source &output, media_format *ioFormat)
535 {
536 	// BBufferProducer function, we implement this function to verify that the
537 	// proposed media_format is suitable for the specified output. If any fields
538 	// in the format are wildcards, and we have a specific requirement, adjust
539 	// those fields to match our requirements before returning.
540 
541 	TRACE("AudioMixer::FormatProposal\n");
542 
543 	// we only have one output (id=0, port=ControlPort())
544 	if (output.id != 0 || output.port != ControlPort())
545 		return B_MEDIA_BAD_SOURCE;
546 
547 	// specialize to raw audio format if necessary
548 	if (ioFormat->type == B_MEDIA_UNKNOWN_TYPE)
549 		ioFormat->type = B_MEDIA_RAW_AUDIO;
550 
551 	// we require a raw audio format
552 	if (ioFormat->type != B_MEDIA_RAW_AUDIO)
553 		return B_MEDIA_BAD_FORMAT;
554 
555 	return B_OK;
556 }
557 
558 /*!	If the format isn't good, put a good format into *io_format and return error
559 	If format has wildcard, specialize to what you can do (and change).
560 	If you can change the format, return OK.
561 	The request comes from your destination sychronously, so you cannot ask it
562 	whether it likes it -- you should assume it will since it asked.
563 */
564 status_t
565 AudioMixer::FormatChangeRequested(const media_source &source,
566 	const media_destination &destination, media_format *io_format,
567 	int32 *_deprecated_)
568 {
569 	// the downstream consumer node (soundcard) requested that we produce
570 	// another format, we need to check if the format is acceptable and
571 	// remove any wildcards before returning OK.
572 
573 	TRACE("AudioMixer::FormatChangeRequested\n");
574 
575 	if (fCore->Settings()->RefuseOutputFormatChange()) {
576 		TRACE("AudioMixer::FormatChangeRequested: output format change refused\n");
577 		return B_ERROR;
578 	}
579 
580 	fCore->Lock();
581 
582 	status_t status = B_OK;
583 	BBufferGroup *group = NULL;
584 	MixerOutput *output = fCore->Output();
585 	if (!output) {
586 		ERROR("AudioMixer::FormatChangeRequested: no output\n");
587 		goto err;
588 	}
589 	if (source != output->MediaOutput().source) {
590 		ERROR("AudioMixer::FormatChangeRequested: wrong output source\n");
591 		goto err;
592 	}
593 	if (destination != output->MediaOutput().destination) {
594 		ERROR("AudioMixer::FormatChangeRequested: wrong output destination "
595 			"(port %ld, id %ld), our is (port %ld, id %ld)\n", destination.port,
596 			destination.id, output->MediaOutput().destination.port,
597 			output->MediaOutput().destination.id);
598 		if (destination.port == output->MediaOutput().destination.port
599 			&& destination.id == output->MediaOutput().destination.id + 1) {
600 			ERROR("AudioMixer::FormatChangeRequested: this might be the broken "
601 				"R5 multi audio add-on\n");
602 		}
603 		goto err;
604 	}
605 	if (io_format->type != B_MEDIA_RAW_AUDIO
606 		&& io_format->type != B_MEDIA_UNKNOWN_TYPE) {
607 		ERROR("AudioMixer::FormatChangeRequested: wrong format type\n");
608 		goto err;
609 	}
610 
611 	/* remove wildcards */
612 	io_format->SpecializeTo(&fDefaultFormat);
613 
614 	media_node_id id;
615 	FindLatencyFor(destination, &fDownstreamLatency, &id);
616 	TRACE("AudioMixer: Downstream Latency is %" B_PRIdBIGTIME " usecs\n",
617 		fDownstreamLatency);
618 
619 	// SetDuration of one buffer
620 	SetBufferDuration(buffer_duration(io_format->u.raw_audio));
621 	TRACE("AudioMixer: buffer duration is %" B_PRIdBIGTIME " usecs\n",
622 		BufferDuration());
623 
624 	// Our internal latency is at least the length of a full output buffer
625 	fInternalLatency = BufferDuration()
626 		+ max((bigtime_t)4500, bigtime_t(0.5 * BufferDuration()));
627 	TRACE("AudioMixer: Internal latency is %" B_PRIdBIGTIME " usecs\n",
628 		fInternalLatency);
629 
630 	SetEventLatency(fDownstreamLatency + fInternalLatency);
631 
632 	// we need to inform all connected *inputs* about *our* change in latency
633 	PublishEventLatencyChange();
634 
635 	// TODO: we might need to create more buffers, to span a larger downstream
636 	// latency
637 
638 	// apply latency change
639 	fCore->SetTimingInfo(TimeSource(), fDownstreamLatency);
640 
641 	// apply format change
642 	fCore->OutputFormatChanged(io_format->u.raw_audio);
643 
644 	status = CreateBufferGroup(&group);
645 	if (status != B_OK)
646 		return status;
647 	else {
648 		delete fBufferGroup;
649 		fBufferGroup = group;
650 		fCore->SetOutputBufferGroup(fBufferGroup);
651 	}
652 
653 err:
654 	fCore->Unlock();
655 	return status;
656 }
657 
658 
659 status_t
660 AudioMixer::GetNextOutput(int32 *cookie, media_output *out_output)
661 {
662 	TRACE("AudioMixer::GetNextOutput\n");
663 
664 	if (*cookie != 0)
665 		return B_BAD_INDEX;
666 
667 	fCore->Lock();
668 	MixerOutput *output = fCore->Output();
669 	if (output) {
670 		*out_output = output->MediaOutput();
671 	} else {
672 		out_output->node = Node();
673 		out_output->source.port = ControlPort();
674 		out_output->source.id = 0;
675 		out_output->destination = media_destination::null;
676 		out_output->format.Clear();
677 		out_output->format.type = B_MEDIA_RAW_AUDIO;
678 		strcpy(out_output->name, "Mixer Output");
679 	}
680 	fCore->Unlock();
681 
682 	*cookie += 1;
683 	return B_OK;
684 }
685 
686 
687 status_t
688 AudioMixer::DisposeOutputCookie(int32 cookie)
689 {
690 	// nothing to do
691 	return B_OK;
692 }
693 
694 
695 status_t
696 AudioMixer::SetBufferGroup(const media_source &for_source,
697 	BBufferGroup *newGroup)
698 {
699 	TRACE("AudioMixer::SetBufferGroup\n");
700 	// the downstream consumer (soundcard) node asks us to use another
701 	// BBufferGroup (might be NULL). We only have one output (id 0)
702 	if (for_source.port != ControlPort() || for_source.id != 0)
703 		return B_MEDIA_BAD_SOURCE;
704 
705 	if (newGroup == fBufferGroup) {
706 		// we're already using this buffergroup
707 		return B_OK;
708 	}
709 
710 	fCore->Lock();
711 	if (!newGroup) {
712 		status_t status = CreateBufferGroup(&newGroup);
713 		if (status != B_OK)
714 			return status;
715 	}
716 	fCore->SetOutputBufferGroup(newGroup);
717 	delete fBufferGroup;
718 	fBufferGroup = newGroup;
719 	fCore->Unlock();
720 
721 	return B_OK;
722 }
723 
724 
725 status_t
726 AudioMixer::GetLatency(bigtime_t *out_latency)
727 {
728 	// report our *total* latency:  internal plus downstream plus scheduling
729 	*out_latency = EventLatency() + SchedulingLatency();
730 
731 	TRACE("AudioMixer::GetLatency %Ld\n", *out_latency);
732 
733 	return B_OK;
734 }
735 
736 
737 void
738 AudioMixer::LatencyChanged(const media_source& source,
739 	const media_destination& destination, bigtime_t new_latency, uint32 flags)
740 {
741 	if (source.port != ControlPort() || source.id != 0) {
742 		ERROR("AudioMixer::LatencyChanged: received but has wrong source "
743 			"%ld/%ld\n", source.port, source.id);
744 		return;
745 	}
746 
747 	TRACE("AudioMixer::LatencyChanged: downstream latency from %ld/%ld to "
748 		"%ld/%ld changed from %Ld to %Ld\n", source.port, source.id,
749 		destination.port, destination.id, fDownstreamLatency, new_latency);
750 
751 #if DEBUG
752 	{
753 		media_node_id id;
754 		bigtime_t l;
755 		FindLatencyFor(destination, &l, &id);
756 		TRACE("AudioMixer: Reported downstream Latency is %Ld usecs\n", l);
757 	}
758 #endif
759 
760 	fDownstreamLatency = new_latency;
761 	SetEventLatency(fDownstreamLatency + fInternalLatency);
762 
763 	// XXX we might need to create more buffers, to span a larger downstream
764 	// latency
765 
766 	fCore->Lock();
767 	fCore->SetTimingInfo(TimeSource(), fDownstreamLatency);
768 	PublishEventLatencyChange();
769 	fCore->Unlock();
770 }
771 
772 status_t
773 AudioMixer::PrepareToConnect(const media_source &what,
774 	const media_destination &where, media_format *format,
775 	media_source *out_source, char *out_name)
776 {
777 	TRACE("AudioMixer::PrepareToConnect\n");
778 	// PrepareToConnect() is the second stage of format negotiations that
779 	// happens inside BMediaRoster::Connect(). At this point, the consumer's
780 	// AcceptFormat() method has been called, and that node has potentially
781 	// changed the proposed format.
782 	// It may also have left wildcards in the format. PrepareToConnect()
783 	// *must* fully specialize the format before returning!
784 	// we also create the new output connection and return it in out_source.
785 
786 	PRINT_FORMAT("AudioMixer::PrepareToConnect: suggested format", *format);
787 
788 	// avoid loop connections
789 	if (where.port == ControlPort())
790 		return B_MEDIA_BAD_SOURCE;
791 
792 	// is the source valid?
793 	if (what.port != ControlPort() || what.id != 0)
794 		return B_MEDIA_BAD_SOURCE;
795 
796 	// is the format acceptable?
797 	if (format->type != B_MEDIA_RAW_AUDIO
798 		&& format->type != B_MEDIA_UNKNOWN_TYPE) {
799 		PRINT_FORMAT("AudioMixer::PrepareToConnect: bad format", *format);
800 		return B_MEDIA_BAD_FORMAT;
801 	}
802 
803 	fCore->Lock();
804 
805 	// are we already connected?
806 	if (fCore->Output() != 0) {
807 		fCore->Unlock();
808 		ERROR("AudioMixer::PrepareToConnect: already connected\n");
809 		return B_MEDIA_ALREADY_CONNECTED;
810 	}
811 
812 	// It is possible that another mixer is connecting.
813 	// To avoid using the default format, we use one of
814 	// a) the format that it indicated as hint in the user_data,
815 	// b) the output format of the system audio mixer
816 	// c) the input format of the system DAC device
817 	// d) if everything failes, keep the wildcard
818 	if (format->u.raw_audio.channel_count == 0
819 		&& format->u.raw_audio.frame_rate < 1
820 		&& format->user_data_type == FORMAT_USER_DATA_TYPE
821 		&& *(uint32 *)&format->user_data[0] == FORMAT_USER_DATA_MAGIC_1
822 		&& *(uint32 *)&format->user_data[44] == FORMAT_USER_DATA_MAGIC_2) {
823 		// ok, a mixer is connecting
824 		uint32 channel_count = *(uint32 *)&format->user_data[4];
825 		float frame_rate = *(float *)&format->user_data[20];
826 		if (channel_count > 0 && frame_rate > 0) {
827 			// format is good, use it
828 			format->u.raw_audio.channel_count = channel_count;
829 			format->u.raw_audio.frame_rate = frame_rate;
830 		} else {
831 			// other mixer's output is probably not connected
832 			media_node node;
833 			BMediaRoster *roster = BMediaRoster::Roster();
834 			media_output out;
835 			media_input in;
836 			int32 count;
837 			if (roster->GetAudioMixer(&node) == B_OK
838 				&& roster->GetConnectedOutputsFor(node, &out, 1, &count)
839 						== B_OK
840 				&& count == 1) {
841 				// use mixer output format
842 				format->u.raw_audio.channel_count
843 					= out.format.u.raw_audio.channel_count;
844 				format->u.raw_audio.frame_rate
845 					= out.format.u.raw_audio.frame_rate;
846 			} else if (roster->GetAudioOutput(&node) == B_OK
847 				&& roster->GetAllInputsFor(node, &in, 1, &count) == B_OK
848 				&& count == 1) {
849 				// use DAC input format
850 				format->u.raw_audio.channel_count
851 					= in.format.u.raw_audio.channel_count;
852 				format->u.raw_audio.frame_rate
853 					= in.format.u.raw_audio.frame_rate;
854 			}
855 		}
856 	}
857 
858 	/* set source and suggest a name */
859 	*out_source = what;
860 	strcpy(out_name, "Mixer Output");
861 
862 	/* remove wildcards */
863 	format->SpecializeTo(&fDefaultFormat);
864 
865 	PRINT_FORMAT("AudioMixer::PrepareToConnect: final format", *format);
866 
867 	/* add output to core */
868 	media_output output;
869 	output.node = Node();
870 	output.source = *out_source;
871 	output.destination = where;
872 	output.format = *format;
873 	strcpy(output.name, out_name);
874 
875 	fCore->EnableOutput(false);
876 	fCore->AddOutput(output);
877 
878 	fCore->Unlock();
879 	return B_OK;
880 }
881 
882 
883 void
884 AudioMixer::Connect(status_t error, const media_source &source,
885 	const media_destination &dest, const media_format &format, char *io_name)
886 {
887 	TRACE("AudioMixer::Connect\n");
888 
889 	fCore->Lock();
890 	// are we still connected?
891 	if (fCore->Output() == 0) {
892 		fCore->Unlock();
893 		ERROR("AudioMixer::Connect: no longer connected\n");
894 		return;
895 	}
896 	fCore->Unlock();
897 
898 	if (error != B_OK) {
899 		// if an error occured, remove output from core
900 		ERROR("AudioMixer::Connect failed with error 0x%08lX, removing "
901 			"connection\n", error);
902 		fCore->Lock();
903 		fCore->RemoveOutput();
904 		fCore->Unlock();
905 		return;
906 	}
907 
908 	// Switch our prefered format to have the same
909 	// frame_rate and channel count as the output.
910 	fDefaultFormat.u.raw_audio.frame_rate = format.u.raw_audio.frame_rate;
911 	fDefaultFormat.u.raw_audio.channel_count = format.u.raw_audio.channel_count;
912 
913 	// if the connection has no name, we set it now
914 	if (strlen(io_name) == 0)
915 		strcpy(io_name, "Mixer Output");
916 
917 	// Now that we're connected, we can determine our downstream latency.
918 	media_node_id id;
919 	FindLatencyFor(dest, &fDownstreamLatency, &id);
920 	TRACE("AudioMixer: Downstream Latency is %Ld usecs\n", fDownstreamLatency);
921 
922 	// SetDuration of one buffer
923 	SetBufferDuration(buffer_duration(format.u.raw_audio));
924 	TRACE("AudioMixer: buffer duration is %Ld usecs\n", BufferDuration());
925 
926 	// Our internal latency is at least the length of a full output buffer
927 	// plus mixing time, plus jitter
928 	fInternalLatency = BufferDuration()
929 		+ max(kMinMixingTime, bigtime_t(0.5 * BufferDuration())) + kMaxJitter;
930 	TRACE("AudioMixer: Internal latency is %Ld usecs\n", fInternalLatency);
931 
932 	SetEventLatency(fDownstreamLatency + fInternalLatency);
933 
934 	// we need to inform all connected *inputs* about *our* change in latency
935 	PublishEventLatencyChange();
936 
937 	fCore->Lock();
938 
939 	// Set up the buffer group for our connection, as long as nobody handed
940 	// us a buffer group (via SetBufferGroup()) prior to this.  That can
941 	// happen, for example, if the consumer calls SetOutputBuffersFor() on
942 	// us from within its Connected() method.
943 	if (!fBufferGroup) {
944 		BBufferGroup *group = NULL;
945 		if (CreateBufferGroup(&group) != B_OK)
946 			return;
947 		fBufferGroup = group;
948 	}
949 
950 	ASSERT(fCore->Output() != 0);
951 
952 	// our source should still be valid, too
953 	ASSERT(fCore->Output()->MediaOutput().source.id == 0);
954 	ASSERT(fCore->Output()->MediaOutput().source.port == ControlPort());
955 
956 	// BBufferConsumer::Connected() may return a different input for the
957 	// newly created connection. The destination can have changed since
958 	// AudioMixer::PrepareToConnect() and we need to update it.
959 	fCore->Output()->MediaOutput().destination = dest;
960 
961 	fCore->EnableOutput(true);
962 	fCore->SetTimingInfo(TimeSource(), fDownstreamLatency);
963 	fCore->SetOutputBufferGroup(fBufferGroup);
964 
965 	fCore->Settings()->LoadConnectionSettings(fCore->Output());
966 
967 	fCore->Unlock();
968 	UpdateParameterWeb();
969 }
970 
971 
972 void
973 AudioMixer::Disconnect(const media_source& what, const media_destination& where)
974 {
975 	TRACE("AudioMixer::Disconnect\n");
976 	fCore->Lock();
977 
978 	// Make sure that our connection is the one being disconnected
979 	MixerOutput* output = fCore->Output();
980 	if (!output
981 		|| output->MediaOutput().node != Node()
982 		|| output->MediaOutput().source != what
983 		|| output->MediaOutput().destination != where) {
984 		ERROR("AudioMixer::Disconnect can't disconnect (wrong connection)\n");
985 		fCore->Unlock();
986 		return;
987 	}
988 
989 	// Switch our prefered format back to default
990 	// frame rate and channel count.
991 	fDefaultFormat.u.raw_audio.frame_rate = 96000;
992 	fDefaultFormat.u.raw_audio.channel_count = 2;
993 
994 	// force a stop
995 	fCore->Stop();
996 
997 	fCore->RemoveOutput();
998 
999 	// destroy buffer group
1000 	delete fBufferGroup;
1001 	fBufferGroup = NULL;
1002 	fCore->SetOutputBufferGroup(0);
1003 
1004 	fCore->Unlock();
1005 	UpdateParameterWeb();
1006 }
1007 
1008 
1009 void
1010 AudioMixer::LateNoticeReceived(const media_source& what, bigtime_t howMuch,
1011 	bigtime_t performanceTime)
1012 {
1013 	// We've produced some late buffers... Increase Latency
1014 	// is the only runmode in which we can do anything about this
1015 	// TODO: quality could be decreased, too
1016 
1017 	ERROR("AudioMixer::LateNoticeReceived, %Ld too late at %Ld\n", howMuch,
1018 		performanceTime);
1019 
1020 	if (what == fCore->Output()->MediaOutput().source
1021 		&& RunMode() == B_INCREASE_LATENCY) {
1022 		// We need to ignore subsequent notices whose arrival time here
1023 		// lies within the last lateness, because queued-up buffers will all be 'late'
1024 		if (performanceTime < fLastLateNotification)
1025 			return;
1026 
1027 		fInternalLatency += howMuch;
1028 
1029 		// At some point a too large latency can get annoying
1030 		// (actually more than annoying, as there won't be enough buffers long before this!)
1031 		if (fInternalLatency > kMaxLatency)
1032 			fInternalLatency = kMaxLatency;
1033 
1034 		fLastLateNotification = TimeSource()->Now() + howMuch;
1035 
1036 		TRACE("AudioMixer: increasing internal latency to %"
1037 			B_PRIdBIGTIME " usec\n", fInternalLatency);
1038 		SetEventLatency(fDownstreamLatency + fInternalLatency);
1039 
1040 		PublishEventLatencyChange();
1041 	}
1042 }
1043 
1044 
1045 void
1046 AudioMixer::EnableOutput(const media_source& what, bool enabled,
1047 	int32 */*deprecated*/)
1048 {
1049 	// we only have one output
1050 	if (what.id != 0 || what.port != ControlPort())
1051 		return;
1052 
1053 	fCore->Lock();
1054 	fCore->EnableOutput(enabled);
1055 	fCore->Unlock();
1056 }
1057 
1058 
1059 //	#pragma mark - BMediaEventLooper methods
1060 
1061 
1062 void
1063 AudioMixer::NodeRegistered()
1064 {
1065 	UpdateParameterWeb();
1066 	SetPriority(B_REAL_TIME_PRIORITY);
1067 	Run();
1068 }
1069 
1070 
1071 void
1072 AudioMixer::SetTimeSource(BTimeSource* timeSource)
1073 {
1074 	TRACE("AudioMixer::SetTimeSource: timesource is now %ld\n",
1075 		timeSource->ID());
1076 	fCore->Lock();
1077 	fCore->SetTimingInfo(timeSource, fDownstreamLatency);
1078 	fCore->Unlock();
1079 }
1080 
1081 
1082 void
1083 AudioMixer::HandleEvent(const media_timed_event *event, bigtime_t lateness,
1084 	bool realTimeEvent)
1085 {
1086 	switch (event->type) {
1087 		case BTimedEventQueue::B_HANDLE_BUFFER:
1088 		{
1089 			HandleInputBuffer((BBuffer *)event->pointer, lateness);
1090 			((BBuffer *)event->pointer)->Recycle();
1091 			break;
1092 		}
1093 
1094 		case BTimedEventQueue::B_START:
1095 		{
1096 			TRACE("AudioMixer::HandleEvent: B_START\n");
1097 			if (RunState() != B_STARTED) {
1098 				fCore->Lock();
1099 				fCore->Start();
1100 				fCore->Unlock();
1101 			}
1102 			break;
1103 		}
1104 
1105 		case BTimedEventQueue::B_STOP:
1106 		{
1107 			TRACE("AudioMixer::HandleEvent: B_STOP\n");
1108 			// stopped - don't process any more buffers, flush all buffers
1109 			// from event queue
1110 			EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true,
1111 				BTimedEventQueue::B_HANDLE_BUFFER);
1112 			fCore->Lock();
1113 			fCore->Stop();
1114 			fCore->Unlock();
1115 			break;
1116 		}
1117 
1118 		case BTimedEventQueue::B_DATA_STATUS:
1119 		{
1120 			ERROR("DataStatus message\n");
1121 			break;
1122 		}
1123 
1124 		case MIXER_PROCESS_EVENT:
1125 			fCore->Process();
1126 		break;
1127 
1128 		default:
1129 			break;
1130 	}
1131 }
1132 
1133 
1134 //	#pragma mark - AudioMixer methods
1135 
1136 
1137 void
1138 AudioMixer::PublishEventLatencyChange()
1139 {
1140 	// our event (processing + downstream) latency has changed,
1141 	// and we need tell all inputs about this
1142 
1143 	TRACE("AudioMixer::PublishEventLatencyChange\n");
1144 
1145 	fCore->Lock();
1146 
1147 	MixerInput *input;
1148 	for (int i = 0; (input = fCore->Input(i)) != 0; i++) {
1149 		TRACE("AudioMixer::PublishEventLatencyChange: SendLatencyChange, "
1150 			"connection %ld/%ld to %ld/%ld event latency is now %Ld\n",
1151 			input->MediaInput().source.port, input->MediaInput().source.id,
1152 			input->MediaInput().destination.port,
1153 			input->MediaInput().destination.id, EventLatency());
1154 		SendLatencyChange(input->MediaInput().source,
1155 			input->MediaInput().destination, EventLatency());
1156 	}
1157 
1158 	fCore->Unlock();
1159 }
1160 
1161 
1162 status_t
1163 AudioMixer::CreateBufferGroup(BBufferGroup** buffer) const
1164 {
1165 	// allocate enough buffers to span our downstream latency
1166 	// (plus one for rounding up), plus one extra
1167 	int32 count = int32(fDownstreamLatency / BufferDuration()) + 2;
1168 
1169 	TRACE("AudioMixer::CreateBufferGroup: fDownstreamLatency %Ld, "
1170 		"BufferDuration %Ld, buffer count = %ld\n", fDownstreamLatency,
1171 		BufferDuration(), count);
1172 
1173 	if (count < 3)
1174 		count = 3;
1175 
1176 	fCore->Lock();
1177 	uint32 size = fCore->Output()->MediaOutput().format.u.raw_audio.buffer_size;
1178 	fCore->Unlock();
1179 
1180 	TRACE("AudioMixer: allocating %ld buffers of %ld bytes each\n",
1181 		count, size);
1182 
1183 	BBufferGroup* buf = new BBufferGroup(size, count);
1184 	if (buf == NULL)
1185 		return B_NO_MEMORY;
1186 
1187 	status_t status = buf->InitCheck();
1188 	if (status != B_OK)
1189 		delete buf;
1190 	else
1191 		*buffer = buf;
1192 
1193 	return status;
1194 }
1195 
1196 
1197 status_t
1198 AudioMixer::SendBuffer(BBuffer* buffer, MixerOutput* output)
1199 {
1200 	return BBufferProducer::SendBuffer(buffer, output->MediaOutput().source,
1201 		output->MediaOutput().destination);
1202 }
1203 
1204 
1205 float
1206 AudioMixer::dB_to_Gain(float db)
1207 {
1208 	TRACE("dB_to_Gain: dB in: %01.2f ", db);
1209 	if (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