blob: 11627a7640a427fafabb6eb41055e355782b7c15 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26#define USE_ERROR
27//#define USE_TRACE
28
29#include "Ports.h"
30#include "PLATFORM_API_LinuxOS_ALSA_CommonUtils.h"
31#include <alsa/asoundlib.h>
32
33#if USE_PORTS == TRUE
34
35#define MAX_ELEMS (300)
36#define MAX_CONTROLS (MAX_ELEMS * 4)
37
38#define CHANNELS_MONO (SND_MIXER_SCHN_LAST + 1)
39#define CHANNELS_STEREO (SND_MIXER_SCHN_LAST + 2)
40
41typedef struct {
42 snd_mixer_elem_t* elem;
43 INT32 portType; /* one of PORT_XXX_xx */
44 char* controlType; /* one of CONTROL_TYPE_xx */
45 /* Values: either SND_MIXER_SCHN_FRONT_xx, CHANNELS_MONO or CHANNELS_STEREO.
46 For SND_MIXER_SCHN_FRONT_xx, exactly this channel is set/retrieved directly.
47 For CHANNELS_MONO, ALSA channel SND_MIXER_SCHN_MONO is set/retrieved directly.
48 For CHANNELS_STEREO, ALSA channels SND_MIXER_SCHN_FRONT_LEFT and SND_MIXER_SCHN_FRONT_RIGHT
49 are set after a calculation that takes balance into account. Retrieved? Average of both
50 channels? (Using a cached value is not a good idea since the value in the HW may have been
51 altered.) */
52 INT32 channel;
53} PortControl;
54
55
56typedef struct tag_PortMixer {
57 snd_mixer_t* mixer_handle;
58 /* Number of array elements used in elems and types. */
59 int numElems;
60 snd_mixer_elem_t** elems;
61 /* Array of port types (PORT_SRC_UNKNOWN etc.). Indices are the same as in elems. */
62 INT32* types;
63 /* Number of array elements used in controls. */
64 int numControls;
65 PortControl* controls;
66} PortMixer;
67
68
69///// implemented functions of Ports.h
70
71INT32 PORT_GetPortMixerCount() {
72 INT32 mixerCount;
73 int card;
74 char devname[16];
75 int err;
76 snd_ctl_t *handle;
77 snd_ctl_card_info_t* info;
78
79 TRACE0("> PORT_GetPortMixerCount\n");
80
81 initAlsaSupport();
82
83 snd_ctl_card_info_malloc(&info);
84 card = -1;
85 mixerCount = 0;
86 if (snd_card_next(&card) >= 0) {
87 while (card >= 0) {
88 sprintf(devname, ALSA_HARDWARE_CARD, card);
89 TRACE1("PORT_GetPortMixerCount: Opening alsa device \"%s\"...\n", devname);
90 err = snd_ctl_open(&handle, devname, 0);
91 if (err < 0) {
92 ERROR2("ERROR: snd_ctl_open, card=%d: %s\n", card, snd_strerror(err));
93 } else {
94 mixerCount++;
95 snd_ctl_close(handle);
96 }
97 if (snd_card_next(&card) < 0) {
98 break;
99 }
100 }
101 }
102 snd_ctl_card_info_free(info);
103 TRACE0("< PORT_GetPortMixerCount\n");
104 return mixerCount;
105}
106
107
108INT32 PORT_GetPortMixerDescription(INT32 mixerIndex, PortMixerDescription* description) {
109 snd_ctl_t* handle;
110 snd_ctl_card_info_t* card_info;
111 char devname[16];
112 int err;
113 char buffer[100];
114
115 TRACE0("> PORT_GetPortMixerDescription\n");
116 snd_ctl_card_info_malloc(&card_info);
117
118 sprintf(devname, ALSA_HARDWARE_CARD, (int) mixerIndex);
119 TRACE1("Opening alsa device \"%s\"...\n", devname);
120 err = snd_ctl_open(&handle, devname, 0);
121 if (err < 0) {
122 ERROR2("ERROR: snd_ctl_open, card=%d: %s\n", (int) mixerIndex, snd_strerror(err));
123 return FALSE;
124 }
125 err = snd_ctl_card_info(handle, card_info);
126 if (err < 0) {
127 ERROR2("ERROR: snd_ctl_card_info, card=%d: %s\n", (int) mixerIndex, snd_strerror(err));
128 }
129 strncpy(description->name, snd_ctl_card_info_get_id(card_info), PORT_STRING_LENGTH - 1);
130 sprintf(buffer, " [%s]", devname);
131 strncat(description->name, buffer, PORT_STRING_LENGTH - 1 - strlen(description->name));
132 strncpy(description->vendor, "ALSA (http://www.alsa-project.org)", PORT_STRING_LENGTH - 1);
133 strncpy(description->description, snd_ctl_card_info_get_name(card_info), PORT_STRING_LENGTH - 1);
134 strncat(description->description, ", ", PORT_STRING_LENGTH - 1 - strlen(description->description));
135 strncat(description->description, snd_ctl_card_info_get_mixername(card_info), PORT_STRING_LENGTH - 1 - strlen(description->description));
136 getALSAVersion(description->version, PORT_STRING_LENGTH - 1);
137
138 snd_ctl_close(handle);
139 snd_ctl_card_info_free(card_info);
140 TRACE0("< PORT_GetPortMixerDescription\n");
141 return TRUE;
142}
143
144
145void* PORT_Open(INT32 mixerIndex) {
146 char devname[16];
147 snd_mixer_t* mixer_handle;
148 int err;
149 PortMixer* handle;
150
151 TRACE0("> PORT_Open\n");
152 sprintf(devname, ALSA_HARDWARE_CARD, (int) mixerIndex);
153 if ((err = snd_mixer_open(&mixer_handle, 0)) < 0) {
154 ERROR2("Mixer %s open error: %s", devname, snd_strerror(err));
155 return NULL;
156 }
157 if ((err = snd_mixer_attach(mixer_handle, devname)) < 0) {
158 ERROR2("Mixer attach %s error: %s", devname, snd_strerror(err));
159 snd_mixer_close(mixer_handle);
160 return NULL;
161 }
162 if ((err = snd_mixer_selem_register(mixer_handle, NULL, NULL)) < 0) {
163 ERROR1("Mixer register error: %s", snd_strerror(err));
164 snd_mixer_close(mixer_handle);
165 return NULL;
166 }
167 err = snd_mixer_load(mixer_handle);
168 if (err < 0) {
169 ERROR2("Mixer %s load error: %s", devname, snd_strerror(err));
170 snd_mixer_close(mixer_handle);
171 return NULL;
172 }
173 handle = (PortMixer*) calloc(1, sizeof(PortMixer));
174 if (handle == NULL) {
175 ERROR0("malloc() failed.");
176 snd_mixer_close(mixer_handle);
177 return NULL;
178 }
179 handle->numElems = 0;
180 handle->elems = (snd_mixer_elem_t**) calloc(MAX_ELEMS, sizeof(snd_mixer_elem_t*));
181 if (handle->elems == NULL) {
182 ERROR0("malloc() failed.");
183 snd_mixer_close(mixer_handle);
184 free(handle);
185 return NULL;
186 }
187 handle->types = (INT32*) calloc(MAX_ELEMS, sizeof(INT32));
188 if (handle->types == NULL) {
189 ERROR0("malloc() failed.");
190 snd_mixer_close(mixer_handle);
191 free(handle->elems);
192 free(handle);
193 return NULL;
194 }
195 handle->controls = (PortControl*) calloc(MAX_CONTROLS, sizeof(PortControl));
196 if (handle->controls == NULL) {
197 ERROR0("malloc() failed.");
198 snd_mixer_close(mixer_handle);
199 free(handle->elems);
200 free(handle->types);
201 free(handle);
202 return NULL;
203 }
204 handle->mixer_handle = mixer_handle;
205 // necessary to initialize data structures
206 PORT_GetPortCount(handle);
207 TRACE0("< PORT_Open\n");
208 return handle;
209}
210
211
212void PORT_Close(void* id) {
213 TRACE0("> PORT_Close\n");
214 if (id != NULL) {
215 PortMixer* handle = (PortMixer*) id;
216 if (handle->mixer_handle != NULL) {
217 snd_mixer_close(handle->mixer_handle);
218 }
219 if (handle->elems != NULL) {
220 free(handle->elems);
221 }
222 if (handle->types != NULL) {
223 free(handle->types);
224 }
225 if (handle->controls != NULL) {
226 free(handle->controls);
227 }
228 free(handle);
229 }
230 TRACE0("< PORT_Close\n");
231}
232
233
234
235INT32 PORT_GetPortCount(void* id) {
236 PortMixer* portMixer;
237 snd_mixer_elem_t *elem;
238
239 TRACE0("> PORT_GetPortCount\n");
240 if (id == NULL) {
241 // $$mp: Should become a descriptive error code (invalid handle).
242 return -1;
243 }
244 portMixer = (PortMixer*) id;
245 if (portMixer->numElems == 0) {
246 for (elem = snd_mixer_first_elem(portMixer->mixer_handle); elem; elem = snd_mixer_elem_next(elem)) {
247 if (!snd_mixer_selem_is_active(elem))
248 continue;
249 TRACE2("Simple mixer control '%s',%i\n",
250 snd_mixer_selem_get_name(elem),
251 snd_mixer_selem_get_index(elem));
252 if (snd_mixer_selem_has_playback_volume(elem)) {
253 portMixer->elems[portMixer->numElems] = elem;
254 portMixer->types[portMixer->numElems] = PORT_DST_UNKNOWN;
255 portMixer->numElems++;
256 }
257 // to prevent buffer overflow
258 if (portMixer->numElems >= MAX_ELEMS) {
259 break;
260 }
261 /* If an element has both playback an capture volume, it is put into the arrays
262 twice. */
263 if (snd_mixer_selem_has_capture_volume(elem)) {
264 portMixer->elems[portMixer->numElems] = elem;
265 portMixer->types[portMixer->numElems] = PORT_SRC_UNKNOWN;
266 portMixer->numElems++;
267 }
268 // to prevent buffer overflow
269 if (portMixer->numElems >= MAX_ELEMS) {
270 break;
271 }
272 }
273 }
274 TRACE0("< PORT_GetPortCount\n");
275 return portMixer->numElems;
276}
277
278
279INT32 PORT_GetPortType(void* id, INT32 portIndex) {
280 PortMixer* portMixer;
281 INT32 type;
282 TRACE0("> PORT_GetPortType\n");
283 if (id == NULL) {
284 // $$mp: Should become a descriptive error code (invalid handle).
285 return -1;
286 }
287 portMixer = (PortMixer*) id;
288 if (portIndex < 0 || portIndex >= portMixer->numElems) {
289 // $$mp: Should become a descriptive error code (index out of bounds).
290 return -1;
291 }
292 type = portMixer->types[portIndex];
293 TRACE0("< PORT_GetPortType\n");
294 return type;
295}
296
297
298INT32 PORT_GetPortName(void* id, INT32 portIndex, char* name, INT32 len) {
299 PortMixer* portMixer;
300 const char* nam;
301
302 TRACE0("> PORT_GetPortName\n");
303 if (id == NULL) {
304 // $$mp: Should become a descriptive error code (invalid handle).
305 return -1;
306 }
307 portMixer = (PortMixer*) id;
308 if (portIndex < 0 || portIndex >= portMixer->numElems) {
309 // $$mp: Should become a descriptive error code (index out of bounds).
310 return -1;
311 }
312 nam = snd_mixer_selem_get_name(portMixer->elems[portIndex]);
313 strncpy(name, nam, len - 1);
314 name[len - 1] = 0;
315 TRACE0("< PORT_GetPortName\n");
316 return TRUE;
317}
318
319
320static int isPlaybackFunction(INT32 portType) {
321 return (portType & PORT_DST_MASK);
322}
323
324
325/* Sets portControl to a pointer to the next free array element in the PortControl (pointer)
326 array of the passed portMixer. Returns TRUE if successful. May return FALSE if there is no
327 free slot. In this case, portControl is not altered */
328static int getControlSlot(PortMixer* portMixer, PortControl** portControl) {
329 if (portMixer->numControls >= MAX_CONTROLS) {
330 return FALSE;
331 } else {
332 *portControl = &(portMixer->controls[portMixer->numControls]);
333 portMixer->numControls++;
334 return TRUE;
335 }
336}
337
338
339/* Protect against illegal min-max values, preventing divisions by zero.
340 */
341inline static long getRange(long min, long max) {
342 if (max > min) {
343 return max - min;
344 } else {
345 return 1;
346 }
347}
348
349
350/* Idea: we may specify that if unit is an empty string, the values are linear and if unit is "dB",
351 the values are logarithmic.
352*/
353static void* createVolumeControl(PortControlCreator* creator,
354 PortControl* portControl,
355 snd_mixer_elem_t* elem, int isPlayback) {
356 void* control;
357 float precision;
358 long min, max;
359
360 if (isPlayback) {
361 snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
362 } else {
363 snd_mixer_selem_get_capture_volume_range(elem, &min, &max);
364 }
365 /* $$mp: The volume values retrieved with the ALSA API are strongly supposed to be logarithmic.
366 So the following calculation is wrong. However, there is no correct calculation, since
367 for equal-distant logarithmic steps, the precision expressed in linear varies over the
368 scale. */
369 precision = 1.0F / getRange(min, max);
370 control = (creator->newFloatControl)(creator, portControl, CONTROL_TYPE_VOLUME, 0.0F, +1.0F, precision, "");
371 return control;
372}
373
374
375void PORT_GetControls(void* id, INT32 portIndex, PortControlCreator* creator) {
376 PortMixer* portMixer;
377 snd_mixer_elem_t* elem;
378 void* control;
379 PortControl* portControl;
380 void* controls[10];
381 int numControls;
382 char* portName;
383 int isPlayback;
384 int isMono;
385 int isStereo;
386 char* type;
387 snd_mixer_selem_channel_id_t channel;
388
389 TRACE0("> PORT_GetControls\n");
390 if (id == NULL) {
391 ERROR0("Invalid handle!");
392 // $$mp: an error code should be returned.
393 return;
394 }
395 portMixer = (PortMixer*) id;
396 if (portIndex < 0 || portIndex >= portMixer->numElems) {
397 ERROR0("Port index out of range!");
398 // $$mp: an error code should be returned.
399 return;
400 }
401 numControls = 0;
402 elem = portMixer->elems[portIndex];
403 if (snd_mixer_selem_has_playback_volume(elem) || snd_mixer_selem_has_capture_volume(elem)) {
404 /* Since we've splitted/duplicated elements with both playback and capture on the recovery
405 of elements, we now can assume that we handle only to deal with either playback or
406 capture. */
407 isPlayback = isPlaybackFunction(portMixer->types[portIndex]);
408 isMono = (isPlayback && snd_mixer_selem_is_playback_mono(elem)) ||
409 (!isPlayback && snd_mixer_selem_is_capture_mono(elem));
410 isStereo = (isPlayback &&
411 snd_mixer_selem_has_playback_channel(elem, SND_MIXER_SCHN_FRONT_LEFT) &&
412 snd_mixer_selem_has_playback_channel(elem, SND_MIXER_SCHN_FRONT_RIGHT)) ||
413 (!isPlayback &&
414 snd_mixer_selem_has_capture_channel(elem, SND_MIXER_SCHN_FRONT_LEFT) &&
415 snd_mixer_selem_has_capture_channel(elem, SND_MIXER_SCHN_FRONT_RIGHT));
416 // single volume control
417 if (isMono || isStereo) {
418 if (getControlSlot(portMixer, &portControl)) {
419 portControl->elem = elem;
420 portControl->portType = portMixer->types[portIndex];
421 portControl->controlType = CONTROL_TYPE_VOLUME;
422 if (isMono) {
423 portControl->channel = CHANNELS_MONO;
424 } else {
425 portControl->channel = CHANNELS_STEREO;
426 }
427 control = createVolumeControl(creator, portControl, elem, isPlayback);
428 if (control != NULL) {
429 controls[numControls++] = control;
430 }
431 }
432 } else { // more than two channels, each channels has its own control.
433 for (channel = SND_MIXER_SCHN_FRONT_LEFT; channel <= SND_MIXER_SCHN_LAST; channel++) {
434 if (isPlayback && snd_mixer_selem_has_playback_channel(elem, channel) ||
435 !isPlayback && snd_mixer_selem_has_capture_channel(elem, channel)) {
436 if (getControlSlot(portMixer, &portControl)) {
437 portControl->elem = elem;
438 portControl->portType = portMixer->types[portIndex];
439 portControl->controlType = CONTROL_TYPE_VOLUME;
440 portControl->channel = channel;
441 control = createVolumeControl(creator, portControl, elem, isPlayback);
442 // We wrap in a compound control to provide the channel name.
443 if (control != NULL) {
444 /* $$mp 2003-09-14: The following cast shouln't be necessary. Instead, the
445 declaration of PORT_NewCompoundControlPtr in Ports.h should be changed
446 to take a const char* parameter. */
447 control = (creator->newCompoundControl)(creator, (char*) snd_mixer_selem_channel_name(channel), &control, 1);
448 }
449 if (control != NULL) {
450 controls[numControls++] = control;
451 }
452 }
453 }
454 }
455 }
456 // BALANCE control
457 if (isStereo) {
458 if (getControlSlot(portMixer, &portControl)) {
459 portControl->elem = elem;
460 portControl->portType = portMixer->types[portIndex];
461 portControl->controlType = CONTROL_TYPE_BALANCE;
462 portControl->channel = CHANNELS_STEREO;
463 /* $$mp: The value for precision is chosen more or less arbitrarily. */
464 control = (creator->newFloatControl)(creator, portControl, CONTROL_TYPE_BALANCE, -1.0F, 1.0F, 0.01F, "");
465 if (control != NULL) {
466 controls[numControls++] = control;
467 }
468 }
469 }
470 }
471 if (snd_mixer_selem_has_playback_switch(elem) || snd_mixer_selem_has_capture_switch(elem)) {
472 if (getControlSlot(portMixer, &portControl)) {
473 type = isPlayback ? CONTROL_TYPE_MUTE : CONTROL_TYPE_SELECT;
474 portControl->elem = elem;
475 portControl->portType = portMixer->types[portIndex];
476 portControl->controlType = type;
477 control = (creator->newBooleanControl)(creator, portControl, type);
478 if (control != NULL) {
479 controls[numControls++] = control;
480 }
481 }
482 }
483 /* $$mp 2003-09-14: The following cast shouln't be necessary. Instead, the
484 declaration of PORT_NewCompoundControlPtr in Ports.h should be changed
485 to take a const char* parameter. */
486 portName = (char*) snd_mixer_selem_get_name(elem);
487 control = (creator->newCompoundControl)(creator, portName, controls, numControls);
488 if (control != NULL) {
489 (creator->addControl)(creator, control);
490 }
491 TRACE0("< PORT_GetControls\n");
492}
493
494
495INT32 PORT_GetIntValue(void* controlIDV) {
496 PortControl* portControl = (PortControl*) controlIDV;
497 int value = 0;
498 snd_mixer_selem_channel_id_t channel;
499
500 if (portControl != NULL) {
501 switch (portControl->channel) {
502 case CHANNELS_MONO:
503 channel = SND_MIXER_SCHN_MONO;
504 break;
505
506 case CHANNELS_STEREO:
507 channel = SND_MIXER_SCHN_FRONT_LEFT;
508 break;
509
510 default:
511 channel = portControl->channel;
512 }
513 if (portControl->controlType == CONTROL_TYPE_MUTE ||
514 portControl->controlType == CONTROL_TYPE_SELECT) {
515 if (isPlaybackFunction(portControl->portType)) {
516 snd_mixer_selem_get_playback_switch(portControl->elem, channel, &value);
517 } else {
518 snd_mixer_selem_get_capture_switch(portControl->elem, channel, &value);
519 }
520 if (portControl->controlType == CONTROL_TYPE_MUTE) {
521 value = ! value;
522 }
523 } else {
524 ERROR1("PORT_GetIntValue(): inappropriate control type: %s\n",
525 portControl->controlType);
526 }
527 }
528 return (INT32) value;
529}
530
531
532void PORT_SetIntValue(void* controlIDV, INT32 value) {
533 PortControl* portControl = (PortControl*) controlIDV;
534 snd_mixer_selem_channel_id_t channel;
535
536 if (portControl != NULL) {
537 if (portControl->controlType == CONTROL_TYPE_MUTE) {
538 value = ! value;
539 }
540 if (portControl->controlType == CONTROL_TYPE_MUTE ||
541 portControl->controlType == CONTROL_TYPE_SELECT) {
542 if (isPlaybackFunction(portControl->portType)) {
543 snd_mixer_selem_set_playback_switch_all(portControl->elem, value);
544 } else {
545 snd_mixer_selem_set_capture_switch_all(portControl->elem, value);
546 }
547 } else {
548 ERROR1("PORT_SetIntValue(): inappropriate control type: %s\n",
549 portControl->controlType);
550 }
551 }
552}
553
554
555static float scaleVolumeValueToNormalized(long value, long min, long max) {
556 return (float) (value - min) / getRange(min, max);
557}
558
559
560static long scaleVolumeValueToHardware(float value, long min, long max) {
561 return (long)(value * getRange(min, max) + min);
562}
563
564
565float getRealVolume(PortControl* portControl,
566 snd_mixer_selem_channel_id_t channel) {
567 float fValue;
568 long lValue = 0;
569 long min = 0;
570 long max = 0;
571
572 if (isPlaybackFunction(portControl->portType)) {
573 snd_mixer_selem_get_playback_volume_range(portControl->elem,
574 &min, &max);
575 snd_mixer_selem_get_playback_volume(portControl->elem,
576 channel, &lValue);
577 } else {
578 snd_mixer_selem_get_capture_volume_range(portControl->elem,
579 &min, &max);
580 snd_mixer_selem_get_capture_volume(portControl->elem,
581 channel, &lValue);
582 }
583 fValue = scaleVolumeValueToNormalized(lValue, min, max);
584 return fValue;
585}
586
587
588void setRealVolume(PortControl* portControl,
589 snd_mixer_selem_channel_id_t channel, float value) {
590 long lValue = 0;
591 long min = 0;
592 long max = 0;
593
594 if (isPlaybackFunction(portControl->portType)) {
595 snd_mixer_selem_get_playback_volume_range(portControl->elem,
596 &min, &max);
597 lValue = scaleVolumeValueToHardware(value, min, max);
598 snd_mixer_selem_set_playback_volume(portControl->elem,
599 channel, lValue);
600 } else {
601 snd_mixer_selem_get_capture_volume_range(portControl->elem,
602 &min, &max);
603 lValue = scaleVolumeValueToHardware(value, min, max);
604 snd_mixer_selem_set_capture_volume(portControl->elem,
605 channel, lValue);
606 }
607}
608
609
610static float getFakeBalance(PortControl* portControl) {
611 float volL, volR;
612
613 // pan is the ratio of left and right
614 volL = getRealVolume(portControl, SND_MIXER_SCHN_FRONT_LEFT);
615 volR = getRealVolume(portControl, SND_MIXER_SCHN_FRONT_RIGHT);
616 if (volL > volR) {
617 return -1.0f + (volR / volL);
618 }
619 else if (volR > volL) {
620 return 1.0f - (volL / volR);
621 }
622 return 0.0f;
623}
624
625
626static float getFakeVolume(PortControl* portControl) {
627 float valueL;
628 float valueR;
629 float value;
630
631 valueL = getRealVolume(portControl, SND_MIXER_SCHN_FRONT_LEFT);
632 valueR = getRealVolume(portControl, SND_MIXER_SCHN_FRONT_RIGHT);
633 // volume is the greater value of both
634 value = valueL > valueR ? valueL : valueR ;
635 return value;
636}
637
638
639/*
640 * sets the unsigned values for left and right volume according to
641 * the given volume (0...1) and balance (-1..0..+1)
642 */
643static void setFakeVolume(PortControl* portControl, float vol, float bal) {
644 float volumeLeft;
645 float volumeRight;
646
647 if (bal < 0.0f) {
648 volumeLeft = vol;
649 volumeRight = vol * (bal + 1.0f);
650 } else {
651 volumeLeft = vol * (1.0f - bal);
652 volumeRight = vol;
653 }
654 setRealVolume(portControl, SND_MIXER_SCHN_FRONT_LEFT, volumeLeft);
655 setRealVolume(portControl, SND_MIXER_SCHN_FRONT_RIGHT, volumeRight);
656}
657
658
659float PORT_GetFloatValue(void* controlIDV) {
660 PortControl* portControl = (PortControl*) controlIDV;
661 float value = 0.0F;
662
663 if (portControl != NULL) {
664 if (portControl->controlType == CONTROL_TYPE_VOLUME) {
665 switch (portControl->channel) {
666 case CHANNELS_MONO:
667 value = getRealVolume(portControl, SND_MIXER_SCHN_MONO);
668 break;
669
670 case CHANNELS_STEREO:
671 value = getFakeVolume(portControl);
672 break;
673
674 default:
675 value = getRealVolume(portControl, portControl->channel);
676 }
677 } else if (portControl->controlType == CONTROL_TYPE_BALANCE) {
678 if (portControl->channel == CHANNELS_STEREO) {
679 value = getFakeBalance(portControl);
680 } else {
681 ERROR0("PORT_GetFloatValue(): Balance only allowed for stereo channels!\n");
682 }
683 } else {
684 ERROR1("PORT_GetFloatValue(): inappropriate control type: %s!\n",
685 portControl->controlType);
686 }
687 }
688 return value;
689}
690
691
692void PORT_SetFloatValue(void* controlIDV, float value) {
693 PortControl* portControl = (PortControl*) controlIDV;
694
695 if (portControl != NULL) {
696 if (portControl->controlType == CONTROL_TYPE_VOLUME) {
697 switch (portControl->channel) {
698 case CHANNELS_MONO:
699 setRealVolume(portControl, SND_MIXER_SCHN_MONO, value);
700 break;
701
702 case CHANNELS_STEREO:
703 setFakeVolume(portControl, value, getFakeBalance(portControl));
704 break;
705
706 default:
707 setRealVolume(portControl, portControl->channel, value);
708 }
709 } else if (portControl->controlType == CONTROL_TYPE_BALANCE) {
710 if (portControl->channel == CHANNELS_STEREO) {
711 setFakeVolume(portControl, getFakeVolume(portControl), value);
712 } else {
713 ERROR0("PORT_SetFloatValue(): Balance only allowed for stereo channels!\n");
714 }
715 } else {
716 ERROR1("PORT_SetFloatValue(): inappropriate control type: %s!\n",
717 portControl->controlType);
718 }
719 }
720}
721
722
723#endif // USE_PORTS