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