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