root / audio / alsaaudio.c @ f3b52983
History | View | Annotate | Download (23.9 kB)
1 | 1d14ffa9 | bellard | /*
|
---|---|---|---|
2 | 1d14ffa9 | bellard | * QEMU ALSA audio driver
|
3 | 1d14ffa9 | bellard | *
|
4 | 1d14ffa9 | bellard | * Copyright (c) 2005 Vassili Karpov (malc)
|
5 | 1d14ffa9 | bellard | *
|
6 | 1d14ffa9 | bellard | * Permission is hereby granted, free of charge, to any person obtaining a copy
|
7 | 1d14ffa9 | bellard | * of this software and associated documentation files (the "Software"), to deal
|
8 | 1d14ffa9 | bellard | * in the Software without restriction, including without limitation the rights
|
9 | 1d14ffa9 | bellard | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10 | 1d14ffa9 | bellard | * copies of the Software, and to permit persons to whom the Software is
|
11 | 1d14ffa9 | bellard | * furnished to do so, subject to the following conditions:
|
12 | 1d14ffa9 | bellard | *
|
13 | 1d14ffa9 | bellard | * The above copyright notice and this permission notice shall be included in
|
14 | 1d14ffa9 | bellard | * all copies or substantial portions of the Software.
|
15 | 1d14ffa9 | bellard | *
|
16 | 1d14ffa9 | bellard | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17 | 1d14ffa9 | bellard | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18 | 1d14ffa9 | bellard | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
19 | 1d14ffa9 | bellard | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20 | 1d14ffa9 | bellard | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21 | 1d14ffa9 | bellard | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22 | 1d14ffa9 | bellard | * THE SOFTWARE.
|
23 | 1d14ffa9 | bellard | */
|
24 | 1d14ffa9 | bellard | #include <alsa/asoundlib.h> |
25 | 749bc4bf | pbrook | #include "qemu-common.h" |
26 | 749bc4bf | pbrook | #include "audio.h" |
27 | 1d14ffa9 | bellard | |
28 | 1d14ffa9 | bellard | #define AUDIO_CAP "alsa" |
29 | 1d14ffa9 | bellard | #include "audio_int.h" |
30 | 1d14ffa9 | bellard | |
31 | 1d14ffa9 | bellard | typedef struct ALSAVoiceOut { |
32 | 1d14ffa9 | bellard | HWVoiceOut hw; |
33 | 1d14ffa9 | bellard | void *pcm_buf;
|
34 | 1d14ffa9 | bellard | snd_pcm_t *handle; |
35 | 1d14ffa9 | bellard | } ALSAVoiceOut; |
36 | 1d14ffa9 | bellard | |
37 | 1d14ffa9 | bellard | typedef struct ALSAVoiceIn { |
38 | 1d14ffa9 | bellard | HWVoiceIn hw; |
39 | 1d14ffa9 | bellard | snd_pcm_t *handle; |
40 | 1d14ffa9 | bellard | void *pcm_buf;
|
41 | 1d14ffa9 | bellard | } ALSAVoiceIn; |
42 | 1d14ffa9 | bellard | |
43 | 1d14ffa9 | bellard | static struct { |
44 | 1d14ffa9 | bellard | int size_in_usec_in;
|
45 | 1d14ffa9 | bellard | int size_in_usec_out;
|
46 | 1d14ffa9 | bellard | const char *pcm_name_in; |
47 | 1d14ffa9 | bellard | const char *pcm_name_out; |
48 | 1d14ffa9 | bellard | unsigned int buffer_size_in; |
49 | 1d14ffa9 | bellard | unsigned int period_size_in; |
50 | 1d14ffa9 | bellard | unsigned int buffer_size_out; |
51 | 1d14ffa9 | bellard | unsigned int period_size_out; |
52 | 1d14ffa9 | bellard | unsigned int threshold; |
53 | 1d14ffa9 | bellard | |
54 | fe8f096b | ths | int buffer_size_in_overridden;
|
55 | fe8f096b | ths | int period_size_in_overridden;
|
56 | 1d14ffa9 | bellard | |
57 | fe8f096b | ths | int buffer_size_out_overridden;
|
58 | fe8f096b | ths | int period_size_out_overridden;
|
59 | 571ec3d6 | bellard | int verbose;
|
60 | 1d14ffa9 | bellard | } conf = { |
61 | 8ead62cf | bellard | .pcm_name_out = "default",
|
62 | 8ead62cf | bellard | .pcm_name_in = "default",
|
63 | 1d14ffa9 | bellard | }; |
64 | 1d14ffa9 | bellard | |
65 | 1d14ffa9 | bellard | struct alsa_params_req {
|
66 | ca9cc28c | balrog | int freq;
|
67 | ca9cc28c | balrog | snd_pcm_format_t fmt; |
68 | ca9cc28c | balrog | int nchannels;
|
69 | 7a24c800 | malc | int size_in_usec;
|
70 | 1d14ffa9 | bellard | unsigned int buffer_size; |
71 | 1d14ffa9 | bellard | unsigned int period_size; |
72 | 1d14ffa9 | bellard | }; |
73 | 1d14ffa9 | bellard | |
74 | 1d14ffa9 | bellard | struct alsa_params_obt {
|
75 | 1d14ffa9 | bellard | int freq;
|
76 | 1d14ffa9 | bellard | audfmt_e fmt; |
77 | ca9cc28c | balrog | int endianness;
|
78 | 1d14ffa9 | bellard | int nchannels;
|
79 | c0fe3827 | bellard | snd_pcm_uframes_t samples; |
80 | 1d14ffa9 | bellard | }; |
81 | 1d14ffa9 | bellard | |
82 | 1d14ffa9 | bellard | static void GCC_FMT_ATTR (2, 3) alsa_logerr (int err, const char *fmt, ...) |
83 | 1d14ffa9 | bellard | { |
84 | 1d14ffa9 | bellard | va_list ap; |
85 | 1d14ffa9 | bellard | |
86 | 1d14ffa9 | bellard | va_start (ap, fmt); |
87 | 1d14ffa9 | bellard | AUD_vlog (AUDIO_CAP, fmt, ap); |
88 | 1d14ffa9 | bellard | va_end (ap); |
89 | 1d14ffa9 | bellard | |
90 | 1d14ffa9 | bellard | AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err));
|
91 | 1d14ffa9 | bellard | } |
92 | 1d14ffa9 | bellard | |
93 | 1d14ffa9 | bellard | static void GCC_FMT_ATTR (3, 4) alsa_logerr2 ( |
94 | 1d14ffa9 | bellard | int err,
|
95 | 1d14ffa9 | bellard | const char *typ, |
96 | 1d14ffa9 | bellard | const char *fmt, |
97 | 1d14ffa9 | bellard | ... |
98 | 1d14ffa9 | bellard | ) |
99 | 1d14ffa9 | bellard | { |
100 | 1d14ffa9 | bellard | va_list ap; |
101 | 1d14ffa9 | bellard | |
102 | c0fe3827 | bellard | AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
|
103 | 1d14ffa9 | bellard | |
104 | 1d14ffa9 | bellard | va_start (ap, fmt); |
105 | 1d14ffa9 | bellard | AUD_vlog (AUDIO_CAP, fmt, ap); |
106 | 1d14ffa9 | bellard | va_end (ap); |
107 | 1d14ffa9 | bellard | |
108 | 1d14ffa9 | bellard | AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err));
|
109 | 1d14ffa9 | bellard | } |
110 | 1d14ffa9 | bellard | |
111 | 1d14ffa9 | bellard | static void alsa_anal_close (snd_pcm_t **handlep) |
112 | 1d14ffa9 | bellard | { |
113 | 1d14ffa9 | bellard | int err = snd_pcm_close (*handlep);
|
114 | 1d14ffa9 | bellard | if (err) {
|
115 | 1d14ffa9 | bellard | alsa_logerr (err, "Failed to close PCM handle %p\n", *handlep);
|
116 | 1d14ffa9 | bellard | } |
117 | 1d14ffa9 | bellard | *handlep = NULL;
|
118 | 1d14ffa9 | bellard | } |
119 | 1d14ffa9 | bellard | |
120 | 1d14ffa9 | bellard | static int alsa_write (SWVoiceOut *sw, void *buf, int len) |
121 | 1d14ffa9 | bellard | { |
122 | 1d14ffa9 | bellard | return audio_pcm_sw_write (sw, buf, len);
|
123 | 1d14ffa9 | bellard | } |
124 | 1d14ffa9 | bellard | |
125 | ca9cc28c | balrog | static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt)
|
126 | 1d14ffa9 | bellard | { |
127 | 1d14ffa9 | bellard | switch (fmt) {
|
128 | 1d14ffa9 | bellard | case AUD_FMT_S8:
|
129 | 1d14ffa9 | bellard | return SND_PCM_FORMAT_S8;
|
130 | 1d14ffa9 | bellard | |
131 | 1d14ffa9 | bellard | case AUD_FMT_U8:
|
132 | 1d14ffa9 | bellard | return SND_PCM_FORMAT_U8;
|
133 | 1d14ffa9 | bellard | |
134 | 1d14ffa9 | bellard | case AUD_FMT_S16:
|
135 | 1d14ffa9 | bellard | return SND_PCM_FORMAT_S16_LE;
|
136 | 1d14ffa9 | bellard | |
137 | 1d14ffa9 | bellard | case AUD_FMT_U16:
|
138 | 1d14ffa9 | bellard | return SND_PCM_FORMAT_U16_LE;
|
139 | 1d14ffa9 | bellard | |
140 | f941aa25 | ths | case AUD_FMT_S32:
|
141 | f941aa25 | ths | return SND_PCM_FORMAT_S32_LE;
|
142 | f941aa25 | ths | |
143 | f941aa25 | ths | case AUD_FMT_U32:
|
144 | f941aa25 | ths | return SND_PCM_FORMAT_U32_LE;
|
145 | f941aa25 | ths | |
146 | 1d14ffa9 | bellard | default:
|
147 | 1d14ffa9 | bellard | dolog ("Internal logic error: Bad audio format %d\n", fmt);
|
148 | 1d14ffa9 | bellard | #ifdef DEBUG_AUDIO
|
149 | 1d14ffa9 | bellard | abort (); |
150 | 1d14ffa9 | bellard | #endif
|
151 | 1d14ffa9 | bellard | return SND_PCM_FORMAT_U8;
|
152 | 1d14ffa9 | bellard | } |
153 | 1d14ffa9 | bellard | } |
154 | 1d14ffa9 | bellard | |
155 | ca9cc28c | balrog | static int alsa_to_audfmt (snd_pcm_format_t alsafmt, audfmt_e *fmt, |
156 | ca9cc28c | balrog | int *endianness)
|
157 | 1d14ffa9 | bellard | { |
158 | 1d14ffa9 | bellard | switch (alsafmt) {
|
159 | 1d14ffa9 | bellard | case SND_PCM_FORMAT_S8:
|
160 | 1d14ffa9 | bellard | *endianness = 0;
|
161 | 1d14ffa9 | bellard | *fmt = AUD_FMT_S8; |
162 | 1d14ffa9 | bellard | break;
|
163 | 1d14ffa9 | bellard | |
164 | 1d14ffa9 | bellard | case SND_PCM_FORMAT_U8:
|
165 | 1d14ffa9 | bellard | *endianness = 0;
|
166 | 1d14ffa9 | bellard | *fmt = AUD_FMT_U8; |
167 | 1d14ffa9 | bellard | break;
|
168 | 1d14ffa9 | bellard | |
169 | 1d14ffa9 | bellard | case SND_PCM_FORMAT_S16_LE:
|
170 | 1d14ffa9 | bellard | *endianness = 0;
|
171 | 1d14ffa9 | bellard | *fmt = AUD_FMT_S16; |
172 | 1d14ffa9 | bellard | break;
|
173 | 1d14ffa9 | bellard | |
174 | 1d14ffa9 | bellard | case SND_PCM_FORMAT_U16_LE:
|
175 | 1d14ffa9 | bellard | *endianness = 0;
|
176 | 1d14ffa9 | bellard | *fmt = AUD_FMT_U16; |
177 | 1d14ffa9 | bellard | break;
|
178 | 1d14ffa9 | bellard | |
179 | 1d14ffa9 | bellard | case SND_PCM_FORMAT_S16_BE:
|
180 | 1d14ffa9 | bellard | *endianness = 1;
|
181 | 1d14ffa9 | bellard | *fmt = AUD_FMT_S16; |
182 | 1d14ffa9 | bellard | break;
|
183 | 1d14ffa9 | bellard | |
184 | 1d14ffa9 | bellard | case SND_PCM_FORMAT_U16_BE:
|
185 | 1d14ffa9 | bellard | *endianness = 1;
|
186 | 1d14ffa9 | bellard | *fmt = AUD_FMT_U16; |
187 | 1d14ffa9 | bellard | break;
|
188 | 1d14ffa9 | bellard | |
189 | f941aa25 | ths | case SND_PCM_FORMAT_S32_LE:
|
190 | f941aa25 | ths | *endianness = 0;
|
191 | f941aa25 | ths | *fmt = AUD_FMT_S32; |
192 | f941aa25 | ths | break;
|
193 | f941aa25 | ths | |
194 | f941aa25 | ths | case SND_PCM_FORMAT_U32_LE:
|
195 | f941aa25 | ths | *endianness = 0;
|
196 | f941aa25 | ths | *fmt = AUD_FMT_U32; |
197 | f941aa25 | ths | break;
|
198 | f941aa25 | ths | |
199 | f941aa25 | ths | case SND_PCM_FORMAT_S32_BE:
|
200 | f941aa25 | ths | *endianness = 1;
|
201 | f941aa25 | ths | *fmt = AUD_FMT_S32; |
202 | f941aa25 | ths | break;
|
203 | f941aa25 | ths | |
204 | f941aa25 | ths | case SND_PCM_FORMAT_U32_BE:
|
205 | f941aa25 | ths | *endianness = 1;
|
206 | f941aa25 | ths | *fmt = AUD_FMT_U32; |
207 | f941aa25 | ths | break;
|
208 | f941aa25 | ths | |
209 | 1d14ffa9 | bellard | default:
|
210 | 1d14ffa9 | bellard | dolog ("Unrecognized audio format %d\n", alsafmt);
|
211 | 1d14ffa9 | bellard | return -1; |
212 | 1d14ffa9 | bellard | } |
213 | 1d14ffa9 | bellard | |
214 | 1d14ffa9 | bellard | return 0; |
215 | 1d14ffa9 | bellard | } |
216 | 1d14ffa9 | bellard | |
217 | 1d14ffa9 | bellard | static void alsa_dump_info (struct alsa_params_req *req, |
218 | 1d14ffa9 | bellard | struct alsa_params_obt *obt)
|
219 | 1d14ffa9 | bellard | { |
220 | 1d14ffa9 | bellard | dolog ("parameter | requested value | obtained value\n");
|
221 | 1d14ffa9 | bellard | dolog ("format | %10d | %10d\n", req->fmt, obt->fmt);
|
222 | 1d14ffa9 | bellard | dolog ("channels | %10d | %10d\n",
|
223 | 1d14ffa9 | bellard | req->nchannels, obt->nchannels); |
224 | 1d14ffa9 | bellard | dolog ("frequency | %10d | %10d\n", req->freq, obt->freq);
|
225 | 1d14ffa9 | bellard | dolog ("============================================\n");
|
226 | 1d14ffa9 | bellard | dolog ("requested: buffer size %d period size %d\n",
|
227 | 1d14ffa9 | bellard | req->buffer_size, req->period_size); |
228 | c0fe3827 | bellard | dolog ("obtained: samples %ld\n", obt->samples);
|
229 | 1d14ffa9 | bellard | } |
230 | 1d14ffa9 | bellard | |
231 | 1d14ffa9 | bellard | static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold) |
232 | 1d14ffa9 | bellard | { |
233 | 1d14ffa9 | bellard | int err;
|
234 | 1d14ffa9 | bellard | snd_pcm_sw_params_t *sw_params; |
235 | 1d14ffa9 | bellard | |
236 | 1d14ffa9 | bellard | snd_pcm_sw_params_alloca (&sw_params); |
237 | 1d14ffa9 | bellard | |
238 | 1d14ffa9 | bellard | err = snd_pcm_sw_params_current (handle, sw_params); |
239 | 1d14ffa9 | bellard | if (err < 0) { |
240 | c0fe3827 | bellard | dolog ("Could not fully initialize DAC\n");
|
241 | 1d14ffa9 | bellard | alsa_logerr (err, "Failed to get current software parameters\n");
|
242 | 1d14ffa9 | bellard | return;
|
243 | 1d14ffa9 | bellard | } |
244 | 1d14ffa9 | bellard | |
245 | 1d14ffa9 | bellard | err = snd_pcm_sw_params_set_start_threshold (handle, sw_params, threshold); |
246 | 1d14ffa9 | bellard | if (err < 0) { |
247 | c0fe3827 | bellard | dolog ("Could not fully initialize DAC\n");
|
248 | 1d14ffa9 | bellard | alsa_logerr (err, "Failed to set software threshold to %ld\n",
|
249 | 1d14ffa9 | bellard | threshold); |
250 | 1d14ffa9 | bellard | return;
|
251 | 1d14ffa9 | bellard | } |
252 | 1d14ffa9 | bellard | |
253 | 1d14ffa9 | bellard | err = snd_pcm_sw_params (handle, sw_params); |
254 | 1d14ffa9 | bellard | if (err < 0) { |
255 | c0fe3827 | bellard | dolog ("Could not fully initialize DAC\n");
|
256 | 1d14ffa9 | bellard | alsa_logerr (err, "Failed to set software parameters\n");
|
257 | 1d14ffa9 | bellard | return;
|
258 | 1d14ffa9 | bellard | } |
259 | 1d14ffa9 | bellard | } |
260 | 1d14ffa9 | bellard | |
261 | 1d14ffa9 | bellard | static int alsa_open (int in, struct alsa_params_req *req, |
262 | 1d14ffa9 | bellard | struct alsa_params_obt *obt, snd_pcm_t **handlep)
|
263 | 1d14ffa9 | bellard | { |
264 | 1d14ffa9 | bellard | snd_pcm_t *handle; |
265 | 1d14ffa9 | bellard | snd_pcm_hw_params_t *hw_params; |
266 | 60fe76f3 | ths | int err;
|
267 | 7a24c800 | malc | int size_in_usec;
|
268 | 60fe76f3 | ths | unsigned int freq, nchannels; |
269 | 1d14ffa9 | bellard | const char *pcm_name = in ? conf.pcm_name_in : conf.pcm_name_out; |
270 | 1d14ffa9 | bellard | snd_pcm_uframes_t obt_buffer_size; |
271 | 1d14ffa9 | bellard | const char *typ = in ? "ADC" : "DAC"; |
272 | ca9cc28c | balrog | snd_pcm_format_t obtfmt; |
273 | 1d14ffa9 | bellard | |
274 | 1d14ffa9 | bellard | freq = req->freq; |
275 | 1d14ffa9 | bellard | nchannels = req->nchannels; |
276 | 7a24c800 | malc | size_in_usec = req->size_in_usec; |
277 | 1d14ffa9 | bellard | |
278 | 1d14ffa9 | bellard | snd_pcm_hw_params_alloca (&hw_params); |
279 | 1d14ffa9 | bellard | |
280 | 1d14ffa9 | bellard | err = snd_pcm_open ( |
281 | 1d14ffa9 | bellard | &handle, |
282 | 1d14ffa9 | bellard | pcm_name, |
283 | 1d14ffa9 | bellard | in ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, |
284 | 1d14ffa9 | bellard | SND_PCM_NONBLOCK |
285 | 1d14ffa9 | bellard | ); |
286 | 1d14ffa9 | bellard | if (err < 0) { |
287 | 1d14ffa9 | bellard | alsa_logerr2 (err, typ, "Failed to open `%s':\n", pcm_name);
|
288 | 1d14ffa9 | bellard | return -1; |
289 | 1d14ffa9 | bellard | } |
290 | 1d14ffa9 | bellard | |
291 | 1d14ffa9 | bellard | err = snd_pcm_hw_params_any (handle, hw_params); |
292 | 1d14ffa9 | bellard | if (err < 0) { |
293 | 1d14ffa9 | bellard | alsa_logerr2 (err, typ, "Failed to initialize hardware parameters\n");
|
294 | 1d14ffa9 | bellard | goto err;
|
295 | 1d14ffa9 | bellard | } |
296 | 1d14ffa9 | bellard | |
297 | 1d14ffa9 | bellard | err = snd_pcm_hw_params_set_access ( |
298 | 1d14ffa9 | bellard | handle, |
299 | 1d14ffa9 | bellard | hw_params, |
300 | 1d14ffa9 | bellard | SND_PCM_ACCESS_RW_INTERLEAVED |
301 | 1d14ffa9 | bellard | ); |
302 | 1d14ffa9 | bellard | if (err < 0) { |
303 | 1d14ffa9 | bellard | alsa_logerr2 (err, typ, "Failed to set access type\n");
|
304 | 1d14ffa9 | bellard | goto err;
|
305 | 1d14ffa9 | bellard | } |
306 | 1d14ffa9 | bellard | |
307 | 1d14ffa9 | bellard | err = snd_pcm_hw_params_set_format (handle, hw_params, req->fmt); |
308 | ca9cc28c | balrog | if (err < 0 && conf.verbose) { |
309 | 1d14ffa9 | bellard | alsa_logerr2 (err, typ, "Failed to set format %d\n", req->fmt);
|
310 | 1d14ffa9 | bellard | } |
311 | 1d14ffa9 | bellard | |
312 | 1d14ffa9 | bellard | err = snd_pcm_hw_params_set_rate_near (handle, hw_params, &freq, 0);
|
313 | 1d14ffa9 | bellard | if (err < 0) { |
314 | 1d14ffa9 | bellard | alsa_logerr2 (err, typ, "Failed to set frequency %d\n", req->freq);
|
315 | 1d14ffa9 | bellard | goto err;
|
316 | 1d14ffa9 | bellard | } |
317 | 1d14ffa9 | bellard | |
318 | 1d14ffa9 | bellard | err = snd_pcm_hw_params_set_channels_near ( |
319 | 1d14ffa9 | bellard | handle, |
320 | 1d14ffa9 | bellard | hw_params, |
321 | 1d14ffa9 | bellard | &nchannels |
322 | 1d14ffa9 | bellard | ); |
323 | 1d14ffa9 | bellard | if (err < 0) { |
324 | 1d14ffa9 | bellard | alsa_logerr2 (err, typ, "Failed to set number of channels %d\n",
|
325 | 1d14ffa9 | bellard | req->nchannels); |
326 | 1d14ffa9 | bellard | goto err;
|
327 | 1d14ffa9 | bellard | } |
328 | 1d14ffa9 | bellard | |
329 | 1d14ffa9 | bellard | if (nchannels != 1 && nchannels != 2) { |
330 | 1d14ffa9 | bellard | alsa_logerr2 (err, typ, |
331 | 1d14ffa9 | bellard | "Can not handle obtained number of channels %d\n",
|
332 | 1d14ffa9 | bellard | nchannels); |
333 | 1d14ffa9 | bellard | goto err;
|
334 | 1d14ffa9 | bellard | } |
335 | 1d14ffa9 | bellard | |
336 | 7a24c800 | malc | if (req->buffer_size) {
|
337 | f3b52983 | malc | unsigned long obt; |
338 | f3b52983 | malc | |
339 | 7a24c800 | malc | if (size_in_usec) {
|
340 | 7a24c800 | malc | int dir = 0; |
341 | 7a24c800 | malc | unsigned int btime = req->buffer_size; |
342 | 1d14ffa9 | bellard | |
343 | 1d14ffa9 | bellard | err = snd_pcm_hw_params_set_buffer_time_near ( |
344 | 1d14ffa9 | bellard | handle, |
345 | 1d14ffa9 | bellard | hw_params, |
346 | 7a24c800 | malc | &btime, |
347 | 7a24c800 | malc | &dir |
348 | c0fe3827 | bellard | ); |
349 | f3b52983 | malc | obt = btime; |
350 | 1d14ffa9 | bellard | } |
351 | 1d14ffa9 | bellard | else {
|
352 | 7a24c800 | malc | snd_pcm_uframes_t bsize = req->buffer_size; |
353 | 1d14ffa9 | bellard | |
354 | 7a24c800 | malc | err = snd_pcm_hw_params_set_buffer_size_near ( |
355 | 7a24c800 | malc | handle, |
356 | 7a24c800 | malc | hw_params, |
357 | 7a24c800 | malc | &bsize |
358 | 7a24c800 | malc | ); |
359 | f3b52983 | malc | obt = bsize; |
360 | 7a24c800 | malc | } |
361 | 7a24c800 | malc | if (err < 0) { |
362 | 7a24c800 | malc | alsa_logerr2 (err, typ, "Failed to set buffer %s to %d\n",
|
363 | 7a24c800 | malc | size_in_usec ? "time" : "size", req->buffer_size); |
364 | 7a24c800 | malc | goto err;
|
365 | 7a24c800 | malc | } |
366 | f3b52983 | malc | |
367 | f3b52983 | malc | if (obt - req->buffer_size)
|
368 | f3b52983 | malc | dolog ("Requested buffer %s %u was rejected, using %lu\n",
|
369 | f3b52983 | malc | size_in_usec ? "time" : "size", req->buffer_size, obt); |
370 | 7a24c800 | malc | } |
371 | 7a24c800 | malc | |
372 | 7a24c800 | malc | if (req->period_size) {
|
373 | f3b52983 | malc | unsigned long obt; |
374 | f3b52983 | malc | |
375 | 7a24c800 | malc | if (size_in_usec) {
|
376 | 7a24c800 | malc | int dir = 0; |
377 | 7a24c800 | malc | unsigned int ptime = req->period_size; |
378 | 1d14ffa9 | bellard | |
379 | 7a24c800 | malc | err = snd_pcm_hw_params_set_period_time_near ( |
380 | 7a24c800 | malc | handle, |
381 | 1d14ffa9 | bellard | hw_params, |
382 | 7a24c800 | malc | &ptime, |
383 | 7a24c800 | malc | &dir |
384 | 1d14ffa9 | bellard | ); |
385 | f3b52983 | malc | obt = ptime; |
386 | 7a24c800 | malc | } |
387 | 7a24c800 | malc | else {
|
388 | 7a24c800 | malc | snd_pcm_uframes_t psize = req->period_size; |
389 | 1d14ffa9 | bellard | |
390 | 7a24c800 | malc | err = snd_pcm_hw_params_set_buffer_size_near ( |
391 | 1d14ffa9 | bellard | handle, |
392 | 1d14ffa9 | bellard | hw_params, |
393 | 7a24c800 | malc | &psize |
394 | 1d14ffa9 | bellard | ); |
395 | f3b52983 | malc | obt = psize; |
396 | 1d14ffa9 | bellard | } |
397 | 7a24c800 | malc | |
398 | 7a24c800 | malc | if (err < 0) { |
399 | 7a24c800 | malc | alsa_logerr2 (err, typ, "Failed to set period %s to %d\n",
|
400 | 7a24c800 | malc | size_in_usec ? "time" : "size", req->period_size); |
401 | 7a24c800 | malc | goto err;
|
402 | 7a24c800 | malc | } |
403 | f3b52983 | malc | |
404 | f3b52983 | malc | if (obt - req->period_size)
|
405 | f3b52983 | malc | dolog ("Requested period %s %u was rejected, using %lu\n",
|
406 | f3b52983 | malc | size_in_usec ? "time" : "size", req->period_size, obt); |
407 | 1d14ffa9 | bellard | } |
408 | 1d14ffa9 | bellard | |
409 | 1d14ffa9 | bellard | err = snd_pcm_hw_params (handle, hw_params); |
410 | 1d14ffa9 | bellard | if (err < 0) { |
411 | 1d14ffa9 | bellard | alsa_logerr2 (err, typ, "Failed to apply audio parameters\n");
|
412 | 1d14ffa9 | bellard | goto err;
|
413 | 1d14ffa9 | bellard | } |
414 | 1d14ffa9 | bellard | |
415 | 1d14ffa9 | bellard | err = snd_pcm_hw_params_get_buffer_size (hw_params, &obt_buffer_size); |
416 | 1d14ffa9 | bellard | if (err < 0) { |
417 | 1d14ffa9 | bellard | alsa_logerr2 (err, typ, "Failed to get buffer size\n");
|
418 | 1d14ffa9 | bellard | goto err;
|
419 | 1d14ffa9 | bellard | } |
420 | 1d14ffa9 | bellard | |
421 | ca9cc28c | balrog | err = snd_pcm_hw_params_get_format (hw_params, &obtfmt); |
422 | ca9cc28c | balrog | if (err < 0) { |
423 | ca9cc28c | balrog | alsa_logerr2 (err, typ, "Failed to get format\n");
|
424 | ca9cc28c | balrog | goto err;
|
425 | ca9cc28c | balrog | } |
426 | ca9cc28c | balrog | |
427 | ca9cc28c | balrog | if (alsa_to_audfmt (obtfmt, &obt->fmt, &obt->endianness)) {
|
428 | ca9cc28c | balrog | dolog ("Invalid format was returned %d\n", obtfmt);
|
429 | ca9cc28c | balrog | goto err;
|
430 | ca9cc28c | balrog | } |
431 | ca9cc28c | balrog | |
432 | 1d14ffa9 | bellard | err = snd_pcm_prepare (handle); |
433 | 1d14ffa9 | bellard | if (err < 0) { |
434 | c0fe3827 | bellard | alsa_logerr2 (err, typ, "Could not prepare handle %p\n", handle);
|
435 | 1d14ffa9 | bellard | goto err;
|
436 | 1d14ffa9 | bellard | } |
437 | 1d14ffa9 | bellard | |
438 | 1d14ffa9 | bellard | if (!in && conf.threshold) {
|
439 | 1d14ffa9 | bellard | snd_pcm_uframes_t threshold; |
440 | 1d14ffa9 | bellard | int bytes_per_sec;
|
441 | 1d14ffa9 | bellard | |
442 | ca9cc28c | balrog | bytes_per_sec = freq << (nchannels == 2);
|
443 | ca9cc28c | balrog | |
444 | ca9cc28c | balrog | switch (obt->fmt) {
|
445 | ca9cc28c | balrog | case AUD_FMT_S8:
|
446 | ca9cc28c | balrog | case AUD_FMT_U8:
|
447 | ca9cc28c | balrog | break;
|
448 | ca9cc28c | balrog | |
449 | ca9cc28c | balrog | case AUD_FMT_S16:
|
450 | ca9cc28c | balrog | case AUD_FMT_U16:
|
451 | ca9cc28c | balrog | bytes_per_sec <<= 1;
|
452 | ca9cc28c | balrog | break;
|
453 | ca9cc28c | balrog | |
454 | ca9cc28c | balrog | case AUD_FMT_S32:
|
455 | ca9cc28c | balrog | case AUD_FMT_U32:
|
456 | ca9cc28c | balrog | bytes_per_sec <<= 2;
|
457 | ca9cc28c | balrog | break;
|
458 | ca9cc28c | balrog | } |
459 | 1d14ffa9 | bellard | |
460 | 1d14ffa9 | bellard | threshold = (conf.threshold * bytes_per_sec) / 1000;
|
461 | 1d14ffa9 | bellard | alsa_set_threshold (handle, threshold); |
462 | 1d14ffa9 | bellard | } |
463 | 1d14ffa9 | bellard | |
464 | 1d14ffa9 | bellard | obt->nchannels = nchannels; |
465 | 1d14ffa9 | bellard | obt->freq = freq; |
466 | c0fe3827 | bellard | obt->samples = obt_buffer_size; |
467 | ca9cc28c | balrog | |
468 | 1d14ffa9 | bellard | *handlep = handle; |
469 | 1d14ffa9 | bellard | |
470 | ca9cc28c | balrog | if (conf.verbose &&
|
471 | ca9cc28c | balrog | (obt->fmt != req->fmt || |
472 | ca9cc28c | balrog | obt->nchannels != req->nchannels || |
473 | ca9cc28c | balrog | obt->freq != req->freq)) { |
474 | ca9cc28c | balrog | dolog ("Audio paramters for %s\n", typ);
|
475 | 1d14ffa9 | bellard | alsa_dump_info (req, obt); |
476 | 1d14ffa9 | bellard | } |
477 | 1d14ffa9 | bellard | |
478 | 1d14ffa9 | bellard | #ifdef DEBUG
|
479 | 1d14ffa9 | bellard | alsa_dump_info (req, obt); |
480 | 1d14ffa9 | bellard | #endif
|
481 | 1d14ffa9 | bellard | return 0; |
482 | 1d14ffa9 | bellard | |
483 | 1d14ffa9 | bellard | err:
|
484 | 1d14ffa9 | bellard | alsa_anal_close (&handle); |
485 | 1d14ffa9 | bellard | return -1; |
486 | 1d14ffa9 | bellard | } |
487 | 1d14ffa9 | bellard | |
488 | 1d14ffa9 | bellard | static int alsa_recover (snd_pcm_t *handle) |
489 | 1d14ffa9 | bellard | { |
490 | 1d14ffa9 | bellard | int err = snd_pcm_prepare (handle);
|
491 | 1d14ffa9 | bellard | if (err < 0) { |
492 | 1d14ffa9 | bellard | alsa_logerr (err, "Failed to prepare handle %p\n", handle);
|
493 | 1d14ffa9 | bellard | return -1; |
494 | 1d14ffa9 | bellard | } |
495 | 1d14ffa9 | bellard | return 0; |
496 | 1d14ffa9 | bellard | } |
497 | 1d14ffa9 | bellard | |
498 | 571ec3d6 | bellard | static snd_pcm_sframes_t alsa_get_avail (snd_pcm_t *handle)
|
499 | 571ec3d6 | bellard | { |
500 | 571ec3d6 | bellard | snd_pcm_sframes_t avail; |
501 | 571ec3d6 | bellard | |
502 | 571ec3d6 | bellard | avail = snd_pcm_avail_update (handle); |
503 | 571ec3d6 | bellard | if (avail < 0) { |
504 | 571ec3d6 | bellard | if (avail == -EPIPE) {
|
505 | 571ec3d6 | bellard | if (!alsa_recover (handle)) {
|
506 | 571ec3d6 | bellard | avail = snd_pcm_avail_update (handle); |
507 | 571ec3d6 | bellard | } |
508 | 571ec3d6 | bellard | } |
509 | 571ec3d6 | bellard | |
510 | 571ec3d6 | bellard | if (avail < 0) { |
511 | 571ec3d6 | bellard | alsa_logerr (avail, |
512 | 571ec3d6 | bellard | "Could not obtain number of available frames\n");
|
513 | 571ec3d6 | bellard | return -1; |
514 | 571ec3d6 | bellard | } |
515 | 571ec3d6 | bellard | } |
516 | 571ec3d6 | bellard | |
517 | 571ec3d6 | bellard | return avail;
|
518 | 571ec3d6 | bellard | } |
519 | 571ec3d6 | bellard | |
520 | 1d14ffa9 | bellard | static int alsa_run_out (HWVoiceOut *hw) |
521 | 1d14ffa9 | bellard | { |
522 | 1d14ffa9 | bellard | ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; |
523 | 1d14ffa9 | bellard | int rpos, live, decr;
|
524 | 1d14ffa9 | bellard | int samples;
|
525 | 1d14ffa9 | bellard | uint8_t *dst; |
526 | 1d14ffa9 | bellard | st_sample_t *src; |
527 | 1d14ffa9 | bellard | snd_pcm_sframes_t avail; |
528 | 1d14ffa9 | bellard | |
529 | 1d14ffa9 | bellard | live = audio_pcm_hw_get_live_out (hw); |
530 | 1d14ffa9 | bellard | if (!live) {
|
531 | 1d14ffa9 | bellard | return 0; |
532 | 1d14ffa9 | bellard | } |
533 | 1d14ffa9 | bellard | |
534 | 571ec3d6 | bellard | avail = alsa_get_avail (alsa->handle); |
535 | 1d14ffa9 | bellard | if (avail < 0) { |
536 | 571ec3d6 | bellard | dolog ("Could not get number of available playback frames\n");
|
537 | 1d14ffa9 | bellard | return 0; |
538 | 1d14ffa9 | bellard | } |
539 | 1d14ffa9 | bellard | |
540 | 1d14ffa9 | bellard | decr = audio_MIN (live, avail); |
541 | 1d14ffa9 | bellard | samples = decr; |
542 | 1d14ffa9 | bellard | rpos = hw->rpos; |
543 | 1d14ffa9 | bellard | while (samples) {
|
544 | 1d14ffa9 | bellard | int left_till_end_samples = hw->samples - rpos;
|
545 | 571ec3d6 | bellard | int len = audio_MIN (samples, left_till_end_samples);
|
546 | 1d14ffa9 | bellard | snd_pcm_sframes_t written; |
547 | 1d14ffa9 | bellard | |
548 | 1d14ffa9 | bellard | src = hw->mix_buf + rpos; |
549 | 1d14ffa9 | bellard | dst = advance (alsa->pcm_buf, rpos << hw->info.shift); |
550 | 1d14ffa9 | bellard | |
551 | 571ec3d6 | bellard | hw->clip (dst, src, len); |
552 | 1d14ffa9 | bellard | |
553 | 571ec3d6 | bellard | while (len) {
|
554 | 571ec3d6 | bellard | written = snd_pcm_writei (alsa->handle, dst, len); |
555 | 4787c71d | bellard | |
556 | 571ec3d6 | bellard | if (written <= 0) { |
557 | 4787c71d | bellard | switch (written) {
|
558 | 571ec3d6 | bellard | case 0: |
559 | 571ec3d6 | bellard | if (conf.verbose) {
|
560 | 571ec3d6 | bellard | dolog ("Failed to write %d frames (wrote zero)\n", len);
|
561 | 4787c71d | bellard | } |
562 | 4787c71d | bellard | goto exit;
|
563 | 4787c71d | bellard | |
564 | 571ec3d6 | bellard | case -EPIPE:
|
565 | 571ec3d6 | bellard | if (alsa_recover (alsa->handle)) {
|
566 | 571ec3d6 | bellard | alsa_logerr (written, "Failed to write %d frames\n",
|
567 | 571ec3d6 | bellard | len); |
568 | 571ec3d6 | bellard | goto exit;
|
569 | 571ec3d6 | bellard | } |
570 | 571ec3d6 | bellard | if (conf.verbose) {
|
571 | 571ec3d6 | bellard | dolog ("Recovering from playback xrun\n");
|
572 | 571ec3d6 | bellard | } |
573 | 4787c71d | bellard | continue;
|
574 | 4787c71d | bellard | |
575 | 571ec3d6 | bellard | case -EAGAIN:
|
576 | 571ec3d6 | bellard | goto exit;
|
577 | 571ec3d6 | bellard | |
578 | 4787c71d | bellard | default:
|
579 | 4787c71d | bellard | alsa_logerr (written, "Failed to write %d frames to %p\n",
|
580 | 571ec3d6 | bellard | len, dst); |
581 | 4787c71d | bellard | goto exit;
|
582 | 1d14ffa9 | bellard | } |
583 | 1d14ffa9 | bellard | } |
584 | 1d14ffa9 | bellard | |
585 | 4787c71d | bellard | rpos = (rpos + written) % hw->samples; |
586 | 4787c71d | bellard | samples -= written; |
587 | 571ec3d6 | bellard | len -= written; |
588 | 4787c71d | bellard | dst = advance (dst, written << hw->info.shift); |
589 | 4787c71d | bellard | src += written; |
590 | 4787c71d | bellard | } |
591 | 1d14ffa9 | bellard | } |
592 | 1d14ffa9 | bellard | |
593 | 1d14ffa9 | bellard | exit:
|
594 | 1d14ffa9 | bellard | hw->rpos = rpos; |
595 | 1d14ffa9 | bellard | return decr;
|
596 | 1d14ffa9 | bellard | } |
597 | 1d14ffa9 | bellard | |
598 | 1d14ffa9 | bellard | static void alsa_fini_out (HWVoiceOut *hw) |
599 | 1d14ffa9 | bellard | { |
600 | 1d14ffa9 | bellard | ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; |
601 | 1d14ffa9 | bellard | |
602 | 1d14ffa9 | bellard | ldebug ("alsa_fini\n");
|
603 | 1d14ffa9 | bellard | alsa_anal_close (&alsa->handle); |
604 | 1d14ffa9 | bellard | |
605 | 1d14ffa9 | bellard | if (alsa->pcm_buf) {
|
606 | 1d14ffa9 | bellard | qemu_free (alsa->pcm_buf); |
607 | 1d14ffa9 | bellard | alsa->pcm_buf = NULL;
|
608 | 1d14ffa9 | bellard | } |
609 | 1d14ffa9 | bellard | } |
610 | 1d14ffa9 | bellard | |
611 | c0fe3827 | bellard | static int alsa_init_out (HWVoiceOut *hw, audsettings_t *as) |
612 | 1d14ffa9 | bellard | { |
613 | 1d14ffa9 | bellard | ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; |
614 | 1d14ffa9 | bellard | struct alsa_params_req req;
|
615 | 1d14ffa9 | bellard | struct alsa_params_obt obt;
|
616 | 1d14ffa9 | bellard | snd_pcm_t *handle; |
617 | c0fe3827 | bellard | audsettings_t obt_as; |
618 | 1d14ffa9 | bellard | |
619 | c0fe3827 | bellard | req.fmt = aud_to_alsafmt (as->fmt); |
620 | c0fe3827 | bellard | req.freq = as->freq; |
621 | c0fe3827 | bellard | req.nchannels = as->nchannels; |
622 | 1d14ffa9 | bellard | req.period_size = conf.period_size_out; |
623 | 1d14ffa9 | bellard | req.buffer_size = conf.buffer_size_out; |
624 | 7a24c800 | malc | req.size_in_usec = conf.size_in_usec_in; |
625 | 1d14ffa9 | bellard | |
626 | 1d14ffa9 | bellard | if (alsa_open (0, &req, &obt, &handle)) { |
627 | 1d14ffa9 | bellard | return -1; |
628 | 1d14ffa9 | bellard | } |
629 | 1d14ffa9 | bellard | |
630 | c0fe3827 | bellard | obt_as.freq = obt.freq; |
631 | c0fe3827 | bellard | obt_as.nchannels = obt.nchannels; |
632 | ca9cc28c | balrog | obt_as.fmt = obt.fmt; |
633 | ca9cc28c | balrog | obt_as.endianness = obt.endianness; |
634 | c0fe3827 | bellard | |
635 | d929eba5 | bellard | audio_pcm_init_info (&hw->info, &obt_as); |
636 | c0fe3827 | bellard | hw->samples = obt.samples; |
637 | 1d14ffa9 | bellard | |
638 | c0fe3827 | bellard | alsa->pcm_buf = audio_calloc (AUDIO_FUNC, obt.samples, 1 << hw->info.shift);
|
639 | 1d14ffa9 | bellard | if (!alsa->pcm_buf) {
|
640 | 4787c71d | bellard | dolog ("Could not allocate DAC buffer (%d samples, each %d bytes)\n",
|
641 | 4787c71d | bellard | hw->samples, 1 << hw->info.shift);
|
642 | 1d14ffa9 | bellard | alsa_anal_close (&handle); |
643 | 1d14ffa9 | bellard | return -1; |
644 | 1d14ffa9 | bellard | } |
645 | 1d14ffa9 | bellard | |
646 | 1d14ffa9 | bellard | alsa->handle = handle; |
647 | 1d14ffa9 | bellard | return 0; |
648 | 1d14ffa9 | bellard | } |
649 | 1d14ffa9 | bellard | |
650 | 571ec3d6 | bellard | static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int pause) |
651 | 1d14ffa9 | bellard | { |
652 | 1d14ffa9 | bellard | int err;
|
653 | 571ec3d6 | bellard | |
654 | 571ec3d6 | bellard | if (pause) {
|
655 | 571ec3d6 | bellard | err = snd_pcm_drop (handle); |
656 | 571ec3d6 | bellard | if (err < 0) { |
657 | 32d448c4 | bellard | alsa_logerr (err, "Could not stop %s\n", typ);
|
658 | 571ec3d6 | bellard | return -1; |
659 | 571ec3d6 | bellard | } |
660 | 571ec3d6 | bellard | } |
661 | 571ec3d6 | bellard | else {
|
662 | 571ec3d6 | bellard | err = snd_pcm_prepare (handle); |
663 | 571ec3d6 | bellard | if (err < 0) { |
664 | 32d448c4 | bellard | alsa_logerr (err, "Could not prepare handle for %s\n", typ);
|
665 | 571ec3d6 | bellard | return -1; |
666 | 571ec3d6 | bellard | } |
667 | 571ec3d6 | bellard | } |
668 | 571ec3d6 | bellard | |
669 | 571ec3d6 | bellard | return 0; |
670 | 571ec3d6 | bellard | } |
671 | 571ec3d6 | bellard | |
672 | 571ec3d6 | bellard | static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...) |
673 | 571ec3d6 | bellard | { |
674 | 1d14ffa9 | bellard | ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; |
675 | 1d14ffa9 | bellard | |
676 | 1d14ffa9 | bellard | switch (cmd) {
|
677 | 1d14ffa9 | bellard | case VOICE_ENABLE:
|
678 | 1d14ffa9 | bellard | ldebug ("enabling voice\n");
|
679 | 571ec3d6 | bellard | return alsa_voice_ctl (alsa->handle, "playback", 0); |
680 | 1d14ffa9 | bellard | |
681 | 1d14ffa9 | bellard | case VOICE_DISABLE:
|
682 | 1d14ffa9 | bellard | ldebug ("disabling voice\n");
|
683 | 571ec3d6 | bellard | return alsa_voice_ctl (alsa->handle, "playback", 1); |
684 | 1d14ffa9 | bellard | } |
685 | 571ec3d6 | bellard | |
686 | 571ec3d6 | bellard | return -1; |
687 | 1d14ffa9 | bellard | } |
688 | 1d14ffa9 | bellard | |
689 | c0fe3827 | bellard | static int alsa_init_in (HWVoiceIn *hw, audsettings_t *as) |
690 | 1d14ffa9 | bellard | { |
691 | 1d14ffa9 | bellard | ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; |
692 | 1d14ffa9 | bellard | struct alsa_params_req req;
|
693 | 1d14ffa9 | bellard | struct alsa_params_obt obt;
|
694 | 1d14ffa9 | bellard | snd_pcm_t *handle; |
695 | c0fe3827 | bellard | audsettings_t obt_as; |
696 | 1d14ffa9 | bellard | |
697 | c0fe3827 | bellard | req.fmt = aud_to_alsafmt (as->fmt); |
698 | c0fe3827 | bellard | req.freq = as->freq; |
699 | c0fe3827 | bellard | req.nchannels = as->nchannels; |
700 | 1d14ffa9 | bellard | req.period_size = conf.period_size_in; |
701 | 1d14ffa9 | bellard | req.buffer_size = conf.buffer_size_in; |
702 | 7a24c800 | malc | req.size_in_usec = conf.size_in_usec_in; |
703 | 1d14ffa9 | bellard | |
704 | 1d14ffa9 | bellard | if (alsa_open (1, &req, &obt, &handle)) { |
705 | 1d14ffa9 | bellard | return -1; |
706 | 1d14ffa9 | bellard | } |
707 | 1d14ffa9 | bellard | |
708 | c0fe3827 | bellard | obt_as.freq = obt.freq; |
709 | c0fe3827 | bellard | obt_as.nchannels = obt.nchannels; |
710 | ca9cc28c | balrog | obt_as.fmt = obt.fmt; |
711 | ca9cc28c | balrog | obt_as.endianness = obt.endianness; |
712 | c0fe3827 | bellard | |
713 | d929eba5 | bellard | audio_pcm_init_info (&hw->info, &obt_as); |
714 | c0fe3827 | bellard | hw->samples = obt.samples; |
715 | c0fe3827 | bellard | |
716 | c0fe3827 | bellard | alsa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
|
717 | 1d14ffa9 | bellard | if (!alsa->pcm_buf) {
|
718 | 4787c71d | bellard | dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n",
|
719 | 4787c71d | bellard | hw->samples, 1 << hw->info.shift);
|
720 | 1d14ffa9 | bellard | alsa_anal_close (&handle); |
721 | 1d14ffa9 | bellard | return -1; |
722 | 1d14ffa9 | bellard | } |
723 | 1d14ffa9 | bellard | |
724 | 1d14ffa9 | bellard | alsa->handle = handle; |
725 | 1d14ffa9 | bellard | return 0; |
726 | 1d14ffa9 | bellard | } |
727 | 1d14ffa9 | bellard | |
728 | 1d14ffa9 | bellard | static void alsa_fini_in (HWVoiceIn *hw) |
729 | 1d14ffa9 | bellard | { |
730 | 1d14ffa9 | bellard | ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; |
731 | 1d14ffa9 | bellard | |
732 | 1d14ffa9 | bellard | alsa_anal_close (&alsa->handle); |
733 | 1d14ffa9 | bellard | |
734 | 1d14ffa9 | bellard | if (alsa->pcm_buf) {
|
735 | 1d14ffa9 | bellard | qemu_free (alsa->pcm_buf); |
736 | 1d14ffa9 | bellard | alsa->pcm_buf = NULL;
|
737 | 1d14ffa9 | bellard | } |
738 | 1d14ffa9 | bellard | } |
739 | 1d14ffa9 | bellard | |
740 | 1d14ffa9 | bellard | static int alsa_run_in (HWVoiceIn *hw) |
741 | 1d14ffa9 | bellard | { |
742 | 1d14ffa9 | bellard | ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; |
743 | 1d14ffa9 | bellard | int hwshift = hw->info.shift;
|
744 | 1d14ffa9 | bellard | int i;
|
745 | 1d14ffa9 | bellard | int live = audio_pcm_hw_get_live_in (hw);
|
746 | 1d14ffa9 | bellard | int dead = hw->samples - live;
|
747 | 571ec3d6 | bellard | int decr;
|
748 | 1d14ffa9 | bellard | struct {
|
749 | 1d14ffa9 | bellard | int add;
|
750 | 1d14ffa9 | bellard | int len;
|
751 | 1d14ffa9 | bellard | } bufs[2] = {
|
752 | 1d14ffa9 | bellard | { hw->wpos, 0 },
|
753 | 1d14ffa9 | bellard | { 0, 0 } |
754 | 1d14ffa9 | bellard | }; |
755 | 571ec3d6 | bellard | snd_pcm_sframes_t avail; |
756 | 1d14ffa9 | bellard | snd_pcm_uframes_t read_samples = 0;
|
757 | 1d14ffa9 | bellard | |
758 | 1d14ffa9 | bellard | if (!dead) {
|
759 | 1d14ffa9 | bellard | return 0; |
760 | 1d14ffa9 | bellard | } |
761 | 1d14ffa9 | bellard | |
762 | 571ec3d6 | bellard | avail = alsa_get_avail (alsa->handle); |
763 | 571ec3d6 | bellard | if (avail < 0) { |
764 | 571ec3d6 | bellard | dolog ("Could not get number of captured frames\n");
|
765 | 571ec3d6 | bellard | return 0; |
766 | 571ec3d6 | bellard | } |
767 | 571ec3d6 | bellard | |
768 | 571ec3d6 | bellard | if (!avail && (snd_pcm_state (alsa->handle) == SND_PCM_STATE_PREPARED)) {
|
769 | 571ec3d6 | bellard | avail = hw->samples; |
770 | 571ec3d6 | bellard | } |
771 | 571ec3d6 | bellard | |
772 | 571ec3d6 | bellard | decr = audio_MIN (dead, avail); |
773 | 571ec3d6 | bellard | if (!decr) {
|
774 | 571ec3d6 | bellard | return 0; |
775 | 571ec3d6 | bellard | } |
776 | 571ec3d6 | bellard | |
777 | 571ec3d6 | bellard | if (hw->wpos + decr > hw->samples) {
|
778 | 1d14ffa9 | bellard | bufs[0].len = (hw->samples - hw->wpos);
|
779 | 571ec3d6 | bellard | bufs[1].len = (decr - (hw->samples - hw->wpos));
|
780 | 1d14ffa9 | bellard | } |
781 | 1d14ffa9 | bellard | else {
|
782 | 571ec3d6 | bellard | bufs[0].len = decr;
|
783 | 1d14ffa9 | bellard | } |
784 | 1d14ffa9 | bellard | |
785 | 1d14ffa9 | bellard | for (i = 0; i < 2; ++i) { |
786 | 1d14ffa9 | bellard | void *src;
|
787 | 1d14ffa9 | bellard | st_sample_t *dst; |
788 | 1d14ffa9 | bellard | snd_pcm_sframes_t nread; |
789 | 1d14ffa9 | bellard | snd_pcm_uframes_t len; |
790 | 1d14ffa9 | bellard | |
791 | 1d14ffa9 | bellard | len = bufs[i].len; |
792 | 1d14ffa9 | bellard | |
793 | 1d14ffa9 | bellard | src = advance (alsa->pcm_buf, bufs[i].add << hwshift); |
794 | 1d14ffa9 | bellard | dst = hw->conv_buf + bufs[i].add; |
795 | 1d14ffa9 | bellard | |
796 | 1d14ffa9 | bellard | while (len) {
|
797 | 1d14ffa9 | bellard | nread = snd_pcm_readi (alsa->handle, src, len); |
798 | 1d14ffa9 | bellard | |
799 | 571ec3d6 | bellard | if (nread <= 0) { |
800 | 1d14ffa9 | bellard | switch (nread) {
|
801 | 571ec3d6 | bellard | case 0: |
802 | 571ec3d6 | bellard | if (conf.verbose) {
|
803 | 571ec3d6 | bellard | dolog ("Failed to read %ld frames (read zero)\n", len);
|
804 | 1d14ffa9 | bellard | } |
805 | 1d14ffa9 | bellard | goto exit;
|
806 | 1d14ffa9 | bellard | |
807 | 571ec3d6 | bellard | case -EPIPE:
|
808 | 571ec3d6 | bellard | if (alsa_recover (alsa->handle)) {
|
809 | 571ec3d6 | bellard | alsa_logerr (nread, "Failed to read %ld frames\n", len);
|
810 | 571ec3d6 | bellard | goto exit;
|
811 | 571ec3d6 | bellard | } |
812 | 571ec3d6 | bellard | if (conf.verbose) {
|
813 | 571ec3d6 | bellard | dolog ("Recovering from capture xrun\n");
|
814 | 571ec3d6 | bellard | } |
815 | 1d14ffa9 | bellard | continue;
|
816 | 1d14ffa9 | bellard | |
817 | 571ec3d6 | bellard | case -EAGAIN:
|
818 | 571ec3d6 | bellard | goto exit;
|
819 | 571ec3d6 | bellard | |
820 | 1d14ffa9 | bellard | default:
|
821 | 1d14ffa9 | bellard | alsa_logerr ( |
822 | 1d14ffa9 | bellard | nread, |
823 | 1d14ffa9 | bellard | "Failed to read %ld frames from %p\n",
|
824 | 1d14ffa9 | bellard | len, |
825 | 1d14ffa9 | bellard | src |
826 | 1d14ffa9 | bellard | ); |
827 | 1d14ffa9 | bellard | goto exit;
|
828 | 1d14ffa9 | bellard | } |
829 | 1d14ffa9 | bellard | } |
830 | 1d14ffa9 | bellard | |
831 | 1d14ffa9 | bellard | hw->conv (dst, src, nread, &nominal_volume); |
832 | 1d14ffa9 | bellard | |
833 | 1d14ffa9 | bellard | src = advance (src, nread << hwshift); |
834 | 1d14ffa9 | bellard | dst += nread; |
835 | 1d14ffa9 | bellard | |
836 | 1d14ffa9 | bellard | read_samples += nread; |
837 | 1d14ffa9 | bellard | len -= nread; |
838 | 1d14ffa9 | bellard | } |
839 | 1d14ffa9 | bellard | } |
840 | 1d14ffa9 | bellard | |
841 | 1d14ffa9 | bellard | exit:
|
842 | 1d14ffa9 | bellard | hw->wpos = (hw->wpos + read_samples) % hw->samples; |
843 | 1d14ffa9 | bellard | return read_samples;
|
844 | 1d14ffa9 | bellard | } |
845 | 1d14ffa9 | bellard | |
846 | 1d14ffa9 | bellard | static int alsa_read (SWVoiceIn *sw, void *buf, int size) |
847 | 1d14ffa9 | bellard | { |
848 | 1d14ffa9 | bellard | return audio_pcm_sw_read (sw, buf, size);
|
849 | 1d14ffa9 | bellard | } |
850 | 1d14ffa9 | bellard | |
851 | 1d14ffa9 | bellard | static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...) |
852 | 1d14ffa9 | bellard | { |
853 | 571ec3d6 | bellard | ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; |
854 | 571ec3d6 | bellard | |
855 | 571ec3d6 | bellard | switch (cmd) {
|
856 | 571ec3d6 | bellard | case VOICE_ENABLE:
|
857 | 571ec3d6 | bellard | ldebug ("enabling voice\n");
|
858 | 571ec3d6 | bellard | return alsa_voice_ctl (alsa->handle, "capture", 0); |
859 | 571ec3d6 | bellard | |
860 | 571ec3d6 | bellard | case VOICE_DISABLE:
|
861 | 571ec3d6 | bellard | ldebug ("disabling voice\n");
|
862 | 571ec3d6 | bellard | return alsa_voice_ctl (alsa->handle, "capture", 1); |
863 | 571ec3d6 | bellard | } |
864 | 571ec3d6 | bellard | |
865 | 571ec3d6 | bellard | return -1; |
866 | 1d14ffa9 | bellard | } |
867 | 1d14ffa9 | bellard | |
868 | 1d14ffa9 | bellard | static void *alsa_audio_init (void) |
869 | 1d14ffa9 | bellard | { |
870 | 1d14ffa9 | bellard | return &conf;
|
871 | 1d14ffa9 | bellard | } |
872 | 1d14ffa9 | bellard | |
873 | 1d14ffa9 | bellard | static void alsa_audio_fini (void *opaque) |
874 | 1d14ffa9 | bellard | { |
875 | 1d14ffa9 | bellard | (void) opaque;
|
876 | 1d14ffa9 | bellard | } |
877 | 1d14ffa9 | bellard | |
878 | 1d14ffa9 | bellard | static struct audio_option alsa_options[] = { |
879 | 1d14ffa9 | bellard | {"DAC_SIZE_IN_USEC", AUD_OPT_BOOL, &conf.size_in_usec_out,
|
880 | 1d14ffa9 | bellard | "DAC period/buffer size in microseconds (otherwise in frames)", NULL, 0}, |
881 | 1d14ffa9 | bellard | {"DAC_PERIOD_SIZE", AUD_OPT_INT, &conf.period_size_out,
|
882 | 7a24c800 | malc | "DAC period size (0 to go with system default)",
|
883 | 7a24c800 | malc | &conf.period_size_out_overridden, 0},
|
884 | 1d14ffa9 | bellard | {"DAC_BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_size_out,
|
885 | 7a24c800 | malc | "DAC buffer size (0 to go with system default)",
|
886 | 7a24c800 | malc | &conf.buffer_size_out_overridden, 0},
|
887 | 1d14ffa9 | bellard | |
888 | 1d14ffa9 | bellard | {"ADC_SIZE_IN_USEC", AUD_OPT_BOOL, &conf.size_in_usec_in,
|
889 | 1d14ffa9 | bellard | "ADC period/buffer size in microseconds (otherwise in frames)", NULL, 0}, |
890 | 1d14ffa9 | bellard | {"ADC_PERIOD_SIZE", AUD_OPT_INT, &conf.period_size_in,
|
891 | 7a24c800 | malc | "ADC period size (0 to go with system default)",
|
892 | 7a24c800 | malc | &conf.period_size_in_overridden, 0},
|
893 | 1d14ffa9 | bellard | {"ADC_BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_size_in,
|
894 | 7a24c800 | malc | "ADC buffer size (0 to go with system default)",
|
895 | 7a24c800 | malc | &conf.buffer_size_in_overridden, 0},
|
896 | 1d14ffa9 | bellard | |
897 | 1d14ffa9 | bellard | {"THRESHOLD", AUD_OPT_INT, &conf.threshold,
|
898 | 1d14ffa9 | bellard | "(undocumented)", NULL, 0}, |
899 | 1d14ffa9 | bellard | |
900 | 1d14ffa9 | bellard | {"DAC_DEV", AUD_OPT_STR, &conf.pcm_name_out,
|
901 | 1d14ffa9 | bellard | "DAC device name (for instance dmix)", NULL, 0}, |
902 | 1d14ffa9 | bellard | |
903 | 1d14ffa9 | bellard | {"ADC_DEV", AUD_OPT_STR, &conf.pcm_name_in,
|
904 | 1d14ffa9 | bellard | "ADC device name", NULL, 0}, |
905 | 571ec3d6 | bellard | |
906 | 571ec3d6 | bellard | {"VERBOSE", AUD_OPT_BOOL, &conf.verbose,
|
907 | 571ec3d6 | bellard | "Behave in a more verbose way", NULL, 0}, |
908 | 571ec3d6 | bellard | |
909 | 1d14ffa9 | bellard | {NULL, 0, NULL, NULL, NULL, 0} |
910 | 1d14ffa9 | bellard | }; |
911 | 1d14ffa9 | bellard | |
912 | 1d14ffa9 | bellard | static struct audio_pcm_ops alsa_pcm_ops = { |
913 | 1d14ffa9 | bellard | alsa_init_out, |
914 | 1d14ffa9 | bellard | alsa_fini_out, |
915 | 1d14ffa9 | bellard | alsa_run_out, |
916 | 1d14ffa9 | bellard | alsa_write, |
917 | 1d14ffa9 | bellard | alsa_ctl_out, |
918 | 1d14ffa9 | bellard | |
919 | 1d14ffa9 | bellard | alsa_init_in, |
920 | 1d14ffa9 | bellard | alsa_fini_in, |
921 | 1d14ffa9 | bellard | alsa_run_in, |
922 | 1d14ffa9 | bellard | alsa_read, |
923 | 1d14ffa9 | bellard | alsa_ctl_in |
924 | 1d14ffa9 | bellard | }; |
925 | 1d14ffa9 | bellard | |
926 | 1d14ffa9 | bellard | struct audio_driver alsa_audio_driver = {
|
927 | 1d14ffa9 | bellard | INIT_FIELD (name = ) "alsa",
|
928 | 1d14ffa9 | bellard | INIT_FIELD (descr = ) "ALSA http://www.alsa-project.org",
|
929 | 1d14ffa9 | bellard | INIT_FIELD (options = ) alsa_options, |
930 | 1d14ffa9 | bellard | INIT_FIELD (init = ) alsa_audio_init, |
931 | 1d14ffa9 | bellard | INIT_FIELD (fini = ) alsa_audio_fini, |
932 | 1d14ffa9 | bellard | INIT_FIELD (pcm_ops = ) &alsa_pcm_ops, |
933 | 1d14ffa9 | bellard | INIT_FIELD (can_be_default = ) 1,
|
934 | 1d14ffa9 | bellard | INIT_FIELD (max_voices_out = ) INT_MAX, |
935 | 1d14ffa9 | bellard | INIT_FIELD (max_voices_in = ) INT_MAX, |
936 | 1d14ffa9 | bellard | INIT_FIELD (voice_size_out = ) sizeof (ALSAVoiceOut),
|
937 | 1d14ffa9 | bellard | INIT_FIELD (voice_size_in = ) sizeof (ALSAVoiceIn)
|
938 | 1d14ffa9 | bellard | }; |