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