Commit 1d14ffa97eacd3cb722271eaf6f093038396eac4
1 parent
3b0d4f61
merged 15a_aqemu.patch audio patch (malc)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1584 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
9 changed files
with
3767 additions
and
677 deletions
Too many changes to show.
To preserve performance only 9 of 28 files are displayed.
Changelog
Makefile.target
... | ... | @@ -262,7 +262,7 @@ endif |
262 | 262 | VL_OBJS=vl.o osdep.o block.o readline.o monitor.o pci.o console.o |
263 | 263 | VL_OBJS+=block-cow.o block-qcow.o aes.o block-vmdk.o block-cloop.o block-dmg.o block-bochs.o block-vpc.o block-vvfat.o |
264 | 264 | |
265 | -SOUND_HW = sb16.o | |
265 | +SOUND_HW = sb16.o es1370.o | |
266 | 266 | AUDIODRV = audio.o noaudio.o wavaudio.o |
267 | 267 | ifdef CONFIG_SDL |
268 | 268 | AUDIODRV += sdlaudio.o |
... | ... | @@ -270,29 +270,38 @@ endif |
270 | 270 | ifdef CONFIG_OSS |
271 | 271 | AUDIODRV += ossaudio.o |
272 | 272 | endif |
273 | - | |
274 | -pc.o: DEFINES := -DUSE_SB16 $(DEFINES) | |
275 | - | |
276 | -ifdef CONFIG_ADLIB | |
277 | -SOUND_HW += fmopl.o adlib.o | |
273 | +ifdef CONFIG_COREAUDIO | |
274 | +AUDIODRV += coreaudio.o | |
275 | +endif | |
276 | +ifdef CONFIG_ALSA | |
277 | +AUDIODRV += alsaaudio.o | |
278 | +LIBS += -lasound | |
279 | +endif | |
280 | +ifdef CONFIG_DSOUND | |
281 | +AUDIODRV += dsoundaudio.o | |
282 | +LIBS += -lole32 -ldxguid | |
278 | 283 | endif |
279 | - | |
280 | 284 | ifdef CONFIG_FMOD |
281 | 285 | AUDIODRV += fmodaudio.o |
282 | 286 | audio.o fmodaudio.o: DEFINES := -I$(CONFIG_FMOD_INC) $(DEFINES) |
283 | 287 | LIBS += $(CONFIG_FMOD_LIB) |
284 | 288 | endif |
289 | +ifdef CONFIG_ADLIB | |
290 | +SOUND_HW += fmopl.o adlib.o | |
291 | +endif | |
285 | 292 | |
286 | 293 | ifeq ($(TARGET_BASE_ARCH), i386) |
287 | 294 | # Hardware support |
288 | 295 | VL_OBJS+= ide.o ne2000.o pckbd.o vga.o $(SOUND_HW) dma.o $(AUDIODRV) |
289 | 296 | VL_OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pc.o |
290 | 297 | VL_OBJS+= cirrus_vga.o mixeng.o apic.o parallel.o |
298 | +DEFINES += -DHAS_AUDIO | |
291 | 299 | endif |
292 | 300 | ifeq ($(TARGET_BASE_ARCH), ppc) |
293 | 301 | VL_OBJS+= ppc.o ide.o ne2000.o pckbd.o vga.o $(SOUND_HW) dma.o $(AUDIODRV) |
294 | 302 | VL_OBJS+= mc146818rtc.o serial.o i8259.o i8254.o fdc.o m48t59.o |
295 | 303 | VL_OBJS+= ppc_prep.o ppc_chrp.o cuda.o adb.o openpic.o heathrow_pic.o mixeng.o |
304 | +DEFINES += -DHAS_AUDIO | |
296 | 305 | endif |
297 | 306 | ifeq ($(TARGET_ARCH), mips) |
298 | 307 | VL_OBJS+= mips_r4k.o dma.o vga.o serial.o ne2000.o i8254.o i8259.o |
... | ... | @@ -317,7 +326,10 @@ VL_OBJS+=sdl.o |
317 | 326 | endif |
318 | 327 | ifdef CONFIG_COCOA |
319 | 328 | VL_OBJS+=cocoa.o |
320 | -COCOA_LIBS=-F/System/Library/Frameworks -framework Cocoa | |
329 | +COCOA_LIBS=-F/System/Library/Frameworks -framework Cocoa -framework IOKit | |
330 | +ifdef CONFIG_COREAUDIO | |
331 | +COCOA_LIBS+=-framework CoreAudio | |
332 | +endif | |
321 | 333 | endif |
322 | 334 | ifdef CONFIG_SLIRP |
323 | 335 | DEFINES+=-I$(SRC_PATH)/slirp |
... | ... | @@ -349,6 +361,10 @@ ifeq ($(ARCH),ia64) |
349 | 361 | VL_LDFLAGS+=-Wl,-G0 -Wl,-T,$(SRC_PATH)/ia64.ld |
350 | 362 | endif |
351 | 363 | |
364 | +ifdef CONFIG_WIN32 | |
365 | +SDL_LIBS := $(filter-out -mwindows, $(SDL_LIBS)) -mconsole | |
366 | +endif | |
367 | + | |
352 | 368 | $(QEMU_SYSTEM): $(VL_OBJS) libqemu.a |
353 | 369 | $(CC) $(VL_LDFLAGS) -o $@ $^ $(LIBS) $(SDL_LIBS) $(COCOA_LIBS) $(VL_LIBS) |
354 | 370 | |
... | ... | @@ -364,6 +380,9 @@ sdlaudio.o: sdlaudio.c |
364 | 380 | depend: $(SRCS) |
365 | 381 | $(CC) -MM $(CFLAGS) $(DEFINES) $^ 1>.depend |
366 | 382 | |
383 | +vldepend: $(VL_OBJS:.o=.c) | |
384 | + $(CC) -MM $(CFLAGS) $(DEFINES) $^ 1>.depend | |
385 | + | |
367 | 386 | # libqemu |
368 | 387 | |
369 | 388 | libqemu.a: $(LIBOBJS) |
... | ... | @@ -415,8 +434,6 @@ op.o: op.c op_template.c op_mem.c |
415 | 434 | op_helper.o: op_helper_mem.c |
416 | 435 | endif |
417 | 436 | |
418 | -mixeng.o: mixeng.c mixeng.h mixeng_template.h | |
419 | - | |
420 | 437 | %.o: %.c |
421 | 438 | $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< |
422 | 439 | |
... | ... | @@ -434,3 +451,9 @@ endif |
434 | 451 | ifneq ($(wildcard .depend),) |
435 | 452 | include .depend |
436 | 453 | endif |
454 | + | |
455 | +ifeq (0, 1) | |
456 | +audio.o sdlaudio.o dsoundaudio.o ossaudio.o wavaudio.o noaudio.o \ | |
457 | +fmodaudio.o alsaaudio.o mixeng.o: \ | |
458 | +CFLAGS := $(CFLAGS) -Wall -Werror -W -Wsign-compare | |
459 | +endif | ... | ... |
audio/alsaaudio.c
0 โ 100644
1 | +/* | |
2 | + * QEMU ALSA audio driver | |
3 | + * | |
4 | + * Copyright (c) 2005 Vassili Karpov (malc) | |
5 | + * | |
6 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | + * of this software and associated documentation files (the "Software"), to deal | |
8 | + * in the Software without restriction, including without limitation the rights | |
9 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | + * copies of the Software, and to permit persons to whom the Software is | |
11 | + * furnished to do so, subject to the following conditions: | |
12 | + * | |
13 | + * The above copyright notice and this permission notice shall be included in | |
14 | + * all copies or substantial portions of the Software. | |
15 | + * | |
16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | + * THE SOFTWARE. | |
23 | + */ | |
24 | +#include <alsa/asoundlib.h> | |
25 | +#include "vl.h" | |
26 | + | |
27 | +#define AUDIO_CAP "alsa" | |
28 | +#include "audio_int.h" | |
29 | + | |
30 | +typedef struct ALSAVoiceOut { | |
31 | + HWVoiceOut hw; | |
32 | + void *pcm_buf; | |
33 | + snd_pcm_t *handle; | |
34 | + int can_pause; | |
35 | + int was_enabled; | |
36 | +} ALSAVoiceOut; | |
37 | + | |
38 | +typedef struct ALSAVoiceIn { | |
39 | + HWVoiceIn hw; | |
40 | + snd_pcm_t *handle; | |
41 | + void *pcm_buf; | |
42 | + int can_pause; | |
43 | +} ALSAVoiceIn; | |
44 | + | |
45 | +static struct { | |
46 | + int size_in_usec_in; | |
47 | + int size_in_usec_out; | |
48 | + const char *pcm_name_in; | |
49 | + const char *pcm_name_out; | |
50 | + unsigned int buffer_size_in; | |
51 | + unsigned int period_size_in; | |
52 | + unsigned int buffer_size_out; | |
53 | + unsigned int period_size_out; | |
54 | + unsigned int threshold; | |
55 | + | |
56 | + int buffer_size_in_overriden; | |
57 | + int period_size_in_overriden; | |
58 | + | |
59 | + int buffer_size_out_overriden; | |
60 | + int period_size_out_overriden; | |
61 | +} conf = { | |
62 | +#ifdef HIGH_LATENCY | |
63 | + .size_in_usec_in = 1, | |
64 | + .size_in_usec_out = 1, | |
65 | +#endif | |
66 | + .pcm_name_out = "hw:0,0", | |
67 | + .pcm_name_in = "hw:0,0", | |
68 | +#ifdef HIGH_LATENCY | |
69 | + .buffer_size_in = 400000, | |
70 | + .period_size_in = 400000 / 4, | |
71 | + .buffer_size_out = 400000, | |
72 | + .period_size_out = 400000 / 4, | |
73 | +#else | |
74 | +#define DEFAULT_BUFFER_SIZE 1024 | |
75 | +#define DEFAULT_PERIOD_SIZE 256 | |
76 | + .buffer_size_in = DEFAULT_BUFFER_SIZE, | |
77 | + .period_size_in = DEFAULT_PERIOD_SIZE, | |
78 | + .buffer_size_out = DEFAULT_BUFFER_SIZE, | |
79 | + .period_size_out = DEFAULT_PERIOD_SIZE, | |
80 | + .buffer_size_in_overriden = 0, | |
81 | + .buffer_size_out_overriden = 0, | |
82 | + .period_size_in_overriden = 0, | |
83 | + .period_size_out_overriden = 0, | |
84 | +#endif | |
85 | + .threshold = 0 | |
86 | +}; | |
87 | + | |
88 | +struct alsa_params_req { | |
89 | + int freq; | |
90 | + audfmt_e fmt; | |
91 | + int nchannels; | |
92 | + unsigned int buffer_size; | |
93 | + unsigned int period_size; | |
94 | +}; | |
95 | + | |
96 | +struct alsa_params_obt { | |
97 | + int freq; | |
98 | + audfmt_e fmt; | |
99 | + int nchannels; | |
100 | + int can_pause; | |
101 | + snd_pcm_uframes_t buffer_size; | |
102 | +}; | |
103 | + | |
104 | +static void GCC_FMT_ATTR (2, 3) alsa_logerr (int err, const char *fmt, ...) | |
105 | +{ | |
106 | + va_list ap; | |
107 | + | |
108 | + va_start (ap, fmt); | |
109 | + AUD_vlog (AUDIO_CAP, fmt, ap); | |
110 | + va_end (ap); | |
111 | + | |
112 | + AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err)); | |
113 | +} | |
114 | + | |
115 | +static void GCC_FMT_ATTR (3, 4) alsa_logerr2 ( | |
116 | + int err, | |
117 | + const char *typ, | |
118 | + const char *fmt, | |
119 | + ... | |
120 | + ) | |
121 | +{ | |
122 | + va_list ap; | |
123 | + | |
124 | + AUD_log (AUDIO_CAP, "Can not initialize %s\n", typ); | |
125 | + | |
126 | + va_start (ap, fmt); | |
127 | + AUD_vlog (AUDIO_CAP, fmt, ap); | |
128 | + va_end (ap); | |
129 | + | |
130 | + AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err)); | |
131 | +} | |
132 | + | |
133 | +static void alsa_anal_close (snd_pcm_t **handlep) | |
134 | +{ | |
135 | + int err = snd_pcm_close (*handlep); | |
136 | + if (err) { | |
137 | + alsa_logerr (err, "Failed to close PCM handle %p\n", *handlep); | |
138 | + } | |
139 | + *handlep = NULL; | |
140 | +} | |
141 | + | |
142 | +static int alsa_write (SWVoiceOut *sw, void *buf, int len) | |
143 | +{ | |
144 | + return audio_pcm_sw_write (sw, buf, len); | |
145 | +} | |
146 | + | |
147 | +static int aud_to_alsafmt (audfmt_e fmt) | |
148 | +{ | |
149 | + switch (fmt) { | |
150 | + case AUD_FMT_S8: | |
151 | + return SND_PCM_FORMAT_S8; | |
152 | + | |
153 | + case AUD_FMT_U8: | |
154 | + return SND_PCM_FORMAT_U8; | |
155 | + | |
156 | + case AUD_FMT_S16: | |
157 | + return SND_PCM_FORMAT_S16_LE; | |
158 | + | |
159 | + case AUD_FMT_U16: | |
160 | + return SND_PCM_FORMAT_U16_LE; | |
161 | + | |
162 | + default: | |
163 | + dolog ("Internal logic error: Bad audio format %d\n", fmt); | |
164 | +#ifdef DEBUG_AUDIO | |
165 | + abort (); | |
166 | +#endif | |
167 | + return SND_PCM_FORMAT_U8; | |
168 | + } | |
169 | +} | |
170 | + | |
171 | +static int alsa_to_audfmt (int alsafmt, audfmt_e *fmt, int *endianness) | |
172 | +{ | |
173 | + switch (alsafmt) { | |
174 | + case SND_PCM_FORMAT_S8: | |
175 | + *endianness = 0; | |
176 | + *fmt = AUD_FMT_S8; | |
177 | + break; | |
178 | + | |
179 | + case SND_PCM_FORMAT_U8: | |
180 | + *endianness = 0; | |
181 | + *fmt = AUD_FMT_U8; | |
182 | + break; | |
183 | + | |
184 | + case SND_PCM_FORMAT_S16_LE: | |
185 | + *endianness = 0; | |
186 | + *fmt = AUD_FMT_S16; | |
187 | + break; | |
188 | + | |
189 | + case SND_PCM_FORMAT_U16_LE: | |
190 | + *endianness = 0; | |
191 | + *fmt = AUD_FMT_U16; | |
192 | + break; | |
193 | + | |
194 | + case SND_PCM_FORMAT_S16_BE: | |
195 | + *endianness = 1; | |
196 | + *fmt = AUD_FMT_S16; | |
197 | + break; | |
198 | + | |
199 | + case SND_PCM_FORMAT_U16_BE: | |
200 | + *endianness = 1; | |
201 | + *fmt = AUD_FMT_U16; | |
202 | + break; | |
203 | + | |
204 | + default: | |
205 | + dolog ("Unrecognized audio format %d\n", alsafmt); | |
206 | + return -1; | |
207 | + } | |
208 | + | |
209 | + return 0; | |
210 | +} | |
211 | + | |
212 | +#ifdef DEBUG_MISMATCHES | |
213 | +static void alsa_dump_info (struct alsa_params_req *req, | |
214 | + struct alsa_params_obt *obt) | |
215 | +{ | |
216 | + dolog ("parameter | requested value | obtained value\n"); | |
217 | + dolog ("format | %10d | %10d\n", req->fmt, obt->fmt); | |
218 | + dolog ("channels | %10d | %10d\n", | |
219 | + req->nchannels, obt->nchannels); | |
220 | + dolog ("frequency | %10d | %10d\n", req->freq, obt->freq); | |
221 | + dolog ("============================================\n"); | |
222 | + dolog ("requested: buffer size %d period size %d\n", | |
223 | + req->buffer_size, req->period_size); | |
224 | + dolog ("obtained: buffer size %ld\n", obt->buffer_size); | |
225 | +} | |
226 | +#endif | |
227 | + | |
228 | +static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold) | |
229 | +{ | |
230 | + int err; | |
231 | + snd_pcm_sw_params_t *sw_params; | |
232 | + | |
233 | + snd_pcm_sw_params_alloca (&sw_params); | |
234 | + | |
235 | + err = snd_pcm_sw_params_current (handle, sw_params); | |
236 | + if (err < 0) { | |
237 | + dolog ("Can not fully initialize DAC\n"); | |
238 | + alsa_logerr (err, "Failed to get current software parameters\n"); | |
239 | + return; | |
240 | + } | |
241 | + | |
242 | + err = snd_pcm_sw_params_set_start_threshold (handle, sw_params, threshold); | |
243 | + if (err < 0) { | |
244 | + dolog ("Can not fully initialize DAC\n"); | |
245 | + alsa_logerr (err, "Failed to set software threshold to %ld\n", | |
246 | + threshold); | |
247 | + return; | |
248 | + } | |
249 | + | |
250 | + err = snd_pcm_sw_params (handle, sw_params); | |
251 | + if (err < 0) { | |
252 | + dolog ("Can not fully initialize DAC\n"); | |
253 | + alsa_logerr (err, "Failed to set software parameters\n"); | |
254 | + return; | |
255 | + } | |
256 | +} | |
257 | + | |
258 | +static int alsa_open (int in, struct alsa_params_req *req, | |
259 | + struct alsa_params_obt *obt, snd_pcm_t **handlep) | |
260 | +{ | |
261 | + snd_pcm_t *handle; | |
262 | + snd_pcm_hw_params_t *hw_params; | |
263 | + int err, freq, nchannels; | |
264 | + const char *pcm_name = in ? conf.pcm_name_in : conf.pcm_name_out; | |
265 | + unsigned int period_size, buffer_size; | |
266 | + snd_pcm_uframes_t obt_buffer_size; | |
267 | + const char *typ = in ? "ADC" : "DAC"; | |
268 | + | |
269 | + freq = req->freq; | |
270 | + period_size = req->period_size; | |
271 | + buffer_size = req->buffer_size; | |
272 | + nchannels = req->nchannels; | |
273 | + | |
274 | + snd_pcm_hw_params_alloca (&hw_params); | |
275 | + | |
276 | + err = snd_pcm_open ( | |
277 | + &handle, | |
278 | + pcm_name, | |
279 | + in ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, | |
280 | + SND_PCM_NONBLOCK | |
281 | + ); | |
282 | + if (err < 0) { | |
283 | + alsa_logerr2 (err, typ, "Failed to open `%s':\n", pcm_name); | |
284 | + return -1; | |
285 | + } | |
286 | + | |
287 | + err = snd_pcm_hw_params_any (handle, hw_params); | |
288 | + if (err < 0) { | |
289 | + alsa_logerr2 (err, typ, "Failed to initialize hardware parameters\n"); | |
290 | + goto err; | |
291 | + } | |
292 | + | |
293 | + err = snd_pcm_hw_params_set_access ( | |
294 | + handle, | |
295 | + hw_params, | |
296 | + SND_PCM_ACCESS_RW_INTERLEAVED | |
297 | + ); | |
298 | + if (err < 0) { | |
299 | + alsa_logerr2 (err, typ, "Failed to set access type\n"); | |
300 | + goto err; | |
301 | + } | |
302 | + | |
303 | + err = snd_pcm_hw_params_set_format (handle, hw_params, req->fmt); | |
304 | + if (err < 0) { | |
305 | + alsa_logerr2 (err, typ, "Failed to set format %d\n", req->fmt); | |
306 | + goto err; | |
307 | + } | |
308 | + | |
309 | + err = snd_pcm_hw_params_set_rate_near (handle, hw_params, &freq, 0); | |
310 | + if (err < 0) { | |
311 | + alsa_logerr2 (err, typ, "Failed to set frequency %d\n", req->freq); | |
312 | + goto err; | |
313 | + } | |
314 | + | |
315 | + err = snd_pcm_hw_params_set_channels_near ( | |
316 | + handle, | |
317 | + hw_params, | |
318 | + &nchannels | |
319 | + ); | |
320 | + if (err < 0) { | |
321 | + alsa_logerr2 (err, typ, "Failed to set number of channels %d\n", | |
322 | + req->nchannels); | |
323 | + goto err; | |
324 | + } | |
325 | + | |
326 | + if (nchannels != 1 && nchannels != 2) { | |
327 | + alsa_logerr2 (err, typ, | |
328 | + "Can not handle obtained number of channels %d\n", | |
329 | + nchannels); | |
330 | + goto err; | |
331 | + } | |
332 | + | |
333 | + if (!((in && conf.size_in_usec_in) || (!in && conf.size_in_usec_out))) { | |
334 | + if (!buffer_size) { | |
335 | + buffer_size = DEFAULT_BUFFER_SIZE; | |
336 | + period_size= DEFAULT_PERIOD_SIZE; | |
337 | + } | |
338 | + } | |
339 | + | |
340 | + if (buffer_size) { | |
341 | + if ((in && conf.size_in_usec_in) || (!in && conf.size_in_usec_out)) { | |
342 | + if (period_size) { | |
343 | + err = snd_pcm_hw_params_set_period_time_near ( | |
344 | + handle, | |
345 | + hw_params, | |
346 | + &period_size, | |
347 | + 0); | |
348 | + if (err < 0) { | |
349 | + alsa_logerr2 (err, typ, | |
350 | + "Failed to set period time %d\n", | |
351 | + req->period_size); | |
352 | + goto err; | |
353 | + } | |
354 | + } | |
355 | + | |
356 | + err = snd_pcm_hw_params_set_buffer_time_near ( | |
357 | + handle, | |
358 | + hw_params, | |
359 | + &buffer_size, | |
360 | + 0); | |
361 | + | |
362 | + if (err < 0) { | |
363 | + alsa_logerr2 (err, typ, | |
364 | + "Failed to set buffer time %d\n", | |
365 | + req->buffer_size); | |
366 | + goto err; | |
367 | + } | |
368 | + } | |
369 | + else { | |
370 | + int dir; | |
371 | + snd_pcm_uframes_t minval; | |
372 | + | |
373 | + if (period_size) { | |
374 | + minval = period_size; | |
375 | + dir = 0; | |
376 | + | |
377 | + err = snd_pcm_hw_params_get_period_size_min ( | |
378 | + hw_params, | |
379 | + &minval, | |
380 | + &dir | |
381 | + ); | |
382 | + if (err < 0) { | |
383 | + alsa_logerr ( | |
384 | + err, | |
385 | + "Can not get minmal period size for %s\n", | |
386 | + typ | |
387 | + ); | |
388 | + } | |
389 | + else { | |
390 | + if (period_size < minval) { | |
391 | + if ((in && conf.period_size_in_overriden) | |
392 | + || (!in && conf.period_size_out_overriden)) { | |
393 | + dolog ("%s period size(%d) is less " | |
394 | + "than minmal period size(%ld)\n", | |
395 | + typ, | |
396 | + period_size, | |
397 | + minval); | |
398 | + } | |
399 | + period_size = minval; | |
400 | + } | |
401 | + } | |
402 | + | |
403 | + err = snd_pcm_hw_params_set_period_size ( | |
404 | + handle, | |
405 | + hw_params, | |
406 | + period_size, | |
407 | + 0 | |
408 | + ); | |
409 | + if (err < 0) { | |
410 | + alsa_logerr2 (err, typ, "Failed to set period size %d\n", | |
411 | + req->period_size); | |
412 | + goto err; | |
413 | + } | |
414 | + } | |
415 | + | |
416 | + minval = buffer_size; | |
417 | + err = snd_pcm_hw_params_get_buffer_size_min ( | |
418 | + hw_params, | |
419 | + &minval | |
420 | + ); | |
421 | + if (err < 0) { | |
422 | + alsa_logerr (err, "Can not get minmal buffer size for %s\n", | |
423 | + typ); | |
424 | + } | |
425 | + else { | |
426 | + if (buffer_size < minval) { | |
427 | + if ((in && conf.buffer_size_in_overriden) | |
428 | + || (!in && conf.buffer_size_out_overriden)) { | |
429 | + dolog ( | |
430 | + "%s buffer size(%d) is less " | |
431 | + "than minimal buffer size(%ld)\n", | |
432 | + typ, | |
433 | + buffer_size, | |
434 | + minval | |
435 | + ); | |
436 | + } | |
437 | + buffer_size = minval; | |
438 | + } | |
439 | + } | |
440 | + | |
441 | + err = snd_pcm_hw_params_set_buffer_size ( | |
442 | + handle, | |
443 | + hw_params, | |
444 | + buffer_size | |
445 | + ); | |
446 | + if (err < 0) { | |
447 | + alsa_logerr2 (err, typ, "Failed to set buffer size %d\n", | |
448 | + req->buffer_size); | |
449 | + goto err; | |
450 | + } | |
451 | + } | |
452 | + } | |
453 | + else { | |
454 | + dolog ("warning: buffer size is not set\n"); | |
455 | + } | |
456 | + | |
457 | + err = snd_pcm_hw_params (handle, hw_params); | |
458 | + if (err < 0) { | |
459 | + alsa_logerr2 (err, typ, "Failed to apply audio parameters\n"); | |
460 | + goto err; | |
461 | + } | |
462 | + | |
463 | + err = snd_pcm_hw_params_get_buffer_size (hw_params, &obt_buffer_size); | |
464 | + if (err < 0) { | |
465 | + alsa_logerr2 (err, typ, "Failed to get buffer size\n"); | |
466 | + goto err; | |
467 | + } | |
468 | + | |
469 | + err = snd_pcm_prepare (handle); | |
470 | + if (err < 0) { | |
471 | + alsa_logerr2 (err, typ, "Can not prepare handle %p\n", handle); | |
472 | + goto err; | |
473 | + } | |
474 | + | |
475 | + obt->can_pause = snd_pcm_hw_params_can_pause (hw_params); | |
476 | + if (obt->can_pause < 0) { | |
477 | + alsa_logerr (err, "Can not get pause capability for %s\n", typ); | |
478 | + obt->can_pause = 0; | |
479 | + } | |
480 | + | |
481 | + if (!in && conf.threshold) { | |
482 | + snd_pcm_uframes_t threshold; | |
483 | + int bytes_per_sec; | |
484 | + | |
485 | + bytes_per_sec = freq | |
486 | + << (nchannels == 2) | |
487 | + << (req->fmt == AUD_FMT_S16 || req->fmt == AUD_FMT_U16); | |
488 | + | |
489 | + threshold = (conf.threshold * bytes_per_sec) / 1000; | |
490 | + alsa_set_threshold (handle, threshold); | |
491 | + } | |
492 | + | |
493 | + obt->fmt = req->fmt; | |
494 | + obt->nchannels = nchannels; | |
495 | + obt->freq = freq; | |
496 | + obt->buffer_size = snd_pcm_frames_to_bytes (handle, obt_buffer_size); | |
497 | + *handlep = handle; | |
498 | + | |
499 | + if (obt->fmt != req->fmt || | |
500 | + obt->nchannels != req->nchannels || | |
501 | + obt->freq != req->freq) { | |
502 | +#ifdef DEBUG_MISMATCHES | |
503 | + dolog ("Audio paramters mismatch for %s\n", typ); | |
504 | + alsa_dump_info (req, obt); | |
505 | +#endif | |
506 | + } | |
507 | + | |
508 | +#ifdef DEBUG | |
509 | + alsa_dump_info (req, obt); | |
510 | +#endif | |
511 | + return 0; | |
512 | + | |
513 | + err: | |
514 | + alsa_anal_close (&handle); | |
515 | + return -1; | |
516 | +} | |
517 | + | |
518 | +static int alsa_recover (snd_pcm_t *handle) | |
519 | +{ | |
520 | + int err = snd_pcm_prepare (handle); | |
521 | + if (err < 0) { | |
522 | + alsa_logerr (err, "Failed to prepare handle %p\n", handle); | |
523 | + return -1; | |
524 | + } | |
525 | + return 0; | |
526 | +} | |
527 | + | |
528 | +static int alsa_run_out (HWVoiceOut *hw) | |
529 | +{ | |
530 | + ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; | |
531 | + int rpos, live, decr; | |
532 | + int samples; | |
533 | + uint8_t *dst; | |
534 | + st_sample_t *src; | |
535 | + snd_pcm_sframes_t avail; | |
536 | + | |
537 | + live = audio_pcm_hw_get_live_out (hw); | |
538 | + if (!live) { | |
539 | + return 0; | |
540 | + } | |
541 | + | |
542 | + avail = snd_pcm_avail_update (alsa->handle); | |
543 | + if (avail < 0) { | |
544 | + if (avail == -EPIPE) { | |
545 | + if (!alsa_recover (alsa->handle)) { | |
546 | + avail = snd_pcm_avail_update (alsa->handle); | |
547 | + if (avail >= 0) { | |
548 | + goto ok; | |
549 | + } | |
550 | + } | |
551 | + } | |
552 | + | |
553 | + alsa_logerr (avail, "Can not get amount free space\n"); | |
554 | + return 0; | |
555 | + } | |
556 | + | |
557 | + ok: | |
558 | + decr = audio_MIN (live, avail); | |
559 | + samples = decr; | |
560 | + rpos = hw->rpos; | |
561 | + while (samples) { | |
562 | + int left_till_end_samples = hw->samples - rpos; | |
563 | + int convert_samples = audio_MIN (samples, left_till_end_samples); | |
564 | + snd_pcm_sframes_t written; | |
565 | + | |
566 | + src = hw->mix_buf + rpos; | |
567 | + dst = advance (alsa->pcm_buf, rpos << hw->info.shift); | |
568 | + | |
569 | + hw->clip (dst, src, convert_samples); | |
570 | + | |
571 | + again: | |
572 | + written = snd_pcm_writei (alsa->handle, dst, convert_samples); | |
573 | + | |
574 | + if (written < 0) { | |
575 | + switch (written) { | |
576 | + case -EPIPE: | |
577 | + if (!alsa_recover (alsa->handle)) { | |
578 | + goto again; | |
579 | + } | |
580 | + dolog ( | |
581 | + "Failed to write %d frames to %p, handle %p not prepared\n", | |
582 | + convert_samples, | |
583 | + dst, | |
584 | + alsa->handle | |
585 | + ); | |
586 | + goto exit; | |
587 | + | |
588 | + case -EAGAIN: | |
589 | + goto again; | |
590 | + | |
591 | + default: | |
592 | + alsa_logerr (written, "Failed to write %d frames to %p\n", | |
593 | + convert_samples, dst); | |
594 | + goto exit; | |
595 | + } | |
596 | + } | |
597 | + | |
598 | + mixeng_clear (src, written); | |
599 | + rpos = (rpos + written) % hw->samples; | |
600 | + samples -= written; | |
601 | + } | |
602 | + | |
603 | + exit: | |
604 | + hw->rpos = rpos; | |
605 | + return decr; | |
606 | +} | |
607 | + | |
608 | +static void alsa_fini_out (HWVoiceOut *hw) | |
609 | +{ | |
610 | + ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; | |
611 | + | |
612 | + ldebug ("alsa_fini\n"); | |
613 | + alsa_anal_close (&alsa->handle); | |
614 | + | |
615 | + if (alsa->pcm_buf) { | |
616 | + qemu_free (alsa->pcm_buf); | |
617 | + alsa->pcm_buf = NULL; | |
618 | + } | |
619 | +} | |
620 | + | |
621 | +static int alsa_init_out (HWVoiceOut *hw, int freq, int nchannels, audfmt_e fmt) | |
622 | +{ | |
623 | + ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; | |
624 | + struct alsa_params_req req; | |
625 | + struct alsa_params_obt obt; | |
626 | + audfmt_e effective_fmt; | |
627 | + int endianness; | |
628 | + int err; | |
629 | + snd_pcm_t *handle; | |
630 | + | |
631 | + req.fmt = aud_to_alsafmt (fmt); | |
632 | + req.freq = freq; | |
633 | + req.nchannels = nchannels; | |
634 | + req.period_size = conf.period_size_out; | |
635 | + req.buffer_size = conf.buffer_size_out; | |
636 | + | |
637 | + if (alsa_open (0, &req, &obt, &handle)) { | |
638 | + return -1; | |
639 | + } | |
640 | + | |
641 | + err = alsa_to_audfmt (obt.fmt, &effective_fmt, &endianness); | |
642 | + if (err) { | |
643 | + alsa_anal_close (&handle); | |
644 | + return -1; | |
645 | + } | |
646 | + | |
647 | + audio_pcm_init_info ( | |
648 | + &hw->info, | |
649 | + obt.freq, | |
650 | + obt.nchannels, | |
651 | + effective_fmt, | |
652 | + audio_need_to_swap_endian (endianness) | |
653 | + ); | |
654 | + alsa->can_pause = obt.can_pause; | |
655 | + hw->bufsize = obt.buffer_size; | |
656 | + | |
657 | + alsa->pcm_buf = qemu_mallocz (hw->bufsize); | |
658 | + if (!alsa->pcm_buf) { | |
659 | + alsa_anal_close (&handle); | |
660 | + return -1; | |
661 | + } | |
662 | + | |
663 | + alsa->handle = handle; | |
664 | + alsa->was_enabled = 0; | |
665 | + return 0; | |
666 | +} | |
667 | + | |
668 | +static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...) | |
669 | +{ | |
670 | + int err; | |
671 | + ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; | |
672 | + | |
673 | + switch (cmd) { | |
674 | + case VOICE_ENABLE: | |
675 | + ldebug ("enabling voice\n"); | |
676 | + audio_pcm_info_clear_buf (&hw->info, alsa->pcm_buf, hw->samples); | |
677 | + if (alsa->can_pause) { | |
678 | + /* Why this was_enabled madness is needed at all?? */ | |
679 | + if (alsa->was_enabled) { | |
680 | + err = snd_pcm_pause (alsa->handle, 0); | |
681 | + if (err < 0) { | |
682 | + alsa_logerr (err, "Failed to resume playing\n"); | |
683 | + /* not fatal really */ | |
684 | + } | |
685 | + } | |
686 | + else { | |
687 | + alsa->was_enabled = 1; | |
688 | + } | |
689 | + } | |
690 | + break; | |
691 | + | |
692 | + case VOICE_DISABLE: | |
693 | + ldebug ("disabling voice\n"); | |
694 | + if (alsa->can_pause) { | |
695 | + err = snd_pcm_pause (alsa->handle, 1); | |
696 | + if (err < 0) { | |
697 | + alsa_logerr (err, "Failed to stop playing\n"); | |
698 | + /* not fatal really */ | |
699 | + } | |
700 | + } | |
701 | + break; | |
702 | + } | |
703 | + return 0; | |
704 | +} | |
705 | + | |
706 | +static int alsa_init_in (HWVoiceIn *hw, | |
707 | + int freq, int nchannels, audfmt_e fmt) | |
708 | +{ | |
709 | + ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; | |
710 | + struct alsa_params_req req; | |
711 | + struct alsa_params_obt obt; | |
712 | + int endianness; | |
713 | + int err; | |
714 | + audfmt_e effective_fmt; | |
715 | + snd_pcm_t *handle; | |
716 | + | |
717 | + req.fmt = aud_to_alsafmt (fmt); | |
718 | + req.freq = freq; | |
719 | + req.nchannels = nchannels; | |
720 | + req.period_size = conf.period_size_in; | |
721 | + req.buffer_size = conf.buffer_size_in; | |
722 | + | |
723 | + if (alsa_open (1, &req, &obt, &handle)) { | |
724 | + return -1; | |
725 | + } | |
726 | + | |
727 | + err = alsa_to_audfmt (obt.fmt, &effective_fmt, &endianness); | |
728 | + if (err) { | |
729 | + alsa_anal_close (&handle); | |
730 | + return -1; | |
731 | + } | |
732 | + | |
733 | + audio_pcm_init_info ( | |
734 | + &hw->info, | |
735 | + obt.freq, | |
736 | + obt.nchannels, | |
737 | + effective_fmt, | |
738 | + audio_need_to_swap_endian (endianness) | |
739 | + ); | |
740 | + alsa->can_pause = obt.can_pause; | |
741 | + hw->bufsize = obt.buffer_size; | |
742 | + alsa->pcm_buf = qemu_mallocz (hw->bufsize); | |
743 | + if (!alsa->pcm_buf) { | |
744 | + alsa_anal_close (&handle); | |
745 | + return -1; | |
746 | + } | |
747 | + | |
748 | + alsa->handle = handle; | |
749 | + return 0; | |
750 | +} | |
751 | + | |
752 | +static void alsa_fini_in (HWVoiceIn *hw) | |
753 | +{ | |
754 | + ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; | |
755 | + | |
756 | + alsa_anal_close (&alsa->handle); | |
757 | + | |
758 | + if (alsa->pcm_buf) { | |
759 | + qemu_free (alsa->pcm_buf); | |
760 | + alsa->pcm_buf = NULL; | |
761 | + } | |
762 | +} | |
763 | + | |
764 | +static int alsa_run_in (HWVoiceIn *hw) | |
765 | +{ | |
766 | + ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; | |
767 | + int hwshift = hw->info.shift; | |
768 | + int i; | |
769 | + int live = audio_pcm_hw_get_live_in (hw); | |
770 | + int dead = hw->samples - live; | |
771 | + struct { | |
772 | + int add; | |
773 | + int len; | |
774 | + } bufs[2] = { | |
775 | + { hw->wpos, 0 }, | |
776 | + { 0, 0 } | |
777 | + }; | |
778 | + | |
779 | + snd_pcm_uframes_t read_samples = 0; | |
780 | + | |
781 | + if (!dead) { | |
782 | + return 0; | |
783 | + } | |
784 | + | |
785 | + if (hw->wpos + dead > hw->samples) { | |
786 | + bufs[0].len = (hw->samples - hw->wpos); | |
787 | + bufs[1].len = (dead - (hw->samples - hw->wpos)); | |
788 | + } | |
789 | + else { | |
790 | + bufs[0].len = dead; | |
791 | + } | |
792 | + | |
793 | + | |
794 | + for (i = 0; i < 2; ++i) { | |
795 | + void *src; | |
796 | + st_sample_t *dst; | |
797 | + snd_pcm_sframes_t nread; | |
798 | + snd_pcm_uframes_t len; | |
799 | + | |
800 | + len = bufs[i].len; | |
801 | + | |
802 | + src = advance (alsa->pcm_buf, bufs[i].add << hwshift); | |
803 | + dst = hw->conv_buf + bufs[i].add; | |
804 | + | |
805 | + while (len) { | |
806 | + nread = snd_pcm_readi (alsa->handle, src, len); | |
807 | + | |
808 | + if (nread < 0) { | |
809 | + switch (nread) { | |
810 | + case -EPIPE: | |
811 | + if (!alsa_recover (alsa->handle)) { | |
812 | + continue; | |
813 | + } | |
814 | + dolog ( | |
815 | + "Failed to read %ld frames from %p, " | |
816 | + "handle %p not prepared\n", | |
817 | + len, | |
818 | + src, | |
819 | + alsa->handle | |
820 | + ); | |
821 | + goto exit; | |
822 | + | |
823 | + case -EAGAIN: | |
824 | + continue; | |
825 | + | |
826 | + default: | |
827 | + alsa_logerr ( | |
828 | + nread, | |
829 | + "Failed to read %ld frames from %p\n", | |
830 | + len, | |
831 | + src | |
832 | + ); | |
833 | + goto exit; | |
834 | + } | |
835 | + } | |
836 | + | |
837 | + hw->conv (dst, src, nread, &nominal_volume); | |
838 | + | |
839 | + src = advance (src, nread << hwshift); | |
840 | + dst += nread; | |
841 | + | |
842 | + read_samples += nread; | |
843 | + len -= nread; | |
844 | + } | |
845 | + } | |
846 | + | |
847 | + exit: | |
848 | + hw->wpos = (hw->wpos + read_samples) % hw->samples; | |
849 | + return read_samples; | |
850 | +} | |
851 | + | |
852 | +static int alsa_read (SWVoiceIn *sw, void *buf, int size) | |
853 | +{ | |
854 | + return audio_pcm_sw_read (sw, buf, size); | |
855 | +} | |
856 | + | |
857 | +static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...) | |
858 | +{ | |
859 | + (void) hw; | |
860 | + (void) cmd; | |
861 | + return 0; | |
862 | +} | |
863 | + | |
864 | +static void *alsa_audio_init (void) | |
865 | +{ | |
866 | + return &conf; | |
867 | +} | |
868 | + | |
869 | +static void alsa_audio_fini (void *opaque) | |
870 | +{ | |
871 | + (void) opaque; | |
872 | +} | |
873 | + | |
874 | +static struct audio_option alsa_options[] = { | |
875 | + {"DAC_SIZE_IN_USEC", AUD_OPT_BOOL, &conf.size_in_usec_out, | |
876 | + "DAC period/buffer size in microseconds (otherwise in frames)", NULL, 0}, | |
877 | + {"DAC_PERIOD_SIZE", AUD_OPT_INT, &conf.period_size_out, | |
878 | + "DAC period size", &conf.period_size_out_overriden, 0}, | |
879 | + {"DAC_BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_size_out, | |
880 | + "DAC buffer size", &conf.buffer_size_out_overriden, 0}, | |
881 | + | |
882 | + {"ADC_SIZE_IN_USEC", AUD_OPT_BOOL, &conf.size_in_usec_in, | |
883 | + "ADC period/buffer size in microseconds (otherwise in frames)", NULL, 0}, | |
884 | + {"ADC_PERIOD_SIZE", AUD_OPT_INT, &conf.period_size_in, | |
885 | + "ADC period size", &conf.period_size_in_overriden, 0}, | |
886 | + {"ADC_BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_size_in, | |
887 | + "ADC buffer size", &conf.buffer_size_in_overriden, 0}, | |
888 | + | |
889 | + {"THRESHOLD", AUD_OPT_INT, &conf.threshold, | |
890 | + "(undocumented)", NULL, 0}, | |
891 | + | |
892 | + {"DAC_DEV", AUD_OPT_STR, &conf.pcm_name_out, | |
893 | + "DAC device name (for instance dmix)", NULL, 0}, | |
894 | + | |
895 | + {"ADC_DEV", AUD_OPT_STR, &conf.pcm_name_in, | |
896 | + "ADC device name", NULL, 0}, | |
897 | + {NULL, 0, NULL, NULL, NULL, 0} | |
898 | +}; | |
899 | + | |
900 | +static struct audio_pcm_ops alsa_pcm_ops = { | |
901 | + alsa_init_out, | |
902 | + alsa_fini_out, | |
903 | + alsa_run_out, | |
904 | + alsa_write, | |
905 | + alsa_ctl_out, | |
906 | + | |
907 | + alsa_init_in, | |
908 | + alsa_fini_in, | |
909 | + alsa_run_in, | |
910 | + alsa_read, | |
911 | + alsa_ctl_in | |
912 | +}; | |
913 | + | |
914 | +struct audio_driver alsa_audio_driver = { | |
915 | + INIT_FIELD (name = ) "alsa", | |
916 | + INIT_FIELD (descr = ) "ALSA http://www.alsa-project.org", | |
917 | + INIT_FIELD (options = ) alsa_options, | |
918 | + INIT_FIELD (init = ) alsa_audio_init, | |
919 | + INIT_FIELD (fini = ) alsa_audio_fini, | |
920 | + INIT_FIELD (pcm_ops = ) &alsa_pcm_ops, | |
921 | + INIT_FIELD (can_be_default = ) 1, | |
922 | + INIT_FIELD (max_voices_out = ) INT_MAX, | |
923 | + INIT_FIELD (max_voices_in = ) INT_MAX, | |
924 | + INIT_FIELD (voice_size_out = ) sizeof (ALSAVoiceOut), | |
925 | + INIT_FIELD (voice_size_in = ) sizeof (ALSAVoiceIn) | |
926 | +}; | ... | ... |
audio/audio.c
1 | 1 | /* |
2 | 2 | * QEMU Audio subsystem |
3 | - * | |
4 | - * Copyright (c) 2003-2004 Vassili Karpov (malc) | |
5 | - * | |
3 | + * | |
4 | + * Copyright (c) 2003-2005 Vassili Karpov (malc) | |
5 | + * | |
6 | 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
7 | 7 | * of this software and associated documentation files (the "Software"), to deal |
8 | 8 | * in the Software without restriction, including without limitation the rights |
... | ... | @@ -21,34 +21,78 @@ |
21 | 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
22 | 22 | * THE SOFTWARE. |
23 | 23 | */ |
24 | -#include <assert.h> | |
25 | 24 | #include "vl.h" |
26 | 25 | |
27 | -#define USE_WAV_AUDIO | |
26 | +#define AUDIO_CAP "audio" | |
27 | +#include "audio_int.h" | |
28 | 28 | |
29 | -#include "audio/audio_int.h" | |
29 | +static void audio_pcm_hw_fini_in (HWVoiceIn *hw); | |
30 | +static void audio_pcm_hw_fini_out (HWVoiceOut *hw); | |
30 | 31 | |
31 | -#define dolog(...) AUD_log ("audio", __VA_ARGS__) | |
32 | -#ifdef DEBUG | |
33 | -#define ldebug(...) dolog (__VA_ARGS__) | |
34 | -#else | |
35 | -#define ldebug(...) | |
36 | -#endif | |
32 | +static LIST_HEAD (hw_in_listhead, HWVoiceIn) hw_head_in; | |
33 | +static LIST_HEAD (hw_out_listhead, HWVoiceOut) hw_head_out; | |
37 | 34 | |
38 | -#define QC_AUDIO_DRV "QEMU_AUDIO_DRV" | |
39 | -#define QC_VOICES "QEMU_VOICES" | |
40 | -#define QC_FIXED_FORMAT "QEMU_FIXED_FORMAT" | |
41 | -#define QC_FIXED_FREQ "QEMU_FIXED_FREQ" | |
35 | +/* #define DEBUG_PLIVE */ | |
36 | +/* #define DEBUG_LIVE */ | |
37 | +/* #define DEBUG_OUT */ | |
42 | 38 | |
43 | -static HWVoice *hw_voices; | |
39 | +static struct audio_driver *drvtab[] = { | |
40 | +#ifdef CONFIG_OSS | |
41 | + &oss_audio_driver, | |
42 | +#endif | |
43 | +#ifdef CONFIG_ALSA | |
44 | + &alsa_audio_driver, | |
45 | +#endif | |
46 | +#ifdef CONFIG_COREAUDIO | |
47 | + &coreaudio_audio_driver, | |
48 | +#endif | |
49 | +#ifdef CONFIG_DSOUND | |
50 | + &dsound_audio_driver, | |
51 | +#endif | |
52 | +#ifdef CONFIG_FMOD | |
53 | + &fmod_audio_driver, | |
54 | +#endif | |
55 | +#ifdef CONFIG_SDL | |
56 | + &sdl_audio_driver, | |
57 | +#endif | |
58 | + &no_audio_driver, | |
59 | + &wav_audio_driver | |
60 | +}; | |
44 | 61 | |
45 | 62 | AudioState audio_state = { |
63 | + /* Out */ | |
64 | + 1, /* use fixed settings */ | |
65 | + 44100, /* fixed frequency */ | |
66 | + 2, /* fixed channels */ | |
67 | + AUD_FMT_S16, /* fixed format */ | |
68 | + 1, /* number of hw voices */ | |
69 | + 1, /* greedy */ | |
70 | + | |
71 | + /* In */ | |
46 | 72 | 1, /* use fixed settings */ |
47 | 73 | 44100, /* fixed frequency */ |
48 | 74 | 2, /* fixed channels */ |
49 | 75 | AUD_FMT_S16, /* fixed format */ |
50 | 76 | 1, /* number of hw voices */ |
51 | - -1 /* voice size */ | |
77 | + 1, /* greedy */ | |
78 | + | |
79 | + NULL, /* driver opaque */ | |
80 | + NULL, /* driver */ | |
81 | + | |
82 | + NULL, /* timer handle */ | |
83 | + { 0 }, /* period */ | |
84 | + 0 /* plive */ | |
85 | +}; | |
86 | + | |
87 | +volume_t nominal_volume = { | |
88 | + 0, | |
89 | +#ifdef FLOAT_MIXENG | |
90 | + 1.0, | |
91 | + 1.0 | |
92 | +#else | |
93 | + UINT_MAX, | |
94 | + UINT_MAX | |
95 | +#endif | |
52 | 96 | }; |
53 | 97 | |
54 | 98 | /* http://www.df.lth.se/~john_e/gems/gem002d.html */ |
... | ... | @@ -68,275 +112,361 @@ inline uint32_t lsbindex (uint32_t u) |
68 | 112 | return popcount ((u&-u)-1); |
69 | 113 | } |
70 | 114 | |
71 | -int audio_get_conf_int (const char *key, int defval) | |
115 | +#ifdef AUDIO_IS_FLAWLESS_AND_NO_CHECKS_ARE_REQURIED | |
116 | +#error No its not | |
117 | +#else | |
118 | +int audio_bug (const char *funcname, int cond) | |
72 | 119 | { |
73 | - int val = defval; | |
74 | - char *strval; | |
75 | - | |
76 | - strval = getenv (key); | |
77 | - if (strval) { | |
78 | - val = atoi (strval); | |
120 | + if (cond) { | |
121 | + static int shown; | |
122 | + | |
123 | + AUD_log (NULL, "Error a bug that was just triggered in %s\n", funcname); | |
124 | + if (!shown) { | |
125 | + shown = 1; | |
126 | + AUD_log (NULL, "Save all your work and restart without audio\n"); | |
127 | + AUD_log (NULL, "Please send bug report to malc@pulsesoft.com\n"); | |
128 | + AUD_log (NULL, "I am sorry\n"); | |
129 | + } | |
130 | + AUD_log (NULL, "Context:\n"); | |
131 | + | |
132 | +#if defined AUDIO_BREAKPOINT_ON_BUG | |
133 | +# if defined HOST_I386 | |
134 | +# if defined __GNUC__ | |
135 | + __asm__ ("int3"); | |
136 | +# elif defined _MSC_VER | |
137 | + _asm _emit 0xcc; | |
138 | +# else | |
139 | + abort (); | |
140 | +# endif | |
141 | +# else | |
142 | + abort (); | |
143 | +# endif | |
144 | +#endif | |
79 | 145 | } |
80 | 146 | |
81 | - return val; | |
147 | + return cond; | |
82 | 148 | } |
149 | +#endif | |
83 | 150 | |
84 | -const char *audio_get_conf_str (const char *key, const char *defval) | |
151 | +static char *audio_alloc_prefix (const char *s) | |
85 | 152 | { |
86 | - const char *val = getenv (key); | |
87 | - if (!val) | |
88 | - return defval; | |
89 | - else | |
90 | - return val; | |
91 | -} | |
153 | + const char qemu_prefix[] = "QEMU_"; | |
154 | + size_t len; | |
155 | + char *r; | |
92 | 156 | |
93 | -void AUD_log (const char *cap, const char *fmt, ...) | |
94 | -{ | |
95 | - va_list ap; | |
96 | - fprintf (stderr, "%s: ", cap); | |
97 | - va_start (ap, fmt); | |
98 | - vfprintf (stderr, fmt, ap); | |
99 | - va_end (ap); | |
100 | -} | |
157 | + if (!s) { | |
158 | + return NULL; | |
159 | + } | |
101 | 160 | |
102 | -/* | |
103 | - * Soft Voice | |
104 | - */ | |
105 | -void pcm_sw_free_resources (SWVoice *sw) | |
106 | -{ | |
107 | - if (sw->buf) qemu_free (sw->buf); | |
108 | - if (sw->rate) st_rate_stop (sw->rate); | |
109 | - sw->buf = NULL; | |
110 | - sw->rate = NULL; | |
111 | -} | |
161 | + len = strlen (s); | |
162 | + r = qemu_malloc (len + sizeof (qemu_prefix)); | |
112 | 163 | |
113 | -int pcm_sw_alloc_resources (SWVoice *sw) | |
114 | -{ | |
115 | - sw->buf = qemu_mallocz (sw->hw->samples * sizeof (st_sample_t)); | |
116 | - if (!sw->buf) | |
117 | - return -1; | |
164 | + if (r) { | |
165 | + size_t i; | |
166 | + char *u = r + sizeof (qemu_prefix) - 1; | |
118 | 167 | |
119 | - sw->rate = st_rate_start (sw->freq, sw->hw->freq); | |
120 | - if (!sw->rate) { | |
121 | - qemu_free (sw->buf); | |
122 | - sw->buf = NULL; | |
123 | - return -1; | |
168 | + strcpy (r, qemu_prefix); | |
169 | + strcat (r, s); | |
170 | + | |
171 | + for (i = 0; i < len; ++i) { | |
172 | + u[i] = toupper (u[i]); | |
173 | + } | |
124 | 174 | } |
125 | - return 0; | |
175 | + return r; | |
126 | 176 | } |
127 | 177 | |
128 | -void pcm_sw_fini (SWVoice *sw) | |
178 | +const char *audio_audfmt_to_string (audfmt_e fmt) | |
129 | 179 | { |
130 | - pcm_sw_free_resources (sw); | |
131 | -} | |
180 | + switch (fmt) { | |
181 | + case AUD_FMT_U8: | |
182 | + return "U8"; | |
132 | 183 | |
133 | -int pcm_sw_init (SWVoice *sw, HWVoice *hw, int freq, | |
134 | - int nchannels, audfmt_e fmt) | |
135 | -{ | |
136 | - int bits = 8, sign = 0; | |
184 | + case AUD_FMT_U16: | |
185 | + return "U16"; | |
137 | 186 | |
138 | - switch (fmt) { | |
139 | 187 | case AUD_FMT_S8: |
140 | - sign = 1; | |
141 | - case AUD_FMT_U8: | |
142 | - break; | |
188 | + return "S8"; | |
143 | 189 | |
144 | 190 | case AUD_FMT_S16: |
145 | - sign = 1; | |
146 | - case AUD_FMT_U16: | |
147 | - bits = 16; | |
148 | - break; | |
191 | + return "S16"; | |
149 | 192 | } |
150 | 193 | |
151 | - sw->hw = hw; | |
152 | - sw->freq = freq; | |
153 | - sw->fmt = fmt; | |
154 | - sw->nchannels = nchannels; | |
155 | - sw->shift = (nchannels == 2) + (bits == 16); | |
156 | - sw->align = (1 << sw->shift) - 1; | |
157 | - sw->left = 0; | |
158 | - sw->pos = 0; | |
159 | - sw->wpos = 0; | |
160 | - sw->live = 0; | |
161 | - sw->ratio = (sw->hw->freq * ((int64_t) INT_MAX)) / sw->freq; | |
162 | - sw->bytes_per_second = sw->freq << sw->shift; | |
163 | - sw->conv = mixeng_conv[nchannels == 2][sign][bits == 16]; | |
164 | - | |
165 | - pcm_sw_free_resources (sw); | |
166 | - return pcm_sw_alloc_resources (sw); | |
167 | -} | |
168 | - | |
169 | -/* Hard voice */ | |
170 | -void pcm_hw_free_resources (HWVoice *hw) | |
171 | -{ | |
172 | - if (hw->mix_buf) | |
173 | - qemu_free (hw->mix_buf); | |
174 | - hw->mix_buf = NULL; | |
194 | + dolog ("Bogus audfmt %d returning S16\n", fmt); | |
195 | + return "S16"; | |
175 | 196 | } |
176 | 197 | |
177 | -int pcm_hw_alloc_resources (HWVoice *hw) | |
198 | +audfmt_e audio_string_to_audfmt (const char *s, audfmt_e defval, int *defaultp) | |
178 | 199 | { |
179 | - hw->mix_buf = qemu_mallocz (hw->samples * sizeof (st_sample_t)); | |
180 | - if (!hw->mix_buf) | |
181 | - return -1; | |
182 | - return 0; | |
200 | + if (!strcasecmp (s, "u8")) { | |
201 | + *defaultp = 0; | |
202 | + return AUD_FMT_U8; | |
203 | + } | |
204 | + else if (!strcasecmp (s, "u16")) { | |
205 | + *defaultp = 0; | |
206 | + return AUD_FMT_U16; | |
207 | + } | |
208 | + else if (!strcasecmp (s, "s8")) { | |
209 | + *defaultp = 0; | |
210 | + return AUD_FMT_S8; | |
211 | + } | |
212 | + else if (!strcasecmp (s, "s16")) { | |
213 | + *defaultp = 0; | |
214 | + return AUD_FMT_S16; | |
215 | + } | |
216 | + else { | |
217 | + dolog ("Bogus audio format `%s' using %s\n", | |
218 | + s, audio_audfmt_to_string (defval)); | |
219 | + *defaultp = 1; | |
220 | + return defval; | |
221 | + } | |
183 | 222 | } |
184 | 223 | |
185 | -void pcm_hw_fini (HWVoice *hw) | |
224 | +static audfmt_e audio_get_conf_fmt (const char *envname, | |
225 | + audfmt_e defval, | |
226 | + int *defaultp) | |
186 | 227 | { |
187 | - if (hw->active) { | |
188 | - ldebug ("pcm_hw_fini: %d %d %d\n", hw->freq, hw->nchannels, hw->fmt); | |
189 | - pcm_hw_free_resources (hw); | |
190 | - hw->pcm_ops->fini (hw); | |
191 | - memset (hw, 0, audio_state.drv->voice_size); | |
228 | + const char *var = getenv (envname); | |
229 | + if (!var) { | |
230 | + *defaultp = 1; | |
231 | + return defval; | |
192 | 232 | } |
233 | + return audio_string_to_audfmt (var, defval, defaultp); | |
193 | 234 | } |
194 | 235 | |
195 | -void pcm_hw_gc (HWVoice *hw) | |
236 | +static int audio_get_conf_int (const char *key, int defval, int *defaultp) | |
196 | 237 | { |
197 | - if (hw->nb_voices) | |
198 | - return; | |
238 | + int val; | |
239 | + char *strval; | |
199 | 240 | |
200 | - pcm_hw_fini (hw); | |
241 | + strval = getenv (key); | |
242 | + if (strval) { | |
243 | + *defaultp = 0; | |
244 | + val = atoi (strval); | |
245 | + return val; | |
246 | + } | |
247 | + else { | |
248 | + *defaultp = 1; | |
249 | + return defval; | |
250 | + } | |
201 | 251 | } |
202 | 252 | |
203 | -int pcm_hw_get_live (HWVoice *hw) | |
253 | +static const char *audio_get_conf_str (const char *key, | |
254 | + const char *defval, | |
255 | + int *defaultp) | |
204 | 256 | { |
205 | - int i, alive = 0, live = hw->samples; | |
206 | - | |
207 | - for (i = 0; i < hw->nb_voices; i++) { | |
208 | - if (hw->pvoice[i]->live) { | |
209 | - live = audio_MIN (hw->pvoice[i]->live, live); | |
210 | - alive += 1; | |
211 | - } | |
257 | + const char *val = getenv (key); | |
258 | + if (!val) { | |
259 | + *defaultp = 1; | |
260 | + return defval; | |
261 | + } | |
262 | + else { | |
263 | + *defaultp = 0; | |
264 | + return val; | |
212 | 265 | } |
213 | - | |
214 | - if (alive) | |
215 | - return live; | |
216 | - else | |
217 | - return -1; | |
218 | 266 | } |
219 | 267 | |
220 | -int pcm_hw_get_live2 (HWVoice *hw, int *nb_active) | |
268 | +void AUD_log (const char *cap, const char *fmt, ...) | |
221 | 269 | { |
222 | - int i, alive = 0, live = hw->samples; | |
223 | - | |
224 | - *nb_active = 0; | |
225 | - for (i = 0; i < hw->nb_voices; i++) { | |
226 | - if (hw->pvoice[i]->live) { | |
227 | - if (hw->pvoice[i]->live < live) { | |
228 | - *nb_active = hw->pvoice[i]->active != 0; | |
229 | - live = hw->pvoice[i]->live; | |
230 | - } | |
231 | - alive += 1; | |
232 | - } | |
270 | + va_list ap; | |
271 | + if (cap) { | |
272 | + fprintf (stderr, "%s: ", cap); | |
233 | 273 | } |
234 | - | |
235 | - if (alive) | |
236 | - return live; | |
237 | - else | |
238 | - return -1; | |
274 | + va_start (ap, fmt); | |
275 | + vfprintf (stderr, fmt, ap); | |
276 | + va_end (ap); | |
239 | 277 | } |
240 | 278 | |
241 | -void pcm_hw_dec_live (HWVoice *hw, int decr) | |
279 | +void AUD_vlog (const char *cap, const char *fmt, va_list ap) | |
242 | 280 | { |
243 | - int i; | |
244 | - | |
245 | - for (i = 0; i < hw->nb_voices; i++) { | |
246 | - if (hw->pvoice[i]->live) { | |
247 | - hw->pvoice[i]->live -= decr; | |
248 | - } | |
281 | + if (cap) { | |
282 | + fprintf (stderr, "%s: ", cap); | |
249 | 283 | } |
284 | + vfprintf (stderr, fmt, ap); | |
250 | 285 | } |
251 | 286 | |
252 | -void pcm_hw_clear (HWVoice *hw, void *buf, int len) | |
287 | +static void audio_print_options (const char *prefix, | |
288 | + struct audio_option *opt) | |
253 | 289 | { |
254 | - if (!len) | |
290 | + char *uprefix; | |
291 | + | |
292 | + if (!prefix) { | |
293 | + dolog ("No prefix specified\n"); | |
294 | + return; | |
295 | + } | |
296 | + | |
297 | + if (!opt) { | |
298 | + dolog ("No options\n"); | |
255 | 299 | return; |
300 | + } | |
256 | 301 | |
257 | - switch (hw->fmt) { | |
258 | - case AUD_FMT_S16: | |
259 | - case AUD_FMT_S8: | |
260 | - memset (buf, len << hw->shift, 0x00); | |
261 | - break; | |
302 | + uprefix = audio_alloc_prefix (prefix); | |
262 | 303 | |
263 | - case AUD_FMT_U8: | |
264 | - memset (buf, len << hw->shift, 0x80); | |
265 | - break; | |
304 | + for (; opt->name; opt++) { | |
305 | + const char *state = "default"; | |
306 | + printf (" %s_%s: ", uprefix, opt->name); | |
266 | 307 | |
267 | - case AUD_FMT_U16: | |
268 | - { | |
269 | - unsigned int i; | |
270 | - uint16_t *p = buf; | |
271 | - int shift = hw->nchannels - 1; | |
308 | + if (opt->overridenp && *opt->overridenp) { | |
309 | + state = "current"; | |
310 | + } | |
272 | 311 | |
273 | - for (i = 0; i < len << shift; i++) { | |
274 | - p[i] = INT16_MAX; | |
312 | + switch (opt->tag) { | |
313 | + case AUD_OPT_BOOL: | |
314 | + { | |
315 | + int *intp = opt->valp; | |
316 | + printf ("boolean, %s = %d\n", state, *intp ? 1 : 0); | |
317 | + } | |
318 | + break; | |
319 | + | |
320 | + case AUD_OPT_INT: | |
321 | + { | |
322 | + int *intp = opt->valp; | |
323 | + printf ("integer, %s = %d\n", state, *intp); | |
324 | + } | |
325 | + break; | |
326 | + | |
327 | + case AUD_OPT_FMT: | |
328 | + { | |
329 | + audfmt_e *fmtp = opt->valp; | |
330 | + printf ( | |
331 | + "format, %s = %s, (one of: U8 S8 U16 S16)\n", | |
332 | + state, | |
333 | + audio_audfmt_to_string (*fmtp) | |
334 | + ); | |
335 | + } | |
336 | + break; | |
337 | + | |
338 | + case AUD_OPT_STR: | |
339 | + { | |
340 | + const char **strp = opt->valp; | |
341 | + printf ("string, %s = %s\n", | |
342 | + state, | |
343 | + *strp ? *strp : "(not set)"); | |
275 | 344 | } |
345 | + break; | |
346 | + | |
347 | + default: | |
348 | + printf ("???\n"); | |
349 | + dolog ("Bad value tag for option %s_%s %d\n", | |
350 | + uprefix, opt->name, opt->tag); | |
351 | + break; | |
276 | 352 | } |
277 | - break; | |
353 | + printf (" %s\n", opt->descr); | |
278 | 354 | } |
355 | + | |
356 | + qemu_free (uprefix); | |
279 | 357 | } |
280 | 358 | |
281 | -int pcm_hw_write (SWVoice *sw, void *buf, int size) | |
359 | +static void audio_process_options (const char *prefix, | |
360 | + struct audio_option *opt) | |
282 | 361 | { |
283 | - int hwsamples, samples, isamp, osamp, wpos, live, dead, left, swlim, blck; | |
284 | - int ret = 0, pos = 0; | |
285 | - if (!sw) | |
286 | - return size; | |
362 | + char *optname; | |
363 | + const char qemu_prefix[] = "QEMU_"; | |
364 | + size_t preflen; | |
287 | 365 | |
288 | - hwsamples = sw->hw->samples; | |
289 | - samples = size >> sw->shift; | |
366 | + if (audio_bug (AUDIO_FUNC, !prefix)) { | |
367 | + dolog ("prefix = NULL\n"); | |
368 | + return; | |
369 | + } | |
290 | 370 | |
291 | - if (!sw->live) { | |
292 | - sw->wpos = sw->hw->rpos; | |
371 | + if (audio_bug (AUDIO_FUNC, !opt)) { | |
372 | + dolog ("opt = NULL\n"); | |
373 | + return; | |
293 | 374 | } |
294 | - wpos = sw->wpos; | |
295 | - live = sw->live; | |
296 | - dead = hwsamples - live; | |
297 | - swlim = (dead * ((int64_t) INT_MAX)) / sw->ratio; | |
298 | - swlim = audio_MIN (swlim, samples); | |
299 | 375 | |
300 | - ldebug ("size=%d live=%d dead=%d swlim=%d wpos=%d\n", | |
301 | - size, live, dead, swlim, wpos); | |
302 | - if (swlim) | |
303 | - sw->conv (sw->buf, buf, swlim); | |
376 | + preflen = strlen (prefix); | |
304 | 377 | |
305 | - while (swlim) { | |
306 | - dead = hwsamples - live; | |
307 | - left = hwsamples - wpos; | |
308 | - blck = audio_MIN (dead, left); | |
309 | - if (!blck) { | |
310 | - /* dolog ("swlim=%d\n", swlim); */ | |
378 | + for (; opt->name; opt++) { | |
379 | + size_t len, i; | |
380 | + int def; | |
381 | + | |
382 | + if (!opt->valp) { | |
383 | + dolog ("Option value pointer for `%s' is not set\n", | |
384 | + opt->name); | |
385 | + continue; | |
386 | + } | |
387 | + | |
388 | + len = strlen (opt->name); | |
389 | + optname = qemu_malloc (len + preflen + sizeof (qemu_prefix) + 1); | |
390 | + if (!optname) { | |
391 | + dolog ("Can not allocate memory for option name `%s'\n", | |
392 | + opt->name); | |
393 | + continue; | |
394 | + } | |
395 | + | |
396 | + strcpy (optname, qemu_prefix); | |
397 | + for (i = 0; i <= preflen; ++i) { | |
398 | + optname[i + sizeof (qemu_prefix) - 1] = toupper (prefix[i]); | |
399 | + } | |
400 | + strcat (optname, "_"); | |
401 | + strcat (optname, opt->name); | |
402 | + | |
403 | + def = 1; | |
404 | + switch (opt->tag) { | |
405 | + case AUD_OPT_BOOL: | |
406 | + case AUD_OPT_INT: | |
407 | + { | |
408 | + int *intp = opt->valp; | |
409 | + *intp = audio_get_conf_int (optname, *intp, &def); | |
410 | + } | |
411 | + break; | |
412 | + | |
413 | + case AUD_OPT_FMT: | |
414 | + { | |
415 | + audfmt_e *fmtp = opt->valp; | |
416 | + *fmtp = audio_get_conf_fmt (optname, *fmtp, &def); | |
417 | + } | |
418 | + break; | |
419 | + | |
420 | + case AUD_OPT_STR: | |
421 | + { | |
422 | + const char **strp = opt->valp; | |
423 | + *strp = audio_get_conf_str (optname, *strp, &def); | |
424 | + } | |
425 | + break; | |
426 | + | |
427 | + default: | |
428 | + dolog ("Bad value tag for option `%s' - %d\n", | |
429 | + optname, opt->tag); | |
311 | 430 | break; |
312 | 431 | } |
313 | - isamp = swlim; | |
314 | - osamp = blck; | |
315 | - st_rate_flow (sw->rate, sw->buf + pos, sw->hw->mix_buf + wpos, &isamp, &osamp); | |
316 | - ret += isamp; | |
317 | - swlim -= isamp; | |
318 | - pos += isamp; | |
319 | - live += osamp; | |
320 | - wpos = (wpos + osamp) % hwsamples; | |
321 | - } | |
322 | 432 | |
323 | - sw->wpos = wpos; | |
324 | - sw->live = live; | |
325 | - return ret << sw->shift; | |
433 | + if (!opt->overridenp) { | |
434 | + opt->overridenp = &opt->overriden; | |
435 | + } | |
436 | + *opt->overridenp = !def; | |
437 | + qemu_free (optname); | |
438 | + } | |
326 | 439 | } |
327 | 440 | |
328 | -int pcm_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt) | |
441 | +static int audio_pcm_info_eq (struct audio_pcm_info *info, int freq, | |
442 | + int nchannels, audfmt_e fmt) | |
329 | 443 | { |
330 | - int sign = 0, bits = 8; | |
444 | + int bits = 8, sign = 0; | |
331 | 445 | |
332 | - pcm_hw_fini (hw); | |
333 | - ldebug ("pcm_hw_init: %d %d %d\n", freq, nchannels, fmt); | |
334 | - if (hw->pcm_ops->init (hw, freq, nchannels, fmt)) { | |
335 | - memset (hw, 0, audio_state.drv->voice_size); | |
336 | - return -1; | |
446 | + switch (fmt) { | |
447 | + case AUD_FMT_S8: | |
448 | + sign = 1; | |
449 | + case AUD_FMT_U8: | |
450 | + break; | |
451 | + | |
452 | + case AUD_FMT_S16: | |
453 | + sign = 1; | |
454 | + case AUD_FMT_U16: | |
455 | + bits = 16; | |
456 | + break; | |
337 | 457 | } |
458 | + return info->freq == freq | |
459 | + && info->nchannels == nchannels | |
460 | + && info->sign == sign | |
461 | + && info->bits == bits; | |
462 | +} | |
338 | 463 | |
339 | - switch (hw->fmt) { | |
464 | +void audio_pcm_init_info (struct audio_pcm_info *info, int freq, | |
465 | + int nchannels, audfmt_e fmt, int swap_endian) | |
466 | +{ | |
467 | + int bits = 8, sign = 0; | |
468 | + | |
469 | + switch (fmt) { | |
340 | 470 | case AUD_FMT_S8: |
341 | 471 | sign = 1; |
342 | 472 | case AUD_FMT_U8: |
... | ... | @@ -349,425 +479,595 @@ int pcm_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt) |
349 | 479 | break; |
350 | 480 | } |
351 | 481 | |
352 | - hw->nb_voices = 0; | |
353 | - hw->active = 1; | |
354 | - hw->shift = (hw->nchannels == 2) + (bits == 16); | |
355 | - hw->bytes_per_second = hw->freq << hw->shift; | |
356 | - hw->align = (1 << hw->shift) - 1; | |
357 | - hw->samples = hw->bufsize >> hw->shift; | |
358 | - hw->clip = mixeng_clip[hw->nchannels == 2][sign][bits == 16]; | |
359 | - if (pcm_hw_alloc_resources (hw)) { | |
360 | - pcm_hw_fini (hw); | |
361 | - return -1; | |
362 | - } | |
363 | - return 0; | |
482 | + info->freq = freq; | |
483 | + info->bits = bits; | |
484 | + info->sign = sign; | |
485 | + info->nchannels = nchannels; | |
486 | + info->shift = (nchannels == 2) + (bits == 16); | |
487 | + info->align = (1 << info->shift) - 1; | |
488 | + info->bytes_per_second = info->freq << info->shift; | |
489 | + info->swap_endian = swap_endian; | |
364 | 490 | } |
365 | 491 | |
366 | -static int dist (void *hw) | |
492 | +void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len) | |
367 | 493 | { |
368 | - if (hw) { | |
369 | - return (((uint8_t *) hw - (uint8_t *) hw_voices) | |
370 | - / audio_state.drv->voice_size) + 1; | |
494 | + if (!len) { | |
495 | + return; | |
496 | + } | |
497 | + | |
498 | + if (info->sign) { | |
499 | + memset (buf, len << info->shift, 0x00); | |
371 | 500 | } |
372 | 501 | else { |
373 | - return 0; | |
502 | + if (info->bits == 8) { | |
503 | + memset (buf, len << info->shift, 0x80); | |
504 | + } | |
505 | + else { | |
506 | + int i; | |
507 | + uint16_t *p = buf; | |
508 | + int shift = info->nchannels - 1; | |
509 | + short s = INT16_MAX; | |
510 | + | |
511 | + if (info->swap_endian) { | |
512 | + s = bswap16 (s); | |
513 | + } | |
514 | + | |
515 | + for (i = 0; i < len << shift; i++) { | |
516 | + p[i] = s; | |
517 | + } | |
518 | + } | |
374 | 519 | } |
375 | 520 | } |
376 | 521 | |
377 | -#define ADVANCE(hw) \ | |
378 | - ((hw) ? advance (hw, audio_state.drv->voice_size) : hw_voices) | |
379 | - | |
380 | -HWVoice *pcm_hw_find_any (HWVoice *hw) | |
522 | +/* | |
523 | + * Hard voice (capture) | |
524 | + */ | |
525 | +static void audio_pcm_hw_free_resources_in (HWVoiceIn *hw) | |
381 | 526 | { |
382 | - int i = dist (hw); | |
383 | - for (; i < audio_state.nb_hw_voices; i++) { | |
384 | - hw = ADVANCE (hw); | |
385 | - return hw; | |
527 | + if (hw->conv_buf) { | |
528 | + qemu_free (hw->conv_buf); | |
386 | 529 | } |
387 | - return NULL; | |
530 | + hw->conv_buf = NULL; | |
388 | 531 | } |
389 | 532 | |
390 | -HWVoice *pcm_hw_find_any_active (HWVoice *hw) | |
533 | +static int audio_pcm_hw_alloc_resources_in (HWVoiceIn *hw) | |
391 | 534 | { |
392 | - int i = dist (hw); | |
393 | - for (; i < audio_state.nb_hw_voices; i++) { | |
394 | - hw = ADVANCE (hw); | |
395 | - if (hw->active) | |
396 | - return hw; | |
535 | + hw->conv_buf = qemu_mallocz (hw->samples * sizeof (st_sample_t)); | |
536 | + if (!hw->conv_buf) { | |
537 | + return -1; | |
397 | 538 | } |
398 | - return NULL; | |
539 | + return 0; | |
399 | 540 | } |
400 | 541 | |
401 | -HWVoice *pcm_hw_find_any_active_enabled (HWVoice *hw) | |
542 | +static int audio_pcm_hw_init_in (HWVoiceIn *hw, int freq, int nchannels, audfmt_e fmt) | |
402 | 543 | { |
403 | - int i = dist (hw); | |
404 | - for (; i < audio_state.nb_hw_voices; i++) { | |
405 | - hw = ADVANCE (hw); | |
406 | - if (hw->active && hw->enabled) | |
407 | - return hw; | |
544 | + audio_pcm_hw_fini_in (hw); | |
545 | + | |
546 | + if (hw->pcm_ops->init_in (hw, freq, nchannels, fmt)) { | |
547 | + memset (hw, 0, audio_state.drv->voice_size_in); | |
548 | + return -1; | |
408 | 549 | } |
409 | - return NULL; | |
550 | + LIST_INIT (&hw->sw_head); | |
551 | + hw->active = 1; | |
552 | + hw->samples = hw->bufsize >> hw->info.shift; | |
553 | + hw->conv = | |
554 | + mixeng_conv | |
555 | + [nchannels == 2] | |
556 | + [hw->info.sign] | |
557 | + [hw->info.swap_endian] | |
558 | + [hw->info.bits == 16]; | |
559 | + if (audio_pcm_hw_alloc_resources_in (hw)) { | |
560 | + audio_pcm_hw_free_resources_in (hw); | |
561 | + return -1; | |
562 | + } | |
563 | + return 0; | |
410 | 564 | } |
411 | 565 | |
412 | -HWVoice *pcm_hw_find_any_passive (HWVoice *hw) | |
566 | +static uint64_t audio_pcm_hw_find_min_in (HWVoiceIn *hw) | |
413 | 567 | { |
414 | - int i = dist (hw); | |
415 | - for (; i < audio_state.nb_hw_voices; i++) { | |
416 | - hw = ADVANCE (hw); | |
417 | - if (!hw->active) | |
418 | - return hw; | |
568 | + SWVoiceIn *sw; | |
569 | + int m = hw->total_samples_captured; | |
570 | + | |
571 | + for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { | |
572 | + if (sw->active) { | |
573 | + m = audio_MIN (m, sw->total_hw_samples_acquired); | |
574 | + } | |
419 | 575 | } |
420 | - return NULL; | |
576 | + return m; | |
421 | 577 | } |
422 | 578 | |
423 | -HWVoice *pcm_hw_find_specific (HWVoice *hw, int freq, | |
424 | - int nchannels, audfmt_e fmt) | |
579 | +int audio_pcm_hw_get_live_in (HWVoiceIn *hw) | |
425 | 580 | { |
426 | - while ((hw = pcm_hw_find_any_active (hw))) { | |
427 | - if (hw->freq == freq && | |
428 | - hw->nchannels == nchannels && | |
429 | - hw->fmt == fmt) | |
430 | - return hw; | |
581 | + int live = hw->total_samples_captured - audio_pcm_hw_find_min_in (hw); | |
582 | + if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) { | |
583 | + dolog ("live=%d hw->samples=%d\n", live, hw->samples); | |
584 | + return 0; | |
431 | 585 | } |
432 | - return NULL; | |
586 | + return live; | |
433 | 587 | } |
434 | 588 | |
435 | -HWVoice *pcm_hw_add (int freq, int nchannels, audfmt_e fmt) | |
589 | +/* | |
590 | + * Soft voice (capture) | |
591 | + */ | |
592 | +static void audio_pcm_sw_free_resources_in (SWVoiceIn *sw) | |
436 | 593 | { |
437 | - HWVoice *hw; | |
438 | - | |
439 | - if (audio_state.fixed_format) { | |
440 | - freq = audio_state.fixed_freq; | |
441 | - nchannels = audio_state.fixed_channels; | |
442 | - fmt = audio_state.fixed_fmt; | |
594 | + if (sw->conv_buf) { | |
595 | + qemu_free (sw->conv_buf); | |
443 | 596 | } |
444 | 597 | |
445 | - hw = pcm_hw_find_specific (NULL, freq, nchannels, fmt); | |
446 | - | |
447 | - if (hw) | |
448 | - return hw; | |
449 | - | |
450 | - hw = pcm_hw_find_any_passive (NULL); | |
451 | - if (hw) { | |
452 | - hw->pcm_ops = audio_state.drv->pcm_ops; | |
453 | - if (!hw->pcm_ops) | |
454 | - return NULL; | |
455 | - | |
456 | - if (pcm_hw_init (hw, freq, nchannels, fmt)) { | |
457 | - pcm_hw_gc (hw); | |
458 | - return NULL; | |
459 | - } | |
460 | - else | |
461 | - return hw; | |
598 | + if (sw->rate) { | |
599 | + st_rate_stop (sw->rate); | |
462 | 600 | } |
463 | 601 | |
464 | - return pcm_hw_find_any (NULL); | |
602 | + sw->conv_buf = NULL; | |
603 | + sw->rate = NULL; | |
465 | 604 | } |
466 | 605 | |
467 | -int pcm_hw_add_sw (HWVoice *hw, SWVoice *sw) | |
606 | +static int audio_pcm_sw_alloc_resources_in (SWVoiceIn *sw) | |
468 | 607 | { |
469 | - SWVoice **pvoice = qemu_mallocz ((hw->nb_voices + 1) * sizeof (sw)); | |
470 | - if (!pvoice) | |
608 | + int samples = ((int64_t) sw->hw->samples << 32) / sw->ratio; | |
609 | + sw->conv_buf = qemu_mallocz (samples * sizeof (st_sample_t)); | |
610 | + if (!sw->conv_buf) { | |
471 | 611 | return -1; |
612 | + } | |
472 | 613 | |
473 | - memcpy (pvoice, hw->pvoice, hw->nb_voices * sizeof (sw)); | |
474 | - qemu_free (hw->pvoice); | |
475 | - hw->pvoice = pvoice; | |
476 | - hw->pvoice[hw->nb_voices++] = sw; | |
614 | + sw->rate = st_rate_start (sw->hw->info.freq, sw->info.freq); | |
615 | + if (!sw->rate) { | |
616 | + qemu_free (sw->conv_buf); | |
617 | + sw->conv_buf = NULL; | |
618 | + return -1; | |
619 | + } | |
477 | 620 | return 0; |
478 | 621 | } |
479 | 622 | |
480 | -int pcm_hw_del_sw (HWVoice *hw, SWVoice *sw) | |
623 | +static int audio_pcm_sw_init_in (SWVoiceIn *sw, HWVoiceIn *hw, const char *name, | |
624 | + int freq, int nchannels, audfmt_e fmt) | |
481 | 625 | { |
482 | - int i, j; | |
483 | - if (hw->nb_voices > 1) { | |
484 | - SWVoice **pvoice = qemu_mallocz ((hw->nb_voices - 1) * sizeof (sw)); | |
626 | + audio_pcm_init_info (&sw->info, freq, nchannels, fmt, | |
627 | + /* None of the cards emulated by QEMU are big-endian | |
628 | + hence following shortcut */ | |
629 | + audio_need_to_swap_endian (0)); | |
630 | + sw->hw = hw; | |
631 | + sw->ratio = ((int64_t) sw->info.freq << 32) / sw->hw->info.freq; | |
485 | 632 | |
486 | - if (!pvoice) { | |
487 | - dolog ("Can not maintain consistent state (not enough memory)\n"); | |
488 | - return -1; | |
489 | - } | |
633 | + sw->clip = | |
634 | + mixeng_clip | |
635 | + [nchannels == 2] | |
636 | + [sw->info.sign] | |
637 | + [sw->info.swap_endian] | |
638 | + [sw->info.bits == 16]; | |
490 | 639 | |
491 | - for (i = 0, j = 0; i < hw->nb_voices; i++) { | |
492 | - if (j >= hw->nb_voices - 1) { | |
493 | - dolog ("Can not maintain consistent state " | |
494 | - "(invariant violated)\n"); | |
495 | - return -1; | |
496 | - } | |
497 | - if (hw->pvoice[i] != sw) | |
498 | - pvoice[j++] = hw->pvoice[i]; | |
499 | - } | |
500 | - qemu_free (hw->pvoice); | |
501 | - hw->pvoice = pvoice; | |
502 | - hw->nb_voices -= 1; | |
640 | + sw->name = qemu_strdup (name); | |
641 | + audio_pcm_sw_free_resources_in (sw); | |
642 | + return audio_pcm_sw_alloc_resources_in (sw); | |
643 | +} | |
644 | + | |
645 | +static int audio_pcm_sw_get_rpos_in (SWVoiceIn *sw) | |
646 | +{ | |
647 | + HWVoiceIn *hw = sw->hw; | |
648 | + int live = hw->total_samples_captured - sw->total_hw_samples_acquired; | |
649 | + int rpos; | |
650 | + | |
651 | + if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) { | |
652 | + dolog ("live=%d hw->samples=%d\n", live, hw->samples); | |
653 | + return 0; | |
654 | + } | |
655 | + | |
656 | + rpos = hw->wpos - live; | |
657 | + if (rpos >= 0) { | |
658 | + return rpos; | |
503 | 659 | } |
504 | 660 | else { |
505 | - qemu_free (hw->pvoice); | |
506 | - hw->pvoice = NULL; | |
507 | - hw->nb_voices = 0; | |
661 | + return hw->samples + rpos; | |
508 | 662 | } |
509 | - return 0; | |
510 | 663 | } |
511 | 664 | |
512 | -SWVoice *pcm_create_voice_pair (int freq, int nchannels, audfmt_e fmt) | |
665 | +int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int size) | |
513 | 666 | { |
514 | - SWVoice *sw; | |
515 | - HWVoice *hw; | |
667 | + HWVoiceIn *hw = sw->hw; | |
668 | + int samples, live, ret = 0, swlim, isamp, osamp, rpos, total = 0; | |
669 | + st_sample_t *src, *dst = sw->conv_buf; | |
670 | + | |
671 | + rpos = audio_pcm_sw_get_rpos_in (sw) % hw->samples; | |
672 | + | |
673 | + live = hw->total_samples_captured - sw->total_hw_samples_acquired; | |
674 | + if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) { | |
675 | + dolog ("live_in=%d hw->samples=%d\n", live, hw->samples); | |
676 | + return 0; | |
677 | + } | |
678 | + | |
679 | + samples = size >> sw->info.shift; | |
680 | + if (!live) { | |
681 | + return 0; | |
682 | + } | |
516 | 683 | |
517 | - sw = qemu_mallocz (sizeof (*sw)); | |
518 | - if (!sw) | |
519 | - goto err1; | |
684 | + swlim = (live * sw->ratio) >> 32; | |
685 | + swlim = audio_MIN (swlim, samples); | |
520 | 686 | |
521 | - hw = pcm_hw_add (freq, nchannels, fmt); | |
522 | - if (!hw) | |
523 | - goto err2; | |
687 | + while (swlim) { | |
688 | + src = hw->conv_buf + rpos; | |
689 | + isamp = hw->wpos - rpos; | |
690 | + /* XXX: <= ? */ | |
691 | + if (isamp <= 0) { | |
692 | + isamp = hw->samples - rpos; | |
693 | + } | |
524 | 694 | |
525 | - if (pcm_hw_add_sw (hw, sw)) | |
526 | - goto err3; | |
695 | + if (!isamp) { | |
696 | + break; | |
697 | + } | |
698 | + osamp = swlim; | |
527 | 699 | |
528 | - if (pcm_sw_init (sw, hw, freq, nchannels, fmt)) | |
529 | - goto err4; | |
700 | + if (audio_bug (AUDIO_FUNC, osamp < 0)) { | |
701 | + dolog ("osamp=%d\n", osamp); | |
702 | + } | |
530 | 703 | |
531 | - return sw; | |
704 | + st_rate_flow (sw->rate, src, dst, &isamp, &osamp); | |
705 | + swlim -= osamp; | |
706 | + rpos = (rpos + isamp) % hw->samples; | |
707 | + dst += osamp; | |
708 | + ret += osamp; | |
709 | + total += isamp; | |
710 | + } | |
532 | 711 | |
533 | -err4: | |
534 | - pcm_hw_del_sw (hw, sw); | |
535 | -err3: | |
536 | - pcm_hw_gc (hw); | |
537 | -err2: | |
538 | - qemu_free (sw); | |
539 | -err1: | |
540 | - return NULL; | |
712 | + sw->clip (buf, sw->conv_buf, ret); | |
713 | + sw->total_hw_samples_acquired += total; | |
714 | + return ret << sw->info.shift; | |
541 | 715 | } |
542 | 716 | |
543 | -SWVoice *AUD_open (SWVoice *sw, const char *name, | |
544 | - int freq, int nchannels, audfmt_e fmt) | |
717 | +/* | |
718 | + * Hard voice (playback) | |
719 | + */ | |
720 | +static int audio_pcm_hw_find_min_out (HWVoiceOut *hw, int *nb_livep) | |
545 | 721 | { |
546 | - if (!audio_state.drv) { | |
547 | - return NULL; | |
722 | + SWVoiceOut *sw; | |
723 | + int m = INT_MAX; | |
724 | + int nb_live = 0; | |
725 | + | |
726 | + for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { | |
727 | + if (sw->active || !sw->empty) { | |
728 | + m = audio_MIN (m, sw->total_hw_samples_mixed); | |
729 | + nb_live += 1; | |
730 | + } | |
548 | 731 | } |
549 | 732 | |
550 | - if (sw && freq == sw->freq && sw->nchannels == nchannels && sw->fmt == fmt) { | |
551 | - return sw; | |
733 | + *nb_livep = nb_live; | |
734 | + return m; | |
735 | +} | |
736 | + | |
737 | +static void audio_pcm_hw_free_resources_out (HWVoiceOut *hw) | |
738 | +{ | |
739 | + if (hw->mix_buf) { | |
740 | + qemu_free (hw->mix_buf); | |
552 | 741 | } |
553 | 742 | |
554 | - if (sw) { | |
555 | - ldebug ("Different format %s %d %d %d\n", | |
556 | - name, | |
557 | - sw->freq == freq, | |
558 | - sw->nchannels == nchannels, | |
559 | - sw->fmt == fmt); | |
743 | + hw->mix_buf = NULL; | |
744 | +} | |
745 | + | |
746 | +static int audio_pcm_hw_alloc_resources_out (HWVoiceOut *hw) | |
747 | +{ | |
748 | + hw->mix_buf = qemu_mallocz (hw->samples * sizeof (st_sample_t)); | |
749 | + if (!hw->mix_buf) { | |
750 | + return -1; | |
560 | 751 | } |
561 | 752 | |
562 | - if (nchannels != 1 && nchannels != 2) { | |
563 | - dolog ("Bogus channel count %d for voice %s\n", nchannels, name); | |
564 | - return NULL; | |
753 | + return 0; | |
754 | +} | |
755 | + | |
756 | +static int audio_pcm_hw_init_out (HWVoiceOut *hw, int freq, | |
757 | + int nchannels, audfmt_e fmt) | |
758 | +{ | |
759 | + audio_pcm_hw_fini_out (hw); | |
760 | + if (hw->pcm_ops->init_out (hw, freq, nchannels, fmt)) { | |
761 | + memset (hw, 0, audio_state.drv->voice_size_out); | |
762 | + return -1; | |
565 | 763 | } |
566 | 764 | |
567 | - if (!audio_state.fixed_format && sw) { | |
568 | - pcm_sw_fini (sw); | |
569 | - pcm_hw_del_sw (sw->hw, sw); | |
570 | - pcm_hw_gc (sw->hw); | |
571 | - if (sw->name) { | |
572 | - qemu_free (sw->name); | |
573 | - sw->name = NULL; | |
574 | - } | |
575 | - qemu_free (sw); | |
576 | - sw = NULL; | |
765 | + LIST_INIT (&hw->sw_head); | |
766 | + hw->active = 1; | |
767 | + hw->samples = hw->bufsize >> hw->info.shift; | |
768 | + hw->clip = | |
769 | + mixeng_clip | |
770 | + [nchannels == 2] | |
771 | + [hw->info.sign] | |
772 | + [hw->info.swap_endian] | |
773 | + [hw->info.bits == 16]; | |
774 | + if (audio_pcm_hw_alloc_resources_out (hw)) { | |
775 | + audio_pcm_hw_fini_out (hw); | |
776 | + return -1; | |
577 | 777 | } |
778 | + return 0; | |
779 | +} | |
578 | 780 | |
579 | - if (sw) { | |
580 | - HWVoice *hw = sw->hw; | |
581 | - if (!hw) { | |
582 | - dolog ("Internal logic error voice %s has no hardware store\n", | |
583 | - name); | |
584 | - return sw; | |
585 | - } | |
781 | +int audio_pcm_hw_get_live_out2 (HWVoiceOut *hw, int *nb_live) | |
782 | +{ | |
783 | + int smin; | |
586 | 784 | |
587 | - if (pcm_sw_init (sw, hw, freq, nchannels, fmt)) { | |
588 | - pcm_sw_fini (sw); | |
589 | - pcm_hw_del_sw (hw, sw); | |
590 | - pcm_hw_gc (hw); | |
591 | - if (sw->name) { | |
592 | - qemu_free (sw->name); | |
593 | - sw->name = NULL; | |
594 | - } | |
595 | - qemu_free (sw); | |
596 | - return NULL; | |
597 | - } | |
785 | + smin = audio_pcm_hw_find_min_out (hw, nb_live); | |
786 | + | |
787 | + if (!*nb_live) { | |
788 | + return 0; | |
598 | 789 | } |
599 | 790 | else { |
600 | - sw = pcm_create_voice_pair (freq, nchannels, fmt); | |
601 | - if (!sw) { | |
602 | - dolog ("Failed to create voice %s\n", name); | |
603 | - return NULL; | |
791 | + int live = smin; | |
792 | + | |
793 | + if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) { | |
794 | + dolog ("live=%d hw->samples=%d\n", live, hw->samples); | |
795 | + return 0; | |
604 | 796 | } |
797 | + return live; | |
605 | 798 | } |
799 | +} | |
800 | + | |
801 | +int audio_pcm_hw_get_live_out (HWVoiceOut *hw) | |
802 | +{ | |
803 | + int nb_live; | |
804 | + int live; | |
606 | 805 | |
607 | - if (sw->name) { | |
608 | - qemu_free (sw->name); | |
609 | - sw->name = NULL; | |
806 | + live = audio_pcm_hw_get_live_out2 (hw, &nb_live); | |
807 | + if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) { | |
808 | + dolog ("live=%d hw->samples=%d\n", live, hw->samples); | |
809 | + return 0; | |
610 | 810 | } |
611 | - sw->name = qemu_strdup (name); | |
612 | - return sw; | |
811 | + return live; | |
613 | 812 | } |
614 | 813 | |
615 | -void AUD_close (SWVoice *sw) | |
814 | +/* | |
815 | + * Soft voice (playback) | |
816 | + */ | |
817 | +static void audio_pcm_sw_free_resources_out (SWVoiceOut *sw) | |
616 | 818 | { |
617 | - if (!sw) | |
618 | - return; | |
819 | + if (sw->buf) { | |
820 | + qemu_free (sw->buf); | |
821 | + } | |
619 | 822 | |
620 | - pcm_sw_fini (sw); | |
621 | - pcm_hw_del_sw (sw->hw, sw); | |
622 | - pcm_hw_gc (sw->hw); | |
623 | - if (sw->name) { | |
624 | - qemu_free (sw->name); | |
625 | - sw->name = NULL; | |
823 | + if (sw->rate) { | |
824 | + st_rate_stop (sw->rate); | |
626 | 825 | } |
627 | - qemu_free (sw); | |
826 | + | |
827 | + sw->buf = NULL; | |
828 | + sw->rate = NULL; | |
628 | 829 | } |
629 | 830 | |
630 | -int AUD_write (SWVoice *sw, void *buf, int size) | |
831 | +static int audio_pcm_sw_alloc_resources_out (SWVoiceOut *sw) | |
631 | 832 | { |
632 | - int bytes; | |
833 | + sw->buf = qemu_mallocz (sw->hw->samples * sizeof (st_sample_t)); | |
834 | + if (!sw->buf) { | |
835 | + return -1; | |
836 | + } | |
633 | 837 | |
634 | - if (!sw->hw->enabled) | |
635 | - dolog ("Writing to disabled voice %s\n", sw->name); | |
636 | - bytes = sw->hw->pcm_ops->write (sw, buf, size); | |
637 | - return bytes; | |
838 | + sw->rate = st_rate_start (sw->info.freq, sw->hw->info.freq); | |
839 | + if (!sw->rate) { | |
840 | + qemu_free (sw->buf); | |
841 | + sw->buf = NULL; | |
842 | + return -1; | |
843 | + } | |
844 | + return 0; | |
638 | 845 | } |
639 | 846 | |
640 | -void AUD_run (void) | |
847 | +static int audio_pcm_sw_init_out (SWVoiceOut *sw, HWVoiceOut *hw, | |
848 | + const char *name, int freq, | |
849 | + int nchannels, audfmt_e fmt) | |
641 | 850 | { |
642 | - HWVoice *hw = NULL; | |
643 | - | |
644 | - while ((hw = pcm_hw_find_any_active_enabled (hw))) { | |
645 | - int i; | |
646 | - if (hw->pending_disable && pcm_hw_get_live (hw) <= 0) { | |
647 | - hw->enabled = 0; | |
648 | - hw->pcm_ops->ctl (hw, VOICE_DISABLE); | |
649 | - for (i = 0; i < hw->nb_voices; i++) { | |
650 | - hw->pvoice[i]->live = 0; | |
651 | - /* hw->pvoice[i]->old_ticks = 0; */ | |
652 | - } | |
653 | - continue; | |
654 | - } | |
851 | + audio_pcm_init_info (&sw->info, freq, nchannels, fmt, | |
852 | + /* None of the cards emulated by QEMU are big-endian | |
853 | + hence following shortcut */ | |
854 | + audio_need_to_swap_endian (0)); | |
855 | + sw->hw = hw; | |
856 | + sw->empty = 1; | |
857 | + sw->active = 0; | |
858 | + sw->ratio = ((int64_t) sw->hw->info.freq << 32) / sw->info.freq; | |
859 | + sw->total_hw_samples_mixed = 0; | |
860 | + | |
861 | + sw->conv = | |
862 | + mixeng_conv | |
863 | + [nchannels == 2] | |
864 | + [sw->info.sign] | |
865 | + [sw->info.swap_endian] | |
866 | + [sw->info.bits == 16]; | |
867 | + sw->name = qemu_strdup (name); | |
655 | 868 | |
656 | - hw->pcm_ops->run (hw); | |
657 | - assert (hw->rpos < hw->samples); | |
658 | - for (i = 0; i < hw->nb_voices; i++) { | |
659 | - SWVoice *sw = hw->pvoice[i]; | |
660 | - if (!sw->active && !sw->live && sw->old_ticks) { | |
661 | - int64_t delta = qemu_get_clock (vm_clock) - sw->old_ticks; | |
662 | - if (delta > audio_state.ticks_threshold) { | |
663 | - ldebug ("resetting old_ticks for %s\n", sw->name); | |
664 | - sw->old_ticks = 0; | |
665 | - } | |
666 | - } | |
667 | - } | |
668 | - } | |
869 | + audio_pcm_sw_free_resources_out (sw); | |
870 | + return audio_pcm_sw_alloc_resources_out (sw); | |
669 | 871 | } |
670 | 872 | |
671 | -int AUD_get_free (SWVoice *sw) | |
873 | +int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int size) | |
672 | 874 | { |
673 | - int free; | |
875 | + int hwsamples, samples, isamp, osamp, wpos, live, dead, left, swlim, blck; | |
876 | + int ret = 0, pos = 0, total = 0; | |
674 | 877 | |
675 | - if (!sw) | |
676 | - return 4096; | |
878 | + if (!sw) { | |
879 | + return size; | |
880 | + } | |
677 | 881 | |
678 | - free = ((sw->hw->samples - sw->live) << sw->hw->shift) * sw->ratio | |
679 | - / INT_MAX; | |
882 | + hwsamples = sw->hw->samples; | |
680 | 883 | |
681 | - free &= ~sw->hw->align; | |
682 | - if (!free) return 0; | |
884 | + live = sw->total_hw_samples_mixed; | |
885 | + if (audio_bug (AUDIO_FUNC, live < 0 || live > hwsamples)){ | |
886 | + dolog ("live=%d hw->samples=%d\n", live, hwsamples); | |
887 | + return 0; | |
888 | + } | |
683 | 889 | |
684 | - return free; | |
685 | -} | |
890 | + if (live == hwsamples) { | |
891 | + return 0; | |
892 | + } | |
686 | 893 | |
687 | -int AUD_get_buffer_size (SWVoice *sw) | |
688 | -{ | |
689 | - return sw->hw->bufsize; | |
690 | -} | |
894 | + wpos = (sw->hw->rpos + live) % hwsamples; | |
895 | + samples = size >> sw->info.shift; | |
691 | 896 | |
692 | -void AUD_adjust (SWVoice *sw, int bytes) | |
693 | -{ | |
694 | - if (!sw) | |
695 | - return; | |
696 | - sw->old_ticks += (ticks_per_sec * (int64_t) bytes) / sw->bytes_per_second; | |
897 | + dead = hwsamples - live; | |
898 | + swlim = ((int64_t) dead << 32) / sw->ratio; | |
899 | + swlim = audio_MIN (swlim, samples); | |
900 | + if (swlim) { | |
901 | + sw->conv (sw->buf, buf, swlim, &sw->vol); | |
902 | + } | |
903 | + | |
904 | + while (swlim) { | |
905 | + dead = hwsamples - live; | |
906 | + left = hwsamples - wpos; | |
907 | + blck = audio_MIN (dead, left); | |
908 | + if (!blck) { | |
909 | + break; | |
910 | + } | |
911 | + isamp = swlim; | |
912 | + osamp = blck; | |
913 | + st_rate_flow_mix ( | |
914 | + sw->rate, | |
915 | + sw->buf + pos, | |
916 | + sw->hw->mix_buf + wpos, | |
917 | + &isamp, | |
918 | + &osamp | |
919 | + ); | |
920 | + ret += isamp; | |
921 | + swlim -= isamp; | |
922 | + pos += isamp; | |
923 | + live += osamp; | |
924 | + wpos = (wpos + osamp) % hwsamples; | |
925 | + total += osamp; | |
926 | + } | |
927 | + | |
928 | + sw->total_hw_samples_mixed += total; | |
929 | + sw->empty = sw->total_hw_samples_mixed == 0; | |
930 | + | |
931 | +#ifdef DEBUG_OUT | |
932 | + dolog ( | |
933 | + "%s: write size %d ret %d total sw %d, hw %d\n", | |
934 | + sw->name, | |
935 | + size >> sw->info.shift, | |
936 | + ret, | |
937 | + sw->total_hw_samples_mixed, | |
938 | + sw->hw->total_samples_played | |
939 | + ); | |
940 | +#endif | |
941 | + | |
942 | + return ret << sw->info.shift; | |
697 | 943 | } |
698 | 944 | |
699 | -void AUD_reset (SWVoice *sw) | |
945 | +#ifdef DEBUG_AUDIO | |
946 | +static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info) | |
700 | 947 | { |
701 | - sw->active = 0; | |
702 | - sw->old_ticks = 0; | |
948 | + dolog ("%s: bits %d, sign %d, freq %d, nchan %d\n", | |
949 | + cap, info->bits, info->sign, info->freq, info->nchannels); | |
703 | 950 | } |
951 | +#endif | |
704 | 952 | |
705 | -int AUD_calc_elapsed (SWVoice *sw) | |
953 | +#define DAC | |
954 | +#include "audio_template.h" | |
955 | +#undef DAC | |
956 | +#include "audio_template.h" | |
957 | + | |
958 | +int AUD_write (SWVoiceOut *sw, void *buf, int size) | |
706 | 959 | { |
707 | - int64_t now, delta, bytes; | |
708 | - int dead, swlim; | |
960 | + int bytes; | |
709 | 961 | |
710 | - if (!sw) | |
711 | - return 0; | |
962 | + if (!sw) { | |
963 | + /* XXX: Consider options */ | |
964 | + return size; | |
965 | + } | |
712 | 966 | |
713 | - now = qemu_get_clock (vm_clock); | |
714 | - delta = now - sw->old_ticks; | |
715 | - bytes = (delta * sw->bytes_per_second) / ticks_per_sec; | |
716 | - if (delta < 0) { | |
717 | - dolog ("whoops delta(<0)=%lld\n", delta); | |
967 | + if (!sw->hw->enabled) { | |
968 | + dolog ("Writing to disabled voice %s\n", sw->name); | |
718 | 969 | return 0; |
719 | 970 | } |
720 | 971 | |
721 | - dead = sw->hw->samples - sw->live; | |
722 | - swlim = ((dead * (int64_t) INT_MAX) / sw->ratio); | |
972 | + bytes = sw->hw->pcm_ops->write (sw, buf, size); | |
973 | + return bytes; | |
974 | +} | |
975 | + | |
976 | +int AUD_read (SWVoiceIn *sw, void *buf, int size) | |
977 | +{ | |
978 | + int bytes; | |
723 | 979 | |
724 | - if (bytes > swlim) { | |
725 | - return swlim; | |
980 | + if (!sw) { | |
981 | + /* XXX: Consider options */ | |
982 | + return size; | |
726 | 983 | } |
727 | - else { | |
728 | - return bytes; | |
984 | + | |
985 | + if (!sw->hw->enabled) { | |
986 | + dolog ("Reading from disabled voice %s\n", sw->name); | |
987 | + return 0; | |
729 | 988 | } |
989 | + | |
990 | + bytes = sw->hw->pcm_ops->read (sw, buf, size); | |
991 | + return bytes; | |
730 | 992 | } |
731 | 993 | |
732 | -void AUD_enable (SWVoice *sw, int on) | |
994 | +int AUD_get_buffer_size_out (SWVoiceOut *sw) | |
733 | 995 | { |
734 | - int i; | |
735 | - HWVoice *hw; | |
996 | + return sw->hw->bufsize; | |
997 | +} | |
998 | + | |
999 | +void AUD_set_active_out (SWVoiceOut *sw, int on) | |
1000 | +{ | |
1001 | + HWVoiceOut *hw; | |
736 | 1002 | |
737 | - if (!sw) | |
1003 | + if (!sw) { | |
738 | 1004 | return; |
1005 | + } | |
739 | 1006 | |
740 | 1007 | hw = sw->hw; |
741 | - if (on) { | |
742 | - if (!sw->live) | |
743 | - sw->wpos = sw->hw->rpos; | |
744 | - if (!sw->old_ticks) { | |
745 | - sw->old_ticks = qemu_get_clock (vm_clock); | |
1008 | + if (sw->active != on) { | |
1009 | + SWVoiceOut *temp_sw; | |
1010 | + | |
1011 | + if (on) { | |
1012 | + int total; | |
1013 | + | |
1014 | + hw->pending_disable = 0; | |
1015 | + if (!hw->enabled) { | |
1016 | + hw->enabled = 1; | |
1017 | + hw->pcm_ops->ctl_out (hw, VOICE_ENABLE); | |
1018 | + } | |
1019 | + | |
1020 | + if (sw->empty) { | |
1021 | + total = 0; | |
1022 | + } | |
1023 | + } | |
1024 | + else { | |
1025 | + if (hw->enabled) { | |
1026 | + int nb_active = 0; | |
1027 | + | |
1028 | + for (temp_sw = hw->sw_head.lh_first; temp_sw; | |
1029 | + temp_sw = temp_sw->entries.le_next) { | |
1030 | + nb_active += temp_sw->active != 0; | |
1031 | + } | |
1032 | + | |
1033 | + hw->pending_disable = nb_active == 1; | |
1034 | + } | |
746 | 1035 | } |
1036 | + sw->active = on; | |
1037 | + } | |
1038 | +} | |
1039 | + | |
1040 | +void AUD_set_active_in (SWVoiceIn *sw, int on) | |
1041 | +{ | |
1042 | + HWVoiceIn *hw; | |
1043 | + | |
1044 | + if (!sw) { | |
1045 | + return; | |
747 | 1046 | } |
748 | 1047 | |
1048 | + hw = sw->hw; | |
749 | 1049 | if (sw->active != on) { |
1050 | + SWVoiceIn *temp_sw; | |
1051 | + | |
750 | 1052 | if (on) { |
751 | - hw->pending_disable = 0; | |
752 | 1053 | if (!hw->enabled) { |
753 | 1054 | hw->enabled = 1; |
754 | - for (i = 0; i < hw->nb_voices; i++) { | |
755 | - ldebug ("resetting voice\n"); | |
756 | - sw = hw->pvoice[i]; | |
757 | - sw->old_ticks = qemu_get_clock (vm_clock); | |
758 | - } | |
759 | - hw->pcm_ops->ctl (hw, VOICE_ENABLE); | |
1055 | + hw->pcm_ops->ctl_in (hw, VOICE_ENABLE); | |
760 | 1056 | } |
1057 | + sw->total_hw_samples_acquired = hw->total_samples_captured; | |
761 | 1058 | } |
762 | 1059 | else { |
763 | - if (hw->enabled && !hw->pending_disable) { | |
1060 | + if (hw->enabled) { | |
764 | 1061 | int nb_active = 0; |
765 | - for (i = 0; i < hw->nb_voices; i++) { | |
766 | - nb_active += hw->pvoice[i]->active != 0; | |
1062 | + | |
1063 | + for (temp_sw = hw->sw_head.lh_first; temp_sw; | |
1064 | + temp_sw = temp_sw->entries.le_next) { | |
1065 | + nb_active += temp_sw->active != 0; | |
767 | 1066 | } |
768 | 1067 | |
769 | 1068 | if (nb_active == 1) { |
770 | - hw->pending_disable = 1; | |
1069 | + hw->enabled = 0; | |
1070 | + hw->pcm_ops->ctl_in (hw, VOICE_DISABLE); | |
771 | 1071 | } |
772 | 1072 | } |
773 | 1073 | } |
... | ... | @@ -775,118 +1075,547 @@ void AUD_enable (SWVoice *sw, int on) |
775 | 1075 | } |
776 | 1076 | } |
777 | 1077 | |
778 | -static struct audio_output_driver *drvtab[] = { | |
779 | -#ifdef CONFIG_OSS | |
780 | - &oss_output_driver, | |
1078 | +static int audio_get_avail (SWVoiceIn *sw) | |
1079 | +{ | |
1080 | + int live; | |
1081 | + | |
1082 | + if (!sw) { | |
1083 | + return 0; | |
1084 | + } | |
1085 | + | |
1086 | + live = sw->hw->total_samples_captured - sw->total_hw_samples_acquired; | |
1087 | + if (audio_bug (AUDIO_FUNC, live < 0 || live > sw->hw->samples)) { | |
1088 | + dolog ("live=%d sw->hw->samples=%d\n", live, sw->hw->samples); | |
1089 | + return 0; | |
1090 | + } | |
1091 | + | |
1092 | + ldebug ( | |
1093 | + "%s: get_avail live %d ret %lld\n", | |
1094 | + sw->name, | |
1095 | + live, (((int64_t) live << 32) / sw->ratio) << sw->info.shift | |
1096 | + ); | |
1097 | + | |
1098 | + return (((int64_t) live << 32) / sw->ratio) << sw->info.shift; | |
1099 | +} | |
1100 | + | |
1101 | +static int audio_get_free (SWVoiceOut *sw) | |
1102 | +{ | |
1103 | + int live, dead; | |
1104 | + | |
1105 | + if (!sw) { | |
1106 | + return 0; | |
1107 | + } | |
1108 | + | |
1109 | + live = sw->total_hw_samples_mixed; | |
1110 | + | |
1111 | + if (audio_bug (AUDIO_FUNC, live < 0 || live > sw->hw->samples)) { | |
1112 | + dolog ("live=%d sw->hw->samples=%d\n", live, sw->hw->samples); | |
1113 | + } | |
1114 | + | |
1115 | + dead = sw->hw->samples - live; | |
1116 | + | |
1117 | +#ifdef DEBUG_OUT | |
1118 | + dolog ("%s: get_free live %d dead %d ret %lld\n", | |
1119 | + sw->name, | |
1120 | + live, dead, (((int64_t) dead << 32) / sw->ratio) << sw->info.shift); | |
781 | 1121 | #endif |
782 | -#ifdef CONFIG_FMOD | |
783 | - &fmod_output_driver, | |
1122 | + | |
1123 | + return (((int64_t) dead << 32) / sw->ratio) << sw->info.shift; | |
1124 | +} | |
1125 | + | |
1126 | +static void audio_run_out (void) | |
1127 | +{ | |
1128 | + HWVoiceOut *hw = NULL; | |
1129 | + SWVoiceOut *sw; | |
1130 | + | |
1131 | + while ((hw = audio_pcm_hw_find_any_active_enabled_out (hw))) { | |
1132 | + int played; | |
1133 | + int live, free, nb_live; | |
1134 | + | |
1135 | + live = audio_pcm_hw_get_live_out2 (hw, &nb_live); | |
1136 | + if (!nb_live) { | |
1137 | + live = 0; | |
1138 | + } | |
1139 | + if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) { | |
1140 | + dolog ("live=%d hw->samples=%d\n", live, hw->samples); | |
1141 | + } | |
1142 | + | |
1143 | + if (hw->pending_disable && !nb_live) { | |
1144 | +#ifdef DEBUG_OUT | |
1145 | + dolog ("Disabling voice\n"); | |
784 | 1146 | #endif |
785 | -#ifdef CONFIG_SDL | |
786 | - &sdl_output_driver, | |
1147 | + hw->enabled = 0; | |
1148 | + hw->pending_disable = 0; | |
1149 | + hw->pcm_ops->ctl_out (hw, VOICE_DISABLE); | |
1150 | + continue; | |
1151 | + } | |
1152 | + | |
1153 | + if (!live) { | |
1154 | + for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { | |
1155 | + if (sw->active) { | |
1156 | + free = audio_get_free (sw); | |
1157 | + if (free > 0) { | |
1158 | + sw->callback.fn (sw->callback.opaque, free); | |
1159 | + } | |
1160 | + } | |
1161 | + } | |
1162 | + continue; | |
1163 | + } | |
1164 | + | |
1165 | + played = hw->pcm_ops->run_out (hw); | |
1166 | + if (audio_bug (AUDIO_FUNC, hw->rpos >= hw->samples)) { | |
1167 | + dolog ("hw->rpos=%d hw->samples=%d played=%d\n", | |
1168 | + hw->rpos, hw->samples, played); | |
1169 | + hw->rpos = 0; | |
1170 | + } | |
1171 | + | |
1172 | +#ifdef DEBUG_OUT | |
1173 | + dolog ("played = %d total %d\n", played, hw->total_samples_played); | |
787 | 1174 | #endif |
788 | - &no_output_driver, | |
789 | -#ifdef USE_WAV_AUDIO | |
790 | - &wav_output_driver, | |
1175 | + | |
1176 | + if (played) { | |
1177 | + hw->ts_helper += played; | |
1178 | + } | |
1179 | + | |
1180 | + for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { | |
1181 | + again: | |
1182 | + if (!sw->active && sw->empty) { | |
1183 | + continue; | |
1184 | + } | |
1185 | + | |
1186 | + if (audio_bug (AUDIO_FUNC, played > sw->total_hw_samples_mixed)) { | |
1187 | + dolog ("played=%d sw->total_hw_samples_mixed=%d\n", | |
1188 | + played, sw->total_hw_samples_mixed); | |
1189 | + played = sw->total_hw_samples_mixed; | |
1190 | + } | |
1191 | + | |
1192 | + sw->total_hw_samples_mixed -= played; | |
1193 | + | |
1194 | + if (!sw->total_hw_samples_mixed) { | |
1195 | + sw->empty = 1; | |
1196 | + | |
1197 | + if (!sw->active && !sw->callback.fn) { | |
1198 | + SWVoiceOut *temp = sw->entries.le_next; | |
1199 | + | |
1200 | +#ifdef DEBUG_PLIVE | |
1201 | + dolog ("Finishing with old voice\n"); | |
791 | 1202 | #endif |
1203 | + AUD_close_out (sw); | |
1204 | + sw = temp; | |
1205 | + if (sw) { | |
1206 | + goto again; | |
1207 | + } | |
1208 | + else { | |
1209 | + break; | |
1210 | + } | |
1211 | + } | |
1212 | + } | |
1213 | + | |
1214 | + if (sw->active) { | |
1215 | + free = audio_get_free (sw); | |
1216 | + if (free > 0) { | |
1217 | + sw->callback.fn (sw->callback.opaque, free); | |
1218 | + } | |
1219 | + } | |
1220 | + } | |
1221 | + } | |
1222 | +} | |
1223 | + | |
1224 | +static void audio_run_in (void) | |
1225 | +{ | |
1226 | + HWVoiceIn *hw = NULL; | |
1227 | + | |
1228 | + while ((hw = audio_pcm_hw_find_any_active_enabled_in (hw))) { | |
1229 | + SWVoiceIn *sw; | |
1230 | + int captured, min; | |
1231 | + | |
1232 | + captured = hw->pcm_ops->run_in (hw); | |
1233 | + | |
1234 | + min = audio_pcm_hw_find_min_in (hw); | |
1235 | + hw->total_samples_captured += captured - min; | |
1236 | + hw->ts_helper += captured; | |
1237 | + | |
1238 | + for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { | |
1239 | + sw->total_hw_samples_acquired -= min; | |
1240 | + | |
1241 | + if (sw->active) { | |
1242 | + int avail; | |
1243 | + | |
1244 | + avail = audio_get_avail (sw); | |
1245 | + if (avail > 0) { | |
1246 | + sw->callback.fn (sw->callback.opaque, avail); | |
1247 | + } | |
1248 | + } | |
1249 | + } | |
1250 | + } | |
1251 | +} | |
1252 | + | |
1253 | +static struct audio_option audio_options[] = { | |
1254 | + /* DAC */ | |
1255 | + {"DAC_FIXED_SETTINGS", AUD_OPT_BOOL, &audio_state.fixed_settings_out, | |
1256 | + "Use fixed settings for host DAC", NULL, 0}, | |
1257 | + | |
1258 | + {"DAC_FIXED_FREQ", AUD_OPT_INT, &audio_state.fixed_freq_out, | |
1259 | + "Frequency for fixed host DAC", NULL, 0}, | |
1260 | + | |
1261 | + {"DAC_FIXED_FMT", AUD_OPT_FMT, &audio_state.fixed_fmt_out, | |
1262 | + "Format for fixed host DAC", NULL, 0}, | |
1263 | + | |
1264 | + {"DAC_FIXED_CHANNELS", AUD_OPT_INT, &audio_state.fixed_channels_out, | |
1265 | + "Number of channels for fixed DAC (1 - mono, 2 - stereo)", NULL, 0}, | |
1266 | + | |
1267 | + {"DAC_VOICES", AUD_OPT_INT, &audio_state.nb_hw_voices_out, | |
1268 | + "Number of voices for DAC", NULL, 0}, | |
1269 | + | |
1270 | + /* ADC */ | |
1271 | + {"ADC_FIXED_SETTINGS", AUD_OPT_BOOL, &audio_state.fixed_settings_out, | |
1272 | + "Use fixed settings for host ADC", NULL, 0}, | |
1273 | + | |
1274 | + {"ADC_FIXED_FREQ", AUD_OPT_INT, &audio_state.fixed_freq_out, | |
1275 | + "Frequency for fixed ADC", NULL, 0}, | |
1276 | + | |
1277 | + {"ADC_FIXED_FMT", AUD_OPT_FMT, &audio_state.fixed_fmt_out, | |
1278 | + "Format for fixed ADC", NULL, 0}, | |
1279 | + | |
1280 | + {"ADC_FIXED_CHANNELS", AUD_OPT_INT, &audio_state.fixed_channels_in, | |
1281 | + "Number of channels for fixed ADC (1 - mono, 2 - stereo)", NULL, 0}, | |
1282 | + | |
1283 | + {"ADC_VOICES", AUD_OPT_INT, &audio_state.nb_hw_voices_out, | |
1284 | + "Number of voices for ADC", NULL, 0}, | |
1285 | + | |
1286 | + /* Misc */ | |
1287 | + {"TIMER_PERIOD", AUD_OPT_INT, &audio_state.period.usec, | |
1288 | + "Timer period in microseconds (0 - try lowest possible)", NULL, 0}, | |
1289 | + | |
1290 | + {"PLIVE", AUD_OPT_BOOL, &audio_state.plive, | |
1291 | + "(undocumented)", NULL, 0}, | |
1292 | + | |
1293 | + {NULL, 0, NULL, NULL, NULL, 0} | |
792 | 1294 | }; |
793 | 1295 | |
794 | -static int voice_init (struct audio_output_driver *drv) | |
1296 | +void AUD_help (void) | |
1297 | +{ | |
1298 | + size_t i; | |
1299 | + | |
1300 | + audio_process_options ("AUDIO", audio_options); | |
1301 | + for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) { | |
1302 | + struct audio_driver *d = drvtab[i]; | |
1303 | + if (d->options) { | |
1304 | + audio_process_options (d->name, d->options); | |
1305 | + } | |
1306 | + } | |
1307 | + | |
1308 | + printf ("Audio options:\n"); | |
1309 | + audio_print_options ("AUDIO", audio_options); | |
1310 | + printf ("\n"); | |
1311 | + | |
1312 | + printf ("Available drivers:\n"); | |
1313 | + | |
1314 | + for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) { | |
1315 | + struct audio_driver *d = drvtab[i]; | |
1316 | + | |
1317 | + printf ("Name: %s\n", d->name); | |
1318 | + printf ("Description: %s\n", d->descr); | |
1319 | + | |
1320 | + switch (d->max_voices_out) { | |
1321 | + case 0: | |
1322 | + printf ("Does not support DAC\n"); | |
1323 | + break; | |
1324 | + case 1: | |
1325 | + printf ("One DAC voice\n"); | |
1326 | + break; | |
1327 | + case INT_MAX: | |
1328 | + printf ("Theoretically supports many DAC voices\n"); | |
1329 | + break; | |
1330 | + default: | |
1331 | + printf ("Theoretically supports upto %d DAC voices\n", | |
1332 | + d->max_voices_out); | |
1333 | + break; | |
1334 | + } | |
1335 | + | |
1336 | + switch (d->max_voices_in) { | |
1337 | + case 0: | |
1338 | + printf ("Does not support ADC\n"); | |
1339 | + break; | |
1340 | + case 1: | |
1341 | + printf ("One ADC voice\n"); | |
1342 | + break; | |
1343 | + case INT_MAX: | |
1344 | + printf ("Theoretically supports many ADC voices\n"); | |
1345 | + break; | |
1346 | + default: | |
1347 | + printf ("Theoretically supports upto %d ADC voices\n", | |
1348 | + d->max_voices_in); | |
1349 | + break; | |
1350 | + } | |
1351 | + | |
1352 | + if (d->options) { | |
1353 | + printf ("Options:\n"); | |
1354 | + audio_print_options (d->name, d->options); | |
1355 | + } | |
1356 | + else { | |
1357 | + printf ("No options\n"); | |
1358 | + } | |
1359 | + printf ("\n"); | |
1360 | + } | |
1361 | + | |
1362 | + printf ( | |
1363 | + "Options are settable through environment variables.\n" | |
1364 | + "Example:\n" | |
1365 | +#ifdef _WIN32 | |
1366 | + " set QEMU_AUDIO_DRV=wav\n" | |
1367 | + " set QEMU_WAV_PATH=c:/tune.wav\n" | |
1368 | +#else | |
1369 | + " export QEMU_AUDIO_DRV=wav\n" | |
1370 | + " export QEMU_WAV_PATH=$HOME/tune.wav\n" | |
1371 | + "(for csh replace export with setenv in the above)\n" | |
1372 | +#endif | |
1373 | + " qemu ...\n\n" | |
1374 | + ); | |
1375 | +} | |
1376 | + | |
1377 | +void audio_timer (void *opaque) | |
1378 | +{ | |
1379 | + AudioState *s = opaque; | |
1380 | + | |
1381 | + audio_run_out (); | |
1382 | + audio_run_in (); | |
1383 | + | |
1384 | + qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + s->period.ticks); | |
1385 | +} | |
1386 | + | |
1387 | +static int audio_driver_init (struct audio_driver *drv) | |
795 | 1388 | { |
1389 | + if (drv->options) { | |
1390 | + audio_process_options (drv->name, drv->options); | |
1391 | + } | |
796 | 1392 | audio_state.opaque = drv->init (); |
1393 | + | |
797 | 1394 | if (audio_state.opaque) { |
798 | - if (audio_state.nb_hw_voices > drv->max_voices) { | |
799 | - dolog ("`%s' does not support %d multiple hardware channels\n" | |
800 | - "Resetting to %d\n", | |
801 | - drv->name, audio_state.nb_hw_voices, drv->max_voices); | |
802 | - audio_state.nb_hw_voices = drv->max_voices; | |
1395 | + int i; | |
1396 | + HWVoiceOut *hwo; | |
1397 | + HWVoiceIn *hwi; | |
1398 | + | |
1399 | + if (audio_state.nb_hw_voices_out > drv->max_voices_out) { | |
1400 | + if (!drv->max_voices_out) { | |
1401 | + dolog ("`%s' does not support DAC\n", drv->name); | |
1402 | + } | |
1403 | + else { | |
1404 | + dolog ( | |
1405 | + "`%s' does not support %d multiple DAC voicess\n" | |
1406 | + "Resetting to %d\n", | |
1407 | + drv->name, | |
1408 | + audio_state.nb_hw_voices_out, | |
1409 | + drv->max_voices_out | |
1410 | + ); | |
1411 | + } | |
1412 | + audio_state.nb_hw_voices_out = drv->max_voices_out; | |
803 | 1413 | } |
804 | - hw_voices = qemu_mallocz (audio_state.nb_hw_voices * drv->voice_size); | |
805 | - if (hw_voices) { | |
806 | - audio_state.drv = drv; | |
807 | - return 1; | |
1414 | + | |
1415 | + LIST_INIT (&hw_head_out); | |
1416 | + hwo = qemu_mallocz (audio_state.nb_hw_voices_out * drv->voice_size_out); | |
1417 | + if (!hwo) { | |
1418 | + dolog ( | |
1419 | + "Not enough memory for %d `%s' DAC voices (each %d bytes)\n", | |
1420 | + audio_state.nb_hw_voices_out, | |
1421 | + drv->name, | |
1422 | + drv->voice_size_out | |
1423 | + ); | |
1424 | + drv->fini (audio_state.opaque); | |
1425 | + return -1; | |
808 | 1426 | } |
809 | - else { | |
810 | - dolog ("Not enough memory for %d `%s' voices (each %d bytes)\n", | |
811 | - audio_state.nb_hw_voices, drv->name, drv->voice_size); | |
1427 | + | |
1428 | + for (i = 0; i < audio_state.nb_hw_voices_out; ++i) { | |
1429 | + LIST_INSERT_HEAD (&hw_head_out, hwo, entries); | |
1430 | + hwo = advance (hwo, drv->voice_size_out); | |
1431 | + } | |
1432 | + | |
1433 | + if (!drv->voice_size_in && drv->max_voices_in) { | |
1434 | + ldebug ("warning: No ADC voice size defined for `%s'\n", | |
1435 | + drv->name); | |
1436 | + drv->max_voices_in = 0; | |
1437 | + } | |
1438 | + | |
1439 | + if (!drv->voice_size_out && drv->max_voices_out) { | |
1440 | + ldebug ("warning: No DAC voice size defined for `%s'\n", | |
1441 | + drv->name); | |
1442 | + } | |
1443 | + | |
1444 | + if (drv->voice_size_in && !drv->max_voices_in) { | |
1445 | + ldebug ("warning: ADC voice size is %d for ADC less driver `%s'\n", | |
1446 | + drv->voice_size_out, drv->name); | |
1447 | + } | |
1448 | + | |
1449 | + if (drv->voice_size_out && !drv->max_voices_out) { | |
1450 | + ldebug ("warning: DAC voice size is %d for DAC less driver `%s'\n", | |
1451 | + drv->voice_size_in, drv->name); | |
1452 | + } | |
1453 | + | |
1454 | + if (audio_state.nb_hw_voices_in > drv->max_voices_in) { | |
1455 | + if (!drv->max_voices_in) { | |
1456 | + ldebug ("`%s' does not support ADC\n", drv->name); | |
1457 | + } | |
1458 | + else { | |
1459 | + dolog ( | |
1460 | + "`%s' does not support %d multiple ADC voices\n" | |
1461 | + "Resetting to %d\n", | |
1462 | + drv->name, | |
1463 | + audio_state.nb_hw_voices_in, | |
1464 | + drv->max_voices_in | |
1465 | + ); | |
1466 | + } | |
1467 | + audio_state.nb_hw_voices_in = drv->max_voices_in; | |
1468 | + } | |
1469 | + | |
1470 | + LIST_INIT (&hw_head_in); | |
1471 | + hwi = qemu_mallocz (audio_state.nb_hw_voices_in * drv->voice_size_in); | |
1472 | + if (!hwi) { | |
1473 | + dolog ( | |
1474 | + "Not enough memory for %d `%s' ADC voices (each %d bytes)\n", | |
1475 | + audio_state.nb_hw_voices_in, | |
1476 | + drv->name, | |
1477 | + drv->voice_size_in | |
1478 | + ); | |
1479 | + qemu_free (hwo); | |
812 | 1480 | drv->fini (audio_state.opaque); |
813 | - return 0; | |
1481 | + return -1; | |
1482 | + } | |
1483 | + | |
1484 | + for (i = 0; i < audio_state.nb_hw_voices_in; ++i) { | |
1485 | + LIST_INSERT_HEAD (&hw_head_in, hwi, entries); | |
1486 | + hwi = advance (hwi, drv->voice_size_in); | |
814 | 1487 | } |
1488 | + | |
1489 | + audio_state.drv = drv; | |
1490 | + return 0; | |
815 | 1491 | } |
816 | 1492 | else { |
817 | - dolog ("Could not init `%s' audio\n", drv->name); | |
818 | - return 0; | |
1493 | + dolog ("Could not init `%s' audio driver\n", drv->name); | |
1494 | + return -1; | |
819 | 1495 | } |
820 | 1496 | } |
821 | 1497 | |
822 | 1498 | static void audio_vm_stop_handler (void *opaque, int reason) |
823 | 1499 | { |
824 | - HWVoice *hw = NULL; | |
1500 | + HWVoiceOut *hwo = NULL; | |
1501 | + HWVoiceIn *hwi = NULL; | |
1502 | + int op = reason ? VOICE_ENABLE : VOICE_DISABLE; | |
1503 | + | |
1504 | + (void) opaque; | |
1505 | + while ((hwo = audio_pcm_hw_find_any_out (hwo))) { | |
1506 | + if (!hwo->pcm_ops) { | |
1507 | + continue; | |
1508 | + } | |
1509 | + | |
1510 | + if (hwo->enabled != reason) { | |
1511 | + hwo->pcm_ops->ctl_out (hwo, op); | |
1512 | + } | |
1513 | + } | |
825 | 1514 | |
826 | - while ((hw = pcm_hw_find_any (hw))) { | |
827 | - if (!hw->pcm_ops) | |
1515 | + while ((hwi = audio_pcm_hw_find_any_in (hwi))) { | |
1516 | + if (!hwi->pcm_ops) { | |
828 | 1517 | continue; |
1518 | + } | |
829 | 1519 | |
830 | - hw->pcm_ops->ctl (hw, reason ? VOICE_ENABLE : VOICE_DISABLE); | |
1520 | + if (hwi->enabled != reason) { | |
1521 | + hwi->pcm_ops->ctl_in (hwi, op); | |
1522 | + } | |
831 | 1523 | } |
832 | 1524 | } |
833 | 1525 | |
834 | 1526 | static void audio_atexit (void) |
835 | 1527 | { |
836 | - HWVoice *hw = NULL; | |
1528 | + HWVoiceOut *hwo = NULL; | |
1529 | + HWVoiceIn *hwi = NULL; | |
1530 | + | |
1531 | + while ((hwo = audio_pcm_hw_find_any_out (hwo))) { | |
1532 | + if (!hwo->pcm_ops) { | |
1533 | + continue; | |
1534 | + } | |
1535 | + | |
1536 | + if (hwo->enabled) { | |
1537 | + hwo->pcm_ops->ctl_out (hwo, VOICE_DISABLE); | |
1538 | + } | |
1539 | + hwo->pcm_ops->fini_out (hwo); | |
1540 | + } | |
837 | 1541 | |
838 | - while ((hw = pcm_hw_find_any (hw))) { | |
839 | - if (!hw->pcm_ops) | |
1542 | + while ((hwi = audio_pcm_hw_find_any_in (hwi))) { | |
1543 | + if (!hwi->pcm_ops) { | |
840 | 1544 | continue; |
1545 | + } | |
841 | 1546 | |
842 | - hw->pcm_ops->ctl (hw, VOICE_DISABLE); | |
843 | - hw->pcm_ops->fini (hw); | |
1547 | + if (hwi->enabled) { | |
1548 | + hwi->pcm_ops->ctl_in (hwi, VOICE_DISABLE); | |
1549 | + } | |
1550 | + hwi->pcm_ops->fini_in (hwi); | |
844 | 1551 | } |
845 | 1552 | audio_state.drv->fini (audio_state.opaque); |
846 | 1553 | } |
847 | 1554 | |
848 | 1555 | static void audio_save (QEMUFile *f, void *opaque) |
849 | 1556 | { |
1557 | + (void) f; | |
1558 | + (void) opaque; | |
850 | 1559 | } |
851 | 1560 | |
852 | 1561 | static int audio_load (QEMUFile *f, void *opaque, int version_id) |
853 | 1562 | { |
854 | - if (version_id != 1) | |
1563 | + (void) f; | |
1564 | + (void) opaque; | |
1565 | + | |
1566 | + if (version_id != 1) { | |
855 | 1567 | return -EINVAL; |
1568 | + } | |
856 | 1569 | |
857 | 1570 | return 0; |
858 | 1571 | } |
859 | 1572 | |
860 | 1573 | void AUD_init (void) |
861 | 1574 | { |
862 | - int i; | |
1575 | + size_t i; | |
863 | 1576 | int done = 0; |
864 | 1577 | const char *drvname; |
1578 | + AudioState *s = &audio_state; | |
1579 | + | |
1580 | + audio_process_options ("AUDIO", audio_options); | |
1581 | + | |
1582 | + if (s->nb_hw_voices_out <= 0) { | |
1583 | + dolog ("Bogus number of DAC voices %d\n", | |
1584 | + s->nb_hw_voices_out); | |
1585 | + s->nb_hw_voices_out = 1; | |
1586 | + } | |
1587 | + | |
1588 | + if (s->nb_hw_voices_in <= 0) { | |
1589 | + dolog ("Bogus number of ADC voices %d\n", | |
1590 | + s->nb_hw_voices_in); | |
1591 | + s->nb_hw_voices_in = 1; | |
1592 | + } | |
865 | 1593 | |
866 | - audio_state.fixed_format = | |
867 | - !!audio_get_conf_int (QC_FIXED_FORMAT, audio_state.fixed_format); | |
868 | - audio_state.fixed_freq = | |
869 | - audio_get_conf_int (QC_FIXED_FREQ, audio_state.fixed_freq); | |
870 | - audio_state.nb_hw_voices = | |
871 | - audio_get_conf_int (QC_VOICES, audio_state.nb_hw_voices); | |
1594 | + { | |
1595 | + int def; | |
1596 | + drvname = audio_get_conf_str ("QEMU_AUDIO_DRV", NULL, &def); | |
1597 | + } | |
872 | 1598 | |
873 | - if (audio_state.nb_hw_voices <= 0) { | |
874 | - dolog ("Bogus number of voices %d, resetting to 1\n", | |
875 | - audio_state.nb_hw_voices); | |
1599 | + s->ts = qemu_new_timer (vm_clock, audio_timer, s); | |
1600 | + if (!s->ts) { | |
1601 | + dolog ("Can not create audio timer\n"); | |
1602 | + return; | |
876 | 1603 | } |
877 | 1604 | |
878 | - drvname = audio_get_conf_str (QC_AUDIO_DRV, NULL); | |
879 | 1605 | if (drvname) { |
880 | 1606 | int found = 0; |
1607 | + | |
881 | 1608 | for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) { |
882 | 1609 | if (!strcmp (drvname, drvtab[i]->name)) { |
883 | - done = voice_init (drvtab[i]); | |
1610 | + done = !audio_driver_init (drvtab[i]); | |
884 | 1611 | found = 1; |
885 | 1612 | break; |
886 | 1613 | } |
887 | 1614 | } |
1615 | + | |
888 | 1616 | if (!found) { |
889 | 1617 | dolog ("Unknown audio driver `%s'\n", drvname); |
1618 | + dolog ("Run with -audio-help to list available drivers\n"); | |
890 | 1619 | } |
891 | 1620 | } |
892 | 1621 | |
... | ... | @@ -895,17 +1624,32 @@ void AUD_init (void) |
895 | 1624 | |
896 | 1625 | if (!done) { |
897 | 1626 | for (i = 0; !done && i < sizeof (drvtab) / sizeof (drvtab[0]); i++) { |
898 | - if (drvtab[i]->can_be_default) | |
899 | - done = voice_init (drvtab[i]); | |
1627 | + if (drvtab[i]->can_be_default) { | |
1628 | + done = !audio_driver_init (drvtab[i]); | |
1629 | + } | |
900 | 1630 | } |
901 | 1631 | } |
902 | 1632 | |
903 | - audio_state.ticks_threshold = ticks_per_sec / 50; | |
904 | - audio_state.freq_threshold = 100; | |
905 | - | |
906 | 1633 | register_savevm ("audio", 0, 1, audio_save, audio_load, NULL); |
907 | 1634 | if (!done) { |
908 | - dolog ("Can not initialize audio subsystem\n"); | |
909 | - voice_init (&no_output_driver); | |
1635 | + if (audio_driver_init (&no_audio_driver)) { | |
1636 | + dolog ("Can not initialize audio subsystem\n"); | |
1637 | + } | |
1638 | + else { | |
1639 | + dolog ("warning: using timer based audio emulation\n"); | |
1640 | + } | |
910 | 1641 | } |
1642 | + | |
1643 | + if (s->period.usec <= 0) { | |
1644 | + if (s->period.usec < 0) { | |
1645 | + dolog ("warning: timer period is negative - %d treating as zero\n", | |
1646 | + s->period.usec); | |
1647 | + } | |
1648 | + s->period.ticks = 1; | |
1649 | + } | |
1650 | + else { | |
1651 | + s->period.ticks = (ticks_per_sec * s->period.usec) / 1000000; | |
1652 | + } | |
1653 | + | |
1654 | + qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + s->period.ticks); | |
911 | 1655 | } | ... | ... |
audio/audio.h
1 | 1 | /* |
2 | 2 | * QEMU Audio subsystem header |
3 | - * | |
4 | - * Copyright (c) 2003-2004 Vassili Karpov (malc) | |
5 | - * | |
3 | + * | |
4 | + * Copyright (c) 2003-2005 Vassili Karpov (malc) | |
5 | + * | |
6 | 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
7 | 7 | * of this software and associated documentation files (the "Software"), to deal |
8 | 8 | * in the Software without restriction, including without limitation the rights |
... | ... | @@ -24,7 +24,7 @@ |
24 | 24 | #ifndef QEMU_AUDIO_H |
25 | 25 | #define QEMU_AUDIO_H |
26 | 26 | |
27 | -#include "mixeng.h" | |
27 | +typedef void (*audio_callback_fn_t) (void *opaque, int avail); | |
28 | 28 | |
29 | 29 | typedef enum { |
30 | 30 | AUD_FMT_U8, |
... | ... | @@ -33,22 +33,60 @@ typedef enum { |
33 | 33 | AUD_FMT_S16 |
34 | 34 | } audfmt_e; |
35 | 35 | |
36 | -typedef struct SWVoice SWVoice; | |
36 | +typedef struct SWVoiceOut SWVoiceOut; | |
37 | +typedef struct SWVoiceIn SWVoiceIn; | |
38 | + | |
39 | +typedef struct QEMUAudioTimeStamp { | |
40 | + uint64_t old_ts; | |
41 | +} QEMUAudioTimeStamp; | |
42 | + | |
43 | +void AUD_vlog (const char *cap, const char *fmt, va_list ap); | |
44 | +void AUD_log (const char *cap, const char *fmt, ...) | |
45 | +#ifdef __GNUC__ | |
46 | + __attribute__ ((__format__ (__printf__, 2, 3))) | |
47 | +#endif | |
48 | + ; | |
37 | 49 | |
38 | -SWVoice * AUD_open (SWVoice *sw, const char *name, int freq, | |
39 | - int nchannels, audfmt_e fmt); | |
40 | -void AUD_init (void); | |
41 | -void AUD_log (const char *cap, const char *fmt, ...) | |
42 | - __attribute__ ((__format__ (__printf__, 2, 3)));; | |
43 | -void AUD_close (SWVoice *sw); | |
44 | -int AUD_write (SWVoice *sw, void *pcm_buf, int size); | |
45 | -void AUD_adjust (SWVoice *sw, int leftover); | |
46 | -void AUD_reset (SWVoice *sw); | |
47 | -int AUD_get_free (SWVoice *sw); | |
48 | -int AUD_get_buffer_size (SWVoice *sw); | |
49 | -void AUD_run (void); | |
50 | -void AUD_enable (SWVoice *sw, int on); | |
51 | -int AUD_calc_elapsed (SWVoice *sw); | |
50 | +void AUD_init (void); | |
51 | +void AUD_help (void); | |
52 | + | |
53 | +SWVoiceOut *AUD_open_out ( | |
54 | + SWVoiceOut *sw, | |
55 | + const char *name, | |
56 | + void *callback_opaque, | |
57 | + audio_callback_fn_t callback_fn, | |
58 | + int freq, | |
59 | + int nchannels, | |
60 | + audfmt_e fmt | |
61 | + ); | |
62 | +void AUD_close_out (SWVoiceOut *sw); | |
63 | +int AUD_write (SWVoiceOut *sw, void *pcm_buf, int size); | |
64 | +int AUD_get_buffer_size_out (SWVoiceOut *sw); | |
65 | +void AUD_set_active_out (SWVoiceOut *sw, int on); | |
66 | +int AUD_is_active_out (SWVoiceOut *sw); | |
67 | +void AUD_init_time_stamp_out (SWVoiceOut *sw, | |
68 | + QEMUAudioTimeStamp *ts); | |
69 | +uint64_t AUD_time_stamp_get_elapsed_usec_out (SWVoiceOut *sw, | |
70 | + QEMUAudioTimeStamp *ts); | |
71 | + | |
72 | +SWVoiceIn *AUD_open_in ( | |
73 | + SWVoiceIn *sw, | |
74 | + const char *name, | |
75 | + void *callback_opaque, | |
76 | + audio_callback_fn_t callback_fn, | |
77 | + int freq, | |
78 | + int nchannels, | |
79 | + audfmt_e fmt | |
80 | + ); | |
81 | +void AUD_close_in (SWVoiceIn *sw); | |
82 | +int AUD_read (SWVoiceIn *sw, void *pcm_buf, int size); | |
83 | +void AUD_adjust_in (SWVoiceIn *sw, int leftover); | |
84 | +void AUD_set_active_in (SWVoiceIn *sw, int on); | |
85 | +int AUD_is_active_in (SWVoiceIn *sw); | |
86 | +void AUD_init_time_stamp_in (SWVoiceIn *sw, | |
87 | + QEMUAudioTimeStamp *ts); | |
88 | +uint64_t AUD_time_stamp_get_elapsed_usec_in (SWVoiceIn *sw, | |
89 | + QEMUAudioTimeStamp *ts); | |
52 | 90 | |
53 | 91 | static inline void *advance (void *p, int incr) |
54 | 92 | { |
... | ... | @@ -59,7 +97,21 @@ static inline void *advance (void *p, int incr) |
59 | 97 | uint32_t popcount (uint32_t u); |
60 | 98 | inline uint32_t lsbindex (uint32_t u); |
61 | 99 | |
100 | +#ifdef __GNUC__ | |
101 | +#define audio_MIN(a, b) ( __extension__ ({ \ | |
102 | + __typeof (a) ta = a; \ | |
103 | + __typeof (b) tb = b; \ | |
104 | + ((ta)>(tb)?(tb):(ta)); \ | |
105 | +})) | |
106 | + | |
107 | +#define audio_MAX(a, b) ( __extension__ ({ \ | |
108 | + __typeof (a) ta = a; \ | |
109 | + __typeof (b) tb = b; \ | |
110 | + ((ta)<(tb)?(tb):(ta)); \ | |
111 | +})) | |
112 | +#else | |
62 | 113 | #define audio_MIN(a, b) ((a)>(b)?(b):(a)) |
63 | 114 | #define audio_MAX(a, b) ((a)<(b)?(b):(a)) |
115 | +#endif | |
64 | 116 | |
65 | 117 | #endif /* audio.h */ | ... | ... |
audio/audio_int.h
1 | 1 | /* |
2 | 2 | * QEMU Audio subsystem header |
3 | - * | |
4 | - * Copyright (c) 2003-2004 Vassili Karpov (malc) | |
5 | - * | |
3 | + * | |
4 | + * Copyright (c) 2003-2005 Vassili Karpov (malc) | |
5 | + * | |
6 | 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
7 | 7 | * of this software and associated documentation files (the "Software"), to deal |
8 | 8 | * in the Software without restriction, including without limitation the rights |
... | ... | @@ -24,140 +24,266 @@ |
24 | 24 | #ifndef QEMU_AUDIO_INT_H |
25 | 25 | #define QEMU_AUDIO_INT_H |
26 | 26 | |
27 | -#include "vl.h" | |
27 | +#include "sys-queue.h" | |
28 | + | |
29 | +#ifdef CONFIG_COREAUDIO | |
30 | +#define FLOAT_MIXENG | |
31 | +/* #define RECIPROCAL */ | |
32 | +#endif | |
33 | +#include "mixeng.h" | |
34 | + | |
35 | +int audio_bug (const char *funcname, int cond); | |
36 | + | |
37 | +struct audio_pcm_ops; | |
38 | + | |
39 | +typedef enum { | |
40 | + AUD_OPT_INT, | |
41 | + AUD_OPT_FMT, | |
42 | + AUD_OPT_STR, | |
43 | + AUD_OPT_BOOL | |
44 | +} audio_option_tag_e; | |
45 | + | |
46 | +struct audio_option { | |
47 | + const char *name; | |
48 | + audio_option_tag_e tag; | |
49 | + void *valp; | |
50 | + const char *descr; | |
51 | + int *overridenp; | |
52 | + int overriden; | |
53 | +}; | |
54 | + | |
55 | +struct audio_callback { | |
56 | + void *opaque; | |
57 | + audio_callback_fn_t fn; | |
58 | +}; | |
28 | 59 | |
29 | -struct pcm_ops; | |
60 | +struct audio_pcm_info { | |
61 | + int bits; | |
62 | + int sign; | |
63 | + int freq; | |
64 | + int nchannels; | |
65 | + int align; | |
66 | + int shift; | |
67 | + int bytes_per_second; | |
68 | + int swap_endian; | |
69 | +}; | |
30 | 70 | |
31 | -typedef struct HWVoice { | |
71 | +typedef struct HWVoiceOut { | |
32 | 72 | int active; |
33 | 73 | int enabled; |
34 | 74 | int pending_disable; |
35 | 75 | int valid; |
36 | - int freq; | |
76 | + struct audio_pcm_info info; | |
37 | 77 | |
38 | 78 | f_sample *clip; |
39 | - audfmt_e fmt; | |
40 | - int nchannels; | |
41 | - | |
42 | - int align; | |
43 | - int shift; | |
44 | 79 | |
45 | 80 | int rpos; |
46 | 81 | int bufsize; |
82 | + uint64_t ts_helper; | |
47 | 83 | |
48 | - int bytes_per_second; | |
49 | 84 | st_sample_t *mix_buf; |
50 | 85 | |
51 | 86 | int samples; |
52 | - int64_t old_ticks; | |
53 | - int nb_voices; | |
54 | - struct SWVoice **pvoice; | |
55 | - struct pcm_ops *pcm_ops; | |
56 | -} HWVoice; | |
87 | + LIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head; | |
88 | + struct audio_pcm_ops *pcm_ops; | |
89 | + LIST_ENTRY (HWVoiceOut) entries; | |
90 | +} HWVoiceOut; | |
57 | 91 | |
58 | -extern struct pcm_ops no_pcm_ops; | |
59 | -extern struct audio_output_driver no_output_driver; | |
92 | +typedef struct HWVoiceIn { | |
93 | + int enabled; | |
94 | + int active; | |
95 | + struct audio_pcm_info info; | |
96 | + | |
97 | + t_sample *conv; | |
60 | 98 | |
61 | -extern struct pcm_ops oss_pcm_ops; | |
62 | -extern struct audio_output_driver oss_output_driver; | |
99 | + int wpos; | |
100 | + int bufsize; | |
101 | + int total_samples_captured; | |
102 | + uint64_t ts_helper; | |
63 | 103 | |
64 | -extern struct pcm_ops sdl_pcm_ops; | |
65 | -extern struct audio_output_driver sdl_output_driver; | |
104 | + st_sample_t *conv_buf; | |
66 | 105 | |
67 | -extern struct pcm_ops wav_pcm_ops; | |
68 | -extern struct audio_output_driver wav_output_driver; | |
106 | + int samples; | |
107 | + LIST_HEAD (sw_in_listhead, SWVoiceIn) sw_head; | |
108 | + struct audio_pcm_ops *pcm_ops; | |
109 | + LIST_ENTRY (HWVoiceIn) entries; | |
110 | +} HWVoiceIn; | |
69 | 111 | |
70 | -extern struct pcm_ops fmod_pcm_ops; | |
71 | -extern struct audio_output_driver fmod_output_driver; | |
112 | +extern struct audio_driver no_audio_driver; | |
113 | +extern struct audio_driver oss_audio_driver; | |
114 | +extern struct audio_driver sdl_audio_driver; | |
115 | +extern struct audio_driver wav_audio_driver; | |
116 | +extern struct audio_driver fmod_audio_driver; | |
117 | +extern struct audio_driver alsa_audio_driver; | |
118 | +extern struct audio_driver coreaudio_audio_driver; | |
119 | +extern struct audio_driver dsound_audio_driver; | |
120 | +extern volume_t nominal_volume; | |
72 | 121 | |
73 | -struct audio_output_driver { | |
122 | +struct audio_driver { | |
74 | 123 | const char *name; |
124 | + const char *descr; | |
125 | + struct audio_option *options; | |
75 | 126 | void *(*init) (void); |
76 | 127 | void (*fini) (void *); |
77 | - struct pcm_ops *pcm_ops; | |
128 | + struct audio_pcm_ops *pcm_ops; | |
78 | 129 | int can_be_default; |
79 | - int max_voices; | |
80 | - int voice_size; | |
130 | + int max_voices_out; | |
131 | + int max_voices_in; | |
132 | + int voice_size_out; | |
133 | + int voice_size_in; | |
81 | 134 | }; |
82 | 135 | |
83 | 136 | typedef struct AudioState { |
84 | - int fixed_format; | |
85 | - int fixed_freq; | |
86 | - int fixed_channels; | |
87 | - int fixed_fmt; | |
88 | - int nb_hw_voices; | |
89 | - int64_t ticks_threshold; | |
90 | - int freq_threshold; | |
137 | + int fixed_settings_out; | |
138 | + int fixed_freq_out; | |
139 | + int fixed_channels_out; | |
140 | + int fixed_fmt_out; | |
141 | + int nb_hw_voices_out; | |
142 | + int greedy_out; | |
143 | + | |
144 | + int fixed_settings_in; | |
145 | + int fixed_freq_in; | |
146 | + int fixed_channels_in; | |
147 | + int fixed_fmt_in; | |
148 | + int nb_hw_voices_in; | |
149 | + int greedy_in; | |
150 | + | |
91 | 151 | void *opaque; |
92 | - struct audio_output_driver *drv; | |
93 | -} AudioState; | |
94 | -extern AudioState audio_state; | |
152 | + struct audio_driver *drv; | |
95 | 153 | |
96 | -struct SWVoice { | |
97 | - int freq; | |
98 | - audfmt_e fmt; | |
99 | - int nchannels; | |
154 | + QEMUTimer *ts; | |
155 | + union { | |
156 | + int usec; | |
157 | + int64_t ticks; | |
158 | + } period; | |
100 | 159 | |
101 | - int shift; | |
102 | - int align; | |
160 | + int plive; | |
161 | +} AudioState; | |
162 | +extern AudioState audio_state; | |
103 | 163 | |
164 | +struct SWVoiceOut { | |
165 | + struct audio_pcm_info info; | |
104 | 166 | t_sample *conv; |
105 | - | |
106 | - int left; | |
107 | - int pos; | |
108 | - int bytes_per_second; | |
109 | 167 | int64_t ratio; |
110 | 168 | st_sample_t *buf; |
111 | 169 | void *rate; |
170 | + int total_hw_samples_mixed; | |
171 | + int active; | |
172 | + int empty; | |
173 | + HWVoiceOut *hw; | |
174 | + char *name; | |
175 | + volume_t vol; | |
176 | + struct audio_callback callback; | |
177 | + LIST_ENTRY (SWVoiceOut) entries; | |
178 | +}; | |
112 | 179 | |
113 | - int wpos; | |
114 | - int live; | |
180 | +struct SWVoiceIn { | |
115 | 181 | int active; |
116 | - int64_t old_ticks; | |
117 | - HWVoice *hw; | |
182 | + struct audio_pcm_info info; | |
183 | + int64_t ratio; | |
184 | + void *rate; | |
185 | + int total_hw_samples_acquired; | |
186 | + st_sample_t *conv_buf; | |
187 | + f_sample *clip; | |
188 | + HWVoiceIn *hw; | |
118 | 189 | char *name; |
190 | + volume_t vol; | |
191 | + struct audio_callback callback; | |
192 | + LIST_ENTRY (SWVoiceIn) entries; | |
119 | 193 | }; |
120 | 194 | |
121 | -struct pcm_ops { | |
122 | - int (*init) (HWVoice *hw, int freq, int nchannels, audfmt_e fmt); | |
123 | - void (*fini) (HWVoice *hw); | |
124 | - void (*run) (HWVoice *hw); | |
125 | - int (*write) (SWVoice *sw, void *buf, int size); | |
126 | - int (*ctl) (HWVoice *hw, int cmd, ...); | |
195 | +struct audio_pcm_ops { | |
196 | + int (*init_out)(HWVoiceOut *hw, int freq, int nchannels, audfmt_e fmt); | |
197 | + void (*fini_out)(HWVoiceOut *hw); | |
198 | + int (*run_out) (HWVoiceOut *hw); | |
199 | + int (*write) (SWVoiceOut *sw, void *buf, int size); | |
200 | + int (*ctl_out) (HWVoiceOut *hw, int cmd, ...); | |
201 | + | |
202 | + int (*init_in) (HWVoiceIn *hw, int freq, int nchannels, audfmt_e fmt); | |
203 | + void (*fini_in) (HWVoiceIn *hw); | |
204 | + int (*run_in) (HWVoiceIn *hw); | |
205 | + int (*read) (SWVoiceIn *sw, void *buf, int size); | |
206 | + int (*ctl_in) (HWVoiceIn *hw, int cmd, ...); | |
127 | 207 | }; |
128 | 208 | |
129 | -void pcm_sw_free_resources (SWVoice *sw); | |
130 | -int pcm_sw_alloc_resources (SWVoice *sw); | |
131 | -void pcm_sw_fini (SWVoice *sw); | |
132 | -int pcm_sw_init (SWVoice *sw, HWVoice *hw, int freq, | |
133 | - int nchannels, audfmt_e fmt); | |
134 | - | |
135 | -void pcm_hw_clear (HWVoice *hw, void *buf, int len); | |
136 | -HWVoice * pcm_hw_find_any (HWVoice *hw); | |
137 | -HWVoice * pcm_hw_find_any_active (HWVoice *hw); | |
138 | -HWVoice * pcm_hw_find_any_passive (HWVoice *hw); | |
139 | -HWVoice * pcm_hw_find_specific (HWVoice *hw, int freq, | |
140 | - int nchannels, audfmt_e fmt); | |
141 | -HWVoice * pcm_hw_add (int freq, int nchannels, audfmt_e fmt); | |
142 | -int pcm_hw_add_sw (HWVoice *hw, SWVoice *sw); | |
143 | -int pcm_hw_del_sw (HWVoice *hw, SWVoice *sw); | |
144 | -SWVoice * pcm_create_voice_pair (int freq, int nchannels, audfmt_e fmt); | |
145 | - | |
146 | -void pcm_hw_free_resources (HWVoice *hw); | |
147 | -int pcm_hw_alloc_resources (HWVoice *hw); | |
148 | -void pcm_hw_fini (HWVoice *hw); | |
149 | -void pcm_hw_gc (HWVoice *hw); | |
150 | -int pcm_hw_get_live (HWVoice *hw); | |
151 | -int pcm_hw_get_live2 (HWVoice *hw, int *nb_active); | |
152 | -void pcm_hw_dec_live (HWVoice *hw, int decr); | |
153 | -int pcm_hw_write (SWVoice *sw, void *buf, int len); | |
154 | - | |
155 | -int audio_get_conf_int (const char *key, int defval); | |
156 | -const char *audio_get_conf_str (const char *key, const char *defval); | |
157 | - | |
158 | -struct audio_output_driver; | |
209 | +void audio_pcm_init_info (struct audio_pcm_info *info, int freq, | |
210 | + int nchannels, audfmt_e fmt, int swap_endian); | |
211 | +void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len); | |
212 | + | |
213 | +int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int len); | |
214 | +int audio_pcm_hw_get_live_in (HWVoiceIn *hw); | |
215 | + | |
216 | +int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int len); | |
217 | +int audio_pcm_hw_get_live_out (HWVoiceOut *hw); | |
218 | +int audio_pcm_hw_get_live_out2 (HWVoiceOut *hw, int *nb_live); | |
159 | 219 | |
160 | 220 | #define VOICE_ENABLE 1 |
161 | 221 | #define VOICE_DISABLE 2 |
162 | 222 | |
223 | +static inline int audio_ring_dist (int dst, int src, int len) | |
224 | +{ | |
225 | + return (dst >= src) ? (dst - src) : (len - src + dst); | |
226 | +} | |
227 | + | |
228 | +static inline int audio_need_to_swap_endian (int endianness) | |
229 | +{ | |
230 | +#ifdef WORDS_BIGENDIAN | |
231 | + return endianness != 1; | |
232 | +#else | |
233 | + return endianness != 0; | |
234 | +#endif | |
235 | +} | |
236 | + | |
237 | +#if defined __GNUC__ | |
238 | +#define GCC_ATTR __attribute__ ((__unused__, __format__ (__printf__, 1, 2))) | |
239 | +#define INIT_FIELD(f) . f | |
240 | +#define GCC_FMT_ATTR(n, m) __attribute__ ((__format__ (printf, n, m))) | |
241 | +#else | |
242 | +#define GCC_ATTR /**/ | |
243 | +#define INIT_FIELD(f) /**/ | |
244 | +#define GCC_FMT_ATTR(n, m) | |
245 | +#endif | |
246 | + | |
247 | +static void GCC_ATTR dolog (const char *fmt, ...) | |
248 | +{ | |
249 | + va_list ap; | |
250 | + | |
251 | + va_start (ap, fmt); | |
252 | + AUD_vlog (AUDIO_CAP, fmt, ap); | |
253 | + va_end (ap); | |
254 | +} | |
255 | + | |
256 | +#ifdef DEBUG | |
257 | +static void GCC_ATTR ldebug (const char *fmt, ...) | |
258 | +{ | |
259 | + va_list ap; | |
260 | + | |
261 | + va_start (ap, fmt); | |
262 | + AUD_vlog (AUDIO_CAP, fmt, ap); | |
263 | + va_end (ap); | |
264 | +} | |
265 | +#else | |
266 | +#if defined NDEBUG && defined __GNUC__ | |
267 | +#define ldebug(...) | |
268 | +#elif defined NDEBUG && defined _MSC_VER | |
269 | +#define ldebug __noop | |
270 | +#else | |
271 | +static void GCC_ATTR ldebug (const char *fmt, ...) | |
272 | +{ | |
273 | + (void) fmt; | |
274 | +} | |
275 | +#endif | |
276 | +#endif | |
277 | + | |
278 | +#undef GCC_ATTR | |
279 | + | |
280 | +#define AUDIO_STRINGIFY_(n) #n | |
281 | +#define AUDIO_STRINGIFY(n) AUDIO_STRINGIFY_(n) | |
282 | + | |
283 | +#if defined _MSC_VER || defined __GNUC__ | |
284 | +#define AUDIO_FUNC __FUNCTION__ | |
285 | +#else | |
286 | +#define AUDIO_FUNC __FILE__ ":" AUDIO_STRINGIFY (__LINE__) | |
287 | +#endif | |
288 | + | |
163 | 289 | #endif /* audio_int.h */ | ... | ... |
audio/audio_template.h
0 โ 100644
1 | +/* | |
2 | + * QEMU Audio subsystem header | |
3 | + * | |
4 | + * Copyright (c) 2005 Vassili Karpov (malc) | |
5 | + * | |
6 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | + * of this software and associated documentation files (the "Software"), to deal | |
8 | + * in the Software without restriction, including without limitation the rights | |
9 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | + * copies of the Software, and to permit persons to whom the Software is | |
11 | + * furnished to do so, subject to the following conditions: | |
12 | + * | |
13 | + * The above copyright notice and this permission notice shall be included in | |
14 | + * all copies or substantial portions of the Software. | |
15 | + * | |
16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | + * THE SOFTWARE. | |
23 | + */ | |
24 | + | |
25 | +#ifdef DAC | |
26 | +#define TYPE out | |
27 | +#define HW glue (HWVoice, Out) | |
28 | +#define SW glue (SWVoice, Out) | |
29 | +#else | |
30 | +#define TYPE in | |
31 | +#define HW glue (HWVoice, In) | |
32 | +#define SW glue (SWVoice, In) | |
33 | +#endif | |
34 | + | |
35 | +static void glue (audio_pcm_sw_fini_, TYPE) (SW *sw) | |
36 | +{ | |
37 | + glue (audio_pcm_sw_free_resources_, TYPE) (sw); | |
38 | + if (sw->name) { | |
39 | + qemu_free (sw->name); | |
40 | + sw->name = NULL; | |
41 | + } | |
42 | +} | |
43 | + | |
44 | +static void glue (audio_pcm_hw_add_sw_, TYPE) (HW *hw, SW *sw) | |
45 | +{ | |
46 | + LIST_INSERT_HEAD (&hw->sw_head, sw, entries); | |
47 | +} | |
48 | + | |
49 | +static void glue (audio_pcm_hw_del_sw_, TYPE) (SW *sw) | |
50 | +{ | |
51 | + LIST_REMOVE (sw, entries); | |
52 | +} | |
53 | + | |
54 | +static void glue (audio_pcm_hw_fini_, TYPE) (HW *hw) | |
55 | +{ | |
56 | + if (hw->active) { | |
57 | + glue (audio_pcm_hw_free_resources_ ,TYPE) (hw); | |
58 | + glue (hw->pcm_ops->fini_, TYPE) (hw); | |
59 | + memset (hw, 0, glue (audio_state.drv->voice_size_, TYPE)); | |
60 | + } | |
61 | +} | |
62 | + | |
63 | +static void glue (audio_pcm_hw_gc_, TYPE) (HW *hw) | |
64 | +{ | |
65 | + if (!hw->sw_head.lh_first) { | |
66 | + glue (audio_pcm_hw_fini_, TYPE) (hw); | |
67 | + } | |
68 | +} | |
69 | + | |
70 | +static HW *glue (audio_pcm_hw_find_any_, TYPE) (HW *hw) | |
71 | +{ | |
72 | + return hw ? hw->entries.le_next : glue (hw_head_, TYPE).lh_first; | |
73 | +} | |
74 | + | |
75 | +static HW *glue (audio_pcm_hw_find_any_active_, TYPE) (HW *hw) | |
76 | +{ | |
77 | + while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (hw))) { | |
78 | + if (hw->active) { | |
79 | + return hw; | |
80 | + } | |
81 | + } | |
82 | + return NULL; | |
83 | +} | |
84 | + | |
85 | +static HW *glue (audio_pcm_hw_find_any_active_enabled_, TYPE) (HW *hw) | |
86 | +{ | |
87 | + while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (hw))) { | |
88 | + if (hw->active && hw->enabled) { | |
89 | + return hw; | |
90 | + } | |
91 | + } | |
92 | + return NULL; | |
93 | +} | |
94 | + | |
95 | +static HW *glue (audio_pcm_hw_find_any_passive_, TYPE) (HW *hw) | |
96 | +{ | |
97 | + while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (hw))) { | |
98 | + if (!hw->active) { | |
99 | + return hw; | |
100 | + } | |
101 | + } | |
102 | + return NULL; | |
103 | +} | |
104 | + | |
105 | +static HW *glue (audio_pcm_hw_find_specific_, TYPE) ( | |
106 | + HW *hw, | |
107 | + int freq, | |
108 | + int nchannels, | |
109 | + audfmt_e fmt | |
110 | + ) | |
111 | +{ | |
112 | + while ((hw = glue (audio_pcm_hw_find_any_active_, TYPE) (hw))) { | |
113 | + if (audio_pcm_info_eq (&hw->info, freq, nchannels, fmt)) { | |
114 | + return hw; | |
115 | + } | |
116 | + } | |
117 | + return NULL; | |
118 | +} | |
119 | + | |
120 | +static HW *glue (audio_pcm_hw_add_new_, TYPE) ( | |
121 | + int freq, | |
122 | + int nchannels, | |
123 | + audfmt_e fmt | |
124 | + ) | |
125 | +{ | |
126 | + HW *hw; | |
127 | + | |
128 | + hw = glue (audio_pcm_hw_find_any_passive_, TYPE) (NULL); | |
129 | + if (hw) { | |
130 | + hw->pcm_ops = audio_state.drv->pcm_ops; | |
131 | + if (!hw->pcm_ops) { | |
132 | + return NULL; | |
133 | + } | |
134 | + | |
135 | + if (glue (audio_pcm_hw_init_, TYPE) (hw, freq, nchannels, fmt)) { | |
136 | + glue (audio_pcm_hw_gc_, TYPE) (hw); | |
137 | + return NULL; | |
138 | + } | |
139 | + else { | |
140 | + return hw; | |
141 | + } | |
142 | + } | |
143 | + | |
144 | + return NULL; | |
145 | +} | |
146 | + | |
147 | +static HW *glue (audio_pcm_hw_add_, TYPE) ( | |
148 | + int freq, | |
149 | + int nchannels, | |
150 | + audfmt_e fmt | |
151 | + ) | |
152 | +{ | |
153 | + HW *hw; | |
154 | + | |
155 | + if (glue (audio_state.greedy_, TYPE)) { | |
156 | + hw = glue (audio_pcm_hw_add_new_, TYPE) (freq, nchannels, fmt); | |
157 | + if (hw) { | |
158 | + return hw; | |
159 | + } | |
160 | + } | |
161 | + | |
162 | + hw = glue (audio_pcm_hw_find_specific_, TYPE) (NULL, freq, nchannels, fmt); | |
163 | + if (hw) { | |
164 | + return hw; | |
165 | + } | |
166 | + | |
167 | + hw = glue (audio_pcm_hw_add_new_, TYPE) (freq, nchannels, fmt); | |
168 | + if (hw) { | |
169 | + return hw; | |
170 | + } | |
171 | + | |
172 | + return glue (audio_pcm_hw_find_any_active_, TYPE) (NULL); | |
173 | +} | |
174 | + | |
175 | +static SW *glue (audio_pcm_create_voice_pair_, TYPE) ( | |
176 | + const char *name, | |
177 | + int freq, | |
178 | + int nchannels, | |
179 | + audfmt_e fmt | |
180 | + ) | |
181 | +{ | |
182 | + SW *sw; | |
183 | + HW *hw; | |
184 | + int hw_freq = freq; | |
185 | + int hw_nchannels = nchannels; | |
186 | + int hw_fmt = fmt; | |
187 | + | |
188 | + if (glue (audio_state.fixed_settings_, TYPE)) { | |
189 | + hw_freq = glue (audio_state.fixed_freq_, TYPE); | |
190 | + hw_nchannels = glue (audio_state.fixed_channels_, TYPE); | |
191 | + hw_fmt = glue (audio_state.fixed_fmt_, TYPE); | |
192 | + } | |
193 | + | |
194 | + sw = qemu_mallocz (sizeof (*sw)); | |
195 | + if (!sw) { | |
196 | + goto err1; | |
197 | + } | |
198 | + | |
199 | + hw = glue (audio_pcm_hw_add_, TYPE) (hw_freq, hw_nchannels, hw_fmt); | |
200 | + if (!hw) { | |
201 | + goto err2; | |
202 | + } | |
203 | + | |
204 | + glue (audio_pcm_hw_add_sw_, TYPE) (hw, sw); | |
205 | + | |
206 | + if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, name, freq, nchannels, fmt)) { | |
207 | + goto err3; | |
208 | + } | |
209 | + | |
210 | + return sw; | |
211 | + | |
212 | +err3: | |
213 | + glue (audio_pcm_hw_del_sw_, TYPE) (sw); | |
214 | + glue (audio_pcm_hw_gc_, TYPE) (hw); | |
215 | +err2: | |
216 | + qemu_free (sw); | |
217 | +err1: | |
218 | + return NULL; | |
219 | +} | |
220 | + | |
221 | +void glue (AUD_close_, TYPE) (SW *sw) | |
222 | +{ | |
223 | + if (sw) { | |
224 | + glue (audio_pcm_sw_fini_, TYPE) (sw); | |
225 | + glue (audio_pcm_hw_del_sw_, TYPE) (sw); | |
226 | + glue (audio_pcm_hw_gc_, TYPE) (sw->hw); | |
227 | + qemu_free (sw); | |
228 | + } | |
229 | +} | |
230 | + | |
231 | +SW *glue (AUD_open_, TYPE) ( | |
232 | + SW *sw, | |
233 | + const char *name, | |
234 | + void *callback_opaque , | |
235 | + audio_callback_fn_t callback_fn, | |
236 | + int freq, | |
237 | + int nchannels, | |
238 | + audfmt_e fmt | |
239 | + ) | |
240 | +{ | |
241 | +#ifdef DAC | |
242 | + int live = 0; | |
243 | + SW *old_sw = NULL; | |
244 | +#endif | |
245 | + | |
246 | + if (!callback_fn) { | |
247 | + dolog ("No callback specifed for voice `%s'\n", name); | |
248 | + goto fail; | |
249 | + } | |
250 | + | |
251 | + if (nchannels != 1 && nchannels != 2) { | |
252 | + dolog ("Bogus channel count %d for voice `%s'\n", nchannels, name); | |
253 | + goto fail; | |
254 | + } | |
255 | + | |
256 | + if (!audio_state.drv) { | |
257 | + dolog ("No audio driver defined\n"); | |
258 | + goto fail; | |
259 | + } | |
260 | + | |
261 | + if (sw && audio_pcm_info_eq (&sw->info, freq, nchannels, fmt)) { | |
262 | + return sw; | |
263 | + } | |
264 | + | |
265 | +#ifdef DAC | |
266 | + if (audio_state.plive && sw && (!sw->active && !sw->empty)) { | |
267 | + live = sw->total_hw_samples_mixed; | |
268 | + | |
269 | +#ifdef DEBUG_PLIVE | |
270 | + dolog ("Replacing voice %s with %d live samples\n", sw->name, live); | |
271 | + dolog ("Old %s freq %d, bits %d, channels %d\n", | |
272 | + sw->name, sw->info.freq, sw->info.bits, sw->info.nchannels); | |
273 | + dolog ("New %s freq %d, bits %d, channels %d\n", | |
274 | + name, freq, (fmt == AUD_FMT_S16 || fmt == AUD_FMT_U16) ? 16 : 8, | |
275 | + nchannels); | |
276 | +#endif | |
277 | + | |
278 | + if (live) { | |
279 | + old_sw = sw; | |
280 | + old_sw->callback.fn = NULL; | |
281 | + sw = NULL; | |
282 | + } | |
283 | + } | |
284 | +#endif | |
285 | + | |
286 | + if (!glue (audio_state.fixed_settings_, TYPE) && sw) { | |
287 | + glue (AUD_close_, TYPE) (sw); | |
288 | + sw = NULL; | |
289 | + } | |
290 | + | |
291 | + if (sw) { | |
292 | + HW *hw = sw->hw; | |
293 | + | |
294 | + if (!hw) { | |
295 | + dolog ("Internal logic error voice %s has no hardware store\n", | |
296 | + name); | |
297 | + goto fail; | |
298 | + } | |
299 | + | |
300 | + if (glue (audio_pcm_sw_init_, TYPE) ( | |
301 | + sw, | |
302 | + hw, | |
303 | + name, | |
304 | + freq, | |
305 | + nchannels, | |
306 | + fmt | |
307 | + )) { | |
308 | + goto fail; | |
309 | + } | |
310 | + } | |
311 | + else { | |
312 | + sw = glue (audio_pcm_create_voice_pair_, TYPE) ( | |
313 | + name, | |
314 | + freq, | |
315 | + nchannels, | |
316 | + fmt); | |
317 | + if (!sw) { | |
318 | + dolog ("Failed to create voice %s\n", name); | |
319 | + goto fail; | |
320 | + } | |
321 | + } | |
322 | + | |
323 | + if (sw) { | |
324 | + sw->vol = nominal_volume; | |
325 | + sw->callback.fn = callback_fn; | |
326 | + sw->callback.opaque = callback_opaque; | |
327 | + | |
328 | +#ifdef DAC | |
329 | + if (live) { | |
330 | + int mixed = | |
331 | + (live << old_sw->info.shift) | |
332 | + * old_sw->info.bytes_per_second | |
333 | + / sw->info.bytes_per_second; | |
334 | + | |
335 | +#ifdef DEBUG_PLIVE | |
336 | + dolog ("Silence will be mixed %d\n", mixed); | |
337 | +#endif | |
338 | + sw->total_hw_samples_mixed += mixed; | |
339 | + } | |
340 | +#endif | |
341 | + | |
342 | +#ifdef DEBUG_AUDIO | |
343 | + dolog ("%s\n", name); | |
344 | + audio_pcm_print_info ("hw", &sw->hw->info); | |
345 | + audio_pcm_print_info ("sw", &sw->info); | |
346 | +#endif | |
347 | + } | |
348 | + | |
349 | + return sw; | |
350 | + | |
351 | + fail: | |
352 | + glue (AUD_close_, TYPE) (sw); | |
353 | + return NULL; | |
354 | +} | |
355 | + | |
356 | +int glue (AUD_is_active_, TYPE) (SW *sw) | |
357 | +{ | |
358 | + return sw ? sw->active : 0; | |
359 | +} | |
360 | + | |
361 | +void glue (AUD_init_time_stamp_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts) | |
362 | +{ | |
363 | + if (!sw) { | |
364 | + return; | |
365 | + } | |
366 | + | |
367 | + ts->old_ts = sw->hw->ts_helper; | |
368 | +} | |
369 | + | |
370 | +uint64_t glue (AUD_time_stamp_get_elapsed_usec_, TYPE) ( | |
371 | + SW *sw, | |
372 | + QEMUAudioTimeStamp *ts | |
373 | + ) | |
374 | +{ | |
375 | + uint64_t delta, cur_ts, old_ts; | |
376 | + | |
377 | + if (!sw) { | |
378 | + return 0; | |
379 | + } | |
380 | + | |
381 | + cur_ts = sw->hw->ts_helper; | |
382 | + old_ts = ts->old_ts; | |
383 | + /* dolog ("cur %lld old %lld\n", cur_ts, old_ts); */ | |
384 | + | |
385 | + if (cur_ts >= old_ts) { | |
386 | + delta = cur_ts - old_ts; | |
387 | + } | |
388 | + else { | |
389 | + delta = UINT64_MAX - old_ts + cur_ts; | |
390 | + } | |
391 | + | |
392 | + if (!delta) { | |
393 | + return 0; | |
394 | + } | |
395 | + | |
396 | + return (delta * sw->hw->info.freq) / 1000000; | |
397 | +} | |
398 | + | |
399 | +#undef TYPE | |
400 | +#undef HW | |
401 | +#undef SW | ... | ... |
audio/coreaudio.c
0 โ 100644
1 | +/* | |
2 | + * QEMU OS X CoreAudio audio driver | |
3 | + * | |
4 | + * Copyright (c) 2005 Mike Kronenberg | |
5 | + * | |
6 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | + * of this software and associated documentation files (the "Software"), to deal | |
8 | + * in the Software without restriction, including without limitation the rights | |
9 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | + * copies of the Software, and to permit persons to whom the Software is | |
11 | + * furnished to do so, subject to the following conditions: | |
12 | + * | |
13 | + * The above copyright notice and this permission notice shall be included in | |
14 | + * all copies or substantial portions of the Software. | |
15 | + * | |
16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | + * THE SOFTWARE. | |
23 | + */ | |
24 | + | |
25 | +#include <CoreAudio/CoreAudio.h> | |
26 | +#include <string.h> /* strerror */ | |
27 | +#include <pthread.h> /* pthread_X */ | |
28 | + | |
29 | +#include "vl.h" | |
30 | + | |
31 | +#define AUDIO_CAP "coreaudio" | |
32 | +#include "audio_int.h" | |
33 | + | |
34 | +#define DEVICE_BUFFER_FRAMES (512) | |
35 | + | |
36 | +struct { | |
37 | + int buffer_frames; | |
38 | +} conf = { | |
39 | + .buffer_frames = 512 | |
40 | +}; | |
41 | + | |
42 | +typedef struct coreaudioVoiceOut { | |
43 | + HWVoiceOut hw; | |
44 | + pthread_mutex_t mutex; | |
45 | + AudioDeviceID outputDeviceID; | |
46 | + UInt32 audioDevicePropertyBufferSize; | |
47 | + AudioStreamBasicDescription outputStreamBasicDescription; | |
48 | + int isPlaying; | |
49 | + int live; | |
50 | + int decr; | |
51 | + int rpos; | |
52 | +} coreaudioVoiceOut; | |
53 | + | |
54 | +static void coreaudio_logstatus (OSStatus status) | |
55 | +{ | |
56 | + char *str = "BUG"; | |
57 | + | |
58 | + switch(status) { | |
59 | + case kAudioHardwareNoError: | |
60 | + str = "kAudioHardwareNoError"; | |
61 | + break; | |
62 | + | |
63 | + case kAudioHardwareNotRunningError: | |
64 | + str = "kAudioHardwareNotRunningError"; | |
65 | + break; | |
66 | + | |
67 | + case kAudioHardwareUnspecifiedError: | |
68 | + str = "kAudioHardwareUnspecifiedError"; | |
69 | + break; | |
70 | + | |
71 | + case kAudioHardwareUnknownPropertyError: | |
72 | + str = "kAudioHardwareUnknownPropertyError"; | |
73 | + break; | |
74 | + | |
75 | + case kAudioHardwareBadPropertySizeError: | |
76 | + str = "kAudioHardwareBadPropertySizeError"; | |
77 | + break; | |
78 | + | |
79 | + case kAudioHardwareIllegalOperationError: | |
80 | + str = "kAudioHardwareIllegalOperationError"; | |
81 | + break; | |
82 | + | |
83 | + case kAudioHardwareBadDeviceError: | |
84 | + str = "kAudioHardwareBadDeviceError"; | |
85 | + break; | |
86 | + | |
87 | + case kAudioHardwareBadStreamError: | |
88 | + str = "kAudioHardwareBadStreamError"; | |
89 | + break; | |
90 | + | |
91 | + case kAudioHardwareUnsupportedOperationError: | |
92 | + str = "kAudioHardwareUnsupportedOperationError"; | |
93 | + break; | |
94 | + | |
95 | + case kAudioDeviceUnsupportedFormatError: | |
96 | + str = "kAudioDeviceUnsupportedFormatError"; | |
97 | + break; | |
98 | + | |
99 | + case kAudioDevicePermissionsError: | |
100 | + str = "kAudioDevicePermissionsError"; | |
101 | + break; | |
102 | + | |
103 | + default: | |
104 | + AUD_log (AUDIO_CAP, "Reason: status code %ld\n", status); | |
105 | + return; | |
106 | + } | |
107 | + | |
108 | + AUD_log (AUDIO_CAP, "Reason: %s\n", str); | |
109 | +} | |
110 | + | |
111 | +static void GCC_FMT_ATTR (2, 3) coreaudio_logerr ( | |
112 | + OSStatus status, | |
113 | + const char *fmt, | |
114 | + ... | |
115 | + ) | |
116 | +{ | |
117 | + va_list ap; | |
118 | + | |
119 | + va_start (ap, fmt); | |
120 | + AUD_log (AUDIO_CAP, fmt, ap); | |
121 | + va_end (ap); | |
122 | + | |
123 | + coreaudio_logstatus (status); | |
124 | +} | |
125 | + | |
126 | +static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 ( | |
127 | + OSStatus status, | |
128 | + const char *typ, | |
129 | + const char *fmt, | |
130 | + ... | |
131 | + ) | |
132 | +{ | |
133 | + va_list ap; | |
134 | + | |
135 | + AUD_log (AUDIO_CAP, "Can not initialize %s\n", typ); | |
136 | + | |
137 | + va_start (ap, fmt); | |
138 | + AUD_vlog (AUDIO_CAP, fmt, ap); | |
139 | + va_end (ap); | |
140 | + | |
141 | + coreaudio_logstatus (status); | |
142 | +} | |
143 | + | |
144 | +static int coreaudio_lock (coreaudioVoiceOut *core, const char *fn_name) | |
145 | +{ | |
146 | + int err; | |
147 | + | |
148 | + err = pthread_mutex_lock (&core->mutex); | |
149 | + if (err) { | |
150 | + dolog ("Can not lock voice for %s\nReason: %s\n", | |
151 | + fn_name, strerror (err)); | |
152 | + return -1; | |
153 | + } | |
154 | + return 0; | |
155 | +} | |
156 | + | |
157 | +static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name) | |
158 | +{ | |
159 | + int err; | |
160 | + | |
161 | + err = pthread_mutex_unlock (&core->mutex); | |
162 | + if (err) { | |
163 | + dolog ("Can not unlock voice for %s\nReason: %s\n", | |
164 | + fn_name, strerror (err)); | |
165 | + return -1; | |
166 | + } | |
167 | + return 0; | |
168 | +} | |
169 | + | |
170 | +static int coreaudio_run_out (HWVoiceOut *hw) | |
171 | +{ | |
172 | + int live, decr; | |
173 | + coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; | |
174 | + | |
175 | + if (coreaudio_lock (core, "coreaudio_run_out")) { | |
176 | + return 0; | |
177 | + } | |
178 | + | |
179 | + live = audio_pcm_hw_get_live_out (hw); | |
180 | + | |
181 | + if (core->decr > live) { | |
182 | + ldebug ("core->decr %d live %d core->live %d\n", | |
183 | + core->decr, | |
184 | + live, | |
185 | + core->live); | |
186 | + } | |
187 | + | |
188 | + decr = audio_MIN (core->decr, live); | |
189 | + core->decr -= decr; | |
190 | + | |
191 | + core->live = live - decr; | |
192 | + hw->rpos = core->rpos; | |
193 | + | |
194 | + coreaudio_unlock (core, "coreaudio_run_out"); | |
195 | + return decr; | |
196 | +} | |
197 | + | |
198 | +/* callback to feed audiooutput buffer */ | |
199 | +static OSStatus audioDeviceIOProc( | |
200 | + AudioDeviceID inDevice, | |
201 | + const AudioTimeStamp* inNow, | |
202 | + const AudioBufferList* inInputData, | |
203 | + const AudioTimeStamp* inInputTime, | |
204 | + AudioBufferList* outOutputData, | |
205 | + const AudioTimeStamp* inOutputTime, | |
206 | + void* hwptr) | |
207 | +{ | |
208 | + unsigned int frame, frameCount; | |
209 | + float *out = outOutputData->mBuffers[0].mData; | |
210 | + HWVoiceOut *hw = hwptr; | |
211 | + coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr; | |
212 | + int rpos, live; | |
213 | + st_sample_t *src; | |
214 | +#ifndef FLOAT_MIXENG | |
215 | +#ifdef RECIPROCAL | |
216 | + const float scale = 1.f / UINT_MAX; | |
217 | +#else | |
218 | + const float scale = UINT_MAX; | |
219 | +#endif | |
220 | +#endif | |
221 | + | |
222 | + if (coreaudio_lock (core, "audioDeviceIOProc")) { | |
223 | + inInputTime = 0; | |
224 | + return 0; | |
225 | + } | |
226 | + | |
227 | + frameCount = conf.buffer_frames; | |
228 | + live = core->live; | |
229 | + | |
230 | + /* if there are not enough samples, set signal and return */ | |
231 | + if (live < frameCount) { | |
232 | + inInputTime = 0; | |
233 | + coreaudio_unlock (core, "audioDeviceIOProc(empty)"); | |
234 | + return 0; | |
235 | + } | |
236 | + | |
237 | + rpos = core->rpos; | |
238 | + src = hw->mix_buf + rpos; | |
239 | + | |
240 | + /* fill buffer */ | |
241 | + for (frame = 0; frame < frameCount; frame++) { | |
242 | +#ifdef FLOAT_MIXENG | |
243 | + *out++ = src[frame].l; /* left channel */ | |
244 | + *out++ = src[frame].r; /* right channel */ | |
245 | +#else | |
246 | +#ifdef RECIPROCAL | |
247 | + *out++ = src[frame].l * scale; /* left channel */ | |
248 | + *out++ = src[frame].r * scale; /* right channel */ | |
249 | +#else | |
250 | + *out++ = src[frame].l / scale; /* left channel */ | |
251 | + *out++ = src[frame].r / scale; /* right channel */ | |
252 | +#endif | |
253 | +#endif | |
254 | + } | |
255 | + | |
256 | + /* cleanup */ | |
257 | + mixeng_clear (src, frameCount); | |
258 | + rpos = (rpos + frameCount) % hw->samples; | |
259 | + core->decr = frameCount; | |
260 | + core->rpos = rpos; | |
261 | + | |
262 | + coreaudio_unlock (core, "audioDeviceIOProc"); | |
263 | + return 0; | |
264 | +} | |
265 | + | |
266 | +static int coreaudio_write (SWVoiceOut *sw, void *buf, int len) | |
267 | +{ | |
268 | + return audio_pcm_sw_write (sw, buf, len); | |
269 | +} | |
270 | + | |
271 | +static int coreaudio_init_out (HWVoiceOut *hw, int freq, | |
272 | + int nchannels, audfmt_e fmt) | |
273 | +{ | |
274 | + OSStatus status; | |
275 | + coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; | |
276 | + UInt32 propertySize; | |
277 | + int err; | |
278 | + int bits = 8; | |
279 | + int endianess = 0; | |
280 | + const char *typ = "DAC"; | |
281 | + | |
282 | + /* create mutex */ | |
283 | + err = pthread_mutex_init(&core->mutex, NULL); | |
284 | + if (err) { | |
285 | + dolog("Can not create mutex\nReason: %s\n", strerror (err)); | |
286 | + return -1; | |
287 | + } | |
288 | + | |
289 | + if (fmt == AUD_FMT_S16 || fmt == AUD_FMT_U16) { | |
290 | + bits = 16; | |
291 | + endianess = 1; | |
292 | + } | |
293 | + | |
294 | + audio_pcm_init_info ( | |
295 | + &hw->info, | |
296 | + freq, | |
297 | + nchannels, | |
298 | + fmt, | |
299 | + /* Following is irrelevant actually since we do not use | |
300 | + mixengs clipping routines */ | |
301 | + audio_need_to_swap_endian (endianess) | |
302 | + ); | |
303 | + hw->bufsize = 4 * conf.buffer_frames * nchannels * bits; | |
304 | + | |
305 | + /* open default output device */ | |
306 | + propertySize = sizeof(core->outputDeviceID); | |
307 | + status = AudioHardwareGetProperty( | |
308 | + kAudioHardwarePropertyDefaultOutputDevice, | |
309 | + &propertySize, | |
310 | + &core->outputDeviceID); | |
311 | + if (status != kAudioHardwareNoError) { | |
312 | + coreaudio_logerr2 (status, typ, | |
313 | + "Can not get default output Device\n"); | |
314 | + return -1; | |
315 | + } | |
316 | + if (core->outputDeviceID == kAudioDeviceUnknown) { | |
317 | + dolog ("Can not initialize %s - Unknown Audiodevice\n", typ); | |
318 | + return -1; | |
319 | + } | |
320 | + | |
321 | + /* set Buffersize to conf.buffer_frames frames */ | |
322 | + propertySize = sizeof(core->audioDevicePropertyBufferSize); | |
323 | + core->audioDevicePropertyBufferSize = | |
324 | + conf.buffer_frames * sizeof(float) * 2; | |
325 | + status = AudioDeviceSetProperty( | |
326 | + core->outputDeviceID, | |
327 | + NULL, | |
328 | + 0, | |
329 | + false, | |
330 | + kAudioDevicePropertyBufferSize, | |
331 | + propertySize, | |
332 | + &core->audioDevicePropertyBufferSize); | |
333 | + if (status != kAudioHardwareNoError) { | |
334 | + coreaudio_logerr2 (status, typ, | |
335 | + "Can not set device buffer size %d\n", | |
336 | + kAudioDevicePropertyBufferSize); | |
337 | + return -1; | |
338 | + } | |
339 | + | |
340 | + /* get Buffersize */ | |
341 | + propertySize = sizeof(core->audioDevicePropertyBufferSize); | |
342 | + status = AudioDeviceGetProperty( | |
343 | + core->outputDeviceID, | |
344 | + 0, | |
345 | + false, | |
346 | + kAudioDevicePropertyBufferSize, | |
347 | + &propertySize, | |
348 | + &core->audioDevicePropertyBufferSize); | |
349 | + if (status != kAudioHardwareNoError) { | |
350 | + coreaudio_logerr2 (status, typ, "Can not get device buffer size\n"); | |
351 | + return -1; | |
352 | + } | |
353 | + | |
354 | + /* get StreamFormat */ | |
355 | + propertySize = sizeof(core->outputStreamBasicDescription); | |
356 | + status = AudioDeviceGetProperty( | |
357 | + core->outputDeviceID, | |
358 | + 0, | |
359 | + false, | |
360 | + kAudioDevicePropertyStreamFormat, | |
361 | + &propertySize, | |
362 | + &core->outputStreamBasicDescription); | |
363 | + if (status != kAudioHardwareNoError) { | |
364 | + coreaudio_logerr2 (status, typ, | |
365 | + "Can not get Device Stream properties\n"); | |
366 | + core->outputDeviceID = kAudioDeviceUnknown; | |
367 | + return -1; | |
368 | + } | |
369 | + | |
370 | + /* set Samplerate */ | |
371 | + core->outputStreamBasicDescription.mSampleRate = (Float64)freq; | |
372 | + propertySize = sizeof(core->outputStreamBasicDescription); | |
373 | + status = AudioDeviceSetProperty( | |
374 | + core->outputDeviceID, | |
375 | + 0, | |
376 | + 0, | |
377 | + 0, | |
378 | + kAudioDevicePropertyStreamFormat, | |
379 | + propertySize, | |
380 | + &core->outputStreamBasicDescription); | |
381 | + if (status != kAudioHardwareNoError) { | |
382 | + coreaudio_logerr2 (status, typ, "Can not set samplerate %d\n", freq); | |
383 | + core->outputDeviceID = kAudioDeviceUnknown; | |
384 | + return -1; | |
385 | + } | |
386 | + | |
387 | + /* set Callback */ | |
388 | + status = AudioDeviceAddIOProc(core->outputDeviceID, audioDeviceIOProc, hw); | |
389 | + if (status != kAudioHardwareNoError) { | |
390 | + coreaudio_logerr2 (status, typ, "Can not set IOProc\n"); | |
391 | + core->outputDeviceID = kAudioDeviceUnknown; | |
392 | + return -1; | |
393 | + } | |
394 | + | |
395 | + /* start Playback */ | |
396 | + if (!core->isPlaying) { | |
397 | + status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc); | |
398 | + if (status != kAudioHardwareNoError) { | |
399 | + coreaudio_logerr2 (status, typ, "Can not start playback\n"); | |
400 | + AudioDeviceRemoveIOProc(core->outputDeviceID, audioDeviceIOProc); | |
401 | + core->outputDeviceID = kAudioDeviceUnknown; | |
402 | + return -1; | |
403 | + } | |
404 | + core->isPlaying = 1; | |
405 | + } | |
406 | + | |
407 | + return 0; | |
408 | +} | |
409 | + | |
410 | +static void coreaudio_fini_out (HWVoiceOut *hw) | |
411 | +{ | |
412 | + OSStatus status; | |
413 | + int err; | |
414 | + coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; | |
415 | + | |
416 | + /* stop playback */ | |
417 | + if (core->isPlaying) { | |
418 | + status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc); | |
419 | + if (status != kAudioHardwareNoError) { | |
420 | + coreaudio_logerr (status, "Can not stop playback\n"); | |
421 | + } | |
422 | + core->isPlaying = 0; | |
423 | + } | |
424 | + | |
425 | + /* remove callback */ | |
426 | + status = AudioDeviceRemoveIOProc(core->outputDeviceID, audioDeviceIOProc); | |
427 | + if (status != kAudioHardwareNoError) { | |
428 | + coreaudio_logerr (status, "Can not remove IOProc\n"); | |
429 | + } | |
430 | + core->outputDeviceID = kAudioDeviceUnknown; | |
431 | + | |
432 | + /* destroy mutex */ | |
433 | + err = pthread_mutex_destroy(&core->mutex); | |
434 | + if (err) { | |
435 | + dolog("Can not destroy mutex\nReason: %s\n", strerror (err)); | |
436 | + } | |
437 | +} | |
438 | + | |
439 | +static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...) | |
440 | +{ | |
441 | + OSStatus status; | |
442 | + coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; | |
443 | + | |
444 | + switch (cmd) { | |
445 | + case VOICE_ENABLE: | |
446 | + /* start playback */ | |
447 | + if (!core->isPlaying) { | |
448 | + status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc); | |
449 | + if (status != kAudioHardwareNoError) { | |
450 | + coreaudio_logerr (status, "Can not unpause playback\n"); | |
451 | + } | |
452 | + core->isPlaying = 1; | |
453 | + } | |
454 | + break; | |
455 | + | |
456 | + case VOICE_DISABLE: | |
457 | + /* stop playback */ | |
458 | + if (core->isPlaying) { | |
459 | + status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc); | |
460 | + if (status != kAudioHardwareNoError) { | |
461 | + coreaudio_logerr (status, "Can not pause playback\n"); | |
462 | + } | |
463 | + core->isPlaying = 0; | |
464 | + } | |
465 | + break; | |
466 | + } | |
467 | + return 0; | |
468 | +} | |
469 | + | |
470 | +static void *coreaudio_audio_init (void) | |
471 | +{ | |
472 | + return &coreaudio_audio_init; | |
473 | +} | |
474 | + | |
475 | +static void coreaudio_audio_fini (void *opaque) | |
476 | +{ | |
477 | + (void) opaque; | |
478 | +} | |
479 | + | |
480 | +static struct audio_option coreaudio_options[] = { | |
481 | + {"BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_frames, | |
482 | + "Size of the buffer in frames", NULL, 0}, | |
483 | + {NULL, 0, NULL, NULL, NULL, 0} | |
484 | +}; | |
485 | + | |
486 | +static struct audio_pcm_ops coreaudio_pcm_ops = { | |
487 | + coreaudio_init_out, | |
488 | + coreaudio_fini_out, | |
489 | + coreaudio_run_out, | |
490 | + coreaudio_write, | |
491 | + coreaudio_ctl_out, | |
492 | + | |
493 | + NULL, | |
494 | + NULL, | |
495 | + NULL, | |
496 | + NULL, | |
497 | + NULL | |
498 | +}; | |
499 | + | |
500 | +struct audio_driver coreaudio_audio_driver = { | |
501 | + INIT_FIELD (name = ) "coreaudio", | |
502 | + INIT_FIELD (descr = ) | |
503 | + "CoreAudio http://developer.apple.com/audio/coreaudio.html", | |
504 | + INIT_FIELD (options = ) coreaudio_options, | |
505 | + INIT_FIELD (init = ) coreaudio_audio_init, | |
506 | + INIT_FIELD (fini = ) coreaudio_audio_fini, | |
507 | + INIT_FIELD (pcm_ops = ) &coreaudio_pcm_ops, | |
508 | + INIT_FIELD (can_be_default = ) 1, | |
509 | + INIT_FIELD (max_voices_out = ) 1, | |
510 | + INIT_FIELD (max_voices_in = ) 0, | |
511 | + INIT_FIELD (voice_size_out = ) sizeof (coreaudioVoiceOut), | |
512 | + INIT_FIELD (voice_size_in = ) 0 | |
513 | +}; | ... | ... |
audio/dsound_template.h
0 โ 100644
1 | +/* | |
2 | + * QEMU DirectSound audio driver header | |
3 | + * | |
4 | + * Copyright (c) 2005 Vassili Karpov (malc) | |
5 | + * | |
6 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | + * of this software and associated documentation files (the "Software"), to deal | |
8 | + * in the Software without restriction, including without limitation the rights | |
9 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | + * copies of the Software, and to permit persons to whom the Software is | |
11 | + * furnished to do so, subject to the following conditions: | |
12 | + * | |
13 | + * The above copyright notice and this permission notice shall be included in | |
14 | + * all copies or substantial portions of the Software. | |
15 | + * | |
16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | + * THE SOFTWARE. | |
23 | + */ | |
24 | +#ifdef DSBTYPE_IN | |
25 | +#define NAME "capture buffer" | |
26 | +#define TYPE in | |
27 | +#define IFACE IDirectSoundCaptureBuffer | |
28 | +#define BUFPTR LPDIRECTSOUNDCAPTUREBUFFER | |
29 | +#define FIELD dsound_capture_buffer | |
30 | +#else | |
31 | +#define NAME "playback buffer" | |
32 | +#define TYPE out | |
33 | +#define IFACE IDirectSoundBuffer | |
34 | +#define BUFPTR LPDIRECTSOUNDBUFFER | |
35 | +#define FIELD dsound_buffer | |
36 | +#endif | |
37 | + | |
38 | +static int glue (dsound_unlock_, TYPE) ( | |
39 | + BUFPTR buf, | |
40 | + LPVOID p1, | |
41 | + LPVOID p2, | |
42 | + DWORD blen1, | |
43 | + DWORD blen2 | |
44 | + ) | |
45 | +{ | |
46 | + HRESULT hr; | |
47 | + | |
48 | + hr = glue (IFACE, _Unlock) (buf, p1, blen1, p2, blen2); | |
49 | + if (FAILED (hr)) { | |
50 | + dsound_logerr (hr, "Can not unlock " NAME "\n"); | |
51 | + return -1; | |
52 | + } | |
53 | + | |
54 | + return 0; | |
55 | +} | |
56 | + | |
57 | +static int glue (dsound_lock_, TYPE) ( | |
58 | + BUFPTR buf, | |
59 | + struct audio_pcm_info *info, | |
60 | + DWORD pos, | |
61 | + DWORD len, | |
62 | + LPVOID *p1p, | |
63 | + LPVOID *p2p, | |
64 | + DWORD *blen1p, | |
65 | + DWORD *blen2p, | |
66 | + int entire | |
67 | + ) | |
68 | +{ | |
69 | + HRESULT hr; | |
70 | + int i; | |
71 | + LPVOID p1 = NULL, p2 = NULL; | |
72 | + DWORD blen1 = 0, blen2 = 0; | |
73 | + | |
74 | + for (i = 0; i < conf.lock_retries; ++i) { | |
75 | + hr = glue (IFACE, _Lock) ( | |
76 | + buf, | |
77 | + pos, | |
78 | + len, | |
79 | + &p1, | |
80 | + &blen1, | |
81 | + &p2, | |
82 | + &blen2, | |
83 | + (entire | |
84 | +#ifdef DSBTYPE_IN | |
85 | + ? DSCBLOCK_ENTIREBUFFER | |
86 | +#else | |
87 | + ? DSBLOCK_ENTIREBUFFER | |
88 | +#endif | |
89 | + : 0) | |
90 | + ); | |
91 | + | |
92 | + if (FAILED (hr)) { | |
93 | +#ifndef DSBTYPE_IN | |
94 | + if (hr == DSERR_BUFFERLOST) { | |
95 | + if (glue (dsound_restore_, TYPE) (buf)) { | |
96 | + dsound_logerr (hr, "Can not lock " NAME "\n"); | |
97 | + goto fail; | |
98 | + } | |
99 | + continue; | |
100 | + } | |
101 | +#endif | |
102 | + dsound_logerr (hr, "Can not lock " NAME "\n"); | |
103 | + goto fail; | |
104 | + } | |
105 | + | |
106 | + break; | |
107 | + } | |
108 | + | |
109 | + if (i == conf.lock_retries) { | |
110 | + dolog ("%d attempts to lock " NAME " failed\n", i); | |
111 | + goto fail; | |
112 | + } | |
113 | + | |
114 | + if ((p1 && (blen1 & info->align)) || (p2 && (blen2 & info->align))) { | |
115 | + dolog ("DirectSound returned misaligned buffer %ld %ld\n", | |
116 | + blen1, blen2); | |
117 | + glue (dsound_unlock_, TYPE) (buf, p1, p2, blen1, blen2); | |
118 | + goto fail; | |
119 | + } | |
120 | + | |
121 | + if (!p1 && blen1) { | |
122 | + dolog ("warning: !p1 && blen1=%ld\n", blen1); | |
123 | + blen1 = 0; | |
124 | + } | |
125 | + | |
126 | + if (!p2 && blen2) { | |
127 | + dolog ("warning: !p2 && blen2=%ld\n", blen2); | |
128 | + blen2 = 0; | |
129 | + } | |
130 | + | |
131 | + *p1p = p1; | |
132 | + *p2p = p2; | |
133 | + *blen1p = blen1; | |
134 | + *blen2p = blen2; | |
135 | + return 0; | |
136 | + | |
137 | + fail: | |
138 | + *p1p = NULL - 1; | |
139 | + *p2p = NULL - 1; | |
140 | + *blen1p = -1; | |
141 | + *blen2p = -1; | |
142 | + return -1; | |
143 | +} | |
144 | + | |
145 | +#ifdef DSBTYPE_IN | |
146 | +static void dsound_fini_in (HWVoiceIn *hw) | |
147 | +#else | |
148 | +static void dsound_fini_out (HWVoiceOut *hw) | |
149 | +#endif | |
150 | +{ | |
151 | + HRESULT hr; | |
152 | +#ifdef DSBTYPE_IN | |
153 | + DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; | |
154 | +#else | |
155 | + DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; | |
156 | +#endif | |
157 | + | |
158 | + if (ds->FIELD) { | |
159 | + hr = glue (IFACE, _Stop) (ds->FIELD); | |
160 | + if (FAILED (hr)) { | |
161 | + dsound_logerr (hr, "Can not stop " NAME "\n"); | |
162 | + } | |
163 | + | |
164 | + hr = glue (IFACE, _Release) (ds->FIELD); | |
165 | + if (FAILED (hr)) { | |
166 | + dsound_logerr (hr, "Can not release " NAME "\n"); | |
167 | + } | |
168 | + ds->FIELD = NULL; | |
169 | + } | |
170 | +} | |
171 | + | |
172 | +#ifdef DSBTYPE_IN | |
173 | +static int dsound_init_in ( | |
174 | + HWVoiceIn *hw, | |
175 | + int freq, | |
176 | + int nchannels, | |
177 | + audfmt_e fmt | |
178 | + ) | |
179 | +#else | |
180 | +static int dsound_init_out ( | |
181 | + HWVoiceOut *hw, | |
182 | + int freq, | |
183 | + int nchannels, | |
184 | + audfmt_e fmt | |
185 | + ) | |
186 | +#endif | |
187 | +{ | |
188 | + int err; | |
189 | + HRESULT hr; | |
190 | + dsound *s = &glob_dsound; | |
191 | + WAVEFORMATEX wfx; | |
192 | + struct full_fmt full_fmt; | |
193 | +#ifdef DSBTYPE_IN | |
194 | + const char *typ = "ADC"; | |
195 | + DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; | |
196 | + DSCBUFFERDESC bd; | |
197 | + DSCBCAPS bc; | |
198 | +#else | |
199 | + const char *typ = "DAC"; | |
200 | + DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; | |
201 | + DSBUFFERDESC bd; | |
202 | + DSBCAPS bc; | |
203 | +#endif | |
204 | + | |
205 | + full_fmt.freq = freq; | |
206 | + full_fmt.nchannels = nchannels; | |
207 | + full_fmt.fmt = fmt; | |
208 | + err = waveformat_from_full_fmt (&wfx, &full_fmt); | |
209 | + if (err) { | |
210 | + return -1; | |
211 | + } | |
212 | + | |
213 | + memset (&bd, 0, sizeof (bd)); | |
214 | + bd.dwSize = sizeof (bd); | |
215 | + bd.lpwfxFormat = &wfx; | |
216 | +#ifdef DSBTYPE_IN | |
217 | + bd.dwBufferBytes = conf.bufsize_in; | |
218 | + hr = IDirectSoundCapture_CreateCaptureBuffer ( | |
219 | + s->dsound_capture, | |
220 | + &bd, | |
221 | + &ds->dsound_capture_buffer, | |
222 | + NULL | |
223 | + ); | |
224 | +#else | |
225 | + bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2; | |
226 | + bd.dwBufferBytes = conf.bufsize_out; | |
227 | + hr = IDirectSound_CreateSoundBuffer ( | |
228 | + s->dsound, | |
229 | + &bd, | |
230 | + &ds->dsound_buffer, | |
231 | + NULL | |
232 | + ); | |
233 | +#endif | |
234 | + | |
235 | + if (FAILED (hr)) { | |
236 | + dsound_logerr2 (hr, typ, "Can not create " NAME "\n"); | |
237 | + return -1; | |
238 | + } | |
239 | + | |
240 | + hr = glue (IFACE, _GetFormat) ( | |
241 | + ds->FIELD, | |
242 | + &wfx, | |
243 | + sizeof (wfx), | |
244 | + NULL | |
245 | + ); | |
246 | + if (FAILED (hr)) { | |
247 | + dsound_logerr2 (hr, typ, "Can not get " NAME " format\n"); | |
248 | + goto fail0; | |
249 | + } | |
250 | + | |
251 | +#ifdef DEBUG_DSOUND | |
252 | + dolog (NAME "\n"); | |
253 | + print_wave_format (&wfx); | |
254 | +#endif | |
255 | + | |
256 | + memset (&bc, 0, sizeof (bc)); | |
257 | + bc.dwSize = sizeof (bc); | |
258 | + | |
259 | + hr = glue (IFACE, _GetCaps) (ds->FIELD, &bc); | |
260 | + if (FAILED (hr)) { | |
261 | + dsound_logerr2 (hr, typ, "Can not get " NAME " format\n"); | |
262 | + goto fail0; | |
263 | + } | |
264 | + | |
265 | + err = waveformat_to_full_fmt (&wfx, &full_fmt); | |
266 | + if (err) { | |
267 | + goto fail0; | |
268 | + } | |
269 | + | |
270 | + ds->first_time = 1; | |
271 | + hw->bufsize = bc.dwBufferBytes; | |
272 | + audio_pcm_init_info ( | |
273 | + &hw->info, | |
274 | + full_fmt.freq, | |
275 | + full_fmt.nchannels, | |
276 | + full_fmt.fmt, | |
277 | + audio_need_to_swap_endian (0) | |
278 | + ); | |
279 | + | |
280 | +#ifdef DEBUG_DSOUND | |
281 | + dolog ("caps %ld, desc %ld\n", | |
282 | + bc.dwBufferBytes, bd.dwBufferBytes); | |
283 | + | |
284 | + dolog ("bufsize %d, freq %d, chan %d, fmt %d\n", | |
285 | + hw->bufsize, full_fmt.freq, full_fmt.nchannels, full_fmt.fmt); | |
286 | +#endif | |
287 | + return 0; | |
288 | + | |
289 | + fail0: | |
290 | + glue (dsound_fini_, TYPE) (hw); | |
291 | + return -1; | |
292 | +} | |
293 | + | |
294 | +#undef NAME | |
295 | +#undef TYPE | |
296 | +#undef IFACE | |
297 | +#undef BUFPTR | |
298 | +#undef FIELD | ... | ... |