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 void proxy_prepare(alsa_device_proxy * proxy, alsa_device_profile* profile,
     43                    struct pcm_config * config)
     44 {
     45     ALOGV("proxy_prepare(c:%d, d:%d)", profile->card, profile->device);
     46 
     47     proxy->profile = profile;
     48 
     49 #ifdef LOG_PCM_PARAMS
     50     log_pcm_config(config, "proxy_setup()");
     51 #endif
     52 
     53     if (config->format != PCM_FORMAT_INVALID && profile_is_format_valid(profile, config->format)) {
     54         proxy->alsa_config.format = config->format;
     55     } else {
     56         ALOGW("Invalid format %d - using default %d.",
     57               config->format, profile->default_config.format);
     58         proxy->alsa_config.format = profile->default_config.format;
     59     }
     60 
     61     if (config->rate != 0 && profile_is_sample_rate_valid(profile, config->rate)) {
     62         proxy->alsa_config.rate = config->rate;
     63     } else {
     64         ALOGW("Invalid sample rate %u - using default %u.",
     65               config->rate, profile->default_config.rate);
     66         proxy->alsa_config.rate = profile->default_config.rate;
     67     }
     68 
     69     if (config->channels != 0 && profile_is_channel_count_valid(profile, config->channels)) {
     70         proxy->alsa_config.channels = config->channels;
     71     } else {
     72         proxy->alsa_config.channels = profile_get_closest_channel_count(profile, config->channels);
     73         ALOGW("Invalid channel count %u - using closest %u.",
     74               config->channels, proxy->alsa_config.channels);
     75     }
     76 
     77     proxy->alsa_config.period_count = profile->default_config.period_count;
     78     proxy->alsa_config.period_size =
     79             profile_get_period_size(proxy->profile, proxy->alsa_config.rate);
     80 
     81     // Hack for USB accessory audio.
     82     // Here we set the correct value for period_count if tinyalsa fails to get it from the
     83     // f_audio_source driver.
     84     if (proxy->alsa_config.period_count == 0) {
     85         proxy->alsa_config.period_count = 4;
     86     }
     87 
     88     proxy->pcm = NULL;
     89     // config format should be checked earlier against profile.
     90     if (config->format >= 0 && (size_t)config->format < ARRAY_SIZE(format_byte_size_map)) {
     91         proxy->frame_size = format_byte_size_map[config->format] * proxy->alsa_config.channels;
     92     } else {
     93         proxy->frame_size = 1;
     94     }
     95 }
     96 
     97 int proxy_open(alsa_device_proxy * proxy)
     98 {
     99     alsa_device_profile* profile = proxy->profile;
    100     ALOGV("proxy_open(card:%d device:%d %s)", profile->card, profile->device,
    101           profile->direction == PCM_OUT ? "PCM_OUT" : "PCM_IN");
    102 
    103     if (profile->card < 0 || profile->device < 0) {
    104         return -EINVAL;
    105     }
    106 
    107     proxy->pcm = pcm_open(profile->card, profile->device,
    108             profile->direction | PCM_MONOTONIC, &proxy->alsa_config);
    109     if (proxy->pcm == NULL) {
    110         return -ENOMEM;
    111     }
    112 
    113     if (!pcm_is_ready(proxy->pcm)) {
    114         ALOGE("  proxy_open() pcm_open() failed: %s", pcm_get_error(proxy->pcm));
    115 #if defined(LOG_PCM_PARAMS)
    116         log_pcm_config(&proxy->alsa_config, "config");
    117 #endif
    118         pcm_close(proxy->pcm);
    119         proxy->pcm = NULL;
    120         return -ENOMEM;
    121     }
    122 
    123     return 0;
    124 }
    125 
    126 void proxy_close(alsa_device_proxy * proxy)
    127 {
    128     ALOGV("proxy_close() [pcm:%p]", proxy->pcm);
    129 
    130     if (proxy->pcm != NULL) {
    131         pcm_close(proxy->pcm);
    132         proxy->pcm = NULL;
    133     }
    134 }
    135 
    136 /*
    137  * Sample Rate
    138  */
    139 unsigned proxy_get_sample_rate(const alsa_device_proxy * proxy)
    140 {
    141     return proxy->alsa_config.rate;
    142 }
    143 
    144 /*
    145  * Format
    146  */
    147 enum pcm_format proxy_get_format(const alsa_device_proxy * proxy)
    148 {
    149     return proxy->alsa_config.format;
    150 }
    151 
    152 /*
    153  * Channel Count
    154  */
    155 unsigned proxy_get_channel_count(const alsa_device_proxy * proxy)
    156 {
    157     return proxy->alsa_config.channels;
    158 }
    159 
    160 /*
    161  * Other
    162  */
    163 unsigned int proxy_get_period_size(const alsa_device_proxy * proxy)
    164 {
    165     return proxy->alsa_config.period_size;
    166 }
    167 
    168 unsigned int proxy_get_period_count(const alsa_device_proxy * proxy)
    169 {
    170     return proxy->alsa_config.period_count;
    171 }
    172 
    173 unsigned proxy_get_latency(const alsa_device_proxy * proxy)
    174 {
    175     return (proxy_get_period_size(proxy) * proxy_get_period_count(proxy) * 1000)
    176                / proxy_get_sample_rate(proxy);
    177 }
    178 
    179 int proxy_get_presentation_position(const alsa_device_proxy * proxy,
    180         uint64_t *frames, struct timespec *timestamp)
    181 {
    182     int ret = -EPERM; // -1
    183     unsigned int avail;
    184     if (proxy->pcm != NULL
    185             && pcm_get_htimestamp(proxy->pcm, &avail, timestamp) == 0) {
    186         const size_t kernel_buffer_size =
    187                 proxy->alsa_config.period_size * proxy->alsa_config.period_count;
    188         if (avail > kernel_buffer_size) {
    189             ALOGE("available frames(%u) > buffer size(%zu)", avail, kernel_buffer_size);
    190         } else {
    191             int64_t signed_frames = proxy->transferred - kernel_buffer_size + avail;
    192             // It is possible to compensate for additional driver and device delay
    193             // by changing signed_frames.  Example:
    194             // signed_frames -= 20 /* ms */ * proxy->alsa_config.rate / 1000;
    195             if (signed_frames >= 0) {
    196                 *frames = signed_frames;
    197                 ret = 0;
    198             }
    199         }
    200     }
    201     return ret;
    202 }
    203 
    204 /*
    205  * I/O
    206  */
    207 int proxy_write(alsa_device_proxy * proxy, const void *data, unsigned int count)
    208 {
    209     int ret = pcm_write(proxy->pcm, data, count);
    210     if (ret == 0) {
    211         proxy->transferred += count / proxy->frame_size;
    212     }
    213     return ret;
    214 }
    215 
    216 int proxy_read(const alsa_device_proxy * proxy, void *data, unsigned int count)
    217 {
    218     return pcm_read(proxy->pcm, data, count);
    219 }
    220 
    221 /*
    222  * Debugging
    223  */
    224 void proxy_dump(const alsa_device_proxy* proxy, int fd)
    225 {
    226     if (proxy != NULL) {
    227         dprintf(fd, "  channels: %d\n", proxy->alsa_config.channels);
    228         dprintf(fd, "  rate: %d\n", proxy->alsa_config.rate);
    229         dprintf(fd, "  period_size: %d\n", proxy->alsa_config.period_size);
    230         dprintf(fd, "  period_count: %d\n", proxy->alsa_config.period_count);
    231         dprintf(fd, "  format: %d\n", proxy->alsa_config.format);
    232     }
    233 }
    234