source: roaraudio/roard/driver_alsa.c @ 4708:c9d40761088a

Last change on this file since 4708:c9d40761088a was 4708:c9d40761088a, checked in by phi, 13 years ago

updated copyright statements

File size: 11.3 KB
Line 
1//driver_alsa.c:
2
3/*
4 *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2008-2011
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
33static snd_pcm_format_t driver_alsa_roar_fmt_to_alsa(struct roar_audio_info * info) {
34 snd_pcm_format_t format = SND_PCM_FORMAT_UNKNOWN;
35
36 switch (info->codec) {
37  case ROAR_CODEC_PCM_S_LE:
38    switch (info->bits) {
39     case  8: format = SND_PCM_FORMAT_S8;      break;
40     case 16: format = SND_PCM_FORMAT_S16_LE;  break;
41     case 24: format = SND_PCM_FORMAT_S24_3LE; break;
42     case 32: format = SND_PCM_FORMAT_S32_LE;  break;
43    }
44   break;
45  case ROAR_CODEC_PCM_S_BE:
46    switch (info->bits) {
47     case  8: format = SND_PCM_FORMAT_S8;      break;
48     case 16: format = SND_PCM_FORMAT_S16_BE;  break;
49     case 24: format = SND_PCM_FORMAT_S24_3BE; break;
50     case 32: format = SND_PCM_FORMAT_S32_BE;  break;
51    }
52   break;
53  case ROAR_CODEC_PCM_U_LE:
54    switch (info->bits) {
55     case  8: format = SND_PCM_FORMAT_U8;      break;
56     case 16: format = SND_PCM_FORMAT_U16_LE;  break;
57     case 24: format = SND_PCM_FORMAT_U24_3LE; break;
58     case 32: format = SND_PCM_FORMAT_U32_LE;  break;
59    }
60   break;
61  case ROAR_CODEC_PCM_U_BE:
62    switch (info->bits) {
63     case  8: format = SND_PCM_FORMAT_U8;      break;
64     case 16: format = SND_PCM_FORMAT_U16_BE;  break;
65     case 24: format = SND_PCM_FORMAT_U24_3BE; break;
66     case 32: format = SND_PCM_FORMAT_U32_BE;  break;
67    }
68   break;
69  case ROAR_CODEC_ALAW:
70    format = SND_PCM_FORMAT_A_LAW;
71   break;
72  case ROAR_CODEC_MULAW:
73    format = SND_PCM_FORMAT_MU_LAW;
74   break;
75  case ROAR_CODEC_GSM:
76    format = SND_PCM_FORMAT_GSM;
77   break;
78 }
79
80 return format;
81}
82
83static int driver_alsa_set_params(roar_alsa_t * device) {
84 unsigned int      buffer_time = 32000; // 32ms buffer if possible.
85                                        // *_buffer_time_near() will pick something as close as possible.
86 snd_pcm_uframes_t frames      = ROAR_OUTPUT_BUFFER_SAMPLES;
87 int               rc;
88
89 ROAR_DBG("driver_alsa_set_params(device=%p) = ?", device);
90
91 if ( snd_pcm_hw_params_malloc(&device->params) < 0 )
92  return -1;
93
94 ROAR_DBG("driver_alsa_set_params(device=%p) = ?", device);
95
96 if ( snd_pcm_hw_params_any(device->handle, device->params) < 0 )
97  goto error;
98
99 ROAR_DBG("driver_alsa_set_params(device=%p) = ?", device);
100
101 if ( snd_pcm_hw_params_set_access(device->handle, device->params, SND_PCM_ACCESS_RW_INTERLEAVED) < 0 )
102  goto error;
103
104 ROAR_DBG("driver_alsa_set_params(device=%p) = ?", device);
105
106 if ( snd_pcm_hw_params_set_format(device->handle, device->params, device->format) < 0)
107  goto error;
108
109 ROAR_DBG("driver_alsa_set_params(device=%p) = ?", device);
110
111 ROAR_DBG("driver_alsa_set_params(device=%p): device->info.channels=%i", device, device->info.channels);
112
113 if ( snd_pcm_hw_params_set_channels(device->handle, device->params, device->info.channels) < 0 )
114  goto error;
115
116 ROAR_DBG("driver_alsa_set_params(device=%p) = ?", device);
117
118 if ( snd_pcm_hw_params_set_rate(device->handle, device->params, device->info.rate, 0) < 0 )
119  goto error;
120
121 ROAR_DBG("driver_alsa_set_params(device=%p) = ?", device);
122
123 if ( snd_pcm_hw_params_set_buffer_time_near(device->handle, device->params, &buffer_time, NULL) < 0 )
124  goto error;
125
126 ROAR_DBG("driver_alsa_set_params(device=%p) = ?", device);
127
128 if ( snd_pcm_hw_params_set_period_size_near(device->handle, device->params, &frames, NULL) < 0 )
129  goto error;
130
131 ROAR_DBG("driver_alsa_set_params(device=%p) = ?", device);
132
133 rc = snd_pcm_hw_params(device->handle, device->params);
134 if (rc < 0) {
135  ROAR_ERR("driver_alsa_open_vio(*): unable to set hw parameters: %s", snd_strerror(rc));
136  goto error;
137 }
138
139 ROAR_DBG("driver_alsa_set_params(device=%p) = ?", device);
140
141 snd_pcm_hw_params_free(device->params);
142
143 ROAR_DBG("driver_alsa_set_params(device=%p) = 0", device);
144
145 return 0;
146
147error:
148 snd_pcm_hw_params_free(device->params);
149 ROAR_DBG("driver_alsa_set_params(device=%p) = -1", device);
150 return -1;
151}
152
153int driver_alsa_open_vio(struct roar_vio_calls * inst, char * device, struct roar_audio_info * info, int fh, struct roar_stream_server * sstream) {
154 roar_alsa_t     * interface;
155 char            * alsa_dev;
156 int               rc;
157 int               autoconf;
158
159 ROAR_DBG("driver_alsa_open_vio(inst=%p, device='%s', info=%p, fh=%i, sstream=%p) = ?", inst, device, info, fh, sstream);
160
161 if ( fh != -1 )
162  return -1;
163
164 if ( (interface = roar_mm_calloc(1, sizeof(roar_alsa_t))) == NULL )
165  return -1;
166
167 ROAR_DBG("driver_alsa_open_vio(inst=%p, device='%s', info=%p, fh=%i, sstream=%p) = ?", inst, device, info, fh, sstream);
168
169 if ( device == NULL ) {
170  alsa_dev = "default";
171 } else {
172  alsa_dev = device;
173 }
174
175 ROAR_DBG("driver_alsa_open_vio(inst=%p, device='%s', info=%p, fh=%i, sstream=%p) = ?", inst, device, info, fh, sstream);
176
177 rc = snd_pcm_open(&(interface->handle), alsa_dev, SND_PCM_STREAM_PLAYBACK, 0);
178 if ( rc < 0 ) {
179  ROAR_ERR("driver_alsa_open_vio(*): Unable to open PCM device: %s", snd_strerror(rc));
180  roar_mm_free(interface);
181  return -1;
182 }
183
184 // Setting sample format, yay.
185 interface->format = driver_alsa_roar_fmt_to_alsa(info);
186
187 ROAR_DBG("driver_alsa_open_vio(inst=%p, device='%s', info=%p, fh=%i, sstream=%p) = ?", inst, device, info, fh, sstream);
188
189 // We did not find a codec
190 if ( interface->format == SND_PCM_FORMAT_UNKNOWN ) {
191  autoconf = streams_get_flag(ROAR_STREAM(sstream)->id, ROAR_FLAG_AUTOCONF);
192  if ( autoconf == -1 ) {
193   ROAR_ERR("driver_alsa_open_vio(*): Could not get autoconf flag.");
194   goto init_error;
195  }
196
197  if ( autoconf ) {
198   info->bits  = 16;
199
200#if   ROAR_CODEC_DEFAULT == ROAR_CODEC_PCM_S_LE
201   interface->format = SND_PCM_FORMAT_S16_LE;
202   info->codec       = ROAR_CODEC_PCM_S_LE;
203#elif ROAR_CODEC_DEFAULT == ROAR_CODEC_PCM_S_BE
204   interface->format = SND_PCM_FORMAT_S16_BE;
205   info->codec       = ROAR_CODEC_PCM_S_BE;
206#else
207   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");
208   goto init_error;
209#endif
210   ROAR_WARN("driver_alsa_open_vio(*): Supplied codec type not available. Using 16 bit sample format.");
211  } else {
212   ROAR_ERR("driver_alsa_open_vio(*): can not set requested codec, set codec manually with -oO codec=<codec>.");
213   goto init_error;
214  }
215 }
216
217 memcpy(&(interface->info), info, sizeof(struct roar_audio_info));
218
219 ROAR_DBG("driver_alsa_open_vio(inst=%p, device='%s', info=%p, fh=%i, sstream=%p) = ?", inst, device, info, fh, sstream);
220
221 if ( driver_alsa_set_params(interface) < 0 )
222  goto init_error;
223
224 ROAR_DBG("driver_alsa_open_vio(inst=%p, device='%s', info=%p, fh=%i, sstream=%p) = ?", inst, device, info, fh, sstream);
225
226 memset(inst, 0, sizeof(struct roar_vio_calls));
227
228 inst->inst  = (void*) interface;
229 inst->write = driver_alsa_write;
230 inst->close = driver_alsa_close;
231 inst->sync  = driver_alsa_sync;
232 inst->ctl   = driver_alsa_ctl;
233
234 ROAR_DBG("driver_alsa_open_vio(inst=%p, device='%s', info=%p, fh=%i, sstream=%p) = 0", inst, device, info, fh, sstream);
235
236 return 0;
237
238init_error:
239 snd_pcm_close(interface->handle);
240 roar_mm_free(interface);
241 return -1;
242}
243
244int driver_alsa_close(struct roar_vio_calls * vio) {
245 roar_alsa_t * device = vio->inst;
246
247 if ( device->handle != NULL ) {
248  snd_pcm_drain(device->handle);
249  snd_pcm_close(device->handle);
250 }
251
252 roar_mm_free(device);
253
254 return 0;
255}
256
257static ssize_t driver_alsa_write_int(struct roar_vio_calls * vio, void *buf, size_t size) {
258 roar_alsa_t       * device    = vio->inst;
259 snd_pcm_sframes_t  write_size = snd_pcm_bytes_to_frames(device->handle, size);
260 snd_pcm_sframes_t  rc;
261
262 ROAR_DBG("driver_alsa_write(vio=%p, buf=%p, size=%llu) = ?", vio, buf, (long long unsigned int)size);
263
264 rc = snd_pcm_writei(device->handle, buf, write_size);
265
266 if ( rc == -EPIPE || rc == -EINTR || rc == -ESTRPIPE ) {
267  if ( snd_pcm_recover(device->handle, rc, 1) < 0 )
268   return -1;
269  return size;
270 } else if (rc < 0) {
271  ROAR_ERR("driver_alsa_write(*): Error from snd_pcm_writei(*): %s", snd_strerror(rc));
272  return -1;
273 } 
274
275 return snd_pcm_frames_to_bytes(device->handle, rc);
276}
277
278ssize_t driver_alsa_write(struct roar_vio_calls * vio, void *buf, size_t size) {
279 char*   mutable_buf = (char*)buf;
280 ssize_t have        = 0;
281 ssize_t ret;
282
283 while (size) {
284  ret = driver_alsa_write_int(vio, mutable_buf, size);
285  if ( ret < 0 )
286   return -1;
287
288  have        += ret;
289  mutable_buf += ret;
290  size        -= ret;
291 }
292
293 return have;
294}
295
296int driver_alsa_sync(struct roar_vio_calls * vio) {
297 roar_alsa_t       * device    = vio->inst;
298 return snd_pcm_drain(device->handle);
299}
300
301/* Stop the driver, set new params, and restart */
302/* Not been able to test this function. */
303/* Assuming that params can be reset after halting the PCM device */
304static int driver_alsa_reopen_device(roar_alsa_t * device) {
305
306 if ( snd_pcm_drop(device->handle) < 0 ) {
307  ROAR_ERR("driver_alsa_reopen_device(*): snd_pcm_drop() failed.");
308  return -1;
309 }
310
311 if ( driver_alsa_set_params(device) < 0 ) {
312  ROAR_ERR("driver_alsa_reopen_device(*): driver_alsa_set_params() failed.");
313  return -1;
314 }
315
316 return 0;
317}
318
319/* Not completely tested.
320 * Tested:
321 * ROAR_VIO_CTL_GET_DELAY
322 * ROAR_VIO_CTL_SET_SSTREAMID
323 * ROAR_VIO_CTL_SET_SSTREAM
324 */
325
326int driver_alsa_ctl(struct roar_vio_calls * vio, int cmd, void * data) {
327 roar_alsa_t       * device    = vio->inst;
328 snd_pcm_sframes_t alsa_delay;
329
330 ROAR_DBG("driver_alsa_ctl(vio=%p, cmd=0x%.8x, data=%p) = ?", vio, cmd, data);
331
332 switch (cmd) {
333  case ROAR_VIO_CTL_GET_DELAY:
334   if ( snd_pcm_delay(device->handle, &alsa_delay) < 0 )
335    return -1;
336   else {
337    *(uint_least32_t *)data = snd_pcm_frames_to_bytes(device->handle, alsa_delay);
338    ROAR_DBG("driver_alsa_ctl(vio=%p, cmd=ROAR_VIO_CTL_GET_DELAY(0x%.8x), data=%p): %i bytes.", vio, cmd, data, (int)(*(uint_least32_t*)data));
339   }
340   break;
341
342  case ROAR_VIO_CTL_SET_SSTREAMID:
343   device->ssid = *(int *)data;
344   ROAR_DBG("driver_alsa_ctl(vio=%p, cmd=ROAR_VIO_CTL_SET_SSTREAMID(0x%.8x), data=%p): ssid=%i", vio, cmd, data, device->ssid);
345   break;
346
347  case ROAR_VIO_CTL_SET_SSTREAM:
348   device->sstream = data;
349   ROAR_DBG("driver_alsa_ctl(vio=%p, cmd=ROAR_VIO_CTL_SET_SSTREAM(0x%.8x), data=%p): sstream=%p", vio, cmd, data, device->sstream);
350   break;
351
352  case ROAR_VIO_CTL_GET_AUINFO:
353   ROAR_DBG("driver_alsa_ctl(vio=%p, cmd=ROAR_VIO_CTL_GET_AUINFO(0x%.8x), data=%p) = ?", vio, cmd, data);
354   memcpy(data, &(device->info), sizeof(struct roar_audio_info));
355   break;
356
357  case ROAR_VIO_CTL_SET_AUINFO:
358   ROAR_DBG("driver_alsa_ctl(vio=%p, cmd=ROAR_VIO_CTL_SET_AUINFO(0x%.8x), data=%p) = ?", vio, cmd, data);
359   memcpy(&(device->info), data, sizeof(struct roar_audio_info));
360   return driver_alsa_reopen_device(device);
361
362  default:
363   return -1;
364 }
365
366 return 0;
367}
368
369#endif
370
Note: See TracBrowser for help on using the repository browser.