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
88const static bigtime_t kMaxLatency = 150000;
89	// 150 ms is the maximum latency we publish
90
91const bigtime_t kMinMixingTime = 3500;
92const bigtime_t kMaxJitter = 1500;
93
94
95AudioMixer::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
141AudioMixer::~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
161void
162AudioMixer::ApplySettings()
163{
164	fCore->Lock();
165	fCore->SetOutputAttenuation(fCore->Settings()->AttenuateOutput() ? 0.708 : 1.0);
166	fCore->Unlock();
167}
168
169
170void
171AudioMixer::DisableNodeStop()
172{
173	fDisableStop = true;
174}
175
176
177//	#pragma mark - BMediaNode methods
178
179
180void
181AudioMixer::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
192BMediaAddOn*
193AudioMixer::AddOn(int32 *internal_id) const
194{
195	*internal_id = 0;
196	return fAddOn;
197}
198
199
200//	#pragma mark - BBufferConsumer methods
201
202
203status_t
204AudioMixer::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
219status_t
220AudioMixer::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
259status_t
260AudioMixer::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
292void
293AudioMixer::DisposeInputCookie(int32 cookie)
294{
295	// nothing to do
296}
297
298
299void
300AudioMixer::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
321void
322AudioMixer::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
357void
358AudioMixer::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
376status_t
377AudioMixer::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
397status_t
398AudioMixer::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
456void
457AudioMixer::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
479status_t
480AudioMixer::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
514status_t
515AudioMixer::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
533status_t
534AudioMixer::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*/
564status_t
565AudioMixer::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
653err:
654	fCore->Unlock();
655	return status;
656}
657
658
659status_t
660AudioMixer::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
687status_t
688AudioMixer::DisposeOutputCookie(int32 cookie)
689{
690	// nothing to do
691	return B_OK;
692}
693
694
695status_t
696AudioMixer::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
725status_t
726AudioMixer::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 %lld\n", *out_latency);
732
733	return B_OK;
734}
735
736
737void
738AudioMixer::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 %lld to %lld\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 %lld 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
772status_t
773AudioMixer::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
883void
884AudioMixer::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 %lld usecs\n", fDownstreamLatency);
921
922	// SetDuration of one buffer
923	SetBufferDuration(buffer_duration(format.u.raw_audio));
924	TRACE("AudioMixer: buffer duration is %lld 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 %lld 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
972void
973AudioMixer::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
1009void
1010AudioMixer::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, %lld too late at %lld\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
1045void
1046AudioMixer::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
1062void
1063AudioMixer::NodeRegistered()
1064{
1065	UpdateParameterWeb();
1066	SetPriority(B_REAL_TIME_PRIORITY);
1067	Run();
1068}
1069
1070
1071void
1072AudioMixer::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
1082void
1083AudioMixer::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
1137void
1138AudioMixer::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 %lld\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
1162status_t
1163AudioMixer::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 %lld, "
1170		"BufferDuration %lld, 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
1197status_t
1198AudioMixer::SendBuffer(BBuffer* buffer, MixerOutput* output)
1199{
1200	return BBufferProducer::SendBuffer(buffer, output->MediaOutput().source,
1201		output->MediaOutput().destination);
1202}
1203
1204
1205float
1206AudioMixer::dB_to_Gain(float db)
1207{
1208	TRACE("dB_to_Gain: dB in: %01.2f ", db);
1209	if (db > 0) {
1210		db = db * (pow(abs(DB_MAX), (1.0 / DB_EXPONENT_POSITIVE))
1211			/ abs(DB_MAX));
1212		db = pow(db, DB_EXPONENT_POSITIVE);
1213	} else {
1214		db = -db;
1215		db = db * (pow(abs(DB_MIN), (1.0 / DB_EXPONENT_NEGATIVE))
1216			/ abs(DB_MIN));
1217		db = pow(db, DB_EXPONENT_NEGATIVE);
1218		db = -db;
1219	}
1220	TRACE("dB out: %01.2f\n", db);
1221	return pow(10.0, db / 20.0);
1222}
1223
1224
1225float
1226AudioMixer::Gain_to_dB(float gain)
1227{
1228	float db;
1229	db = 20.0 * log10(gain);
1230	if (db > 0) {
1231		db = pow(db, (1.0 / DB_EXPONENT_POSITIVE));
1232		db = db * (abs(DB_MAX) / pow(abs(DB_MAX),
1233			(1.0 / DB_EXPONENT_POSITIVE)));
1234	} else {
1235		db = -db;
1236		db = pow(db, (1.0 / DB_EXPONENT_NEGATIVE));
1237		db = db * (abs(DB_MIN) / pow(abs(DB_MIN),
1238			(1.0 / DB_EXPONENT_NEGATIVE)));
1239		db = -db;
1240	}
1241	return db;
1242}
1243
1244
1245// #pragma mark - BControllable methods
1246
1247
1248status_t
1249AudioMixer::GetParameterValue(int32 id, bigtime_t *last_change, void *value,
1250	size_t *ioSize)
1251{
1252	TRACE("GetParameterValue: id 0x%08lx, ioSize %ld\n", id, *ioSize);
1253	int param = PARAM(id);
1254	fCore->Lock();
1255	if (PARAM_IS_ETC(id)) {
1256		switch (ETC(id)) {
1257			case 10:	// Attenuate mixer output by 3dB
1258				*ioSize = sizeof(int32);
1259				static_cast<int32 *>(value)[0] = fCore->Settings()->AttenuateOutput();
1260				break;
1261			case 30:	// Display balance control for stereo connections
1262				*ioSize = sizeof(int32);
1263				static_cast<int32 *>(value)[0] = fCore->Settings()->UseBalanceControl();
1264				break;
1265			case 40:	// Allow output channel remapping
1266				*ioSize = sizeof(int32);
1267				static_cast<int32 *>(value)[0] = fCore->Settings()->AllowOutputChannelRemapping();
1268				break;
1269			case 50:	// Allow input channel remapping
1270				*ioSize = sizeof(int32);
1271				static_cast<int32 *>(value)[0] = fCore->Settings()->AllowInputChannelRemapping();
1272				break;
1273			case 60:	// Input gain controls
1274				*ioSize = sizeof(int32);
1275				static_cast<int32 *>(value)[0] = fCore->Settings()->InputGainControls();
1276				break;
1277			case 70:	// Resampling algorithm
1278				*ioSize = sizeof(int32);
1279				static_cast<int32 *>(value)[0] = fCore->Settings()->ResamplingAlgorithm();
1280				break;
1281			case 80:	// Refuse output format changes
1282				*ioSize = sizeof(int32);
1283				static_cast<int32 *>(value)[0] = fCore->Settings()->RefuseOutputFormatChange();
1284				break;
1285			case 90:	// Refuse input format changes
1286				*ioSize = sizeof(int32);
1287				static_cast<int32 *>(value)[0] = fCore->Settings()->RefuseInputFormatChange();
1288				break;
1289			default:
1290				ERROR("unhandled ETC 0x%08lx\n", id);
1291				break;
1292		}
1293	} else if (param == 0) {
1294		MixerOutput *output = fCore->Output();
1295		if (!output || (!PARAM_IS_MUTE(id) && !PARAM_IS_GAIN(id) && !PARAM_IS_SRC_ENABLE(id) && !PARAM_IS_SRC_GAIN(id) && !PARAM_IS_BALANCE(id)))
1296			goto err;
1297		if (PARAM_IS_MUTE(id)) {
1298			// output mute control
1299			if (*ioSize < sizeof(int32))
1300				goto err;
1301			*ioSize = sizeof(int32);
1302			static_cast<int32 *>(value)[0] = output->IsMuted();
1303		}
1304		if (PARAM_IS_GAIN(id)) {
1305			// output gain control
1306			if (fCore->Settings()->UseBalanceControl() && output->GetOutputChannelCount() == 2 && 1 /*channel mask is stereo */) {
1307				// single channel control + balance
1308				if (*ioSize < sizeof(float))
1309					goto err;
1310				*ioSize = sizeof(float);
1311				static_cast<float *>(value)[0] = GAIN_TO_DB((output->GetOutputChannelGain(0) + output->GetOutputChannelGain(1)) / 2);
1312			} else {
1313				// multi channel control
1314				if (*ioSize == sizeof(float)) {
1315					// get combined gain for all controls
1316					float gain = 0;
1317					for (int channel = 0;
1318							channel < output->GetOutputChannelCount();
1319							channel++) {
1320						gain += GAIN_TO_DB(
1321							output->GetOutputChannelGain(channel));
1322					}
1323					static_cast<float *>(value)[0] = gain
1324						/ output->GetOutputChannelCount();
1325				} else {
1326					if (*ioSize < output->GetOutputChannelCount()
1327							* sizeof(float))
1328						goto err;
1329
1330					*ioSize = output->GetOutputChannelCount() * sizeof(float);
1331
1332					for (int channel = 0;
1333							channel < output->GetOutputChannelCount();
1334							channel++) {
1335						static_cast<float *>(value)[channel]
1336							= GAIN_TO_DB(output->GetOutputChannelGain(channel));
1337					}
1338				}
1339			}
1340		}
1341		if (PARAM_IS_BALANCE(id)) {
1342			float l = output->GetOutputChannelGain(0);
1343			float r = output->GetOutputChannelGain(1);
1344			float v = r / (l+r);
1345			TRACE("balance l %1.3f, r %1.3f, v %1.3f\n",l,r,v);
1346			if (*ioSize < sizeof(float))
1347				goto err;
1348			*ioSize = sizeof(float);
1349			static_cast<float *>(value)[0] = v * 100;
1350		}
1351		if (PARAM_IS_SRC_ENABLE(id)) {
1352			if (*ioSize < sizeof(int32))
1353				goto err;
1354			*ioSize = sizeof(int32);
1355			static_cast<int32 *>(value)[0] = output->HasOutputChannelSource(PARAM_CHAN(id), PARAM_SRC(id));
1356		}
1357		if (PARAM_IS_SRC_GAIN(id)) {
1358			if (*ioSize < sizeof(float))
1359				goto err;
1360			*ioSize = sizeof(float);
1361			static_cast<float *>(value)[0] = GAIN_TO_PERCENT(output->GetOutputChannelSourceGain(PARAM_CHAN(id), PARAM_SRC(id)));
1362		}
1363	} else {
1364		MixerInput *input;
1365		for (int i = 0; (input = fCore->Input(i)); i++)
1366			if (input->ID() == param)
1367				break;
1368		if (!input || (!PARAM_IS_MUTE(id) && !PARAM_IS_GAIN(id) && !PARAM_IS_DST_ENABLE(id) && !PARAM_IS_BALANCE(id)))
1369			goto err;
1370		if (PARAM_IS_MUTE(id)) {
1371			// input mute control
1372			if (*ioSize < sizeof(int32))
1373				goto err;
1374			*ioSize = sizeof(int32);
1375			static_cast<int32 *>(value)[0] = !input->IsEnabled();
1376		}
1377		if (PARAM_IS_GAIN(id)) {
1378			// input gain control
1379			if (fCore->Settings()->InputGainControls() == 0) {
1380				// Physical input channels
1381				if (fCore->Settings()->UseBalanceControl() && input->GetInputChannelCount() == 2 && 1 /*channel mask is stereo */) {
1382					// single channel control + balance
1383					if (*ioSize < sizeof(float))
1384						goto err;
1385					*ioSize = sizeof(float);
1386					static_cast<float *>(value)[0] = GAIN_TO_DB((input->GetInputChannelGain(0) + input->GetInputChannelGain(1)) / 2);
1387				} else {
1388					// multi channel control
1389					if (*ioSize < input->GetInputChannelCount() * sizeof(float))
1390						goto err;
1391					*ioSize = input->GetInputChannelCount() * sizeof(float);
1392					for (int chan = 0; chan < input->GetInputChannelCount(); chan++)
1393						static_cast<float *>(value)[chan] = GAIN_TO_DB(input->GetInputChannelGain(chan));
1394				}
1395			} else {
1396				// Virtual output channels
1397				if (fCore->Settings()->UseBalanceControl() && input->GetMixerChannelCount() == 2 && 1 /*channel mask is stereo */) {
1398					// single channel control + balance
1399					if (*ioSize < sizeof(float))
1400						goto err;
1401					*ioSize = sizeof(float);
1402					static_cast<float *>(value)[0] = GAIN_TO_DB((input->GetMixerChannelGain(0) + input->GetMixerChannelGain(1)) / 2);
1403				} else {
1404					// multi channel control
1405					if (*ioSize < input->GetMixerChannelCount() * sizeof(float))
1406						goto err;
1407					*ioSize = input->GetMixerChannelCount() * sizeof(float);
1408					for (int chan = 0; chan < input->GetMixerChannelCount(); chan++)
1409						static_cast<float *>(value)[chan] = GAIN_TO_DB(input->GetMixerChannelGain(chan));
1410				}
1411			}
1412		}
1413		if (PARAM_IS_BALANCE(id)) {
1414			if (fCore->Settings()->InputGainControls() == 0) {
1415				// Physical input channels
1416				float l = input->GetInputChannelGain(0);
1417				float r = input->GetInputChannelGain(1);
1418				float v = r / (l+r);
1419				TRACE("balance l %1.3f, r %1.3f, v %1.3f\n",l,r,v);
1420				if (*ioSize < sizeof(float))
1421					goto err;
1422				*ioSize = sizeof(float);
1423				static_cast<float *>(value)[0] = v * 100;
1424			} else {
1425				// Virtual output channels
1426				float l = input->GetMixerChannelGain(0);
1427				float r = input->GetMixerChannelGain(1);
1428				float v = r / (l+r);
1429				TRACE("balance l %1.3f, r %1.3f, v %1.3f\n",l,r,v);
1430				if (*ioSize < sizeof(float))
1431					goto err;
1432				*ioSize = sizeof(float);
1433				static_cast<float *>(value)[0] = v * 100;
1434			}
1435		}
1436		if (PARAM_IS_DST_ENABLE(id)) {
1437			if (*ioSize < sizeof(int32))
1438				goto err;
1439			*ioSize = sizeof(int32);
1440			static_cast<int32 *>(value)[0] = input->HasInputChannelDestination(PARAM_CHAN(id), PARAM_DST(id));
1441		}
1442	}
1443	*last_change = TimeSource()->Now(); // XXX we could do better
1444	fCore->Unlock();
1445	return B_OK;
1446err:
1447	fCore->Unlock();
1448	return B_ERROR;
1449}
1450
1451
1452void
1453AudioMixer::SetParameterValue(int32 id, bigtime_t when, const void *value,
1454	size_t size)
1455{
1456	TRACE("SetParameterValue: id 0x%08lx, size %ld\n", id, size);
1457	bool update = false;
1458	int param = PARAM(id);
1459	fCore->Lock();
1460	if (PARAM_IS_ETC(id)) {
1461		switch (ETC(id)) {
1462			case 10:	// Attenuate mixer output by 3dB
1463				if (size != sizeof(int32))
1464					goto err;
1465				fCore->Settings()->SetAttenuateOutput(static_cast<const int32 *>(value)[0]);
1466				// this value is special (see MixerCore.h) and we need to notify the core
1467				fCore->SetOutputAttenuation((static_cast<const int32 *>(value)[0]) ? 0.708 : 1.0);
1468				break;
1469			case 30:	// Display balance control for stereo connections
1470				if (size != sizeof(int32))
1471					goto err;
1472				fCore->Settings()->SetUseBalanceControl(static_cast<const int32 *>(value)[0]);
1473				update = true;
1474				break;
1475			case 40:	// Allow output channel remapping
1476				if (size != sizeof(int32))
1477					goto err;
1478				fCore->Settings()->SetAllowOutputChannelRemapping(static_cast<const int32 *>(value)[0]);
1479				update = true;
1480				break;
1481			case 50:	// Allow input channel remapping
1482				if (size != sizeof(int32))
1483					goto err;
1484				fCore->Settings()->SetAllowInputChannelRemapping(static_cast<const int32 *>(value)[0]);
1485				update = true;
1486				break;
1487			case 60:	// Input gain controls represent
1488						// (0, "Physical input channels")
1489						// (1, "Virtual output channels")
1490				if (size != sizeof(int32))
1491					goto err;
1492				fCore->Settings()->SetInputGainControls(static_cast<const int32 *>(value)[0]);
1493				update = true; // XXX should use BroadcastChangedParameter()
1494				break;
1495			case 70:	// Resampling algorithm
1496				if (size != sizeof(int32))
1497					goto err;
1498				fCore->Settings()->SetResamplingAlgorithm(static_cast<const int32 *>(value)[0]);
1499				fCore->UpdateResamplingAlgorithm();
1500				break;
1501			case 80:	// Refuse output format changes
1502				if (size != sizeof(int32))
1503					goto err;
1504				fCore->Settings()->SetRefuseOutputFormatChange(static_cast<const int32 *>(value)[0]);
1505				break;
1506			case 90:	// Refuse input format changes
1507				if (size != sizeof(int32))
1508					goto err;
1509				fCore->Settings()->SetRefuseInputFormatChange(static_cast<const int32 *>(value)[0]);
1510				break;
1511			default:
1512				ERROR("unhandled ETC 0x%08lx\n", id);
1513				break;
1514		}
1515	} else if (param == 0) {
1516		MixerOutput *output = fCore->Output();
1517		if (!output || (!PARAM_IS_MUTE(id) && !PARAM_IS_GAIN(id) && !PARAM_IS_SRC_ENABLE(id) && !PARAM_IS_SRC_GAIN(id) && !PARAM_IS_BALANCE(id)))
1518			goto err;
1519		if (PARAM_IS_MUTE(id)) {
1520			// output mute control
1521			if (size != sizeof(int32))
1522				goto err;
1523			output->SetMuted(static_cast<const int32 *>(value)[0]);
1524		}
1525		if (PARAM_IS_GAIN(id)) {
1526			// output gain control
1527			if (fCore->Settings()->UseBalanceControl()
1528				&& output->GetOutputChannelCount() == 2 && 1 /*channel mask is stereo */) {
1529				// single channel control + balance
1530				float l = output->GetOutputChannelGain(0);
1531				float r = output->GetOutputChannelGain(1);
1532				float m = (l + r) / 2;	// master volume
1533				float v = DB_TO_GAIN(static_cast<const float *>(value)[0]);
1534				float f = v / m;		// factor for both channels
1535				TRACE("gain set l %1.3f, r %1.3f, m %1.3f, v %1.3f, f %1.3f\n",l,r,m,v,f);
1536				output->SetOutputChannelGain(0, output->GetOutputChannelGain(0) * f);
1537				output->SetOutputChannelGain(1, output->GetOutputChannelGain(1) * f);
1538			} else {
1539				// multi channel control
1540				if (size == sizeof(float)) {
1541					// set same volume for all channels
1542					float gain = static_cast<const float *>(value)[0];
1543					for (int channel = 0;
1544							channel < output->GetOutputChannelCount();
1545							channel++) {
1546						output->SetOutputChannelGain(channel,
1547							DB_TO_GAIN(gain));
1548					}
1549				} else {
1550					if (size < output->GetOutputChannelCount() * sizeof(float))
1551						goto err;
1552					for (int channel = 0;
1553							channel < output->GetOutputChannelCount();
1554							channel++) {
1555						output->SetOutputChannelGain(channel,
1556							DB_TO_GAIN(static_cast<const float *>(
1557								value)[channel]));
1558					}
1559				}
1560			}
1561		}
1562		if (PARAM_IS_BALANCE(id)) {
1563			float l = output->GetOutputChannelGain(0);
1564			float r = output->GetOutputChannelGain(1);
1565			float m = (l + r) / 2;	// master volume
1566			float v = static_cast<const float *>(value)[0] / 100; // current balance value
1567			float fl = 2 * (1 - v);	// left channel factor of master volume
1568			float fr = 2 * v;		// right channel factor of master volume
1569			TRACE("balance set l %1.3f, r %1.3f, m %1.3f, v %1.3f, fl %1.3f, fr %1.3f\n",l,r,m,v,fl,fr);
1570			output->SetOutputChannelGain(0, m * fl);
1571			output->SetOutputChannelGain(1, m * fr);
1572		}
1573		if (PARAM_IS_SRC_ENABLE(id)) {
1574			if (size != sizeof(int32))
1575				goto err;
1576			if (static_cast<const int32 *>(value)[0]) {
1577				output->AddOutputChannelSource(PARAM_CHAN(id), PARAM_SRC(id));
1578			} else {
1579				output->RemoveOutputChannelSource(PARAM_CHAN(id), PARAM_SRC(id));
1580			}
1581		}
1582		if (PARAM_IS_SRC_GAIN(id)) {
1583			if (size != sizeof(float))
1584				goto err;
1585			output->SetOutputChannelSourceGain(PARAM_CHAN(id), PARAM_SRC(id), PERCENT_TO_GAIN(static_cast<const float *>(value)[0]));
1586		}
1587		fCore->Settings()->SaveConnectionSettings(output);
1588	} else {
1589		MixerInput *input;
1590		for (int i = 0; (input = fCore->Input(i)); i++)
1591			if (input->ID() == param)
1592				break;
1593		if (!input || (!PARAM_IS_MUTE(id) && !PARAM_IS_GAIN(id) && !PARAM_IS_DST_ENABLE(id) && !PARAM_IS_BALANCE(id)))
1594			goto err;
1595		if (PARAM_IS_MUTE(id)) {
1596			// input mute control
1597			if (size != sizeof(int32))
1598				goto err;
1599			input->SetEnabled(!static_cast<const int32 *>(value)[0]);
1600		}
1601		if (PARAM_IS_GAIN(id)) {
1602			// input gain control
1603			if (fCore->Settings()->InputGainControls() == 0) {
1604				// Physical input channels
1605				if (fCore->Settings()->UseBalanceControl() && input->GetInputChannelCount() == 2 && 1 /*channel mask is stereo */) {
1606					// single channel control + balance
1607					float l = input->GetInputChannelGain(0);
1608					float r = input->GetInputChannelGain(1);
1609					float m = (l + r) / 2;	// master volume
1610					float v = DB_TO_GAIN(static_cast<const float *>(value)[0]);
1611					float f = v / m;		// factor for both channels
1612					TRACE("gain set l %1.3f, r %1.3f, m %1.3f, v %1.3f, f %1.3f\n",l,r,m,v,f);
1613					input->SetInputChannelGain(0, input->GetInputChannelGain(0) * f);
1614					input->SetInputChannelGain(1, input->GetInputChannelGain(1) * f);
1615				} else {
1616					// multi channel control
1617					if (size < input->GetInputChannelCount() * sizeof(float))
1618						goto err;
1619					for (int chan = 0; chan < input->GetInputChannelCount(); chan++)
1620						input->SetInputChannelGain(chan, DB_TO_GAIN(static_cast<const float *>(value)[chan]));
1621				}
1622			} else {
1623				// Virtual output channels
1624				if (fCore->Settings()->UseBalanceControl() && input->GetMixerChannelCount() == 2 && 1 /*channel mask is stereo */) {
1625					// single channel control + balance
1626					float l = input->GetMixerChannelGain(0);
1627					float r = input->GetMixerChannelGain(1);
1628					float m = (l + r) / 2;	// master volume
1629					float v = DB_TO_GAIN(static_cast<const float *>(value)[0]);
1630					float f = v / m;		// factor for both channels
1631					TRACE("gain set l %1.3f, r %1.3f, m %1.3f, v %1.3f, f %1.3f\n",l,r,m,v,f);
1632					input->SetMixerChannelGain(0, input->GetMixerChannelGain(0) * f);
1633					input->SetMixerChannelGain(1, input->GetMixerChannelGain(1) * f);
1634				} else {
1635					// multi channel control
1636					if (size < input->GetMixerChannelCount() * sizeof(float))
1637						goto err;
1638					for (int chan = 0; chan < input->GetMixerChannelCount(); chan++)
1639						input->SetMixerChannelGain(chan, DB_TO_GAIN(static_cast<const float *>(value)[chan]));
1640				}
1641			}
1642		}
1643		if (PARAM_IS_BALANCE(id)) {
1644			if (fCore->Settings()->InputGainControls() == 0) {
1645				// Physical input channels
1646				float l = input->GetInputChannelGain(0);
1647				float r = input->GetInputChannelGain(1);
1648				float m = (l + r) / 2;	// master volume
1649				float v = static_cast<const float *>(value)[0] / 100; // current balance value
1650				float fl = 2 * (1 - v);	// left channel factor of master volume
1651				float fr = 2 * v;		// right channel factor of master volume
1652				TRACE("balance set l %1.3f, r %1.3f, m %1.3f, v %1.3f, fl %1.3f, fr %1.3f\n",l,r,m,v,fl,fr);
1653				input->SetInputChannelGain(0, m * fl);
1654				input->SetInputChannelGain(1, m * fr);
1655			} else {
1656				// Virtual output channels
1657				float l = input->GetMixerChannelGain(0);
1658				float r = input->GetMixerChannelGain(1);
1659				float m = (l + r) / 2;	// master volume
1660				float v = static_cast<const float *>(value)[0] / 100; // current balance value
1661				float fl = 2 * (1 - v);	// left channel factor of master volume
1662				float fr = 2 * v;		// right channel factor of master volume
1663				TRACE("balance set l %1.3f, r %1.3f, m %1.3f, v %1.3f, fl %1.3f, fr %1.3f\n",l,r,m,v,fl,fr);
1664				input->SetMixerChannelGain(0, m * fl);
1665				input->SetMixerChannelGain(1, m * fr);
1666			}
1667		}
1668		if (PARAM_IS_DST_ENABLE(id)) {
1669			if (size != sizeof(int32))
1670				goto err;
1671			if (static_cast<const int32 *>(value)[0]) {
1672				int oldchan = input->GetInputChannelForDestination(PARAM_DST(id));
1673				if (oldchan != -1) {
1674					input->RemoveInputChannelDestination(oldchan, PARAM_DST(id));
1675					int32 null = 0;
1676					BroadcastNewParameterValue(when, PARAM_DST_ENABLE(PARAM(id), oldchan, PARAM_DST(id)), &null, sizeof(null));
1677				}
1678				input->AddInputChannelDestination(PARAM_CHAN(id), PARAM_DST(id));
1679			} else {
1680				input->RemoveInputChannelDestination(PARAM_CHAN(id), PARAM_DST(id));
1681			}
1682			// TODO: this is really annoying
1683			// The slider count of the gain control needs to be changed,
1684			// but calling SetChannelCount(input->GetMixerChannelCount())
1685			// on it has no effect on remote BParameterWebs in other apps.
1686			// BroadcastChangedParameter() should be correct, but doesn't work
1687			BroadcastChangedParameter(PARAM_GAIN(PARAM(id)));
1688			// We trigger a complete ParameterWeb update as workaround
1689			// but it will change the focus from tab 3 to tab 1
1690			update = true;
1691		}
1692		fCore->Settings()->SaveConnectionSettings(input);
1693	}
1694
1695	BroadcastNewParameterValue(when, id, const_cast<void *>(value), size);
1696
1697err:
1698	fCore->Unlock();
1699	if (update)
1700		UpdateParameterWeb();
1701}
1702
1703
1704void
1705AudioMixer::UpdateParameterWeb()
1706{
1707	fCore->Lock();
1708	BParameterWeb *web = new BParameterWeb();
1709	BParameterGroup *top;
1710	BParameterGroup *outputchannels;
1711	BParameterGroup *inputchannels;
1712	BParameterGroup *group;
1713	BParameterGroup *subgroup;
1714	BParameterGroup *subsubgroup;
1715	BDiscreteParameter *dp;
1716	MixerInput *in;
1717	MixerOutput *out;
1718	char buf[50];
1719
1720	top = web->MakeGroup(B_TRANSLATE("Gain controls"));
1721
1722	out = fCore->Output();
1723	group = top->MakeGroup("");
1724	group->MakeNullParameter(PARAM_STR1(0), B_MEDIA_RAW_AUDIO,
1725		B_TRANSLATE("Master output"), B_WEB_BUFFER_INPUT);
1726	if (!out) {
1727		group->MakeNullParameter(PARAM_STR2(0), B_MEDIA_RAW_AUDIO,
1728			B_TRANSLATE("not connected"), B_GENERIC);
1729	} else {
1730		group->MakeNullParameter(PARAM_STR2(0), B_MEDIA_RAW_AUDIO,
1731			StringForFormat(buf, out), B_GENERIC);
1732		group->MakeDiscreteParameter(PARAM_MUTE(0), B_MEDIA_RAW_AUDIO,
1733			B_TRANSLATE("Mute"), B_MUTE);
1734		if (fCore->Settings()->UseBalanceControl()
1735			&& out->GetOutputChannelCount() == 2 && 1
1736			/*channel mask is stereo */) {
1737			// single channel control + balance
1738			group->MakeContinuousParameter(PARAM_GAIN(0), B_MEDIA_RAW_AUDIO,
1739				B_TRANSLATE("Gain"), B_MASTER_GAIN, B_TRANSLATE("dB"),
1740				DB_MIN, DB_MAX, 0.1);
1741			group->MakeContinuousParameter(PARAM_BALANCE(0), B_MEDIA_RAW_AUDIO,
1742				"", B_BALANCE, "", 0, 100, 1);
1743		} else {
1744			// multi channel control
1745			group->MakeContinuousParameter(PARAM_GAIN(0), B_MEDIA_RAW_AUDIO,
1746				B_TRANSLATE("Gain"), B_MASTER_GAIN, B_TRANSLATE("dB"),
1747				DB_MIN, DB_MAX, 0.1)
1748				   ->SetChannelCount(out->GetOutputChannelCount());
1749		}
1750		group->MakeNullParameter(PARAM_STR3(0), B_MEDIA_RAW_AUDIO,
1751			B_TRANSLATE("To output"), B_WEB_BUFFER_OUTPUT);
1752	}
1753
1754	for (int i = 0; (in = fCore->Input(i)); i++) {
1755		group = top->MakeGroup("");
1756		group->MakeNullParameter(PARAM_STR1(in->ID()), B_MEDIA_RAW_AUDIO,
1757			in->MediaInput().name, B_WEB_BUFFER_INPUT);
1758		group->MakeNullParameter(PARAM_STR2(in->ID()), B_MEDIA_RAW_AUDIO,
1759			StringForFormat(buf, in), B_GENERIC);
1760		group->MakeDiscreteParameter(PARAM_MUTE(in->ID()), B_MEDIA_RAW_AUDIO,
1761			B_TRANSLATE("Mute"), B_MUTE);
1762		// XXX the gain control is ugly once you have more than two channels,
1763		//     as you don't know what channel each slider controls. Tooltips might help...
1764		if (fCore->Settings()->InputGainControls() == 0) {
1765			// Physical input channels
1766			if (fCore->Settings()->UseBalanceControl()
1767				&& in->GetInputChannelCount() == 2 && 1
1768				/*channel mask is stereo */) {
1769				// single channel control + balance
1770				group->MakeContinuousParameter(PARAM_GAIN(in->ID()),
1771					B_MEDIA_RAW_AUDIO, B_TRANSLATE("Gain"), B_GAIN,
1772					B_TRANSLATE("dB"), DB_MIN, DB_MAX, 0.1);
1773				group->MakeContinuousParameter(PARAM_BALANCE(in->ID()),
1774					B_MEDIA_RAW_AUDIO, "", B_BALANCE, "", 0, 100, 1);
1775			} else {
1776				// multi channel control
1777				group->MakeContinuousParameter(PARAM_GAIN(in->ID()),
1778					B_MEDIA_RAW_AUDIO, B_TRANSLATE("Gain"), B_GAIN,
1779					B_TRANSLATE("dB"), DB_MIN, DB_MAX, 0.1)
1780						->SetChannelCount(in->GetInputChannelCount());
1781			}
1782		} else {
1783			// Virtual output channels
1784			if (fCore->Settings()->UseBalanceControl()
1785				&& in->GetMixerChannelCount() == 2 && 1
1786				/*channel mask is stereo */) {
1787				// single channel control + balance
1788				group->MakeContinuousParameter(PARAM_GAIN(in->ID()),
1789					B_MEDIA_RAW_AUDIO, B_TRANSLATE("Gain"), B_GAIN,
1790					B_TRANSLATE("dB"), DB_MIN, DB_MAX, 0.1);
1791				group->MakeContinuousParameter(PARAM_BALANCE(in->ID()),
1792					B_MEDIA_RAW_AUDIO, "", B_BALANCE, "", 0, 100, 1);
1793			} else {
1794				// multi channel control
1795				group->MakeContinuousParameter(PARAM_GAIN(in->ID()),
1796					B_MEDIA_RAW_AUDIO, B_TRANSLATE("Gain"), B_GAIN,
1797					B_TRANSLATE("dB"), DB_MIN, DB_MAX, 0.1)
1798						->SetChannelCount(in->GetMixerChannelCount());
1799			}
1800		}
1801		group->MakeNullParameter(PARAM_STR3(in->ID()), B_MEDIA_RAW_AUDIO,
1802			B_TRANSLATE("To master"), B_WEB_BUFFER_OUTPUT);
1803	}
1804
1805	if (fCore->Settings()->AllowOutputChannelRemapping()) {
1806		top = web->MakeGroup(B_TRANSLATE("Output mapping")); // top level group
1807		outputchannels = top->MakeGroup("");
1808		outputchannels->MakeNullParameter(PARAM_STR4(0), B_MEDIA_RAW_AUDIO,
1809			B_TRANSLATE("Output channel sources"), B_GENERIC);
1810
1811		group = outputchannels->MakeGroup("");
1812		group->MakeNullParameter(PARAM_STR5(0), B_MEDIA_RAW_AUDIO,
1813			B_TRANSLATE("Master output"), B_GENERIC);
1814		group = group->MakeGroup("");
1815		if (!out) {
1816			group->MakeNullParameter(PARAM_STR6(0), B_MEDIA_RAW_AUDIO,
1817				B_TRANSLATE("not connected"), B_GENERIC);
1818		} else {
1819			for (int chan = 0; chan < out->GetOutputChannelCount(); chan++) {
1820				subgroup = group->MakeGroup("");
1821				subgroup->MakeNullParameter(PARAM_SRC_STR(0, chan),
1822					B_MEDIA_RAW_AUDIO, StringForChannelType(buf,
1823						out->GetOutputChannelType(chan)), B_GENERIC);
1824				for (int src = 0; src < MAX_CHANNEL_TYPES; src++) {
1825					subsubgroup = subgroup->MakeGroup("");
1826					subsubgroup->MakeDiscreteParameter(
1827						PARAM_SRC_ENABLE(0, chan, src), B_MEDIA_RAW_AUDIO, "",
1828						B_ENABLE);
1829					subsubgroup->MakeContinuousParameter(
1830						PARAM_SRC_GAIN(0, chan, src), B_MEDIA_RAW_AUDIO,
1831						StringForChannelType(buf, src), B_GAIN, "%", 0.0,
1832						100.0, 0.1);
1833				}
1834			}
1835		}
1836	}
1837
1838	if (fCore->Settings()->AllowInputChannelRemapping()) {
1839		top = web->MakeGroup(B_TRANSLATE("Input mapping")); // top level group
1840		inputchannels = top->MakeGroup("");
1841		inputchannels->MakeNullParameter(PARAM_STR7(0), B_MEDIA_RAW_AUDIO,
1842			B_TRANSLATE("Input channel destinations"), B_GENERIC);
1843
1844		for (int i = 0; (in = fCore->Input(i)); i++) {
1845			group = inputchannels->MakeGroup("");
1846			group->MakeNullParameter(PARAM_STR4(in->ID()), B_MEDIA_RAW_AUDIO,
1847				in->MediaInput().name, B_GENERIC);
1848			group = group->MakeGroup("");
1849
1850			for (int chan = 0; chan < in->GetInputChannelCount(); chan++) {
1851				subgroup = group->MakeGroup("");
1852				subgroup->MakeNullParameter(PARAM_DST_STR(in->ID(), chan),
1853					B_MEDIA_RAW_AUDIO, StringForChannelType(buf,
1854					in->GetInputChannelType(chan)), B_GENERIC);
1855				for (int dst = 0; dst < MAX_CHANNEL_TYPES; dst++) {
1856					subgroup->MakeDiscreteParameter(PARAM_DST_ENABLE(in->ID(),
1857					chan, dst), B_MEDIA_RAW_AUDIO, StringForChannelType(buf, dst),
1858					B_ENABLE);
1859				}
1860			}
1861		}
1862	}
1863
1864	top = web->MakeGroup(B_TRANSLATE("Setup")); // top level group
1865	group = top->MakeGroup("");
1866
1867	group->MakeDiscreteParameter(PARAM_ETC(10), B_MEDIA_RAW_AUDIO,
1868		B_TRANSLATE("Attenuate mixer output by 3 dB"), B_ENABLE);
1869	group->MakeDiscreteParameter(PARAM_ETC(30), B_MEDIA_RAW_AUDIO,
1870		B_TRANSLATE("Display balance control for stereo connections"),
1871		B_ENABLE);
1872
1873	group->MakeDiscreteParameter(PARAM_ETC(40), B_MEDIA_RAW_AUDIO,
1874		B_TRANSLATE("Allow output channel remapping"), B_ENABLE);
1875	group->MakeDiscreteParameter(PARAM_ETC(50), B_MEDIA_RAW_AUDIO,
1876		B_TRANSLATE("Allow input channel remapping"), B_ENABLE);
1877
1878	dp = group->MakeDiscreteParameter(PARAM_ETC(60), B_MEDIA_RAW_AUDIO,
1879		B_TRANSLATE("Input gain controls represent:"), B_INPUT_MUX);
1880	dp->AddItem(0, B_TRANSLATE("Physical input channels"));
1881	dp->AddItem(1, B_TRANSLATE("Virtual output channels"));
1882
1883	dp = group->MakeDiscreteParameter(PARAM_ETC(70), B_MEDIA_RAW_AUDIO,
1884		B_TRANSLATE("Resampling algorithm:"), B_INPUT_MUX);
1885	dp->AddItem(0, B_TRANSLATE("Low quality (drop/repeat samples)"));
1886	dp->AddItem(2, B_TRANSLATE("High quality (linear interpolation)"));
1887
1888	// Note: The following code is outcommented on purpose
1889	// and is about to be modified at a later point
1890	/*
1891	dp->AddItem(1, B_TRANSLATE("Drop/repeat samples (template based)"));
1892	dp->AddItem(3, B_TRANSLATE("17th order filtering"));
1893	*/
1894
1895	/* Remove those option from the GUI, but keep them in the settings
1896	group->MakeDiscreteParameter(PARAM_ETC(80), B_MEDIA_RAW_AUDIO,
1897		B_TRANSLATE("Refuse output format changes"), B_ENABLE);
1898	group->MakeDiscreteParameter(PARAM_ETC(90), B_MEDIA_RAW_AUDIO,
1899		B_TRANSLATE("Refuse input format changes"), B_ENABLE);
1900	*/
1901	fCore->Unlock();
1902	SetParameterWeb(web);
1903}
1904