Home | History | Annotate | Download | only in alsa_sound
      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 {
     82     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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