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