Home | History | Annotate | Download | only in alsa_utils
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #define LOG_TAG "alsa_device_proxy"
     18 /*#define LOG_NDEBUG 0*/
     19 /*#define LOG_PCM_PARAMS 0*/
     20 
     21 #include <log/log.h>
     22 
     23 #include <errno.h>
     24 
     25 #include "include/alsa_device_proxy.h"
     26 
     27 #include "include/alsa_logging.h"
     28 
     29 #define DEFAULT_PERIOD_SIZE     1024
     30 #define DEFAULT_PERIOD_COUNT    2
     31 
     32 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
     33 
     34 static const unsigned format_byte_size_map[] = {
     35     2, /* PCM_FORMAT_S16_LE */
     36     4, /* PCM_FORMAT_S32_LE */
     37     1, /* PCM_FORMAT_S8 */
     38     4, /* PCM_FORMAT_S24_LE */
     39     3, /* PCM_FORMAT_S24_3LE */
     40 };
     41 
     42 int proxy_prepare(alsa_device_proxy * proxy, alsa_device_profile* profile,
     43                    struct pcm_config * config)
     44 {
     45     int ret = 0;
     46 
     47     ALOGV("proxy_prepare(c:%d, d:%d)", profile->card, profile->device);
     48 
     49     proxy->profile = profile;
     50 
     51 #ifdef LOG_PCM_PARAMS
     52     log_pcm_config(config, "proxy_setup()");
     53 #endif
     54 
     55     if (config->format != PCM_FORMAT_INVALID && profile_is_format_valid(profile, config->format)) {
     56         proxy->alsa_config.format = config->format;
     57     } else {
     58         proxy->alsa_config.format = profile->default_config.format;
     59         ALOGW("Invalid format %d - using default %d.",
     60               config->format, profile->default_config.format);
     61         // Indicate override when default format was not requested
     62         if (config->format != PCM_FORMAT_INVALID) {
     63             ret = -EINVAL;
     64         }
     65     }
     66 
     67     if (config->rate != 0 && profile_is_sample_rate_valid(profile, config->rate)) {
     68         proxy->alsa_config.rate = config->rate;
     69     } else {
     70         proxy->alsa_config.rate = profile->default_config.rate;
     71         ALOGW("Invalid sample rate %u - using default %u.",
     72               config->rate, profile->default_config.rate);
     73         // Indicate override when default rate was not requested
     74         if (config->rate != 0) {
     75             ret = -EINVAL;
     76         }
     77     }
     78 
     79     if (config->channels != 0 && profile_is_channel_count_valid(profile, config->channels)) {
     80         proxy->alsa_config.channels = config->channels;
     81     } else {
     82         proxy->alsa_config.channels = profile_get_closest_channel_count(profile, config->channels);
     83         ALOGW("Invalid channel count %u - using closest %u.",
     84               config->channels, proxy->alsa_config.channels);
     85         // Indicate override when default channel count was not requested
     86         if (config->channels != 0) {
     87             ret = -EINVAL;
     88         }
     89     }
     90 
     91     proxy->alsa_config.period_count = profile->default_config.period_count;
     92     proxy->alsa_config.period_size =
     93             profile_get_period_size(proxy->profile, proxy->alsa_config.rate);
     94 
     95     // Hack for USB accessory audio.
     96     // Here we set the correct value for period_count if tinyalsa fails to get it from the
     97     // f_audio_source driver.
     98     if (proxy->alsa_config.period_count == 0) {
     99         proxy->alsa_config.period_count = 4;
    100     }
    101 
    102     proxy->pcm = NULL;
    103     // config format should be checked earlier against profile.
    104     if (config->format >= 0 && (size_t)config->format < ARRAY_SIZE(format_byte_size_map)) {
    105         proxy->frame_size = format_byte_size_map[config->format] * proxy->alsa_config.channels;
    106     } else {
    107         proxy->frame_size = 1;
    108     }
    109 
    110     // let's check to make sure we can ACTUALLY use the maximum rate (with the channel count)
    111     // Note that profile->sample_rates is sorted highest to lowest, so the scan will get
    112     // us the highest working rate
    113     int max_rate_index = proxy_scan_rates(proxy, profile->sample_rates);
    114     if (max_rate_index >= 0) {
    115         if (proxy->alsa_config.rate > profile->sample_rates[max_rate_index]) {
    116             ALOGW("Limiting samplnig rate from %u to %u.",
    117                   proxy->alsa_config.rate, profile->sample_rates[max_rate_index]);
    118             proxy->alsa_config.rate = profile->sample_rates[max_rate_index];
    119             ret = -EINVAL;
    120         }
    121     }
    122     return ret;
    123 }
    124 
    125 int proxy_open(alsa_device_proxy * proxy)
    126 {
    127     alsa_device_profile* profile = proxy->profile;
    128     ALOGV("proxy_open(card:%d device:%d %s)", profile->card, profile->device,
    129           profile->direction == PCM_OUT ? "PCM_OUT" : "PCM_IN");
    130 
    131     if (profile->card < 0 || profile->device < 0) {
    132         return -EINVAL;
    133     }
    134 
    135     proxy->pcm = pcm_open(profile->card, profile->device,
    136             profile->direction | PCM_MONOTONIC, &proxy->alsa_config);
    137     if (proxy->pcm == NULL) {
    138         return -ENOMEM;
    139     }
    140 
    141     if (!pcm_is_ready(proxy->pcm)) {
    142         ALOGE("  proxy_open() pcm_is_ready() failed: %s", pcm_get_error(proxy->pcm));
    143 #if defined(LOG_PCM_PARAMS)
    144         log_pcm_config(&proxy->alsa_config, "config");
    145 #endif
    146         pcm_close(proxy->pcm);
    147         proxy->pcm = NULL;
    148         return -ENOMEM;
    149     }
    150 
    151     return 0;
    152 }
    153 
    154 void proxy_close(alsa_device_proxy * proxy)
    155 {
    156     ALOGV("proxy_close() [pcm:%p]", proxy->pcm);
    157 
    158     if (proxy->pcm != NULL) {
    159         pcm_close(proxy->pcm);
    160         proxy->pcm = NULL;
    161     }
    162 }
    163 
    164 /*
    165  * Sample Rate
    166  */
    167 unsigned proxy_get_sample_rate(const alsa_device_proxy * proxy)
    168 {
    169     return proxy->alsa_config.rate;
    170 }
    171 
    172 /*
    173  * Format
    174  */
    175 enum pcm_format proxy_get_format(const alsa_device_proxy * proxy)
    176 {
    177     return proxy->alsa_config.format;
    178 }
    179 
    180 /*
    181  * Channel Count
    182  */
    183 unsigned proxy_get_channel_count(const alsa_device_proxy * proxy)
    184 {
    185     return proxy->alsa_config.channels;
    186 }
    187 
    188 /*
    189  * Other
    190  */
    191 unsigned int proxy_get_period_size(const alsa_device_proxy * proxy)
    192 {
    193     return proxy->alsa_config.period_size;
    194 }
    195 
    196 unsigned int proxy_get_period_count(const alsa_device_proxy * proxy)
    197 {
    198     return proxy->alsa_config.period_count;
    199 }
    200 
    201 unsigned proxy_get_latency(const alsa_device_proxy * proxy)
    202 {
    203     return (proxy_get_period_size(proxy) * proxy_get_period_count(proxy) * 1000)
    204                / proxy_get_sample_rate(proxy);
    205 }
    206 
    207 int proxy_get_presentation_position(const alsa_device_proxy * proxy,
    208         uint64_t *frames, struct timespec *timestamp)
    209 {
    210     int ret = -EPERM; // -1
    211     unsigned int avail;
    212     if (proxy->pcm != NULL
    213             && pcm_get_htimestamp(proxy->pcm, &avail, timestamp) == 0) {
    214         const size_t kernel_buffer_size =
    215                 proxy->alsa_config.period_size * proxy->alsa_config.period_count;
    216         if (avail > kernel_buffer_size) {
    217             ALOGE("available frames(%u) > buffer size(%zu)", avail, kernel_buffer_size);
    218         } else {
    219             int64_t signed_frames = proxy->transferred - kernel_buffer_size + avail;
    220             // It is possible to compensate for additional driver and device delay
    221             // by changing signed_frames.  Example:
    222             // signed_frames -= 20 /* ms */ * proxy->alsa_config.rate / 1000;
    223             if (signed_frames >= 0) {
    224                 *frames = signed_frames;
    225                 ret = 0;
    226             }
    227         }
    228     }
    229     return ret;
    230 }
    231 
    232 /*
    233  * I/O
    234  */
    235 int proxy_write(alsa_device_proxy * proxy, const void *data, unsigned int count)
    236 {
    237     int ret = pcm_write(proxy->pcm, data, count);
    238     if (ret == 0) {
    239         proxy->transferred += count / proxy->frame_size;
    240     }
    241     return ret;
    242 }
    243 
    244 int proxy_read(const alsa_device_proxy * proxy, void *data, unsigned int count)
    245 {
    246     return pcm_read(proxy->pcm, data, count);
    247 }
    248 
    249 /*
    250  * Debugging
    251  */
    252 void proxy_dump(const alsa_device_proxy* proxy, int fd)
    253 {
    254     if (proxy != NULL) {
    255         dprintf(fd, "  channels: %d\n", proxy->alsa_config.channels);
    256         dprintf(fd, "  rate: %d\n", proxy->alsa_config.rate);
    257         dprintf(fd, "  period_size: %d\n", proxy->alsa_config.period_size);
    258         dprintf(fd, "  period_count: %d\n", proxy->alsa_config.period_count);
    259         dprintf(fd, "  format: %d\n", proxy->alsa_config.format);
    260     }
    261 }
    262 
    263 int proxy_scan_rates(alsa_device_proxy * proxy, unsigned sample_rates[]) {
    264     alsa_device_profile* profile = proxy->profile;
    265     if (profile->card < 0 || profile->device < 0) {
    266         return -EINVAL;
    267     }
    268 
    269     struct pcm_config alsa_config;
    270     memcpy(&alsa_config, &proxy->alsa_config, sizeof(alsa_config));
    271 
    272     struct pcm * alsa_pcm;
    273     int rate_index = 0;
    274     while (sample_rates[rate_index] != 0) {
    275         alsa_config.rate = sample_rates[rate_index];
    276         alsa_pcm = pcm_open(profile->card, profile->device,
    277                 profile->direction | PCM_MONOTONIC, &alsa_config);
    278         if (alsa_pcm != NULL) {
    279             if (pcm_is_ready(alsa_pcm)) {
    280                 pcm_close(alsa_pcm);
    281                 return rate_index;
    282             }
    283 
    284             pcm_close(alsa_pcm);
    285         }
    286 
    287         rate_index++;
    288     }
    289 
    290     return -EINVAL;
    291 }
    292