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