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