chiark / gitweb /
Add xcalloc_noptr(), which allows uaudio-thread.c to ask for
[disorder] / lib / uaudio-alsa.c
1 /*
2  * This file is part of DisOrder.
3  * Copyright (C) 2009 Richard Kettlewell
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 /** @file lib/uaudio-alsa.c
19  * @brief Support for ALSA backend */
20 #include "common.h"
21
22 #if HAVE_ALSA_ASOUNDLIB_H
23
24 #include <alsa/asoundlib.h>
25
26 #include "mem.h"
27 #include "log.h"
28 #include "uaudio.h"
29
30 /** @brief The current PCM handle */
31 static snd_pcm_t *alsa_pcm;
32
33 static const char *const alsa_options[] = {
34   "device",
35   NULL
36 };
37
38 /** @brief Actually play sound via ALSA */
39 static size_t alsa_play(void *buffer, size_t samples) {
40   int err;
41   /* ALSA wants 'frames', where frame = several concurrently played samples */
42   const snd_pcm_uframes_t frames = samples / uaudio_channels;
43
44   snd_pcm_sframes_t rc = snd_pcm_writei(alsa_pcm, buffer, frames);
45   if(rc < 0) {
46     switch(rc) {
47     case -EPIPE:
48       if((err = snd_pcm_prepare(alsa_pcm)))
49         fatal(0, "error calling snd_pcm_prepare: %d", err);
50       return 0;
51     case -EAGAIN:
52       return 0;
53     default:
54       fatal(0, "error calling snd_pcm_writei: %d", (int)rc);
55     }
56   }
57   return rc * uaudio_channels;
58 }
59
60 /** @brief Open the ALSA sound device */
61 static void alsa_open(void) {
62   const char *device = uaudio_get("device");
63   int err;
64
65   if(!device || !*device)
66     device = "default";
67   if((err = snd_pcm_open(&alsa_pcm,
68                          device,
69                          SND_PCM_STREAM_PLAYBACK,
70                          0)))
71     fatal(0, "error from snd_pcm_open: %d", err);
72   snd_pcm_hw_params_t *hwparams;
73   snd_pcm_hw_params_alloca(&hwparams);
74   if((err = snd_pcm_hw_params_any(alsa_pcm, hwparams)) < 0)
75     fatal(0, "error from snd_pcm_hw_params_any: %d", err);
76   if((err = snd_pcm_hw_params_set_access(alsa_pcm, hwparams,
77                                          SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
78     fatal(0, "error from snd_pcm_hw_params_set_access: %d", err);
79   int sample_format;
80   if(uaudio_bits == 16)
81     sample_format = uaudio_signed ? SND_PCM_FORMAT_S16 : SND_PCM_FORMAT_U16;
82   else
83     sample_format = uaudio_signed ? SND_PCM_FORMAT_S8 : SND_PCM_FORMAT_U8;
84   if((err = snd_pcm_hw_params_set_format(alsa_pcm, hwparams,
85                                          sample_format)) < 0)
86     fatal(0, "error from snd_pcm_hw_params_set_format (%d): %d",
87           sample_format, err);
88   unsigned rate = uaudio_rate;
89   if((err = snd_pcm_hw_params_set_rate_near(alsa_pcm, hwparams, &rate, 0)) < 0)
90     fatal(0, "error from snd_pcm_hw_params_set_rate_near (%d): %d",
91           rate, err);
92   if((err = snd_pcm_hw_params_set_channels(alsa_pcm, hwparams,
93                                            uaudio_channels)) < 0)
94     fatal(0, "error from snd_pcm_hw_params_set_channels (%d): %d",
95           uaudio_channels, err);
96   if((err = snd_pcm_hw_params(alsa_pcm, hwparams)) < 0)
97     fatal(0, "error calling snd_pcm_hw_params: %d", err);
98   
99 }
100
101 static void alsa_activate(void) {
102   uaudio_thread_activate();
103 }
104
105 static void alsa_deactivate(void) {
106   uaudio_thread_deactivate();
107 }
108   
109 static void alsa_start(uaudio_callback *callback,
110                       void *userdata) {
111   if(uaudio_channels != 1 && uaudio_channels != 2)
112     fatal(0, "asked for %d channels but only support 1 or 2",
113           uaudio_channels); 
114   if(uaudio_bits != 8 && uaudio_bits != 16)
115     fatal(0, "asked for %d bits/channel but only support 8 or 16",
116           uaudio_bits); 
117   alsa_open();
118   uaudio_thread_start(callback, userdata, alsa_play,
119                       32 / uaudio_sample_size,
120                       4096 / uaudio_sample_size);
121 }
122
123 static void alsa_stop(void) {
124   uaudio_thread_stop();
125   snd_pcm_close(alsa_pcm);
126   alsa_pcm = 0;
127 }
128
129 const struct uaudio uaudio_alsa = {
130   .name = "alsa",
131   .options = alsa_options,
132   .start = alsa_start,
133   .stop = alsa_stop,
134   .activate = alsa_activate,
135   .deactivate = alsa_deactivate
136 };
137
138 #endif
139
140 /*
141 Local Variables:
142 c-basic-offset:2
143 comment-column:40
144 fill-column:79
145 indent-tabs-mode:nil
146 End:
147 */