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