• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* ALSAMixer.cpp
2  **
3  ** Copyright 2008-2010 Wind River Systems
4  **
5  ** Licensed under the Apache License, Version 2.0 (the "License");
6  ** you may not use this file except in compliance with the License.
7  ** You may obtain a copy of the License at
8  **
9  **     http://www.apache.org/licenses/LICENSE-2.0
10  **
11  ** Unless required by applicable law or agreed to in writing, software
12  ** distributed under the License is distributed on an "AS IS" BASIS,
13  ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  ** See the License for the specific language governing permissions and
15  ** limitations under the License.
16  */
17 
18 #include <errno.h>
19 #include <stdarg.h>
20 #include <sys/stat.h>
21 #include <fcntl.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <dlfcn.h>
25 
26 #define LOG_TAG "AudioHardwareALSA"
27 #include <utils/Log.h>
28 #include <utils/String8.h>
29 
30 #include <cutils/properties.h>
31 #include <media/AudioRecord.h>
32 #include <hardware_legacy/power.h>
33 
34 #include "AudioHardwareALSA.h"
35 
36 #define SND_MIXER_VOL_RANGE_MIN  (0)
37 #define SND_MIXER_VOL_RANGE_MAX  (100)
38 
39 #define ALSA_NAME_MAX 128
40 
41 #define ALSA_STRCAT(x,y) \
42     if (strlen(x) + strlen(y) < ALSA_NAME_MAX) \
43         strcat(x, y);
44 
45 namespace android
46 {
47 
48 // ----------------------------------------------------------------------------
49 
50 struct mixer_info_t;
51 
52 struct alsa_properties_t
53 {
54     const AudioSystem::audio_devices device;
55     const char         *propName;
56     const char         *propDefault;
57     mixer_info_t       *mInfo;
58 };
59 
60 #define ALSA_PROP(dev, name, out, in) \
61     {\
62         {dev, "alsa.mixer.playback." name, out, NULL},\
63         {dev, "alsa.mixer.capture." name, in, NULL}\
64     }
65 
66 static alsa_properties_t
67 mixerMasterProp[SND_PCM_STREAM_LAST+1] =
68         ALSA_PROP(AudioSystem::DEVICE_OUT_ALL, "master", "PCM", "Capture");
69 
70 static alsa_properties_t
71 mixerProp[][SND_PCM_STREAM_LAST+1] = {
72     ALSA_PROP(AudioSystem::DEVICE_OUT_EARPIECE, "earpiece", "Earpiece", "Capture"),
73     ALSA_PROP(AudioSystem::DEVICE_OUT_SPEAKER, "speaker", "Speaker",  ""),
74     ALSA_PROP(AudioSystem::DEVICE_OUT_WIRED_HEADSET, "headset", "Headphone", "Capture"),
75     ALSA_PROP(AudioSystem::DEVICE_OUT_BLUETOOTH_SCO, "bluetooth.sco", "Bluetooth", "Bluetooth Capture"),
76     ALSA_PROP(AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP, "bluetooth.a2dp", "Bluetooth A2DP", "Bluetooth A2DP Capture"),
77     ALSA_PROP(static_cast<AudioSystem::audio_devices>(0), "", NULL, NULL)
78 };
79 
80 struct mixer_info_t
81 {
mixer_info_tandroid::mixer_info_t82     mixer_info_t() :
83         elem(0),
84         min(SND_MIXER_VOL_RANGE_MIN),
85         max(SND_MIXER_VOL_RANGE_MAX),
86         mute(false)
87     {
88     }
89 
90     snd_mixer_elem_t *elem;
91     long              min;
92     long              max;
93     long              volume;
94     bool              mute;
95     char              name[ALSA_NAME_MAX];
96 };
97 
initMixer(snd_mixer_t ** mixer,const char * name)98 static int initMixer (snd_mixer_t **mixer, const char *name)
99 {
100     int err;
101 
102     if ((err = snd_mixer_open(mixer, 0)) < 0) {
103         ALOGE("Unable to open mixer: %s", snd_strerror(err));
104         return err;
105     }
106 
107     if ((err = snd_mixer_attach(*mixer, name)) < 0) {
108         ALOGW("Unable to attach mixer to device %s: %s",
109             name, snd_strerror(err));
110 
111         if ((err = snd_mixer_attach(*mixer, "hw:00")) < 0) {
112             ALOGE("Unable to attach mixer to device default: %s",
113                 snd_strerror(err));
114 
115             snd_mixer_close (*mixer);
116             *mixer = NULL;
117             return err;
118         }
119     }
120 
121     if ((err = snd_mixer_selem_register(*mixer, NULL, NULL)) < 0) {
122         ALOGE("Unable to register mixer elements: %s", snd_strerror(err));
123         snd_mixer_close (*mixer);
124         *mixer = NULL;
125         return err;
126     }
127 
128     // Get the mixer controls from the kernel
129     if ((err = snd_mixer_load(*mixer)) < 0) {
130         ALOGE("Unable to load mixer elements: %s", snd_strerror(err));
131         snd_mixer_close (*mixer);
132         *mixer = NULL;
133         return err;
134     }
135 
136     return 0;
137 }
138 
139 typedef int (*hasVolume_t)(snd_mixer_elem_t*);
140 
141 static const hasVolume_t hasVolume[] = {
142     snd_mixer_selem_has_playback_volume,
143     snd_mixer_selem_has_capture_volume
144 };
145 
146 typedef int (*getVolumeRange_t)(snd_mixer_elem_t*, long int*, long int*);
147 
148 static const getVolumeRange_t getVolumeRange[] = {
149     snd_mixer_selem_get_playback_volume_range,
150     snd_mixer_selem_get_capture_volume_range
151 };
152 
153 typedef int (*setVolume_t)(snd_mixer_elem_t*, long int);
154 
155 static const setVolume_t setVol[] = {
156     snd_mixer_selem_set_playback_volume_all,
157     snd_mixer_selem_set_capture_volume_all
158 };
159 
ALSAMixer()160 ALSAMixer::ALSAMixer()
161 {
162     int err;
163 
164     initMixer (&mMixer[SND_PCM_STREAM_PLAYBACK], "AndroidOut");
165     initMixer (&mMixer[SND_PCM_STREAM_CAPTURE], "AndroidIn");
166 
167     snd_mixer_selem_id_t *sid;
168     snd_mixer_selem_id_alloca(&sid);
169 
170     for (int i = 0; i <= SND_PCM_STREAM_LAST; i++) {
171 
172         if (!mMixer[i]) continue;
173 
174         mixer_info_t *info = mixerMasterProp[i].mInfo = new mixer_info_t;
175 
176         property_get (mixerMasterProp[i].propName,
177                       info->name,
178                       mixerMasterProp[i].propDefault);
179 
180         for (snd_mixer_elem_t *elem = snd_mixer_first_elem(mMixer[i]);
181              elem;
182              elem = snd_mixer_elem_next(elem)) {
183 
184             if (!snd_mixer_selem_is_active(elem))
185                 continue;
186 
187             snd_mixer_selem_get_id(elem, sid);
188 
189             // Find PCM playback volume control element.
190             const char *elementName = snd_mixer_selem_id_get_name(sid);
191 
192             if (info->elem == NULL &&
193                 strcmp(elementName, info->name) == 0 &&
194                 hasVolume[i] (elem)) {
195 
196                 info->elem = elem;
197                 getVolumeRange[i] (elem, &info->min, &info->max);
198                 info->volume = info->max;
199                 setVol[i] (elem, info->volume);
200                 if (i == SND_PCM_STREAM_PLAYBACK &&
201                     snd_mixer_selem_has_playback_switch (elem))
202                     snd_mixer_selem_set_playback_switch_all (elem, 1);
203                 break;
204             }
205         }
206 
207         ALOGV("Mixer: master '%s' %s.", info->name, info->elem ? "found" : "not found");
208 
209         for (int j = 0; mixerProp[j][i].device; j++) {
210 
211             mixer_info_t *info = mixerProp[j][i].mInfo = new mixer_info_t;
212 
213             property_get (mixerProp[j][i].propName,
214                           info->name,
215                           mixerProp[j][i].propDefault);
216 
217             for (snd_mixer_elem_t *elem = snd_mixer_first_elem(mMixer[i]);
218                  elem;
219                  elem = snd_mixer_elem_next(elem)) {
220 
221                 if (!snd_mixer_selem_is_active(elem))
222                     continue;
223 
224                 snd_mixer_selem_get_id(elem, sid);
225 
226                 // Find PCM playback volume control element.
227                 const char *elementName = snd_mixer_selem_id_get_name(sid);
228 
229                if (info->elem == NULL &&
230                     strcmp(elementName, info->name) == 0 &&
231                     hasVolume[i] (elem)) {
232 
233                     info->elem = elem;
234                     getVolumeRange[i] (elem, &info->min, &info->max);
235                     info->volume = info->max;
236                     setVol[i] (elem, info->volume);
237                     if (i == SND_PCM_STREAM_PLAYBACK &&
238                         snd_mixer_selem_has_playback_switch (elem))
239                         snd_mixer_selem_set_playback_switch_all (elem, 1);
240                     break;
241                 }
242             }
243             ALOGV("Mixer: route '%s' %s.", info->name, info->elem ? "found" : "not found");
244         }
245     }
246     ALOGV("mixer initialized.");
247 }
248 
~ALSAMixer()249 ALSAMixer::~ALSAMixer()
250 {
251     for (int i = 0; i <= SND_PCM_STREAM_LAST; i++) {
252         if (mMixer[i]) snd_mixer_close (mMixer[i]);
253         if (mixerMasterProp[i].mInfo) {
254             delete mixerMasterProp[i].mInfo;
255             mixerMasterProp[i].mInfo = NULL;
256         }
257         for (int j = 0; mixerProp[j][i].device; j++) {
258             if (mixerProp[j][i].mInfo) {
259                 delete mixerProp[j][i].mInfo;
260                 mixerProp[j][i].mInfo = NULL;
261             }
262         }
263     }
264     ALOGV("mixer destroyed.");
265 }
266 
setMasterVolume(float volume)267 status_t ALSAMixer::setMasterVolume(float volume)
268 {
269     mixer_info_t *info = mixerMasterProp[SND_PCM_STREAM_PLAYBACK].mInfo;
270     if (!info || !info->elem) return INVALID_OPERATION;
271 
272     long minVol = info->min;
273     long maxVol = info->max;
274 
275     // Make sure volume is between bounds.
276     long vol = minVol + volume * (maxVol - minVol);
277     if (vol > maxVol) vol = maxVol;
278     if (vol < minVol) vol = minVol;
279 
280     info->volume = vol;
281     snd_mixer_selem_set_playback_volume_all (info->elem, vol);
282 
283     return NO_ERROR;
284 }
285 
setMasterGain(float gain)286 status_t ALSAMixer::setMasterGain(float gain)
287 {
288     mixer_info_t *info = mixerMasterProp[SND_PCM_STREAM_CAPTURE].mInfo;
289     if (!info || !info->elem) return INVALID_OPERATION;
290 
291     long minVol = info->min;
292     long maxVol = info->max;
293 
294     // Make sure volume is between bounds.
295     long vol = minVol + gain * (maxVol - minVol);
296     if (vol > maxVol) vol = maxVol;
297     if (vol < minVol) vol = minVol;
298 
299     info->volume = vol;
300     snd_mixer_selem_set_capture_volume_all (info->elem, vol);
301 
302     return NO_ERROR;
303 }
304 
setVolume(uint32_t device,float left,float right)305 status_t ALSAMixer::setVolume(uint32_t device, float left, float right)
306 {
307     for (int j = 0; mixerProp[j][SND_PCM_STREAM_PLAYBACK].device; j++)
308         if (mixerProp[j][SND_PCM_STREAM_PLAYBACK].device & device) {
309 
310             mixer_info_t *info = mixerProp[j][SND_PCM_STREAM_PLAYBACK].mInfo;
311             if (!info || !info->elem) return INVALID_OPERATION;
312 
313             long minVol = info->min;
314             long maxVol = info->max;
315 
316             // Make sure volume is between bounds.
317             long vol = minVol + left * (maxVol - minVol);
318             if (vol > maxVol) vol = maxVol;
319             if (vol < minVol) vol = minVol;
320 
321             info->volume = vol;
322             snd_mixer_selem_set_playback_volume_all (info->elem, vol);
323         }
324 
325     return NO_ERROR;
326 }
327 
setGain(uint32_t device,float gain)328 status_t ALSAMixer::setGain(uint32_t device, float gain)
329 {
330     for (int j = 0; mixerProp[j][SND_PCM_STREAM_CAPTURE].device; j++)
331         if (mixerProp[j][SND_PCM_STREAM_CAPTURE].device & device) {
332 
333             mixer_info_t *info = mixerProp[j][SND_PCM_STREAM_CAPTURE].mInfo;
334             if (!info || !info->elem) return INVALID_OPERATION;
335 
336             long minVol = info->min;
337             long maxVol = info->max;
338 
339             // Make sure volume is between bounds.
340             long vol = minVol + gain * (maxVol - minVol);
341             if (vol > maxVol) vol = maxVol;
342             if (vol < minVol) vol = minVol;
343 
344             info->volume = vol;
345             snd_mixer_selem_set_capture_volume_all (info->elem, vol);
346         }
347 
348     return NO_ERROR;
349 }
350 
setCaptureMuteState(uint32_t device,bool state)351 status_t ALSAMixer::setCaptureMuteState(uint32_t device, bool state)
352 {
353     for (int j = 0; mixerProp[j][SND_PCM_STREAM_CAPTURE].device; j++)
354         if (mixerProp[j][SND_PCM_STREAM_CAPTURE].device & device) {
355 
356             mixer_info_t *info = mixerProp[j][SND_PCM_STREAM_CAPTURE].mInfo;
357             if (!info || !info->elem) return INVALID_OPERATION;
358 
359             if (snd_mixer_selem_has_capture_switch (info->elem)) {
360 
361                 int err = snd_mixer_selem_set_capture_switch_all (info->elem, static_cast<int>(!state));
362                 if (err < 0) {
363                     ALOGE("Unable to %s capture mixer switch %s",
364                         state ? "enable" : "disable", info->name);
365                     return INVALID_OPERATION;
366                 }
367             }
368 
369             info->mute = state;
370         }
371 
372     return NO_ERROR;
373 }
374 
getCaptureMuteState(uint32_t device,bool * state)375 status_t ALSAMixer::getCaptureMuteState(uint32_t device, bool *state)
376 {
377     if (!state) return BAD_VALUE;
378 
379     for (int j = 0; mixerProp[j][SND_PCM_STREAM_CAPTURE].device; j++)
380         if (mixerProp[j][SND_PCM_STREAM_CAPTURE].device & device) {
381 
382             mixer_info_t *info = mixerProp[j][SND_PCM_STREAM_CAPTURE].mInfo;
383             if (!info || !info->elem) return INVALID_OPERATION;
384 
385             *state = info->mute;
386             return NO_ERROR;
387         }
388 
389     return BAD_VALUE;
390 }
391 
setPlaybackMuteState(uint32_t device,bool state)392 status_t ALSAMixer::setPlaybackMuteState(uint32_t device, bool state)
393 {
394     for (int j = 0; mixerProp[j][SND_PCM_STREAM_PLAYBACK].device; j++)
395         if (mixerProp[j][SND_PCM_STREAM_PLAYBACK].device & device) {
396 
397             mixer_info_t *info = mixerProp[j][SND_PCM_STREAM_PLAYBACK].mInfo;
398             if (!info || !info->elem) return INVALID_OPERATION;
399 
400             if (snd_mixer_selem_has_playback_switch (info->elem)) {
401 
402                 int err = snd_mixer_selem_set_playback_switch_all (info->elem, static_cast<int>(!state));
403                 if (err < 0) {
404                     ALOGE("Unable to %s playback mixer switch %s",
405                         state ? "enable" : "disable", info->name);
406                     return INVALID_OPERATION;
407                 }
408             }
409 
410             info->mute = state;
411         }
412 
413     return NO_ERROR;
414 }
415 
getPlaybackMuteState(uint32_t device,bool * state)416 status_t ALSAMixer::getPlaybackMuteState(uint32_t device, bool *state)
417 {
418     if (!state) return BAD_VALUE;
419 
420     for (int j = 0; mixerProp[j][SND_PCM_STREAM_PLAYBACK].device; j++)
421         if (mixerProp[j][SND_PCM_STREAM_PLAYBACK].device & device) {
422 
423             mixer_info_t *info = mixerProp[j][SND_PCM_STREAM_PLAYBACK].mInfo;
424             if (!info || !info->elem) return INVALID_OPERATION;
425 
426             *state = info->mute;
427             return NO_ERROR;
428         }
429 
430     return BAD_VALUE;
431 }
432 
433 };        // namespace android
434