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