[3693] | 1 | //driver_alsa.c: |
---|
| 2 | |
---|
| 3 | /* |
---|
| 4 | * Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2008-2010 |
---|
| 5 | * Copyright (C) Hans-Kristian 'maister' Arntzen - 2010 |
---|
| 6 | * |
---|
| 7 | * This file is part of roard a part of RoarAudio, |
---|
| 8 | * a cross-platform sound system for both, home and professional use. |
---|
| 9 | * See README for details. |
---|
| 10 | * |
---|
| 11 | * This file is free software; you can redistribute it and/or modify |
---|
| 12 | * it under the terms of the GNU General Public License version 3 |
---|
| 13 | * as published by the Free Software Foundation. |
---|
| 14 | * |
---|
| 15 | * RoarAudio is distributed in the hope that it will be useful, |
---|
| 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
| 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
| 18 | * GNU General Public License for more details. |
---|
| 19 | * |
---|
| 20 | * You should have received a copy of the GNU General Public License |
---|
| 21 | * along with this software; see the file COPYING. If not, write to |
---|
| 22 | * the Free Software Foundation, 51 Franklin Street, Fifth Floor, |
---|
| 23 | * Boston, MA 02110-1301, USA. |
---|
| 24 | * |
---|
| 25 | */ |
---|
| 26 | |
---|
| 27 | #include "roard.h" |
---|
| 28 | |
---|
| 29 | #ifdef ROAR_HAVE_LIBASOUND |
---|
| 30 | |
---|
| 31 | #define ALSA_PCM_NEW_HW_PARAMS_API |
---|
| 32 | |
---|
| 33 | typedef struct roar_alsa { |
---|
| 34 | snd_pcm_t *handle; |
---|
| 35 | snd_pcm_hw_params_t* params; |
---|
| 36 | } roar_alsa_t; |
---|
| 37 | |
---|
| 38 | int driver_alsa_open_vio(struct roar_vio_calls * inst, char * device, struct roar_audio_info * info, int fh, struct roar_stream_server * sstream) { |
---|
| 39 | roar_alsa_t * interface; |
---|
| 40 | char * alsa_dev; |
---|
| 41 | int rc; |
---|
| 42 | unsigned buffer_time = 200000; // 200ms buffer |
---|
[3695] | 43 | snd_pcm_uframes_t frames = ROAR_OUTPUT_BUFFER_SAMPLES; // ALSA frames |
---|
[3693] | 44 | snd_pcm_format_t format = SND_PCM_FORMAT_UNKNOWN; // If this isn't set further down, we pick 16 bit audio if we're using autoconf. |
---|
| 45 | uint16_t channels = info->channels; |
---|
| 46 | uint32_t samplerate = info->rate; |
---|
| 47 | int autoconf; |
---|
| 48 | |
---|
| 49 | |
---|
| 50 | if ( fh != -1 ) |
---|
| 51 | return -1; |
---|
| 52 | |
---|
| 53 | if ( (interface = roar_mm_calloc(1, sizeof(roar_alsa_t))) == NULL ) |
---|
| 54 | return -1; |
---|
| 55 | |
---|
| 56 | if ( device == NULL ) { |
---|
| 57 | alsa_dev = "default"; |
---|
| 58 | } else { |
---|
| 59 | alsa_dev = device; |
---|
| 60 | } |
---|
| 61 | |
---|
| 62 | rc = snd_pcm_open(&(interface->handle), alsa_dev, SND_PCM_STREAM_PLAYBACK, 0); |
---|
| 63 | if ( rc < 0 ) { |
---|
| 64 | ROAR_ERR("driver_alsa_open_vio(*): Unable to open PCM device: %s", snd_strerror(rc)); |
---|
| 65 | roar_mm_free(interface); |
---|
| 66 | return -1; |
---|
| 67 | } |
---|
| 68 | |
---|
| 69 | // Setting sample format, yay. |
---|
| 70 | if ( info->codec == ROAR_CODEC_PCM_S_LE ) { |
---|
| 71 | switch (info->bits) { |
---|
| 72 | case 8: |
---|
| 73 | format = SND_PCM_FORMAT_S8; |
---|
| 74 | break; |
---|
| 75 | case 16: |
---|
| 76 | format = SND_PCM_FORMAT_S16_LE; |
---|
| 77 | break; |
---|
| 78 | case 32: |
---|
| 79 | format = SND_PCM_FORMAT_S32_LE; |
---|
| 80 | break; |
---|
| 81 | } |
---|
| 82 | } else if ( info->codec == ROAR_CODEC_PCM_U_LE ) { |
---|
| 83 | switch (info->bits) { |
---|
| 84 | case 8: |
---|
| 85 | format = SND_PCM_FORMAT_U8; |
---|
| 86 | break; |
---|
| 87 | case 16: |
---|
| 88 | format = SND_PCM_FORMAT_U16_LE; |
---|
| 89 | break; |
---|
| 90 | case 32: |
---|
| 91 | format = SND_PCM_FORMAT_U32_LE; |
---|
| 92 | break; |
---|
| 93 | } |
---|
| 94 | } else if ( info->codec == ROAR_CODEC_PCM_S_BE ) { |
---|
| 95 | switch (info->bits) { |
---|
| 96 | case 8: |
---|
| 97 | format = SND_PCM_FORMAT_S8; |
---|
| 98 | break; |
---|
| 99 | case 16: |
---|
| 100 | format = SND_PCM_FORMAT_S16_BE; |
---|
| 101 | break; |
---|
| 102 | case 32: |
---|
| 103 | format = SND_PCM_FORMAT_S32_BE; |
---|
| 104 | break; |
---|
| 105 | } |
---|
| 106 | } else if ( info->codec == ROAR_CODEC_PCM_U_BE ) { |
---|
| 107 | switch (info->bits) { |
---|
| 108 | case 8: |
---|
| 109 | format = SND_PCM_FORMAT_U8; |
---|
| 110 | break; |
---|
| 111 | case 16: |
---|
| 112 | format = SND_PCM_FORMAT_U16_BE; |
---|
| 113 | break; |
---|
| 114 | case 32: |
---|
| 115 | format = SND_PCM_FORMAT_U32_BE; |
---|
| 116 | break; |
---|
| 117 | } |
---|
| 118 | } |
---|
| 119 | |
---|
| 120 | // We did not find a codec |
---|
| 121 | if ( format == SND_PCM_FORMAT_UNKNOWN ) { |
---|
| 122 | autoconf = streams_get_flag(ROAR_STREAM(sstream)->id, ROAR_FLAG_AUTOCONF); |
---|
| 123 | if ( autoconf == -1 ) { |
---|
| 124 | ROAR_ERR("driver_alsa_open_vio(*): Could not get autoconf flag."); |
---|
| 125 | goto init_error; |
---|
| 126 | } |
---|
| 127 | |
---|
| 128 | if ( autoconf ) { |
---|
| 129 | info->bits = 16; |
---|
| 130 | |
---|
| 131 | #if ROAR_CODEC_DEFAULT == ROAR_CODEC_PCM_S_LE |
---|
| 132 | format = SND_PCM_FORMAT_S16_LE; |
---|
| 133 | info->codec = ROAR_CODEC_PCM_S_LE; |
---|
| 134 | #elif ROAR_CODEC_DEFAULT == ROAR_CODEC_PCM_S_BE |
---|
| 135 | format = SND_PCM_FORMAT_S16_BE; |
---|
| 136 | info->codec = ROAR_CODEC_PCM_S_BE; |
---|
| 137 | #else |
---|
| 138 | ROAR_ERR("driver_alsa_open_vio(*): ALSA only support little- or big-endian sample format. Please select -oO codec=pcm_s_le or pcm_s_be"); |
---|
| 139 | goto init_error; |
---|
| 140 | #endif |
---|
| 141 | ROAR_WARN("driver_alsa_open_vio(*): Supplied codec type not available. Using 16 bit sample format."); |
---|
| 142 | } else { |
---|
| 143 | ROAR_ERR("driver_alsa_open_vio(*): can not set requested codec, set codec manually with -oO codec=<codec>."); |
---|
| 144 | goto init_error; |
---|
| 145 | } |
---|
| 146 | } |
---|
| 147 | |
---|
| 148 | |
---|
| 149 | if ( snd_pcm_hw_params_malloc(&interface->params) < 0 ) |
---|
| 150 | goto init_error; |
---|
| 151 | |
---|
| 152 | if ( snd_pcm_hw_params_any(interface->handle, interface->params) < 0 ) |
---|
| 153 | goto init_error; |
---|
| 154 | if ( snd_pcm_hw_params_set_access(interface->handle, interface->params, SND_PCM_ACCESS_RW_INTERLEAVED) < 0 ) |
---|
| 155 | goto init_error; |
---|
| 156 | if ( snd_pcm_hw_params_set_format(interface->handle, interface->params, format) < 0) |
---|
| 157 | goto init_error; |
---|
| 158 | if ( snd_pcm_hw_params_set_channels(interface->handle, interface->params, channels) < 0 ) |
---|
| 159 | goto init_error; |
---|
| 160 | if ( snd_pcm_hw_params_set_rate_near(interface->handle, interface->params, &samplerate, NULL) < 0 ) |
---|
| 161 | goto init_error; |
---|
| 162 | if ( snd_pcm_hw_params_set_buffer_time_near(interface->handle, interface->params, &buffer_time, NULL) < 0 ) |
---|
| 163 | goto init_error; |
---|
| 164 | if ( snd_pcm_hw_params_set_period_size_near(interface->handle, interface->params, &frames, NULL) < 0 ) |
---|
| 165 | goto init_error; |
---|
| 166 | |
---|
| 167 | rc = snd_pcm_hw_params(interface->handle, interface->params); |
---|
| 168 | if (rc < 0) { |
---|
| 169 | ROAR_ERR("driver_alsa_open_vio(*): unable to set hw parameters: %s", snd_strerror(rc)); |
---|
| 170 | snd_pcm_hw_params_free(interface->params); |
---|
| 171 | goto init_error; |
---|
| 172 | } |
---|
| 173 | |
---|
| 174 | snd_pcm_hw_params_free(interface->params); |
---|
| 175 | |
---|
| 176 | memset(inst, 0, sizeof(struct roar_vio_calls)); |
---|
| 177 | |
---|
| 178 | inst->inst = (void*) interface; |
---|
| 179 | inst->write = driver_alsa_write; |
---|
| 180 | inst->close = driver_alsa_close; |
---|
| 181 | |
---|
| 182 | return 0; |
---|
| 183 | |
---|
| 184 | init_error: |
---|
| 185 | snd_pcm_close(interface->handle); |
---|
| 186 | roar_mm_free(interface); |
---|
| 187 | return -1; |
---|
| 188 | } |
---|
| 189 | |
---|
| 190 | int driver_alsa_close(struct roar_vio_calls * vio) { |
---|
| 191 | roar_alsa_t * device = vio->inst; |
---|
| 192 | |
---|
| 193 | if ( device->handle != NULL ) { |
---|
| 194 | snd_pcm_drain(device->handle); |
---|
| 195 | snd_pcm_close(device->handle); |
---|
| 196 | } |
---|
| 197 | |
---|
| 198 | roar_mm_free(device); |
---|
| 199 | |
---|
| 200 | return 0; |
---|
| 201 | } |
---|
| 202 | |
---|
| 203 | static ssize_t driver_alsa_write_int(struct roar_vio_calls * vio, void *buf, size_t size) { |
---|
| 204 | roar_alsa_t * device = vio->inst; |
---|
| 205 | snd_pcm_sframes_t write_size = snd_pcm_bytes_to_frames(device->handle, size); |
---|
| 206 | snd_pcm_sframes_t rc; |
---|
| 207 | |
---|
| 208 | ROAR_DBG("driver_alsa_write(vio=%p, buf=%p, size=%llu) = ?", vio, buf, (long long unsigned int)size); |
---|
| 209 | |
---|
| 210 | rc = snd_pcm_writei(device->handle, buf, write_size); |
---|
| 211 | |
---|
| 212 | if ( rc == -EPIPE || rc == -EINTR || rc == -ESTRPIPE ) { |
---|
| 213 | if ( snd_pcm_recover(device->handle, rc, 1) < 0 ) |
---|
| 214 | return -1; |
---|
| 215 | return size; |
---|
| 216 | } else if (rc < 0) { |
---|
| 217 | ROAR_ERR("driver_alsa_write(*): Error from snd_pcm_writei(*): %s", snd_strerror(rc)); |
---|
| 218 | return -1; |
---|
| 219 | } |
---|
| 220 | |
---|
| 221 | return snd_pcm_frames_to_bytes(device->handle, rc); |
---|
| 222 | } |
---|
| 223 | |
---|
| 224 | ssize_t driver_alsa_write(struct roar_vio_calls * vio, void *buf, size_t size) { |
---|
| 225 | ssize_t have = 0; |
---|
| 226 | ssize_t ret; |
---|
| 227 | |
---|
| 228 | while (size) { |
---|
| 229 | ret = driver_alsa_write_int(vio, buf, size); |
---|
| 230 | if ( ret < 0 ) |
---|
| 231 | return -1; |
---|
| 232 | |
---|
| 233 | have += ret; |
---|
| 234 | buf += ret; |
---|
| 235 | size -= ret; |
---|
| 236 | } |
---|
| 237 | |
---|
| 238 | return have; |
---|
| 239 | } |
---|
| 240 | |
---|
| 241 | #endif |
---|
| 242 | |
---|