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