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