Commit 85571bc7415c3fa9390f5edc3720ec7975219a68
1 parent
8f46820d
audio merge (malc)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1125 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
17 changed files
with
3646 additions
and
73 deletions
Too many changes to show.
To preserve performance only 17 of 24 files are displayed.
Makefile.target
1 | 1 | include config.mak |
2 | 2 | |
3 | +#After enabling Adlib and/or FMOD rebuild QEMU from scratch | |
4 | +#Uncomment following for adlib support | |
5 | +#USE_ADLIB=1 | |
6 | + | |
7 | +#Uncomment following and specify proper paths/names for FMOD support | |
8 | +#USE_FMOD=1 | |
9 | +#FMOD_INCLUDE=/net/include/fmod | |
10 | +#FMOD_LIBPATH=/net/lib | |
11 | +#FMOD_VERSION=3.74 | |
12 | + | |
3 | 13 | TARGET_PATH=$(SRC_PATH)/target-$(TARGET_ARCH) |
4 | -VPATH=$(SRC_PATH):$(TARGET_PATH):$(SRC_PATH)/hw | |
14 | +VPATH=$(SRC_PATH):$(TARGET_PATH):$(SRC_PATH)/hw:$(SRC_PATH)/audio | |
5 | 15 | DEFINES=-I. -I$(TARGET_PATH) -I$(SRC_PATH) |
6 | 16 | ifdef CONFIG_USER_ONLY |
7 | 17 | VPATH+=:$(SRC_PATH)/linux-user |
... | ... | @@ -267,16 +277,31 @@ endif |
267 | 277 | VL_OBJS=vl.o osdep.o block.o readline.o monitor.o pci.o console.o |
268 | 278 | VL_OBJS+=block-cow.o block-qcow.o aes.o block-vmdk.o block-cloop.o |
269 | 279 | |
280 | +SOUND_HW = sb16.o | |
281 | +AUDIODRV = audio.o ossaudio.o sdlaudio.o wavaudio.o | |
282 | + | |
283 | +ifeq ($(USE_ADLIB),1) | |
284 | +SOUND_HW += fmopl.o adlib.o | |
285 | +audio.o: DEFINES := -DUSE_ADLIB $(DEFINES) | |
286 | +endif | |
287 | + | |
288 | +ifeq ($(USE_FMOD),1) | |
289 | +AUDIODRV += fmodaudio.o | |
290 | +audio.o fmodaudio.o: DEFINES := -DUSE_FMOD_AUDIO -I$(FMOD_INCLUDE) $(DEFINES) | |
291 | +LDFLAGS += -L$(FMOD_LIBPATH) -Wl,-rpath,$(FMOD_LIBPATH) | |
292 | +LIBS += -lfmod-$(FMOD_VERSION) | |
293 | +endif | |
294 | + | |
270 | 295 | ifeq ($(TARGET_ARCH), i386) |
271 | 296 | # Hardware support |
272 | -VL_OBJS+= ide.o ne2000.o pckbd.o vga.o sb16.o dma.o oss.o | |
273 | -VL_OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pc.o | |
274 | -VL_OBJS+= cirrus_vga.o | |
297 | +VL_OBJS+= ide.o ne2000.o pckbd.o vga.o $(SOUND_HW) dma.o $(AUDIODRV) | |
298 | +VL_OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pc.o | |
299 | +VL_OBJS+= cirrus_vga.o mixeng.o | |
275 | 300 | endif |
276 | 301 | ifeq ($(TARGET_ARCH), ppc) |
277 | -VL_OBJS+= ppc.o ide.o ne2000.o pckbd.o vga.o sb16.o dma.o oss.o | |
302 | +VL_OBJS+= ppc.o ide.o ne2000.o pckbd.o vga.o $(SOUND_HW) dma.o $(AUDIODRV) | |
278 | 303 | VL_OBJS+= mc146818rtc.o serial.o i8259.o i8254.o fdc.o m48t59.o |
279 | -VL_OBJS+= ppc_prep.o ppc_chrp.o cuda.o adb.o openpic.o | |
304 | +VL_OBJS+= ppc_prep.o ppc_chrp.o cuda.o adb.o openpic.o mixeng.o | |
280 | 305 | endif |
281 | 306 | ifeq ($(TARGET_ARCH), sparc) |
282 | 307 | VL_OBJS+= sun4m.o tcx.o lance.o iommu.o sched.o m48t08.o magic-load.o timer.o |
... | ... | @@ -360,6 +385,8 @@ op.o: op.c op_template.h op_mem.h |
360 | 385 | op_helper.o: op_helper_mem.h |
361 | 386 | endif |
362 | 387 | |
388 | +mixeng.o: mixeng.c mixeng.h mixeng_template.h | |
389 | + | |
363 | 390 | %.o: %.c |
364 | 391 | $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< |
365 | 392 | ... | ... |
audio/audio.c
0 → 100644
1 | +/* | |
2 | + * QEMU Audio subsystem | |
3 | + * | |
4 | + * Copyright (c) 2003-2004 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 <assert.h> | |
25 | +#include <limits.h> | |
26 | +#include "vl.h" | |
27 | + | |
28 | +#define AUDIO_CAP "audio" | |
29 | +#include "audio/audio.h" | |
30 | + | |
31 | +#define USE_SDL_AUDIO | |
32 | +#define USE_WAV_AUDIO | |
33 | + | |
34 | +#if defined __linux__ || (defined _BSD && !defined __APPLE__) | |
35 | +#define USE_OSS_AUDIO | |
36 | +#endif | |
37 | + | |
38 | +#ifdef USE_OSS_AUDIO | |
39 | +#include "audio/ossaudio.h" | |
40 | +#endif | |
41 | + | |
42 | +#ifdef USE_SDL_AUDIO | |
43 | +#include "audio/sdlaudio.h" | |
44 | +#endif | |
45 | + | |
46 | +#ifdef USE_WAV_AUDIO | |
47 | +#include "audio/wavaudio.h" | |
48 | +#endif | |
49 | + | |
50 | +#ifdef USE_FMOD_AUDIO | |
51 | +#include "audio/fmodaudio.h" | |
52 | +#endif | |
53 | + | |
54 | +#define QC_AUDIO_DRV "QEMU_AUDIO_DRV" | |
55 | +#define QC_VOICES "QEMU_VOICES" | |
56 | +#define QC_FIXED_FORMAT "QEMU_FIXED_FORMAT" | |
57 | +#define QC_FIXED_FREQ "QEMU_FIXED_FREQ" | |
58 | + | |
59 | +extern void SB16_init (void); | |
60 | + | |
61 | +#ifdef USE_ADLIB | |
62 | +extern void Adlib_init (void); | |
63 | +#endif | |
64 | + | |
65 | +#ifdef USE_GUS | |
66 | +extern void GUS_init (void); | |
67 | +#endif | |
68 | + | |
69 | +static void (*hw_ctors[]) (void) = { | |
70 | + SB16_init, | |
71 | +#ifdef USE_ADLIB | |
72 | + Adlib_init, | |
73 | +#endif | |
74 | +#ifdef USE_GUS | |
75 | + GUS_init, | |
76 | +#endif | |
77 | + NULL | |
78 | +}; | |
79 | + | |
80 | +static HWVoice *hw_voice; | |
81 | + | |
82 | +AudioState audio_state = { | |
83 | + 1, /* use fixed settings */ | |
84 | + 44100, /* fixed frequency */ | |
85 | + 2, /* fixed channels */ | |
86 | + AUD_FMT_S16, /* fixed format */ | |
87 | + 1, /* number of hw voices */ | |
88 | + -1 /* voice size */ | |
89 | +}; | |
90 | + | |
91 | +/* http://www.df.lth.se/~john_e/gems/gem002d.html */ | |
92 | +/* http://www.multi-platforms.com/Tips/PopCount.htm */ | |
93 | +uint32_t popcount (uint32_t u) | |
94 | +{ | |
95 | + u = ((u&0x55555555) + ((u>>1)&0x55555555)); | |
96 | + u = ((u&0x33333333) + ((u>>2)&0x33333333)); | |
97 | + u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f)); | |
98 | + u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff)); | |
99 | + u = ( u&0x0000ffff) + (u>>16); | |
100 | + return u; | |
101 | +} | |
102 | + | |
103 | +inline uint32_t lsbindex (uint32_t u) | |
104 | +{ | |
105 | + return popcount ((u&-u)-1); | |
106 | +} | |
107 | + | |
108 | +int audio_get_conf_int (const char *key, int defval) | |
109 | +{ | |
110 | + int val = defval; | |
111 | + char *strval; | |
112 | + | |
113 | + strval = getenv (key); | |
114 | + if (strval) { | |
115 | + val = atoi (strval); | |
116 | + } | |
117 | + | |
118 | + return val; | |
119 | +} | |
120 | + | |
121 | +const char *audio_get_conf_str (const char *key, const char *defval) | |
122 | +{ | |
123 | + const char *val = getenv (key); | |
124 | + if (!val) | |
125 | + return defval; | |
126 | + else | |
127 | + return val; | |
128 | +} | |
129 | + | |
130 | +void audio_log (const char *fmt, ...) | |
131 | +{ | |
132 | + va_list ap; | |
133 | + va_start (ap, fmt); | |
134 | + vfprintf (stderr, fmt, ap); | |
135 | + va_end (ap); | |
136 | +} | |
137 | + | |
138 | +/* | |
139 | + * Soft Voice | |
140 | + */ | |
141 | +void pcm_sw_free_resources (SWVoice *sw) | |
142 | +{ | |
143 | + if (sw->buf) qemu_free (sw->buf); | |
144 | + if (sw->rate) st_rate_stop (sw->rate); | |
145 | + sw->buf = NULL; | |
146 | + sw->rate = NULL; | |
147 | +} | |
148 | + | |
149 | +int pcm_sw_alloc_resources (SWVoice *sw) | |
150 | +{ | |
151 | + sw->buf = qemu_mallocz (sw->hw->samples * sizeof (st_sample_t)); | |
152 | + if (!sw->buf) | |
153 | + return -1; | |
154 | + | |
155 | + sw->rate = st_rate_start (sw->freq, sw->hw->freq); | |
156 | + if (!sw->rate) { | |
157 | + qemu_free (sw->buf); | |
158 | + sw->buf = NULL; | |
159 | + return -1; | |
160 | + } | |
161 | + return 0; | |
162 | +} | |
163 | + | |
164 | +void pcm_sw_fini (SWVoice *sw) | |
165 | +{ | |
166 | + pcm_sw_free_resources (sw); | |
167 | +} | |
168 | + | |
169 | +int pcm_sw_init (SWVoice *sw, HWVoice *hw, int freq, | |
170 | + int nchannels, audfmt_e fmt) | |
171 | +{ | |
172 | + int bits = 8, sign = 0; | |
173 | + | |
174 | + switch (fmt) { | |
175 | + case AUD_FMT_S8: | |
176 | + sign = 1; | |
177 | + case AUD_FMT_U8: | |
178 | + break; | |
179 | + | |
180 | + case AUD_FMT_S16: | |
181 | + sign = 1; | |
182 | + case AUD_FMT_U16: | |
183 | + bits = 16; | |
184 | + break; | |
185 | + } | |
186 | + | |
187 | + sw->hw = hw; | |
188 | + sw->freq = freq; | |
189 | + sw->fmt = fmt; | |
190 | + sw->nchannels = nchannels; | |
191 | + sw->shift = (nchannels == 2) + (bits == 16); | |
192 | + sw->align = (1 << sw->shift) - 1; | |
193 | + sw->left = 0; | |
194 | + sw->pos = 0; | |
195 | + sw->wpos = 0; | |
196 | + sw->live = 0; | |
197 | + sw->ratio = (sw->hw->freq * ((int64_t) INT_MAX)) / sw->freq; | |
198 | + sw->bytes_per_second = sw->freq << sw->shift; | |
199 | + sw->conv = mixeng_conv[nchannels == 2][sign][bits == 16]; | |
200 | + | |
201 | + pcm_sw_free_resources (sw); | |
202 | + return pcm_sw_alloc_resources (sw); | |
203 | +} | |
204 | + | |
205 | +/* Hard voice */ | |
206 | +void pcm_hw_free_resources (HWVoice *hw) | |
207 | +{ | |
208 | + if (hw->mix_buf) | |
209 | + qemu_free (hw->mix_buf); | |
210 | + hw->mix_buf = NULL; | |
211 | +} | |
212 | + | |
213 | +int pcm_hw_alloc_resources (HWVoice *hw) | |
214 | +{ | |
215 | + hw->mix_buf = qemu_mallocz (hw->samples * sizeof (st_sample_t)); | |
216 | + if (!hw->mix_buf) | |
217 | + return -1; | |
218 | + return 0; | |
219 | +} | |
220 | + | |
221 | + | |
222 | +void pcm_hw_fini (HWVoice *hw) | |
223 | +{ | |
224 | + if (hw->active) { | |
225 | + ldebug ("pcm_hw_fini: %d %d %d\n", hw->freq, hw->nchannels, hw->fmt); | |
226 | + pcm_hw_free_resources (hw); | |
227 | + hw->pcm_ops->fini (hw); | |
228 | + memset (hw, 0, audio_state.drv->voice_size); | |
229 | + } | |
230 | +} | |
231 | + | |
232 | +void pcm_hw_gc (HWVoice *hw) | |
233 | +{ | |
234 | + if (hw->nb_voices) | |
235 | + return; | |
236 | + | |
237 | + pcm_hw_fini (hw); | |
238 | +} | |
239 | + | |
240 | +int pcm_hw_get_live (HWVoice *hw) | |
241 | +{ | |
242 | + int i, alive = 0, live = hw->samples; | |
243 | + | |
244 | + for (i = 0; i < hw->nb_voices; i++) { | |
245 | + if (hw->pvoice[i]->live) { | |
246 | + live = audio_MIN (hw->pvoice[i]->live, live); | |
247 | + alive += 1; | |
248 | + } | |
249 | + } | |
250 | + | |
251 | + if (alive) | |
252 | + return live; | |
253 | + else | |
254 | + return -1; | |
255 | +} | |
256 | + | |
257 | +int pcm_hw_get_live2 (HWVoice *hw, int *nb_active) | |
258 | +{ | |
259 | + int i, alive = 0, live = hw->samples; | |
260 | + | |
261 | + *nb_active = 0; | |
262 | + for (i = 0; i < hw->nb_voices; i++) { | |
263 | + if (hw->pvoice[i]->live) { | |
264 | + if (hw->pvoice[i]->live < live) { | |
265 | + *nb_active = hw->pvoice[i]->active != 0; | |
266 | + live = hw->pvoice[i]->live; | |
267 | + } | |
268 | + alive += 1; | |
269 | + } | |
270 | + } | |
271 | + | |
272 | + if (alive) | |
273 | + return live; | |
274 | + else | |
275 | + return -1; | |
276 | +} | |
277 | + | |
278 | +void pcm_hw_dec_live (HWVoice *hw, int decr) | |
279 | +{ | |
280 | + int i; | |
281 | + | |
282 | + for (i = 0; i < hw->nb_voices; i++) { | |
283 | + if (hw->pvoice[i]->live) { | |
284 | + hw->pvoice[i]->live -= decr; | |
285 | + } | |
286 | + } | |
287 | +} | |
288 | + | |
289 | +void pcm_hw_clear (HWVoice *hw, void *buf, int len) | |
290 | +{ | |
291 | + if (!len) | |
292 | + return; | |
293 | + | |
294 | + switch (hw->fmt) { | |
295 | + case AUD_FMT_S16: | |
296 | + case AUD_FMT_S8: | |
297 | + memset (buf, len << hw->shift, 0x00); | |
298 | + break; | |
299 | + | |
300 | + case AUD_FMT_U8: | |
301 | + memset (buf, len << hw->shift, 0x80); | |
302 | + break; | |
303 | + | |
304 | + case AUD_FMT_U16: | |
305 | + { | |
306 | + unsigned int i; | |
307 | + uint16_t *p = buf; | |
308 | + int shift = hw->nchannels - 1; | |
309 | + | |
310 | + for (i = 0; i < len << shift; i++) { | |
311 | + p[i] = INT16_MAX; | |
312 | + } | |
313 | + } | |
314 | + break; | |
315 | + } | |
316 | +} | |
317 | + | |
318 | +int pcm_hw_write (SWVoice *sw, void *buf, int size) | |
319 | +{ | |
320 | + int hwsamples, samples, isamp, osamp, wpos, live, dead, left, swlim, blck; | |
321 | + int ret = 0, pos = 0; | |
322 | + if (!sw) | |
323 | + return size; | |
324 | + | |
325 | + hwsamples = sw->hw->samples; | |
326 | + samples = size >> sw->shift; | |
327 | + | |
328 | + if (!sw->live) { | |
329 | + sw->wpos = sw->hw->rpos; | |
330 | + } | |
331 | + wpos = sw->wpos; | |
332 | + live = sw->live; | |
333 | + dead = hwsamples - live; | |
334 | + swlim = (dead * ((int64_t) INT_MAX)) / sw->ratio; | |
335 | + swlim = audio_MIN (swlim, samples); | |
336 | + | |
337 | + ldebug ("size=%d live=%d dead=%d swlim=%d wpos=%d\n", | |
338 | + size, live, dead, swlim, wpos); | |
339 | + if (swlim) | |
340 | + sw->conv (sw->buf, buf, swlim); | |
341 | + | |
342 | + while (swlim) { | |
343 | + dead = hwsamples - live; | |
344 | + left = hwsamples - wpos; | |
345 | + blck = audio_MIN (dead, left); | |
346 | + if (!blck) { | |
347 | + /* dolog ("swlim=%d\n", swlim); */ | |
348 | + break; | |
349 | + } | |
350 | + isamp = swlim; | |
351 | + osamp = blck; | |
352 | + st_rate_flow (sw->rate, sw->buf + pos, sw->hw->mix_buf + wpos, &isamp, &osamp); | |
353 | + ret += isamp; | |
354 | + swlim -= isamp; | |
355 | + pos += isamp; | |
356 | + live += osamp; | |
357 | + wpos = (wpos + osamp) % hwsamples; | |
358 | + } | |
359 | + | |
360 | + sw->wpos = wpos; | |
361 | + sw->live = live; | |
362 | + return ret << sw->shift; | |
363 | +} | |
364 | + | |
365 | +int pcm_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt) | |
366 | +{ | |
367 | + int sign = 0, bits = 8; | |
368 | + | |
369 | + pcm_hw_fini (hw); | |
370 | + ldebug ("pcm_hw_init: %d %d %d\n", freq, nchannels, fmt); | |
371 | + if (hw->pcm_ops->init (hw, freq, nchannels, fmt)) { | |
372 | + memset (hw, 0, audio_state.drv->voice_size); | |
373 | + return -1; | |
374 | + } | |
375 | + | |
376 | + switch (hw->fmt) { | |
377 | + case AUD_FMT_S8: | |
378 | + sign = 1; | |
379 | + case AUD_FMT_U8: | |
380 | + break; | |
381 | + | |
382 | + case AUD_FMT_S16: | |
383 | + sign = 1; | |
384 | + case AUD_FMT_U16: | |
385 | + bits = 16; | |
386 | + break; | |
387 | + } | |
388 | + | |
389 | + hw->nb_voices = 0; | |
390 | + hw->active = 1; | |
391 | + hw->shift = (hw->nchannels == 2) + (bits == 16); | |
392 | + hw->bytes_per_second = hw->freq << hw->shift; | |
393 | + hw->align = (1 << hw->shift) - 1; | |
394 | + hw->samples = hw->bufsize >> hw->shift; | |
395 | + hw->clip = mixeng_clip[hw->nchannels == 2][sign][bits == 16]; | |
396 | + if (pcm_hw_alloc_resources (hw)) { | |
397 | + pcm_hw_fini (hw); | |
398 | + return -1; | |
399 | + } | |
400 | + return 0; | |
401 | +} | |
402 | + | |
403 | +static int dist (void *hw) | |
404 | +{ | |
405 | + if (hw) { | |
406 | + return (((uint8_t *) hw - (uint8_t *) hw_voice) | |
407 | + / audio_state.voice_size) + 1; | |
408 | + } | |
409 | + else { | |
410 | + return 0; | |
411 | + } | |
412 | +} | |
413 | + | |
414 | +#define ADVANCE(hw) hw ? advance (hw, audio_state.voice_size) : hw_voice | |
415 | + | |
416 | +HWVoice *pcm_hw_find_any (HWVoice *hw) | |
417 | +{ | |
418 | + int i = dist (hw); | |
419 | + for (; i < audio_state.nb_hw_voices; i++) { | |
420 | + hw = ADVANCE (hw); | |
421 | + return hw; | |
422 | + } | |
423 | + return NULL; | |
424 | +} | |
425 | + | |
426 | +HWVoice *pcm_hw_find_any_active (HWVoice *hw) | |
427 | +{ | |
428 | + int i = dist (hw); | |
429 | + for (; i < audio_state.nb_hw_voices; i++) { | |
430 | + hw = ADVANCE (hw); | |
431 | + if (hw->active) | |
432 | + return hw; | |
433 | + } | |
434 | + return NULL; | |
435 | +} | |
436 | + | |
437 | +HWVoice *pcm_hw_find_any_active_enabled (HWVoice *hw) | |
438 | +{ | |
439 | + int i = dist (hw); | |
440 | + for (; i < audio_state.nb_hw_voices; i++) { | |
441 | + hw = ADVANCE (hw); | |
442 | + if (hw->active && hw->enabled) | |
443 | + return hw; | |
444 | + } | |
445 | + return NULL; | |
446 | +} | |
447 | + | |
448 | +HWVoice *pcm_hw_find_any_passive (HWVoice *hw) | |
449 | +{ | |
450 | + int i = dist (hw); | |
451 | + for (; i < audio_state.nb_hw_voices; i++) { | |
452 | + hw = ADVANCE (hw); | |
453 | + if (!hw->active) | |
454 | + return hw; | |
455 | + } | |
456 | + return NULL; | |
457 | +} | |
458 | + | |
459 | +HWVoice *pcm_hw_find_specific (HWVoice *hw, int freq, | |
460 | + int nchannels, audfmt_e fmt) | |
461 | +{ | |
462 | + while ((hw = pcm_hw_find_any_active (hw))) { | |
463 | + if (hw->freq == freq && | |
464 | + hw->nchannels == nchannels && | |
465 | + hw->fmt == fmt) | |
466 | + return hw; | |
467 | + } | |
468 | + return NULL; | |
469 | +} | |
470 | + | |
471 | +HWVoice *pcm_hw_add (int freq, int nchannels, audfmt_e fmt) | |
472 | +{ | |
473 | + HWVoice *hw; | |
474 | + | |
475 | + if (audio_state.fixed_format) { | |
476 | + freq = audio_state.fixed_freq; | |
477 | + nchannels = audio_state.fixed_channels; | |
478 | + fmt = audio_state.fixed_fmt; | |
479 | + } | |
480 | + | |
481 | + hw = pcm_hw_find_specific (NULL, freq, nchannels, fmt); | |
482 | + | |
483 | + if (hw) | |
484 | + return hw; | |
485 | + | |
486 | + hw = pcm_hw_find_any_passive (NULL); | |
487 | + if (hw) { | |
488 | + hw->pcm_ops = audio_state.drv->pcm_ops; | |
489 | + if (!hw->pcm_ops) | |
490 | + return NULL; | |
491 | + | |
492 | + if (pcm_hw_init (hw, freq, nchannels, fmt)) { | |
493 | + pcm_hw_gc (hw); | |
494 | + return NULL; | |
495 | + } | |
496 | + else | |
497 | + return hw; | |
498 | + } | |
499 | + | |
500 | + return pcm_hw_find_any (NULL); | |
501 | +} | |
502 | + | |
503 | +int pcm_hw_add_sw (HWVoice *hw, SWVoice *sw) | |
504 | +{ | |
505 | + SWVoice **pvoice = qemu_mallocz ((hw->nb_voices + 1) * sizeof (sw)); | |
506 | + if (!pvoice) | |
507 | + return -1; | |
508 | + | |
509 | + memcpy (pvoice, hw->pvoice, hw->nb_voices * sizeof (sw)); | |
510 | + qemu_free (hw->pvoice); | |
511 | + hw->pvoice = pvoice; | |
512 | + hw->pvoice[hw->nb_voices++] = sw; | |
513 | + return 0; | |
514 | +} | |
515 | + | |
516 | +int pcm_hw_del_sw (HWVoice *hw, SWVoice *sw) | |
517 | +{ | |
518 | + int i, j; | |
519 | + if (hw->nb_voices > 1) { | |
520 | + SWVoice **pvoice = qemu_mallocz ((hw->nb_voices - 1) * sizeof (sw)); | |
521 | + | |
522 | + if (!pvoice) { | |
523 | + dolog ("Can not maintain consistent state (not enough memory)\n"); | |
524 | + return -1; | |
525 | + } | |
526 | + | |
527 | + for (i = 0, j = 0; i < hw->nb_voices; i++) { | |
528 | + if (j >= hw->nb_voices - 1) { | |
529 | + dolog ("Can not maintain consistent state " | |
530 | + "(invariant violated)\n"); | |
531 | + return -1; | |
532 | + } | |
533 | + if (hw->pvoice[i] != sw) | |
534 | + pvoice[j++] = hw->pvoice[i]; | |
535 | + } | |
536 | + qemu_free (hw->pvoice); | |
537 | + hw->pvoice = pvoice; | |
538 | + hw->nb_voices -= 1; | |
539 | + } | |
540 | + else { | |
541 | + qemu_free (hw->pvoice); | |
542 | + hw->pvoice = NULL; | |
543 | + hw->nb_voices = 0; | |
544 | + } | |
545 | + return 0; | |
546 | +} | |
547 | + | |
548 | +SWVoice *pcm_create_voice_pair (int freq, int nchannels, audfmt_e fmt) | |
549 | +{ | |
550 | + SWVoice *sw; | |
551 | + HWVoice *hw; | |
552 | + | |
553 | + sw = qemu_mallocz (sizeof (*sw)); | |
554 | + if (!sw) | |
555 | + goto err1; | |
556 | + | |
557 | + hw = pcm_hw_add (freq, nchannels, fmt); | |
558 | + if (!hw) | |
559 | + goto err2; | |
560 | + | |
561 | + if (pcm_hw_add_sw (hw, sw)) | |
562 | + goto err3; | |
563 | + | |
564 | + if (pcm_sw_init (sw, hw, freq, nchannels, fmt)) | |
565 | + goto err4; | |
566 | + | |
567 | + return sw; | |
568 | + | |
569 | +err4: | |
570 | + pcm_hw_del_sw (hw, sw); | |
571 | +err3: | |
572 | + pcm_hw_gc (hw); | |
573 | +err2: | |
574 | + qemu_free (sw); | |
575 | +err1: | |
576 | + return NULL; | |
577 | +} | |
578 | + | |
579 | +SWVoice *AUD_open (SWVoice *sw, const char *name, | |
580 | + int freq, int nchannels, audfmt_e fmt) | |
581 | +{ | |
582 | + if (!audio_state.drv) { | |
583 | + return NULL; | |
584 | + } | |
585 | + | |
586 | + if (sw && freq == sw->freq && sw->nchannels == nchannels && sw->fmt == fmt) { | |
587 | + return sw; | |
588 | + } | |
589 | + | |
590 | + if (sw) { | |
591 | + ldebug ("Different format %s %d %d %d\n", | |
592 | + name, | |
593 | + sw->freq == freq, | |
594 | + sw->nchannels == nchannels, | |
595 | + sw->fmt == fmt); | |
596 | + } | |
597 | + | |
598 | + if (nchannels != 1 && nchannels != 2) { | |
599 | + dolog ("Bogus channel count %d for voice %s\n", nchannels, name); | |
600 | + return NULL; | |
601 | + } | |
602 | + | |
603 | + if (!audio_state.fixed_format && sw) { | |
604 | + pcm_sw_fini (sw); | |
605 | + pcm_hw_del_sw (sw->hw, sw); | |
606 | + pcm_hw_gc (sw->hw); | |
607 | + if (sw->name) { | |
608 | + qemu_free (sw->name); | |
609 | + sw->name = NULL; | |
610 | + } | |
611 | + qemu_free (sw); | |
612 | + sw = NULL; | |
613 | + } | |
614 | + | |
615 | + if (sw) { | |
616 | + HWVoice *hw = sw->hw; | |
617 | + if (!hw) { | |
618 | + dolog ("Internal logic error voice %s has no hardware store\n", | |
619 | + name); | |
620 | + return sw; | |
621 | + } | |
622 | + | |
623 | + if (pcm_sw_init (sw, hw, freq, nchannels, fmt)) { | |
624 | + pcm_sw_fini (sw); | |
625 | + pcm_hw_del_sw (hw, sw); | |
626 | + pcm_hw_gc (hw); | |
627 | + if (sw->name) { | |
628 | + qemu_free (sw->name); | |
629 | + sw->name = NULL; | |
630 | + } | |
631 | + qemu_free (sw); | |
632 | + return NULL; | |
633 | + } | |
634 | + } | |
635 | + else { | |
636 | + sw = pcm_create_voice_pair (freq, nchannels, fmt); | |
637 | + if (!sw) { | |
638 | + dolog ("Failed to create voice %s\n", name); | |
639 | + return NULL; | |
640 | + } | |
641 | + } | |
642 | + | |
643 | + if (sw->name) { | |
644 | + qemu_free (sw->name); | |
645 | + sw->name = NULL; | |
646 | + } | |
647 | + sw->name = qemu_strdup (name); | |
648 | + return sw; | |
649 | +} | |
650 | + | |
651 | +int AUD_write (SWVoice *sw, void *buf, int size) | |
652 | +{ | |
653 | + int bytes; | |
654 | + | |
655 | + if (!sw->hw->enabled) | |
656 | + dolog ("Writing to disabled voice %s\n", sw->name); | |
657 | + bytes = sw->hw->pcm_ops->write (sw, buf, size); | |
658 | + return bytes; | |
659 | +} | |
660 | + | |
661 | +void AUD_run (void) | |
662 | +{ | |
663 | + HWVoice *hw = NULL; | |
664 | + | |
665 | + while ((hw = pcm_hw_find_any_active_enabled (hw))) { | |
666 | + int i; | |
667 | + if (hw->pending_disable && pcm_hw_get_live (hw) <= 0) { | |
668 | + hw->enabled = 0; | |
669 | + hw->pcm_ops->ctl (hw, VOICE_DISABLE); | |
670 | + for (i = 0; i < hw->nb_voices; i++) { | |
671 | + hw->pvoice[i]->live = 0; | |
672 | + /* hw->pvoice[i]->old_ticks = 0; */ | |
673 | + } | |
674 | + continue; | |
675 | + } | |
676 | + | |
677 | + hw->pcm_ops->run (hw); | |
678 | + assert (hw->rpos < hw->samples); | |
679 | + for (i = 0; i < hw->nb_voices; i++) { | |
680 | + SWVoice *sw = hw->pvoice[i]; | |
681 | + if (!sw->active && !sw->live && sw->old_ticks) { | |
682 | + int64_t delta = qemu_get_clock (vm_clock) - sw->old_ticks; | |
683 | + if (delta > audio_state.ticks_threshold) { | |
684 | + ldebug ("resetting old_ticks for %s\n", sw->name); | |
685 | + sw->old_ticks = 0; | |
686 | + } | |
687 | + } | |
688 | + } | |
689 | + } | |
690 | +} | |
691 | + | |
692 | +int AUD_get_free (SWVoice *sw) | |
693 | +{ | |
694 | + int free; | |
695 | + | |
696 | + if (!sw) | |
697 | + return 4096; | |
698 | + | |
699 | + free = ((sw->hw->samples - sw->live) << sw->hw->shift) * sw->ratio | |
700 | + / INT_MAX; | |
701 | + | |
702 | + free &= ~sw->hw->align; | |
703 | + if (!free) return 0; | |
704 | + | |
705 | + return free; | |
706 | +} | |
707 | + | |
708 | +int AUD_get_buffer_size (SWVoice *sw) | |
709 | +{ | |
710 | + return sw->hw->bufsize; | |
711 | +} | |
712 | + | |
713 | +void AUD_adjust (SWVoice *sw, int bytes) | |
714 | +{ | |
715 | + if (!sw) | |
716 | + return; | |
717 | + sw->old_ticks += (ticks_per_sec * (int64_t) bytes) / sw->bytes_per_second; | |
718 | +} | |
719 | + | |
720 | +void AUD_reset (SWVoice *sw) | |
721 | +{ | |
722 | + sw->active = 0; | |
723 | + sw->old_ticks = 0; | |
724 | +} | |
725 | + | |
726 | +int AUD_calc_elapsed (SWVoice *sw) | |
727 | +{ | |
728 | + int64_t now, delta, bytes; | |
729 | + int dead, swlim; | |
730 | + | |
731 | + if (!sw) | |
732 | + return 0; | |
733 | + | |
734 | + now = qemu_get_clock (vm_clock); | |
735 | + delta = now - sw->old_ticks; | |
736 | + bytes = (delta * sw->bytes_per_second) / ticks_per_sec; | |
737 | + if (delta < 0) { | |
738 | + dolog ("whoops delta(<0)=%lld\n", delta); | |
739 | + return 0; | |
740 | + } | |
741 | + | |
742 | + dead = sw->hw->samples - sw->live; | |
743 | + swlim = ((dead * (int64_t) INT_MAX) / sw->ratio); | |
744 | + | |
745 | + if (bytes > swlim) { | |
746 | + return swlim; | |
747 | + } | |
748 | + else { | |
749 | + return bytes; | |
750 | + } | |
751 | +} | |
752 | + | |
753 | +void AUD_enable (SWVoice *sw, int on) | |
754 | +{ | |
755 | + int i; | |
756 | + HWVoice *hw; | |
757 | + | |
758 | + if (!sw) | |
759 | + return; | |
760 | + | |
761 | + hw = sw->hw; | |
762 | + if (on) { | |
763 | + if (!sw->live) | |
764 | + sw->wpos = sw->hw->rpos; | |
765 | + if (!sw->old_ticks) { | |
766 | + sw->old_ticks = qemu_get_clock (vm_clock); | |
767 | + } | |
768 | + } | |
769 | + | |
770 | + if (sw->active != on) { | |
771 | + if (on) { | |
772 | + hw->pending_disable = 0; | |
773 | + if (!hw->enabled) { | |
774 | + hw->enabled = 1; | |
775 | + for (i = 0; i < hw->nb_voices; i++) { | |
776 | + ldebug ("resetting voice\n"); | |
777 | + sw = hw->pvoice[i]; | |
778 | + sw->old_ticks = qemu_get_clock (vm_clock); | |
779 | + } | |
780 | + hw->pcm_ops->ctl (hw, VOICE_ENABLE); | |
781 | + } | |
782 | + } | |
783 | + else { | |
784 | + if (hw->enabled && !hw->pending_disable) { | |
785 | + int nb_active = 0; | |
786 | + for (i = 0; i < hw->nb_voices; i++) { | |
787 | + nb_active += hw->pvoice[i]->active != 0; | |
788 | + } | |
789 | + | |
790 | + if (nb_active == 1) { | |
791 | + hw->pending_disable = 1; | |
792 | + } | |
793 | + } | |
794 | + } | |
795 | + sw->active = on; | |
796 | + } | |
797 | +} | |
798 | + | |
799 | +static struct audio_output_driver *drvtab[] = { | |
800 | +#ifdef USE_OSS_AUDIO | |
801 | + &oss_output_driver, | |
802 | +#endif | |
803 | +#ifdef USE_FMOD_AUDIO | |
804 | + &fmod_output_driver, | |
805 | +#endif | |
806 | +#ifdef USE_SDL_AUDIO | |
807 | + &sdl_output_driver, | |
808 | +#endif | |
809 | +#ifdef USE_WAV_AUDIO | |
810 | + &wav_output_driver, | |
811 | +#endif | |
812 | +}; | |
813 | + | |
814 | +static int voice_init (struct audio_output_driver *drv) | |
815 | +{ | |
816 | + audio_state.opaque = drv->init (); | |
817 | + if (audio_state.opaque) { | |
818 | + if (audio_state.nb_hw_voices > drv->max_voices) { | |
819 | + dolog ("`%s' does not support %d multiple hardware channels\n" | |
820 | + "Resetting to %d\n", | |
821 | + drv->name, audio_state.nb_hw_voices, drv->max_voices); | |
822 | + audio_state.nb_hw_voices = drv->max_voices; | |
823 | + } | |
824 | + hw_voice = qemu_mallocz (audio_state.nb_hw_voices * drv->voice_size); | |
825 | + if (hw_voice) { | |
826 | + audio_state.drv = drv; | |
827 | + return 1; | |
828 | + } | |
829 | + else { | |
830 | + dolog ("Not enough memory for %d `%s' voices (each %d bytes)\n", | |
831 | + audio_state.nb_hw_voices, drv->name, drv->voice_size); | |
832 | + drv->fini (audio_state.opaque); | |
833 | + return 0; | |
834 | + } | |
835 | + } | |
836 | + else { | |
837 | + dolog ("Could not init `%s' audio\n", drv->name); | |
838 | + return 0; | |
839 | + } | |
840 | +} | |
841 | + | |
842 | +static void audio_vm_stop_handler (void *opaque, int reason) | |
843 | +{ | |
844 | + HWVoice *hw = NULL; | |
845 | + | |
846 | + while ((hw = pcm_hw_find_any (hw))) { | |
847 | + if (!hw->pcm_ops) | |
848 | + continue; | |
849 | + | |
850 | + hw->pcm_ops->ctl (hw, reason ? VOICE_ENABLE : VOICE_DISABLE); | |
851 | + } | |
852 | +} | |
853 | + | |
854 | +static void audio_atexit (void) | |
855 | +{ | |
856 | + HWVoice *hw = NULL; | |
857 | + | |
858 | + while ((hw = pcm_hw_find_any (hw))) { | |
859 | + if (!hw->pcm_ops) | |
860 | + continue; | |
861 | + | |
862 | + hw->pcm_ops->ctl (hw, VOICE_DISABLE); | |
863 | + hw->pcm_ops->fini (hw); | |
864 | + } | |
865 | + audio_state.drv->fini (audio_state.opaque); | |
866 | +} | |
867 | + | |
868 | +static void audio_save (QEMUFile *f, void *opaque) | |
869 | +{ | |
870 | +} | |
871 | + | |
872 | +static int audio_load (QEMUFile *f, void *opaque, int version_id) | |
873 | +{ | |
874 | + if (version_id != 1) | |
875 | + return -EINVAL; | |
876 | + | |
877 | + return 0; | |
878 | +} | |
879 | + | |
880 | +void AUD_init (void) | |
881 | +{ | |
882 | + int i; | |
883 | + int done = 0; | |
884 | + const char *drvname; | |
885 | + | |
886 | + audio_state.fixed_format = | |
887 | + !!audio_get_conf_int (QC_FIXED_FORMAT, audio_state.fixed_format); | |
888 | + audio_state.fixed_freq = | |
889 | + audio_get_conf_int (QC_FIXED_FREQ, audio_state.fixed_freq); | |
890 | + audio_state.nb_hw_voices = | |
891 | + audio_get_conf_int (QC_VOICES, audio_state.nb_hw_voices); | |
892 | + | |
893 | + if (audio_state.nb_hw_voices <= 0) { | |
894 | + dolog ("Bogus number of voices %d, resetting to 1\n", | |
895 | + audio_state.nb_hw_voices); | |
896 | + } | |
897 | + | |
898 | + drvname = audio_get_conf_str (QC_AUDIO_DRV, NULL); | |
899 | + if (drvname) { | |
900 | + int found = 0; | |
901 | + for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) { | |
902 | + if (!strcmp (drvname, drvtab[i]->name)) { | |
903 | + done = voice_init (drvtab[i]); | |
904 | + found = 1; | |
905 | + break; | |
906 | + } | |
907 | + } | |
908 | + if (!found) { | |
909 | + dolog ("Unknown audio driver `%s'\n", drvname); | |
910 | + } | |
911 | + } | |
912 | + | |
913 | + qemu_add_vm_stop_handler (audio_vm_stop_handler, NULL); | |
914 | + atexit (audio_atexit); | |
915 | + | |
916 | + if (!done) { | |
917 | + for (i = 0; !done && i < sizeof (drvtab) / sizeof (drvtab[0]); i++) { | |
918 | + if (drvtab[i]->can_be_default) | |
919 | + done = voice_init (drvtab[i]); | |
920 | + } | |
921 | + } | |
922 | + | |
923 | + audio_state.ticks_threshold = ticks_per_sec / 50; | |
924 | + audio_state.freq_threshold = 100; | |
925 | + | |
926 | + register_savevm ("audio", 0, 1, audio_save, audio_load, NULL); | |
927 | + if (!done) { | |
928 | + dolog ("Can not initialize audio subsystem\n"); | |
929 | + return; | |
930 | + } | |
931 | + | |
932 | + for (i = 0; hw_ctors[i]; i++) { | |
933 | + hw_ctors[i] (); | |
934 | + } | |
935 | +} | ... | ... |
audio/audio.h
0 → 100644
1 | +/* | |
2 | + * QEMU Audio subsystem header | |
3 | + * | |
4 | + * Copyright (c) 2003-2004 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 | +#ifndef QEMU_AUDIO_H | |
25 | +#define QEMU_AUDIO_H | |
26 | + | |
27 | +#include "mixeng.h" | |
28 | + | |
29 | +#define dolog(...) fprintf (stderr, AUDIO_CAP ": " __VA_ARGS__) | |
30 | +#ifdef DEBUG | |
31 | +#define ldebug(...) dolog (__VA_ARGS__) | |
32 | +#else | |
33 | +#define ldebug(...) | |
34 | +#endif | |
35 | + | |
36 | +typedef enum { | |
37 | + AUD_FMT_U8, | |
38 | + AUD_FMT_S8, | |
39 | + AUD_FMT_U16, | |
40 | + AUD_FMT_S16 | |
41 | +} audfmt_e; | |
42 | + | |
43 | +typedef struct HWVoice HWVoice; | |
44 | +struct audio_output_driver; | |
45 | + | |
46 | +typedef struct AudioState { | |
47 | + int fixed_format; | |
48 | + int fixed_freq; | |
49 | + int fixed_channels; | |
50 | + int fixed_fmt; | |
51 | + int nb_hw_voices; | |
52 | + int voice_size; | |
53 | + int64_t ticks_threshold; | |
54 | + int freq_threshold; | |
55 | + void *opaque; | |
56 | + struct audio_output_driver *drv; | |
57 | +} AudioState; | |
58 | + | |
59 | +extern AudioState audio_state; | |
60 | + | |
61 | +typedef struct SWVoice { | |
62 | + int freq; | |
63 | + audfmt_e fmt; | |
64 | + int nchannels; | |
65 | + | |
66 | + int shift; | |
67 | + int align; | |
68 | + | |
69 | + t_sample *conv; | |
70 | + | |
71 | + int left; | |
72 | + int pos; | |
73 | + int bytes_per_second; | |
74 | + int64_t ratio; | |
75 | + st_sample_t *buf; | |
76 | + void *rate; | |
77 | + | |
78 | + int wpos; | |
79 | + int live; | |
80 | + int active; | |
81 | + int64_t old_ticks; | |
82 | + HWVoice *hw; | |
83 | + char *name; | |
84 | +} SWVoice; | |
85 | + | |
86 | +#define VOICE_ENABLE 1 | |
87 | +#define VOICE_DISABLE 2 | |
88 | + | |
89 | +struct pcm_ops { | |
90 | + int (*init) (HWVoice *hw, int freq, int nchannels, audfmt_e fmt); | |
91 | + void (*fini) (HWVoice *hw); | |
92 | + void (*run) (HWVoice *hw); | |
93 | + int (*write) (SWVoice *sw, void *buf, int size); | |
94 | + int (*ctl) (HWVoice *hw, int cmd, ...); | |
95 | +}; | |
96 | + | |
97 | +struct audio_output_driver { | |
98 | + const char *name; | |
99 | + void *(*init) (void); | |
100 | + void (*fini) (void *); | |
101 | + struct pcm_ops *pcm_ops; | |
102 | + int can_be_default; | |
103 | + int max_voices; | |
104 | + int voice_size; | |
105 | +}; | |
106 | + | |
107 | +struct HWVoice { | |
108 | + int active; | |
109 | + int enabled; | |
110 | + int pending_disable; | |
111 | + int valid; | |
112 | + int freq; | |
113 | + | |
114 | + f_sample *clip; | |
115 | + audfmt_e fmt; | |
116 | + int nchannels; | |
117 | + | |
118 | + int align; | |
119 | + int shift; | |
120 | + | |
121 | + int rpos; | |
122 | + int bufsize; | |
123 | + | |
124 | + int bytes_per_second; | |
125 | + st_sample_t *mix_buf; | |
126 | + | |
127 | + int samples; | |
128 | + int64_t old_ticks; | |
129 | + int nb_voices; | |
130 | + struct SWVoice **pvoice; | |
131 | + struct pcm_ops *pcm_ops; | |
132 | +}; | |
133 | + | |
134 | +void audio_log (const char *fmt, ...); | |
135 | +void pcm_sw_free_resources (SWVoice *sw); | |
136 | +int pcm_sw_alloc_resources (SWVoice *sw); | |
137 | +void pcm_sw_fini (SWVoice *sw); | |
138 | +int pcm_sw_init (SWVoice *sw, HWVoice *hw, int freq, | |
139 | + int nchannels, audfmt_e fmt); | |
140 | + | |
141 | +void pcm_hw_clear (HWVoice *hw, void *buf, int len); | |
142 | +HWVoice * pcm_hw_find_any (HWVoice *hw); | |
143 | +HWVoice * pcm_hw_find_any_active (HWVoice *hw); | |
144 | +HWVoice * pcm_hw_find_any_passive (HWVoice *hw); | |
145 | +HWVoice * pcm_hw_find_specific (HWVoice *hw, int freq, | |
146 | + int nchannels, audfmt_e fmt); | |
147 | +HWVoice * pcm_hw_add (int freq, int nchannels, audfmt_e fmt); | |
148 | +int pcm_hw_add_sw (HWVoice *hw, SWVoice *sw); | |
149 | +int pcm_hw_del_sw (HWVoice *hw, SWVoice *sw); | |
150 | +SWVoice * pcm_create_voice_pair (int freq, int nchannels, audfmt_e fmt); | |
151 | + | |
152 | +void pcm_hw_free_resources (HWVoice *hw); | |
153 | +int pcm_hw_alloc_resources (HWVoice *hw); | |
154 | +void pcm_hw_fini (HWVoice *hw); | |
155 | +void pcm_hw_gc (HWVoice *hw); | |
156 | +int pcm_hw_get_live (HWVoice *hw); | |
157 | +int pcm_hw_get_live2 (HWVoice *hw, int *nb_active); | |
158 | +void pcm_hw_dec_live (HWVoice *hw, int decr); | |
159 | +int pcm_hw_write (SWVoice *sw, void *buf, int len); | |
160 | + | |
161 | +int audio_get_conf_int (const char *key, int defval); | |
162 | +const char *audio_get_conf_str (const char *key, const char *defval); | |
163 | + | |
164 | +/* Public API */ | |
165 | +SWVoice * AUD_open (SWVoice *sw, const char *name, int freq, | |
166 | + int nchannels, audfmt_e fmt); | |
167 | +int AUD_write (SWVoice *sw, void *pcm_buf, int size); | |
168 | +void AUD_adjust (SWVoice *sw, int leftover); | |
169 | +void AUD_reset (SWVoice *sw); | |
170 | +int AUD_get_free (SWVoice *sw); | |
171 | +int AUD_get_buffer_size (SWVoice *sw); | |
172 | +void AUD_run (void); | |
173 | +void AUD_enable (SWVoice *sw, int on); | |
174 | +int AUD_calc_elapsed (SWVoice *sw); | |
175 | + | |
176 | +static inline void *advance (void *p, int incr) | |
177 | +{ | |
178 | + uint8_t *d = p; | |
179 | + return (d + incr); | |
180 | +} | |
181 | + | |
182 | +uint32_t popcount (uint32_t u); | |
183 | +inline uint32_t lsbindex (uint32_t u); | |
184 | + | |
185 | +#define audio_MIN(a, b) ((a)>(b)?(b):(a)) | |
186 | +#define audio_MAX(a, b) ((a)<(b)?(b):(a)) | |
187 | + | |
188 | +#endif /* audio.h */ | ... | ... |
audio/fmodaudio.c
0 → 100644
1 | +/* | |
2 | + * QEMU FMOD audio output driver | |
3 | + * | |
4 | + * Copyright (c) 2004 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 <fmod.h> | |
25 | +#include <fmod_errors.h> | |
26 | +#include "vl.h" | |
27 | + | |
28 | +#define AUDIO_CAP "fmod" | |
29 | +#include "audio/audio.h" | |
30 | +#include "audio/fmodaudio.h" | |
31 | + | |
32 | +#define QC_FMOD_DRV "QEMU_FMOD_DRV" | |
33 | +#define QC_FMOD_FREQ "QEMU_FMOD_FREQ" | |
34 | +#define QC_FMOD_SAMPLES "QEMU_FMOD_SAMPLES" | |
35 | +#define QC_FMOD_CHANNELS "QEMU_FMOD_CHANNELS" | |
36 | +#define QC_FMOD_BUFSIZE "QEMU_FMOD_BUFSIZE" | |
37 | +#define QC_FMOD_THRESHOLD "QEMU_FMOD_THRESHOLD" | |
38 | + | |
39 | +static struct { | |
40 | + int nb_samples; | |
41 | + int freq; | |
42 | + int nb_channels; | |
43 | + int bufsize; | |
44 | + int threshold; | |
45 | +} conf = { | |
46 | + 2048, | |
47 | + 44100, | |
48 | + 1, | |
49 | + 0, | |
50 | + 128 | |
51 | +}; | |
52 | + | |
53 | +#define errstr() FMOD_ErrorString (FSOUND_GetError ()) | |
54 | + | |
55 | +static int fmod_hw_write (SWVoice *sw, void *buf, int len) | |
56 | +{ | |
57 | + return pcm_hw_write (sw, buf, len); | |
58 | +} | |
59 | + | |
60 | +static void fmod_clear_sample (FMODVoice *fmd) | |
61 | +{ | |
62 | + HWVoice *hw = &fmd->hw; | |
63 | + int status; | |
64 | + void *p1 = 0, *p2 = 0; | |
65 | + unsigned int len1 = 0, len2 = 0; | |
66 | + | |
67 | + status = FSOUND_Sample_Lock ( | |
68 | + fmd->fmod_sample, | |
69 | + 0, | |
70 | + hw->samples << hw->shift, | |
71 | + &p1, | |
72 | + &p2, | |
73 | + &len1, | |
74 | + &len2 | |
75 | + ); | |
76 | + | |
77 | + if (!status) { | |
78 | + dolog ("Failed to lock sample\nReason: %s\n", errstr ()); | |
79 | + return; | |
80 | + } | |
81 | + | |
82 | + if ((len1 & hw->align) || (len2 & hw->align)) { | |
83 | + dolog ("Locking sample returned unaligned length %d, %d\n", | |
84 | + len1, len2); | |
85 | + goto fail; | |
86 | + } | |
87 | + | |
88 | + if (len1 + len2 != hw->samples << hw->shift) { | |
89 | + dolog ("Locking sample returned incomplete length %d, %d\n", | |
90 | + len1 + len2, hw->samples << hw->shift); | |
91 | + goto fail; | |
92 | + } | |
93 | + pcm_hw_clear (hw, p1, hw->samples); | |
94 | + | |
95 | + fail: | |
96 | + status = FSOUND_Sample_Unlock (fmd->fmod_sample, p1, p2, len1, len2); | |
97 | + if (!status) { | |
98 | + dolog ("Failed to unlock sample\nReason: %s\n", errstr ()); | |
99 | + } | |
100 | +} | |
101 | + | |
102 | +static int fmod_write_sample (HWVoice *hw, uint8_t *dst, st_sample_t *src, | |
103 | + int src_size, int src_pos, int dst_len) | |
104 | +{ | |
105 | + int src_len1 = dst_len, src_len2 = 0, pos = src_pos + dst_len; | |
106 | + st_sample_t *src1 = src + src_pos, *src2 = 0; | |
107 | + | |
108 | + if (src_pos + dst_len > src_size) { | |
109 | + src_len1 = src_size - src_pos; | |
110 | + src2 = src; | |
111 | + src_len2 = dst_len - src_len1; | |
112 | + pos = src_len2; | |
113 | + } | |
114 | + | |
115 | + if (src_len1) { | |
116 | + hw->clip (dst, src1, src_len1); | |
117 | + memset (src1, 0, src_len1 * sizeof (st_sample_t)); | |
118 | + advance (dst, src_len1); | |
119 | + } | |
120 | + | |
121 | + if (src_len2) { | |
122 | + hw->clip (dst, src2, src_len2); | |
123 | + memset (src2, 0, src_len2 * sizeof (st_sample_t)); | |
124 | + } | |
125 | + return pos; | |
126 | +} | |
127 | + | |
128 | +static int fmod_unlock_sample (FMODVoice *fmd, void *p1, void *p2, | |
129 | + unsigned int blen1, unsigned int blen2) | |
130 | +{ | |
131 | + int status = FSOUND_Sample_Unlock (fmd->fmod_sample, p1, p2, blen1, blen2); | |
132 | + if (!status) { | |
133 | + dolog ("Failed to unlock sample\nReason: %s\n", errstr ()); | |
134 | + return -1; | |
135 | + } | |
136 | + return 0; | |
137 | +} | |
138 | + | |
139 | +static int fmod_lock_sample (FMODVoice *fmd, int pos, int len, | |
140 | + void **p1, void **p2, | |
141 | + unsigned int *blen1, unsigned int *blen2) | |
142 | +{ | |
143 | + HWVoice *hw = &fmd->hw; | |
144 | + int status; | |
145 | + | |
146 | + status = FSOUND_Sample_Lock ( | |
147 | + fmd->fmod_sample, | |
148 | + pos << hw->shift, | |
149 | + len << hw->shift, | |
150 | + p1, | |
151 | + p2, | |
152 | + blen1, | |
153 | + blen2 | |
154 | + ); | |
155 | + | |
156 | + if (!status) { | |
157 | + dolog ("Failed to lock sample\nReason: %s\n", errstr ()); | |
158 | + return -1; | |
159 | + } | |
160 | + | |
161 | + if ((*blen1 & hw->align) || (*blen2 & hw->align)) { | |
162 | + dolog ("Locking sample returned unaligned length %d, %d\n", | |
163 | + *blen1, *blen2); | |
164 | + fmod_unlock_sample (fmd, *p1, *p2, *blen1, *blen2); | |
165 | + return -1; | |
166 | + } | |
167 | + return 0; | |
168 | +} | |
169 | + | |
170 | +static void fmod_hw_run (HWVoice *hw) | |
171 | +{ | |
172 | + FMODVoice *fmd = (FMODVoice *) hw; | |
173 | + int rpos, live, decr; | |
174 | + void *p1 = 0, *p2 = 0; | |
175 | + unsigned int blen1 = 0, blen2 = 0; | |
176 | + unsigned int len1 = 0, len2 = 0; | |
177 | + int nb_active; | |
178 | + | |
179 | + live = pcm_hw_get_live2 (hw, &nb_active); | |
180 | + if (live <= 0) { | |
181 | + return; | |
182 | + } | |
183 | + | |
184 | + if (!hw->pending_disable | |
185 | + && nb_active | |
186 | + && conf.threshold | |
187 | + && live <= conf.threshold) { | |
188 | + ldebug ("live=%d nb_active=%d\n", live, nb_active); | |
189 | + return; | |
190 | + } | |
191 | + | |
192 | + decr = live; | |
193 | + | |
194 | +#if 1 | |
195 | + if (fmd->channel >= 0) { | |
196 | + int pos2 = (fmd->old_pos + decr) % hw->samples; | |
197 | + int pos = FSOUND_GetCurrentPosition (fmd->channel); | |
198 | + | |
199 | + if (fmd->old_pos < pos && pos2 >= pos) { | |
200 | + decr = pos - fmd->old_pos - (pos2 == pos) - 1; | |
201 | + } | |
202 | + else if (fmd->old_pos > pos && pos2 >= pos && pos2 < fmd->old_pos) { | |
203 | + decr = (hw->samples - fmd->old_pos) + pos - (pos2 == pos) - 1; | |
204 | + } | |
205 | +/* ldebug ("pos=%d pos2=%d old=%d live=%d decr=%d\n", */ | |
206 | +/* pos, pos2, fmd->old_pos, live, decr); */ | |
207 | + } | |
208 | +#endif | |
209 | + | |
210 | + if (decr <= 0) { | |
211 | + return; | |
212 | + } | |
213 | + | |
214 | + if (fmod_lock_sample (fmd, fmd->old_pos, decr, &p1, &p2, &blen1, &blen2)) { | |
215 | + return; | |
216 | + } | |
217 | + | |
218 | + len1 = blen1 >> hw->shift; | |
219 | + len2 = blen2 >> hw->shift; | |
220 | + ldebug ("%p %p %d %d %d %d\n", p1, p2, len1, len2, blen1, blen2); | |
221 | + decr = len1 + len2; | |
222 | + rpos = hw->rpos; | |
223 | + | |
224 | + if (len1) { | |
225 | + rpos = fmod_write_sample (hw, p1, hw->mix_buf, hw->samples, rpos, len1); | |
226 | + } | |
227 | + | |
228 | + if (len2) { | |
229 | + rpos = fmod_write_sample (hw, p2, hw->mix_buf, hw->samples, rpos, len2); | |
230 | + } | |
231 | + | |
232 | + fmod_unlock_sample (fmd, p1, p2, blen1, blen2); | |
233 | + | |
234 | + pcm_hw_dec_live (hw, decr); | |
235 | + hw->rpos = rpos % hw->samples; | |
236 | + fmd->old_pos = (fmd->old_pos + decr) % hw->samples; | |
237 | +} | |
238 | + | |
239 | +static int AUD_to_fmodfmt (audfmt_e fmt, int stereo) | |
240 | +{ | |
241 | + int mode = FSOUND_LOOP_NORMAL; | |
242 | + | |
243 | + switch (fmt) { | |
244 | + case AUD_FMT_S8: | |
245 | + mode |= FSOUND_SIGNED | FSOUND_8BITS; | |
246 | + break; | |
247 | + | |
248 | + case AUD_FMT_U8: | |
249 | + mode |= FSOUND_UNSIGNED | FSOUND_8BITS; | |
250 | + break; | |
251 | + | |
252 | + case AUD_FMT_S16: | |
253 | + mode |= FSOUND_SIGNED | FSOUND_16BITS; | |
254 | + break; | |
255 | + | |
256 | + case AUD_FMT_U16: | |
257 | + mode |= FSOUND_UNSIGNED | FSOUND_16BITS; | |
258 | + break; | |
259 | + | |
260 | + default: | |
261 | + dolog ("Internal logic error: Bad audio format %d\nAborting\n", fmt); | |
262 | + exit (EXIT_FAILURE); | |
263 | + } | |
264 | + mode |= stereo ? FSOUND_STEREO : FSOUND_MONO; | |
265 | + return mode; | |
266 | +} | |
267 | + | |
268 | +static void fmod_hw_fini (HWVoice *hw) | |
269 | +{ | |
270 | + FMODVoice *fmd = (FMODVoice *) hw; | |
271 | + | |
272 | + if (fmd->fmod_sample) { | |
273 | + FSOUND_Sample_Free (fmd->fmod_sample); | |
274 | + fmd->fmod_sample = 0; | |
275 | + | |
276 | + if (fmd->channel >= 0) { | |
277 | + FSOUND_StopSound (fmd->channel); | |
278 | + } | |
279 | + } | |
280 | +} | |
281 | + | |
282 | +static int fmod_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt) | |
283 | +{ | |
284 | + int bits16, mode, channel; | |
285 | + FMODVoice *fmd = (FMODVoice *) hw; | |
286 | + | |
287 | + mode = AUD_to_fmodfmt (fmt, nchannels == 2 ? 1 : 0); | |
288 | + fmd->fmod_sample = FSOUND_Sample_Alloc ( | |
289 | + FSOUND_FREE, /* index */ | |
290 | + conf.nb_samples, /* length */ | |
291 | + mode, /* mode */ | |
292 | + freq, /* freq */ | |
293 | + 255, /* volume */ | |
294 | + 128, /* pan */ | |
295 | + 255 /* priority */ | |
296 | + ); | |
297 | + | |
298 | + if (!fmd->fmod_sample) { | |
299 | + dolog ("Failed to allocate FMOD sample\nReason: %s\n", errstr ()); | |
300 | + return -1; | |
301 | + } | |
302 | + | |
303 | + channel = FSOUND_PlaySoundEx (FSOUND_FREE, fmd->fmod_sample, 0, 1); | |
304 | + if (channel < 0) { | |
305 | + dolog ("Failed to start playing sound\nReason: %s\n", errstr ()); | |
306 | + FSOUND_Sample_Free (fmd->fmod_sample); | |
307 | + return -1; | |
308 | + } | |
309 | + fmd->channel = channel; | |
310 | + | |
311 | + hw->freq = freq; | |
312 | + hw->fmt = fmt; | |
313 | + hw->nchannels = nchannels; | |
314 | + bits16 = fmt == AUD_FMT_U16 || fmt == AUD_FMT_S16; | |
315 | + hw->bufsize = conf.nb_samples << (nchannels == 2) << bits16; | |
316 | + return 0; | |
317 | +} | |
318 | + | |
319 | +static int fmod_hw_ctl (HWVoice *hw, int cmd, ...) | |
320 | +{ | |
321 | + int status; | |
322 | + FMODVoice *fmd = (FMODVoice *) hw; | |
323 | + | |
324 | + switch (cmd) { | |
325 | + case VOICE_ENABLE: | |
326 | + fmod_clear_sample (fmd); | |
327 | + status = FSOUND_SetPaused (fmd->channel, 0); | |
328 | + if (!status) { | |
329 | + dolog ("Failed to resume channel %d\nReason: %s\n", | |
330 | + fmd->channel, errstr ()); | |
331 | + } | |
332 | + break; | |
333 | + | |
334 | + case VOICE_DISABLE: | |
335 | + status = FSOUND_SetPaused (fmd->channel, 1); | |
336 | + if (!status) { | |
337 | + dolog ("Failed to pause channel %d\nReason: %s\n", | |
338 | + fmd->channel, errstr ()); | |
339 | + } | |
340 | + break; | |
341 | + } | |
342 | + return 0; | |
343 | +} | |
344 | + | |
345 | +static struct { | |
346 | + const char *name; | |
347 | + int type; | |
348 | +} drvtab[] = { | |
349 | + {"none", FSOUND_OUTPUT_NOSOUND}, | |
350 | +#ifdef _WIN32 | |
351 | + {"winmm", FSOUND_OUTPUT_WINMM}, | |
352 | + {"dsound", FSOUND_OUTPUT_DSOUND}, | |
353 | + {"a3d", FSOUND_OUTPUT_A3D}, | |
354 | + {"asio", FSOUND_OUTPUT_ASIO}, | |
355 | +#endif | |
356 | +#ifdef __linux__ | |
357 | + {"oss", FSOUND_OUTPUT_OSS}, | |
358 | + {"alsa", FSOUND_OUTPUT_ALSA}, | |
359 | + {"esd", FSOUND_OUTPUT_ESD}, | |
360 | +#endif | |
361 | +#ifdef __APPLE__ | |
362 | + {"mac", FSOUND_OUTPUT_MAC}, | |
363 | +#endif | |
364 | +#if 0 | |
365 | + {"xbox", FSOUND_OUTPUT_XBOX}, | |
366 | + {"ps2", FSOUND_OUTPUT_PS2}, | |
367 | + {"gcube", FSOUND_OUTPUT_GC}, | |
368 | +#endif | |
369 | + {"nort", FSOUND_OUTPUT_NOSOUND_NONREALTIME} | |
370 | +}; | |
371 | + | |
372 | +static void *fmod_audio_init (void) | |
373 | +{ | |
374 | + int i; | |
375 | + double ver; | |
376 | + int status; | |
377 | + int output_type = -1; | |
378 | + const char *drv = audio_get_conf_str (QC_FMOD_DRV, NULL); | |
379 | + | |
380 | + ver = FSOUND_GetVersion (); | |
381 | + if (ver < FMOD_VERSION) { | |
382 | + dolog ("Wrong FMOD version %f, need at least %f\n", ver, FMOD_VERSION); | |
383 | + return NULL; | |
384 | + } | |
385 | + | |
386 | + if (drv) { | |
387 | + int found = 0; | |
388 | + for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) { | |
389 | + if (!strcmp (drv, drvtab[i].name)) { | |
390 | + output_type = drvtab[i].type; | |
391 | + found = 1; | |
392 | + break; | |
393 | + } | |
394 | + } | |
395 | + if (!found) { | |
396 | + dolog ("Unknown FMOD output driver `%s'\n", drv); | |
397 | + } | |
398 | + } | |
399 | + | |
400 | + if (output_type != -1) { | |
401 | + status = FSOUND_SetOutput (output_type); | |
402 | + if (!status) { | |
403 | + dolog ("FSOUND_SetOutput(%d) failed\nReason: %s\n", | |
404 | + output_type, errstr ()); | |
405 | + return NULL; | |
406 | + } | |
407 | + } | |
408 | + | |
409 | + conf.freq = audio_get_conf_int (QC_FMOD_FREQ, conf.freq); | |
410 | + conf.nb_samples = audio_get_conf_int (QC_FMOD_SAMPLES, conf.nb_samples); | |
411 | + conf.nb_channels = | |
412 | + audio_get_conf_int (QC_FMOD_CHANNELS, | |
413 | + (audio_state.nb_hw_voices > 1 | |
414 | + ? audio_state.nb_hw_voices | |
415 | + : conf.nb_channels)); | |
416 | + conf.bufsize = audio_get_conf_int (QC_FMOD_BUFSIZE, conf.bufsize); | |
417 | + conf.threshold = audio_get_conf_int (QC_FMOD_THRESHOLD, conf.threshold); | |
418 | + | |
419 | + if (conf.bufsize) { | |
420 | + status = FSOUND_SetBufferSize (conf.bufsize); | |
421 | + if (!status) { | |
422 | + dolog ("FSOUND_SetBufferSize (%d) failed\nReason: %s\n", | |
423 | + conf.bufsize, errstr ()); | |
424 | + } | |
425 | + } | |
426 | + | |
427 | + status = FSOUND_Init (conf.freq, conf.nb_channels, 0); | |
428 | + if (!status) { | |
429 | + dolog ("FSOUND_Init failed\nReason: %s\n", errstr ()); | |
430 | + return NULL; | |
431 | + } | |
432 | + | |
433 | + return &conf; | |
434 | +} | |
435 | + | |
436 | +static void fmod_audio_fini (void *opaque) | |
437 | +{ | |
438 | + FSOUND_Close (); | |
439 | +} | |
440 | + | |
441 | +struct pcm_ops fmod_pcm_ops = { | |
442 | + fmod_hw_init, | |
443 | + fmod_hw_fini, | |
444 | + fmod_hw_run, | |
445 | + fmod_hw_write, | |
446 | + fmod_hw_ctl | |
447 | +}; | |
448 | + | |
449 | +struct audio_output_driver fmod_output_driver = { | |
450 | + "fmod", | |
451 | + fmod_audio_init, | |
452 | + fmod_audio_fini, | |
453 | + &fmod_pcm_ops, | |
454 | + 1, | |
455 | + INT_MAX, | |
456 | + sizeof (FMODVoice) | |
457 | +}; | ... | ... |
audio/fmodaudio.h
0 → 100644
1 | +/* | |
2 | + * QEMU FMOD audio output driver header | |
3 | + * | |
4 | + * Copyright (c) 2004 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 | +#ifndef QEMU_FMODAUDIO_H | |
25 | +#define QEMU_FMODAUDIO_H | |
26 | + | |
27 | +#include <fmod.h> | |
28 | + | |
29 | +typedef struct FMODVoice { | |
30 | + struct HWVoice hw; | |
31 | + unsigned int old_pos; | |
32 | + FSOUND_SAMPLE *fmod_sample; | |
33 | + int channel; | |
34 | +} FMODVoice; | |
35 | + | |
36 | +extern struct pcm_ops fmod_pcm_ops; | |
37 | +extern struct audio_output_driver fmod_output_driver; | |
38 | + | |
39 | +#endif /* fmodaudio.h */ | ... | ... |
audio/mixeng.c
0 → 100644
1 | +/* | |
2 | + * QEMU Mixing engine | |
3 | + * | |
4 | + * Copyright (c) 2004 Vassili Karpov (malc) | |
5 | + * Copyright (c) 1998 Fabrice Bellard | |
6 | + * | |
7 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
8 | + * of this software and associated documentation files (the "Software"), to deal | |
9 | + * in the Software without restriction, including without limitation the rights | |
10 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
11 | + * copies of the Software, and to permit persons to whom the Software is | |
12 | + * furnished to do so, subject to the following conditions: | |
13 | + * | |
14 | + * The above copyright notice and this permission notice shall be included in | |
15 | + * all copies or substantial portions of the Software. | |
16 | + * | |
17 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
18 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
19 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
20 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
21 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
22 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
23 | + * THE SOFTWARE. | |
24 | + */ | |
25 | +#include "vl.h" | |
26 | +//#define DEBUG_FP | |
27 | +#include "audio/mixeng.h" | |
28 | + | |
29 | +#define IN_T int8_t | |
30 | +#define IN_MIN CHAR_MIN | |
31 | +#define IN_MAX CHAR_MAX | |
32 | +#define SIGNED | |
33 | +#include "mixeng_template.h" | |
34 | +#undef SIGNED | |
35 | +#undef IN_MAX | |
36 | +#undef IN_MIN | |
37 | +#undef IN_T | |
38 | + | |
39 | +#define IN_T uint8_t | |
40 | +#define IN_MIN 0 | |
41 | +#define IN_MAX UCHAR_MAX | |
42 | +#include "mixeng_template.h" | |
43 | +#undef IN_MAX | |
44 | +#undef IN_MIN | |
45 | +#undef IN_T | |
46 | + | |
47 | +#define IN_T int16_t | |
48 | +#define IN_MIN SHRT_MIN | |
49 | +#define IN_MAX SHRT_MAX | |
50 | +#define SIGNED | |
51 | +#include "mixeng_template.h" | |
52 | +#undef SIGNED | |
53 | +#undef IN_MAX | |
54 | +#undef IN_MIN | |
55 | +#undef IN_T | |
56 | + | |
57 | +#define IN_T uint16_t | |
58 | +#define IN_MIN 0 | |
59 | +#define IN_MAX USHRT_MAX | |
60 | +#include "mixeng_template.h" | |
61 | +#undef IN_MAX | |
62 | +#undef IN_MIN | |
63 | +#undef IN_T | |
64 | + | |
65 | +t_sample *mixeng_conv[2][2][2] = { | |
66 | + { | |
67 | + { | |
68 | + conv_uint8_t_to_mono, | |
69 | + conv_uint16_t_to_mono | |
70 | + }, | |
71 | + { | |
72 | + conv_int8_t_to_mono, | |
73 | + conv_int16_t_to_mono | |
74 | + } | |
75 | + }, | |
76 | + { | |
77 | + { | |
78 | + conv_uint8_t_to_stereo, | |
79 | + conv_uint16_t_to_stereo | |
80 | + }, | |
81 | + { | |
82 | + conv_int8_t_to_stereo, | |
83 | + conv_int16_t_to_stereo | |
84 | + } | |
85 | + } | |
86 | +}; | |
87 | + | |
88 | +f_sample *mixeng_clip[2][2][2] = { | |
89 | + { | |
90 | + { | |
91 | + clip_uint8_t_from_mono, | |
92 | + clip_uint16_t_from_mono | |
93 | + }, | |
94 | + { | |
95 | + clip_int8_t_from_mono, | |
96 | + clip_int16_t_from_mono | |
97 | + } | |
98 | + }, | |
99 | + { | |
100 | + { | |
101 | + clip_uint8_t_from_stereo, | |
102 | + clip_uint16_t_from_stereo | |
103 | + }, | |
104 | + { | |
105 | + clip_int8_t_from_stereo, | |
106 | + clip_int16_t_from_stereo | |
107 | + } | |
108 | + } | |
109 | +}; | |
110 | + | |
111 | +/* | |
112 | + * August 21, 1998 | |
113 | + * Copyright 1998 Fabrice Bellard. | |
114 | + * | |
115 | + * [Rewrote completly the code of Lance Norskog And Sundry | |
116 | + * Contributors with a more efficient algorithm.] | |
117 | + * | |
118 | + * This source code is freely redistributable and may be used for | |
119 | + * any purpose. This copyright notice must be maintained. | |
120 | + * Lance Norskog And Sundry Contributors are not responsible for | |
121 | + * the consequences of using this software. | |
122 | + */ | |
123 | + | |
124 | +/* | |
125 | + * Sound Tools rate change effect file. | |
126 | + */ | |
127 | +/* | |
128 | + * Linear Interpolation. | |
129 | + * | |
130 | + * The use of fractional increment allows us to use no buffer. It | |
131 | + * avoid the problems at the end of the buffer we had with the old | |
132 | + * method which stored a possibly big buffer of size | |
133 | + * lcm(in_rate,out_rate). | |
134 | + * | |
135 | + * Limited to 16 bit samples and sampling frequency <= 65535 Hz. If | |
136 | + * the input & output frequencies are equal, a delay of one sample is | |
137 | + * introduced. Limited to processing 32-bit count worth of samples. | |
138 | + * | |
139 | + * 1 << FRAC_BITS evaluating to zero in several places. Changed with | |
140 | + * an (unsigned long) cast to make it safe. MarkMLl 2/1/99 | |
141 | + */ | |
142 | + | |
143 | +/* Private data */ | |
144 | +typedef struct ratestuff { | |
145 | + uint64_t opos; | |
146 | + uint64_t opos_inc; | |
147 | + uint32_t ipos; /* position in the input stream (integer) */ | |
148 | + st_sample_t ilast; /* last sample in the input stream */ | |
149 | +} *rate_t; | |
150 | + | |
151 | +/* | |
152 | + * Prepare processing. | |
153 | + */ | |
154 | +void *st_rate_start (int inrate, int outrate) | |
155 | +{ | |
156 | + rate_t rate = (rate_t) qemu_mallocz (sizeof (struct ratestuff)); | |
157 | + | |
158 | + if (!rate) { | |
159 | + exit (EXIT_FAILURE); | |
160 | + } | |
161 | + | |
162 | + if (inrate == outrate) { | |
163 | + // exit (EXIT_FAILURE); | |
164 | + } | |
165 | + | |
166 | + if (inrate >= 65535 || outrate >= 65535) { | |
167 | + // exit (EXIT_FAILURE); | |
168 | + } | |
169 | + | |
170 | + rate->opos = 0; | |
171 | + | |
172 | + /* increment */ | |
173 | + rate->opos_inc = (inrate * ((int64_t) UINT_MAX)) / outrate; | |
174 | + | |
175 | + rate->ipos = 0; | |
176 | + rate->ilast.l = 0; | |
177 | + rate->ilast.r = 0; | |
178 | + return rate; | |
179 | +} | |
180 | + | |
181 | +/* | |
182 | + * Processed signed long samples from ibuf to obuf. | |
183 | + * Return number of samples processed. | |
184 | + */ | |
185 | +void st_rate_flow (void *opaque, st_sample_t *ibuf, st_sample_t *obuf, | |
186 | + int *isamp, int *osamp) | |
187 | +{ | |
188 | + rate_t rate = (rate_t) opaque; | |
189 | + st_sample_t *istart, *iend; | |
190 | + st_sample_t *ostart, *oend; | |
191 | + st_sample_t ilast, icur, out; | |
192 | + int64_t t; | |
193 | + | |
194 | + ilast = rate->ilast; | |
195 | + | |
196 | + istart = ibuf; | |
197 | + iend = ibuf + *isamp; | |
198 | + | |
199 | + ostart = obuf; | |
200 | + oend = obuf + *osamp; | |
201 | + | |
202 | + if (rate->opos_inc == 1ULL << 32) { | |
203 | + int i, n = *isamp > *osamp ? *osamp : *isamp; | |
204 | + for (i = 0; i < n; i++) { | |
205 | + obuf[i].l += ibuf[i].r; | |
206 | + obuf[i].r += ibuf[i].r; | |
207 | + } | |
208 | + *isamp = n; | |
209 | + *osamp = n; | |
210 | + return; | |
211 | + } | |
212 | + | |
213 | + while (obuf < oend) { | |
214 | + | |
215 | + /* Safety catch to make sure we have input samples. */ | |
216 | + if (ibuf >= iend) | |
217 | + break; | |
218 | + | |
219 | + /* read as many input samples so that ipos > opos */ | |
220 | + | |
221 | + while (rate->ipos <= (rate->opos >> 32)) { | |
222 | + ilast = *ibuf++; | |
223 | + rate->ipos++; | |
224 | + /* See if we finished the input buffer yet */ | |
225 | + if (ibuf >= iend) goto the_end; | |
226 | + } | |
227 | + | |
228 | + icur = *ibuf; | |
229 | + | |
230 | + /* interpolate */ | |
231 | + t = rate->opos & 0xffffffff; | |
232 | + out.l = (ilast.l * (INT_MAX - t) + icur.l * t) / INT_MAX; | |
233 | + out.r = (ilast.r * (INT_MAX - t) + icur.r * t) / INT_MAX; | |
234 | + | |
235 | + /* output sample & increment position */ | |
236 | +#if 0 | |
237 | + *obuf++ = out; | |
238 | +#else | |
239 | + obuf->l += out.l; | |
240 | + obuf->r += out.r; | |
241 | + obuf += 1; | |
242 | +#endif | |
243 | + rate->opos += rate->opos_inc; | |
244 | + } | |
245 | + | |
246 | +the_end: | |
247 | + *isamp = ibuf - istart; | |
248 | + *osamp = obuf - ostart; | |
249 | + rate->ilast = ilast; | |
250 | +} | |
251 | + | |
252 | +void st_rate_stop (void *opaque) | |
253 | +{ | |
254 | + qemu_free (opaque); | |
255 | +} | ... | ... |
audio/mixeng.h
0 → 100644
1 | +/* | |
2 | + * QEMU Mixing engine header | |
3 | + * | |
4 | + * Copyright (c) 2004 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 | +#ifndef QEMU_MIXENG_H | |
25 | +#define QEMU_MIXENG_H | |
26 | + | |
27 | +typedef void (t_sample) (void *dst, const void *src, int samples); | |
28 | +typedef void (f_sample) (void *dst, const void *src, int samples); | |
29 | +typedef struct { int64_t l; int64_t r; } st_sample_t; | |
30 | + | |
31 | +extern t_sample *mixeng_conv[2][2][2]; | |
32 | +extern f_sample *mixeng_clip[2][2][2]; | |
33 | + | |
34 | +void *st_rate_start (int inrate, int outrate); | |
35 | +void st_rate_flow (void *opaque, st_sample_t *ibuf, st_sample_t *obuf, | |
36 | + int *isamp, int *osamp); | |
37 | +void st_rate_stop (void *opaque); | |
38 | + | |
39 | +#endif /* mixeng.h */ | ... | ... |
audio/mixeng_template.h
0 → 100644
1 | +/* | |
2 | + * QEMU Mixing engine | |
3 | + * | |
4 | + * Copyright (c) 2004 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 | +/* | |
26 | + * Tusen tack till Mike Nordell | |
27 | + * dec++'ified by Dscho | |
28 | + */ | |
29 | + | |
30 | +#ifdef SIGNED | |
31 | +#define HALFT IN_MAX | |
32 | +#define HALF IN_MAX | |
33 | +#else | |
34 | +#define HALFT ((IN_MAX)>>1) | |
35 | +#define HALF HALFT | |
36 | +#endif | |
37 | + | |
38 | +static int64_t inline glue(conv_,IN_T) (IN_T v) | |
39 | +{ | |
40 | +#ifdef SIGNED | |
41 | + return (INT_MAX*(int64_t)v)/HALF; | |
42 | +#else | |
43 | + return (INT_MAX*((int64_t)v-HALFT))/HALF; | |
44 | +#endif | |
45 | +} | |
46 | + | |
47 | +static IN_T inline glue(clip_,IN_T) (int64_t v) | |
48 | +{ | |
49 | + if (v >= INT_MAX) | |
50 | + return IN_MAX; | |
51 | + else if (v < -INT_MAX) | |
52 | + return IN_MIN; | |
53 | + | |
54 | +#ifdef SIGNED | |
55 | + return (IN_T) (v*HALF/INT_MAX); | |
56 | +#else | |
57 | + return (IN_T) (v+INT_MAX/2)*HALF/INT_MAX; | |
58 | +#endif | |
59 | +} | |
60 | + | |
61 | +static void glue(glue(conv_,IN_T),_to_stereo) (void *dst, const void *src, | |
62 | + int samples) | |
63 | +{ | |
64 | + st_sample_t *out = (st_sample_t *) dst; | |
65 | + IN_T *in = (IN_T *) src; | |
66 | + while (samples--) { | |
67 | + out->l = glue(conv_,IN_T) (*in++); | |
68 | + out->r = glue(conv_,IN_T) (*in++); | |
69 | + out += 1; | |
70 | + } | |
71 | +} | |
72 | + | |
73 | +static void glue(glue(conv_,IN_T),_to_mono) (void *dst, const void *src, | |
74 | + int samples) | |
75 | +{ | |
76 | + st_sample_t *out = (st_sample_t *) dst; | |
77 | + IN_T *in = (IN_T *) src; | |
78 | + while (samples--) { | |
79 | + out->l = glue(conv_,IN_T) (in[0]); | |
80 | + out->r = out->l; | |
81 | + out += 1; | |
82 | + in += 1; | |
83 | + } | |
84 | +} | |
85 | + | |
86 | +static void glue(glue(clip_,IN_T),_from_stereo) (void *dst, const void *src, | |
87 | + int samples) | |
88 | +{ | |
89 | + st_sample_t *in = (st_sample_t *) src; | |
90 | + IN_T *out = (IN_T *) dst; | |
91 | + while (samples--) { | |
92 | + *out++ = glue(clip_,IN_T) (in->l); | |
93 | + *out++ = glue(clip_,IN_T) (in->r); | |
94 | + in += 1; | |
95 | + } | |
96 | +} | |
97 | + | |
98 | +static void glue(glue(clip_,IN_T),_from_mono) (void *dst, const void *src, | |
99 | + int samples) | |
100 | +{ | |
101 | + st_sample_t *in = (st_sample_t *) src; | |
102 | + IN_T *out = (IN_T *) dst; | |
103 | + while (samples--) { | |
104 | + *out++ = glue(clip_,IN_T) (in->l + in->r); | |
105 | + in += 1; | |
106 | + } | |
107 | +} | |
108 | + | |
109 | +#undef HALF | |
110 | +#undef HALFT | |
111 | + | ... | ... |
audio/ossaudio.c
0 → 100644
1 | +/* | |
2 | + * QEMU OSS audio output driver | |
3 | + * | |
4 | + * Copyright (c) 2003-2004 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 | +/* Temporary kludge */ | |
26 | +#if defined __linux__ || (defined _BSD && !defined __APPLE__) | |
27 | +#include <assert.h> | |
28 | +#include "vl.h" | |
29 | + | |
30 | +#include <sys/mman.h> | |
31 | +#include <sys/types.h> | |
32 | +#include <sys/ioctl.h> | |
33 | +#include <sys/soundcard.h> | |
34 | + | |
35 | +#define AUDIO_CAP "oss" | |
36 | +#include "audio/audio.h" | |
37 | +#include "audio/ossaudio.h" | |
38 | + | |
39 | +#define QC_OSS_FRAGSIZE "QEMU_OSS_FRAGSIZE" | |
40 | +#define QC_OSS_NFRAGS "QEMU_OSS_NFRAGS" | |
41 | +#define QC_OSS_MMAP "QEMU_OSS_MMAP" | |
42 | +#define QC_OSS_DEV "QEMU_OSS_DEV" | |
43 | + | |
44 | +#define errstr() strerror (errno) | |
45 | + | |
46 | +static struct { | |
47 | + int try_mmap; | |
48 | + int nfrags; | |
49 | + int fragsize; | |
50 | + const char *dspname; | |
51 | +} conf = { | |
52 | + .try_mmap = 0, | |
53 | + .nfrags = 4, | |
54 | + .fragsize = 4096, | |
55 | + .dspname = "/dev/dsp" | |
56 | +}; | |
57 | + | |
58 | +struct oss_params { | |
59 | + int freq; | |
60 | + audfmt_e fmt; | |
61 | + int nchannels; | |
62 | + int nfrags; | |
63 | + int fragsize; | |
64 | +}; | |
65 | + | |
66 | +static int oss_hw_write (SWVoice *sw, void *buf, int len) | |
67 | +{ | |
68 | + return pcm_hw_write (sw, buf, len); | |
69 | +} | |
70 | + | |
71 | +static int AUD_to_ossfmt (audfmt_e fmt) | |
72 | +{ | |
73 | + switch (fmt) { | |
74 | + case AUD_FMT_S8: return AFMT_S8; | |
75 | + case AUD_FMT_U8: return AFMT_U8; | |
76 | + case AUD_FMT_S16: return AFMT_S16_LE; | |
77 | + case AUD_FMT_U16: return AFMT_U16_LE; | |
78 | + default: | |
79 | + dolog ("Internal logic error: Bad audio format %d\nAborting\n", fmt); | |
80 | + exit (EXIT_FAILURE); | |
81 | + } | |
82 | +} | |
83 | + | |
84 | +static int oss_to_audfmt (int fmt) | |
85 | +{ | |
86 | + switch (fmt) { | |
87 | + case AFMT_S8: return AUD_FMT_S8; | |
88 | + case AFMT_U8: return AUD_FMT_U8; | |
89 | + case AFMT_S16_LE: return AUD_FMT_S16; | |
90 | + case AFMT_U16_LE: return AUD_FMT_U16; | |
91 | + default: | |
92 | + dolog ("Internal logic error: Unrecognized OSS audio format %d\n" | |
93 | + "Aborting\n", | |
94 | + fmt); | |
95 | + exit (EXIT_FAILURE); | |
96 | + } | |
97 | +} | |
98 | + | |
99 | +#ifdef DEBUG_PCM | |
100 | +static void oss_dump_pcm_info (struct oss_params *req, struct oss_params *obt) | |
101 | +{ | |
102 | + dolog ("parameter | requested value | obtained value\n"); | |
103 | + dolog ("format | %10d | %10d\n", req->fmt, obt->fmt); | |
104 | + dolog ("channels | %10d | %10d\n", req->nchannels, obt->nchannels); | |
105 | + dolog ("frequency | %10d | %10d\n", req->freq, obt->freq); | |
106 | + dolog ("nfrags | %10d | %10d\n", req->nfrags, obt->nfrags); | |
107 | + dolog ("fragsize | %10d | %10d\n", req->fragsize, obt->fragsize); | |
108 | +} | |
109 | +#endif | |
110 | + | |
111 | +static int oss_open (struct oss_params *req, struct oss_params *obt, int *pfd) | |
112 | +{ | |
113 | + int fd; | |
114 | + int mmmmssss; | |
115 | + audio_buf_info abinfo; | |
116 | + int fmt, freq, nchannels; | |
117 | + const char *dspname = conf.dspname; | |
118 | + | |
119 | + fd = open (dspname, O_RDWR | O_NONBLOCK); | |
120 | + if (-1 == fd) { | |
121 | + dolog ("Could not initialize audio hardware. Failed to open `%s':\n" | |
122 | + "Reason:%s\n", | |
123 | + dspname, | |
124 | + errstr ()); | |
125 | + return -1; | |
126 | + } | |
127 | + | |
128 | + freq = req->freq; | |
129 | + nchannels = req->nchannels; | |
130 | + fmt = req->fmt; | |
131 | + | |
132 | + if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) { | |
133 | + dolog ("Could not initialize audio hardware\n" | |
134 | + "Failed to set sample size\n" | |
135 | + "Reason: %s\n", | |
136 | + errstr ()); | |
137 | + goto err; | |
138 | + } | |
139 | + | |
140 | + if (ioctl (fd, SNDCTL_DSP_CHANNELS, &nchannels)) { | |
141 | + dolog ("Could not initialize audio hardware\n" | |
142 | + "Failed to set number of channels\n" | |
143 | + "Reason: %s\n", | |
144 | + errstr ()); | |
145 | + goto err; | |
146 | + } | |
147 | + | |
148 | + if (ioctl (fd, SNDCTL_DSP_SPEED, &freq)) { | |
149 | + dolog ("Could not initialize audio hardware\n" | |
150 | + "Failed to set frequency\n" | |
151 | + "Reason: %s\n", | |
152 | + errstr ()); | |
153 | + goto err; | |
154 | + } | |
155 | + | |
156 | + if (ioctl (fd, SNDCTL_DSP_NONBLOCK)) { | |
157 | + dolog ("Could not initialize audio hardware\n" | |
158 | + "Failed to set non-blocking mode\n" | |
159 | + "Reason: %s\n", | |
160 | + errstr ()); | |
161 | + goto err; | |
162 | + } | |
163 | + | |
164 | + mmmmssss = (req->nfrags << 16) | lsbindex (req->fragsize); | |
165 | + if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss)) { | |
166 | + dolog ("Could not initialize audio hardware\n" | |
167 | + "Failed to set buffer length (%d, %d)\n" | |
168 | + "Reason:%s\n", | |
169 | + conf.nfrags, conf.fragsize, | |
170 | + errstr ()); | |
171 | + goto err; | |
172 | + } | |
173 | + | |
174 | + if (ioctl (fd, SNDCTL_DSP_GETOSPACE, &abinfo)) { | |
175 | + dolog ("Could not initialize audio hardware\n" | |
176 | + "Failed to get buffer length\n" | |
177 | + "Reason:%s\n", | |
178 | + errstr ()); | |
179 | + goto err; | |
180 | + } | |
181 | + | |
182 | + obt->fmt = fmt; | |
183 | + obt->nchannels = nchannels; | |
184 | + obt->freq = freq; | |
185 | + obt->nfrags = abinfo.fragstotal; | |
186 | + obt->fragsize = abinfo.fragsize; | |
187 | + *pfd = fd; | |
188 | + | |
189 | + if ((req->fmt != obt->fmt) || | |
190 | + (req->nchannels != obt->nchannels) || | |
191 | + (req->freq != obt->freq) || | |
192 | + (req->fragsize != obt->fragsize) || | |
193 | + (req->nfrags != obt->nfrags)) { | |
194 | +#ifdef DEBUG_PCM | |
195 | + dolog ("Audio parameters mismatch\n"); | |
196 | + oss_dump_pcm_info (req, obt); | |
197 | +#endif | |
198 | + } | |
199 | + | |
200 | +#ifdef DEBUG_PCM | |
201 | + oss_dump_pcm_info (req, obt); | |
202 | +#endif | |
203 | + return 0; | |
204 | + | |
205 | +err: | |
206 | + close (fd); | |
207 | + return -1; | |
208 | +} | |
209 | + | |
210 | +static void oss_hw_run (HWVoice *hw) | |
211 | +{ | |
212 | + OSSVoice *oss = (OSSVoice *) hw; | |
213 | + int err, rpos, live, decr; | |
214 | + int samples; | |
215 | + uint8_t *dst; | |
216 | + st_sample_t *src; | |
217 | + struct audio_buf_info abinfo; | |
218 | + struct count_info cntinfo; | |
219 | + | |
220 | + live = pcm_hw_get_live (hw); | |
221 | + if (live <= 0) | |
222 | + return; | |
223 | + | |
224 | + if (oss->mmapped) { | |
225 | + int bytes; | |
226 | + | |
227 | + err = ioctl (oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo); | |
228 | + if (err < 0) { | |
229 | + dolog ("SNDCTL_DSP_GETOPTR failed\nReason: %s\n", errstr ()); | |
230 | + return; | |
231 | + } | |
232 | + | |
233 | + if (cntinfo.ptr == oss->old_optr) { | |
234 | + if (abs (hw->samples - live) < 64) | |
235 | + dolog ("overrun\n"); | |
236 | + return; | |
237 | + } | |
238 | + | |
239 | + if (cntinfo.ptr > oss->old_optr) { | |
240 | + bytes = cntinfo.ptr - oss->old_optr; | |
241 | + } | |
242 | + else { | |
243 | + bytes = hw->bufsize + cntinfo.ptr - oss->old_optr; | |
244 | + } | |
245 | + | |
246 | + decr = audio_MIN (bytes >> hw->shift, live); | |
247 | + } | |
248 | + else { | |
249 | + err = ioctl (oss->fd, SNDCTL_DSP_GETOSPACE, &abinfo); | |
250 | + if (err < 0) { | |
251 | + dolog ("SNDCTL_DSP_GETOSPACE failed\nReason: %s\n", errstr ()); | |
252 | + return; | |
253 | + } | |
254 | + | |
255 | + decr = audio_MIN (abinfo.bytes >> hw->shift, live); | |
256 | + if (decr <= 0) | |
257 | + return; | |
258 | + } | |
259 | + | |
260 | + samples = decr; | |
261 | + rpos = hw->rpos; | |
262 | + while (samples) { | |
263 | + int left_till_end_samples = hw->samples - rpos; | |
264 | + int convert_samples = audio_MIN (samples, left_till_end_samples); | |
265 | + | |
266 | + src = advance (hw->mix_buf, rpos * sizeof (st_sample_t)); | |
267 | + dst = advance (oss->pcm_buf, rpos << hw->shift); | |
268 | + | |
269 | + hw->clip (dst, src, convert_samples); | |
270 | + if (!oss->mmapped) { | |
271 | + int written; | |
272 | + | |
273 | + written = write (oss->fd, dst, convert_samples << hw->shift); | |
274 | + /* XXX: follow errno recommendations ? */ | |
275 | + if (written == -1) { | |
276 | + dolog ("Failed to write audio\nReason: %s\n", errstr ()); | |
277 | + continue; | |
278 | + } | |
279 | + | |
280 | + if (written != convert_samples << hw->shift) { | |
281 | + int wsamples = written >> hw->shift; | |
282 | + int wbytes = wsamples << hw->shift; | |
283 | + if (wbytes != written) { | |
284 | + dolog ("Unaligned write %d, %d\n", wbytes, written); | |
285 | + } | |
286 | + memset (src, 0, wbytes); | |
287 | + decr -= samples; | |
288 | + rpos = (rpos + wsamples) % hw->samples; | |
289 | + break; | |
290 | + } | |
291 | + } | |
292 | + memset (src, 0, convert_samples * sizeof (st_sample_t)); | |
293 | + | |
294 | + rpos = (rpos + convert_samples) % hw->samples; | |
295 | + samples -= convert_samples; | |
296 | + } | |
297 | + if (oss->mmapped) { | |
298 | + oss->old_optr = cntinfo.ptr; | |
299 | + } | |
300 | + | |
301 | + pcm_hw_dec_live (hw, decr); | |
302 | + hw->rpos = rpos; | |
303 | +} | |
304 | + | |
305 | +static void oss_hw_fini (HWVoice *hw) | |
306 | +{ | |
307 | + int err; | |
308 | + OSSVoice *oss = (OSSVoice *) hw; | |
309 | + | |
310 | + ldebug ("oss_hw_fini\n"); | |
311 | + err = close (oss->fd); | |
312 | + if (err) { | |
313 | + dolog ("Failed to close OSS descriptor\nReason: %s\n", errstr ()); | |
314 | + } | |
315 | + oss->fd = -1; | |
316 | + | |
317 | + if (oss->pcm_buf) { | |
318 | + if (oss->mmapped) { | |
319 | + err = munmap (oss->pcm_buf, hw->bufsize); | |
320 | + if (err) { | |
321 | + dolog ("Failed to unmap OSS buffer\nReason: %s\n", | |
322 | + errstr ()); | |
323 | + } | |
324 | + } | |
325 | + else { | |
326 | + qemu_free (oss->pcm_buf); | |
327 | + } | |
328 | + oss->pcm_buf = NULL; | |
329 | + } | |
330 | +} | |
331 | + | |
332 | +static int oss_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt) | |
333 | +{ | |
334 | + OSSVoice *oss = (OSSVoice *) hw; | |
335 | + struct oss_params req, obt; | |
336 | + | |
337 | + assert (!oss->fd); | |
338 | + req.fmt = AUD_to_ossfmt (fmt); | |
339 | + req.freq = freq; | |
340 | + req.nchannels = nchannels; | |
341 | + req.fragsize = conf.fragsize; | |
342 | + req.nfrags = conf.nfrags; | |
343 | + | |
344 | + if (oss_open (&req, &obt, &oss->fd)) | |
345 | + return -1; | |
346 | + | |
347 | + hw->freq = obt.freq; | |
348 | + hw->fmt = oss_to_audfmt (obt.fmt); | |
349 | + hw->nchannels = obt.nchannels; | |
350 | + | |
351 | + oss->nfrags = obt.nfrags; | |
352 | + oss->fragsize = obt.fragsize; | |
353 | + hw->bufsize = obt.nfrags * obt.fragsize; | |
354 | + | |
355 | + oss->mmapped = 0; | |
356 | + if (conf.try_mmap) { | |
357 | + oss->pcm_buf = mmap (0, hw->bufsize, PROT_READ | PROT_WRITE, | |
358 | + MAP_SHARED, oss->fd, 0); | |
359 | + if (oss->pcm_buf == MAP_FAILED) { | |
360 | + dolog ("Failed to mmap OSS device\nReason: %s\n", | |
361 | + errstr ()); | |
362 | + } | |
363 | + | |
364 | + for (;;) { | |
365 | + int err; | |
366 | + int trig = 0; | |
367 | + if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { | |
368 | + dolog ("SNDCTL_DSP_SETTRIGGER 0 failed\nReason: %s\n", | |
369 | + errstr ()); | |
370 | + goto fail; | |
371 | + } | |
372 | + | |
373 | + trig = PCM_ENABLE_OUTPUT; | |
374 | + if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { | |
375 | + dolog ("SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n" | |
376 | + "Reason: %s\n", errstr ()); | |
377 | + goto fail; | |
378 | + } | |
379 | + oss->mmapped = 1; | |
380 | + break; | |
381 | + | |
382 | + fail: | |
383 | + err = munmap (oss->pcm_buf, hw->bufsize); | |
384 | + if (err) { | |
385 | + dolog ("Failed to unmap OSS device\nReason: %s\n", | |
386 | + errstr ()); | |
387 | + } | |
388 | + } | |
389 | + } | |
390 | + | |
391 | + if (!oss->mmapped) { | |
392 | + oss->pcm_buf = qemu_mallocz (hw->bufsize); | |
393 | + if (!oss->pcm_buf) { | |
394 | + close (oss->fd); | |
395 | + oss->fd = -1; | |
396 | + return -1; | |
397 | + } | |
398 | + } | |
399 | + | |
400 | + return 0; | |
401 | +} | |
402 | + | |
403 | +static int oss_hw_ctl (HWVoice *hw, int cmd, ...) | |
404 | +{ | |
405 | + int trig; | |
406 | + OSSVoice *oss = (OSSVoice *) hw; | |
407 | + | |
408 | + if (!oss->mmapped) | |
409 | + return 0; | |
410 | + | |
411 | + switch (cmd) { | |
412 | + case VOICE_ENABLE: | |
413 | + ldebug ("enabling voice\n"); | |
414 | + pcm_hw_clear (hw, oss->pcm_buf, hw->samples); | |
415 | + trig = PCM_ENABLE_OUTPUT; | |
416 | + if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { | |
417 | + dolog ("SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n" | |
418 | + "Reason: %s\n", errstr ()); | |
419 | + return -1; | |
420 | + } | |
421 | + break; | |
422 | + | |
423 | + case VOICE_DISABLE: | |
424 | + ldebug ("disabling voice\n"); | |
425 | + trig = 0; | |
426 | + if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { | |
427 | + dolog ("SNDCTL_DSP_SETTRIGGER 0 failed\nReason: %s\n", | |
428 | + errstr ()); | |
429 | + return -1; | |
430 | + } | |
431 | + break; | |
432 | + } | |
433 | + return 0; | |
434 | +} | |
435 | + | |
436 | +static void *oss_audio_init (void) | |
437 | +{ | |
438 | + conf.fragsize = audio_get_conf_int (QC_OSS_FRAGSIZE, conf.fragsize); | |
439 | + conf.nfrags = audio_get_conf_int (QC_OSS_NFRAGS, conf.nfrags); | |
440 | + conf.try_mmap = audio_get_conf_int (QC_OSS_MMAP, conf.try_mmap); | |
441 | + conf.dspname = audio_get_conf_str (QC_OSS_DEV, conf.dspname); | |
442 | + return &conf; | |
443 | +} | |
444 | + | |
445 | +static void oss_audio_fini (void *opaque) | |
446 | +{ | |
447 | +} | |
448 | + | |
449 | +struct pcm_ops oss_pcm_ops = { | |
450 | + oss_hw_init, | |
451 | + oss_hw_fini, | |
452 | + oss_hw_run, | |
453 | + oss_hw_write, | |
454 | + oss_hw_ctl | |
455 | +}; | |
456 | + | |
457 | +struct audio_output_driver oss_output_driver = { | |
458 | + "oss", | |
459 | + oss_audio_init, | |
460 | + oss_audio_fini, | |
461 | + &oss_pcm_ops, | |
462 | + 1, | |
463 | + INT_MAX, | |
464 | + sizeof (OSSVoice) | |
465 | +}; | |
466 | +#endif | ... | ... |
audio/ossaudio.h
0 → 100644
1 | +/* | |
2 | + * QEMU OSS audio output driver header | |
3 | + * | |
4 | + * Copyright (c) 2003-2004 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 | +#ifndef QEMU_OSSAUDIO_H | |
25 | +#define QEMU_OSSAUDIO_H | |
26 | + | |
27 | +typedef struct OSSVoice { | |
28 | + struct HWVoice hw; | |
29 | + void *pcm_buf; | |
30 | + int fd; | |
31 | + int nfrags; | |
32 | + int fragsize; | |
33 | + int mmapped; | |
34 | + int old_optr; | |
35 | +} OSSVoice; | |
36 | + | |
37 | +extern struct pcm_ops oss_pcm_ops; | |
38 | +extern struct audio_output_driver oss_output_driver; | |
39 | + | |
40 | +#endif /* ossaudio.h */ | ... | ... |
audio/sdlaudio.c
0 → 100644
1 | +/* | |
2 | + * QEMU SDL audio output driver | |
3 | + * | |
4 | + * Copyright (c) 2004 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 <SDL/SDL.h> | |
25 | +#include <SDL/SDL_thread.h> | |
26 | +#include "vl.h" | |
27 | + | |
28 | +#define AUDIO_CAP "sdl" | |
29 | +#include "audio/audio.h" | |
30 | +#include "audio/sdlaudio.h" | |
31 | + | |
32 | +#define QC_SDL_SAMPLES "QEMU_SDL_SAMPLES" | |
33 | + | |
34 | +#define errstr() SDL_GetError () | |
35 | + | |
36 | +static struct { | |
37 | + int nb_samples; | |
38 | +} conf = { | |
39 | + 1024 | |
40 | +}; | |
41 | + | |
42 | +struct SDLAudioState { | |
43 | + int exit; | |
44 | + SDL_mutex *mutex; | |
45 | + SDL_sem *sem; | |
46 | + int initialized; | |
47 | +} glob_sdl; | |
48 | +typedef struct SDLAudioState SDLAudioState; | |
49 | + | |
50 | +static void sdl_hw_run (HWVoice *hw) | |
51 | +{ | |
52 | + (void) hw; | |
53 | +} | |
54 | + | |
55 | +static int sdl_lock (SDLAudioState *s) | |
56 | +{ | |
57 | + if (SDL_LockMutex (s->mutex)) { | |
58 | + dolog ("SDL_LockMutex failed\nReason: %s\n", errstr ()); | |
59 | + return -1; | |
60 | + } | |
61 | + return 0; | |
62 | +} | |
63 | + | |
64 | +static int sdl_unlock (SDLAudioState *s) | |
65 | +{ | |
66 | + if (SDL_UnlockMutex (s->mutex)) { | |
67 | + dolog ("SDL_UnlockMutex failed\nReason: %s\n", errstr ()); | |
68 | + return -1; | |
69 | + } | |
70 | + return 0; | |
71 | +} | |
72 | + | |
73 | +static int sdl_post (SDLAudioState *s) | |
74 | +{ | |
75 | + if (SDL_SemPost (s->sem)) { | |
76 | + dolog ("SDL_SemPost failed\nReason: %s\n", errstr ()); | |
77 | + return -1; | |
78 | + } | |
79 | + return 0; | |
80 | +} | |
81 | + | |
82 | +static int sdl_wait (SDLAudioState *s) | |
83 | +{ | |
84 | + if (SDL_SemWait (s->sem)) { | |
85 | + dolog ("SDL_SemWait failed\nReason: %s\n", errstr ()); | |
86 | + return -1; | |
87 | + } | |
88 | + return 0; | |
89 | +} | |
90 | + | |
91 | +static int sdl_unlock_and_post (SDLAudioState *s) | |
92 | +{ | |
93 | + if (sdl_unlock (s)) | |
94 | + return -1; | |
95 | + | |
96 | + return sdl_post (s); | |
97 | +} | |
98 | + | |
99 | +static int sdl_hw_write (SWVoice *sw, void *buf, int len) | |
100 | +{ | |
101 | + int ret; | |
102 | + SDLAudioState *s = &glob_sdl; | |
103 | + sdl_lock (s); | |
104 | + ret = pcm_hw_write (sw, buf, len); | |
105 | + sdl_unlock_and_post (s); | |
106 | + return ret; | |
107 | +} | |
108 | + | |
109 | +static int AUD_to_sdlfmt (audfmt_e fmt, int *shift) | |
110 | +{ | |
111 | + *shift = 0; | |
112 | + switch (fmt) { | |
113 | + case AUD_FMT_S8: return AUDIO_S8; | |
114 | + case AUD_FMT_U8: return AUDIO_U8; | |
115 | + case AUD_FMT_S16: *shift = 1; return AUDIO_S16LSB; | |
116 | + case AUD_FMT_U16: *shift = 1; return AUDIO_U16LSB; | |
117 | + default: | |
118 | + dolog ("Internal logic error: Bad audio format %d\nAborting\n", fmt); | |
119 | + exit (EXIT_FAILURE); | |
120 | + } | |
121 | +} | |
122 | + | |
123 | +static int sdl_to_audfmt (int fmt) | |
124 | +{ | |
125 | + switch (fmt) { | |
126 | + case AUDIO_S8: return AUD_FMT_S8; | |
127 | + case AUDIO_U8: return AUD_FMT_U8; | |
128 | + case AUDIO_S16LSB: return AUD_FMT_S16; | |
129 | + case AUDIO_U16LSB: return AUD_FMT_U16; | |
130 | + default: | |
131 | + dolog ("Internal logic error: Unrecognized SDL audio format %d\n" | |
132 | + "Aborting\n", fmt); | |
133 | + exit (EXIT_FAILURE); | |
134 | + } | |
135 | +} | |
136 | + | |
137 | +static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt) | |
138 | +{ | |
139 | + int status; | |
140 | + | |
141 | + status = SDL_OpenAudio (req, obt); | |
142 | + if (status) { | |
143 | + dolog ("SDL_OpenAudio failed\nReason: %s\n", errstr ()); | |
144 | + } | |
145 | + return status; | |
146 | +} | |
147 | + | |
148 | +static void sdl_close (SDLAudioState *s) | |
149 | +{ | |
150 | + if (s->initialized) { | |
151 | + sdl_lock (s); | |
152 | + s->exit = 1; | |
153 | + sdl_unlock_and_post (s); | |
154 | + SDL_PauseAudio (1); | |
155 | + SDL_CloseAudio (); | |
156 | + s->initialized = 0; | |
157 | + } | |
158 | +} | |
159 | + | |
160 | +static void sdl_callback (void *opaque, Uint8 *buf, int len) | |
161 | +{ | |
162 | + SDLVoice *sdl = opaque; | |
163 | + SDLAudioState *s = &glob_sdl; | |
164 | + HWVoice *hw = &sdl->hw; | |
165 | + int samples = len >> hw->shift; | |
166 | + | |
167 | + if (s->exit) { | |
168 | + return; | |
169 | + } | |
170 | + | |
171 | + while (samples) { | |
172 | + int to_mix, live, decr; | |
173 | + | |
174 | + /* dolog ("in callback samples=%d\n", samples); */ | |
175 | + sdl_wait (s); | |
176 | + if (s->exit) { | |
177 | + return; | |
178 | + } | |
179 | + | |
180 | + sdl_lock (s); | |
181 | + live = pcm_hw_get_live (hw); | |
182 | + if (live <= 0) | |
183 | + goto again; | |
184 | + | |
185 | + /* dolog ("in callback live=%d\n", live); */ | |
186 | + to_mix = audio_MIN (samples, live); | |
187 | + decr = to_mix; | |
188 | + while (to_mix) { | |
189 | + int chunk = audio_MIN (to_mix, hw->samples - hw->rpos); | |
190 | + st_sample_t *src = hw->mix_buf + hw->rpos; | |
191 | + | |
192 | + /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */ | |
193 | + hw->clip (buf, src, chunk); | |
194 | + memset (src, 0, chunk * sizeof (st_sample_t)); | |
195 | + hw->rpos = (hw->rpos + chunk) % hw->samples; | |
196 | + to_mix -= chunk; | |
197 | + buf += chunk << hw->shift; | |
198 | + } | |
199 | + samples -= decr; | |
200 | + pcm_hw_dec_live (hw, decr); | |
201 | + | |
202 | + again: | |
203 | + sdl_unlock (s); | |
204 | + } | |
205 | + /* dolog ("done len=%d\n", len); */ | |
206 | +} | |
207 | + | |
208 | +static void sdl_hw_fini (HWVoice *hw) | |
209 | +{ | |
210 | + ldebug ("sdl_hw_fini %d fixed=%d\n", | |
211 | + glob_sdl.initialized, audio_conf.fixed_format); | |
212 | + sdl_close (&glob_sdl); | |
213 | +} | |
214 | + | |
215 | +static int sdl_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt) | |
216 | +{ | |
217 | + SDLVoice *sdl = (SDLVoice *) hw; | |
218 | + SDLAudioState *s = &glob_sdl; | |
219 | + SDL_AudioSpec req, obt; | |
220 | + int shift; | |
221 | + | |
222 | + ldebug ("sdl_hw_init %d freq=%d fixed=%d\n", | |
223 | + s->initialized, freq, audio_conf.fixed_format); | |
224 | + | |
225 | + if (nchannels != 2) { | |
226 | + dolog ("Bogus channel count %d\n", nchannels); | |
227 | + return -1; | |
228 | + } | |
229 | + | |
230 | + req.freq = freq; | |
231 | + req.format = AUD_to_sdlfmt (fmt, &shift); | |
232 | + req.channels = nchannels; | |
233 | + req.samples = conf.nb_samples; | |
234 | + shift <<= nchannels == 2; | |
235 | + | |
236 | + req.callback = sdl_callback; | |
237 | + req.userdata = sdl; | |
238 | + | |
239 | + if (sdl_open (&req, &obt)) | |
240 | + return -1; | |
241 | + | |
242 | + hw->freq = obt.freq; | |
243 | + hw->fmt = sdl_to_audfmt (obt.format); | |
244 | + hw->nchannels = obt.channels; | |
245 | + hw->bufsize = obt.samples << shift; | |
246 | + | |
247 | + s->initialized = 1; | |
248 | + s->exit = 0; | |
249 | + SDL_PauseAudio (0); | |
250 | + return 0; | |
251 | +} | |
252 | + | |
253 | +static int sdl_hw_ctl (HWVoice *hw, int cmd, ...) | |
254 | +{ | |
255 | + (void) hw; | |
256 | + | |
257 | + switch (cmd) { | |
258 | + case VOICE_ENABLE: | |
259 | + SDL_PauseAudio (0); | |
260 | + break; | |
261 | + | |
262 | + case VOICE_DISABLE: | |
263 | + SDL_PauseAudio (1); | |
264 | + break; | |
265 | + } | |
266 | + return 0; | |
267 | +} | |
268 | + | |
269 | +static void *sdl_audio_init (void) | |
270 | +{ | |
271 | + SDLAudioState *s = &glob_sdl; | |
272 | + conf.nb_samples = audio_get_conf_int (QC_SDL_SAMPLES, conf.nb_samples); | |
273 | + | |
274 | + if (SDL_InitSubSystem (SDL_INIT_AUDIO)) { | |
275 | + dolog ("SDL failed to initialize audio subsystem\nReason: %s\n", | |
276 | + errstr ()); | |
277 | + return NULL; | |
278 | + } | |
279 | + | |
280 | + s->mutex = SDL_CreateMutex (); | |
281 | + if (!s->mutex) { | |
282 | + dolog ("Failed to create SDL mutex\nReason: %s\n", errstr ()); | |
283 | + SDL_QuitSubSystem (SDL_INIT_AUDIO); | |
284 | + return NULL; | |
285 | + } | |
286 | + | |
287 | + s->sem = SDL_CreateSemaphore (0); | |
288 | + if (!s->sem) { | |
289 | + dolog ("Failed to create SDL semaphore\nReason: %s\n", errstr ()); | |
290 | + SDL_DestroyMutex (s->mutex); | |
291 | + SDL_QuitSubSystem (SDL_INIT_AUDIO); | |
292 | + return NULL; | |
293 | + } | |
294 | + | |
295 | + return s; | |
296 | +} | |
297 | + | |
298 | +static void sdl_audio_fini (void *opaque) | |
299 | +{ | |
300 | + SDLAudioState *s = opaque; | |
301 | + sdl_close (s); | |
302 | + SDL_DestroySemaphore (s->sem); | |
303 | + SDL_DestroyMutex (s->mutex); | |
304 | + SDL_QuitSubSystem (SDL_INIT_AUDIO); | |
305 | +} | |
306 | + | |
307 | +struct pcm_ops sdl_pcm_ops = { | |
308 | + sdl_hw_init, | |
309 | + sdl_hw_fini, | |
310 | + sdl_hw_run, | |
311 | + sdl_hw_write, | |
312 | + sdl_hw_ctl | |
313 | +}; | |
314 | + | |
315 | +struct audio_output_driver sdl_output_driver = { | |
316 | + "sdl", | |
317 | + sdl_audio_init, | |
318 | + sdl_audio_fini, | |
319 | + &sdl_pcm_ops, | |
320 | + 1, | |
321 | + 1, | |
322 | + sizeof (SDLVoice) | |
323 | +}; | ... | ... |
audio/sdlaudio.h
0 → 100644
1 | +/* | |
2 | + * QEMU SDL audio output driver header | |
3 | + * | |
4 | + * Copyright (c) 2004 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 | +#ifndef QEMU_SDLAUDIO_H | |
25 | +#define QEMU_SDLAUDIO_H | |
26 | + | |
27 | +typedef struct SDLVoice { | |
28 | + struct HWVoice hw; | |
29 | +} SDLVoice; | |
30 | + | |
31 | +extern struct pcm_ops sdl_pcm_ops; | |
32 | +extern struct audio_output_driver sdl_output_driver; | |
33 | + | |
34 | +#endif /* sdlaudio.h */ | ... | ... |
audio/wavaudio.c
0 → 100644
1 | +/* | |
2 | + * QEMU WAV audio output driver | |
3 | + * | |
4 | + * Copyright (c) 2004 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 "vl.h" | |
25 | + | |
26 | +#define AUDIO_CAP "wav" | |
27 | +#include "audio/audio.h" | |
28 | +#include "audio/wavaudio.h" | |
29 | + | |
30 | +static struct { | |
31 | + const char *wav_path; | |
32 | +} conf = { | |
33 | + .wav_path = "qemu.wav" | |
34 | +}; | |
35 | + | |
36 | +static void wav_hw_run (HWVoice *hw) | |
37 | +{ | |
38 | + WAVVoice *wav = (WAVVoice *) hw; | |
39 | + int rpos, live, decr, samples; | |
40 | + uint8_t *dst; | |
41 | + st_sample_t *src; | |
42 | + int64_t now = qemu_get_clock (vm_clock); | |
43 | + int64_t ticks = now - wav->old_ticks; | |
44 | + int64_t bytes = (ticks * hw->bytes_per_second) / ticks_per_sec; | |
45 | + wav->old_ticks = now; | |
46 | + | |
47 | + if (bytes > INT_MAX) | |
48 | + samples = INT_MAX >> hw->shift; | |
49 | + else | |
50 | + samples = bytes >> hw->shift; | |
51 | + | |
52 | + live = pcm_hw_get_live (hw); | |
53 | + if (live <= 0) | |
54 | + return; | |
55 | + | |
56 | + decr = audio_MIN (live, samples); | |
57 | + samples = decr; | |
58 | + rpos = hw->rpos; | |
59 | + while (samples) { | |
60 | + int left_till_end_samples = hw->samples - rpos; | |
61 | + int convert_samples = audio_MIN (samples, left_till_end_samples); | |
62 | + | |
63 | + src = advance (hw->mix_buf, rpos * sizeof (st_sample_t)); | |
64 | + dst = advance (wav->pcm_buf, rpos << hw->shift); | |
65 | + | |
66 | + hw->clip (dst, src, convert_samples); | |
67 | + qemu_put_buffer (wav->f, dst, convert_samples << hw->shift); | |
68 | + memset (src, 0, convert_samples * sizeof (st_sample_t)); | |
69 | + | |
70 | + rpos = (rpos + convert_samples) % hw->samples; | |
71 | + samples -= convert_samples; | |
72 | + wav->total_samples += convert_samples; | |
73 | + } | |
74 | + | |
75 | + pcm_hw_dec_live (hw, decr); | |
76 | + hw->rpos = rpos; | |
77 | +} | |
78 | + | |
79 | +static int wav_hw_write (SWVoice *sw, void *buf, int len) | |
80 | +{ | |
81 | + return pcm_hw_write (sw, buf, len); | |
82 | +} | |
83 | + | |
84 | + | |
85 | +/* VICE code: Store number as little endian. */ | |
86 | +static void le_store (uint8_t *buf, uint32_t val, int len) | |
87 | +{ | |
88 | + int i; | |
89 | + for (i = 0; i < len; i++) { | |
90 | + buf[i] = (uint8_t) (val & 0xff); | |
91 | + val >>= 8; | |
92 | + } | |
93 | +} | |
94 | + | |
95 | +static int wav_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt) | |
96 | +{ | |
97 | + WAVVoice *wav = (WAVVoice *) hw; | |
98 | + int bits16 = 0, stereo = audio_state.fixed_channels == 2; | |
99 | + uint8_t hdr[] = { | |
100 | + 0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56, | |
101 | + 0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, | |
102 | + 0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04, | |
103 | + 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00 | |
104 | + }; | |
105 | + | |
106 | + switch (audio_state.fixed_fmt) { | |
107 | + case AUD_FMT_S8: | |
108 | + case AUD_FMT_U8: | |
109 | + break; | |
110 | + | |
111 | + case AUD_FMT_S16: | |
112 | + case AUD_FMT_U16: | |
113 | + bits16 = 1; | |
114 | + break; | |
115 | + } | |
116 | + | |
117 | + hdr[34] = bits16 ? 0x10 : 0x08; | |
118 | + hw->freq = 44100; | |
119 | + hw->nchannels = stereo ? 2 : 1; | |
120 | + hw->fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8; | |
121 | + hw->bufsize = 4096; | |
122 | + wav->pcm_buf = qemu_mallocz (hw->bufsize); | |
123 | + if (!wav->pcm_buf) | |
124 | + return -1; | |
125 | + | |
126 | + le_store (hdr + 22, hw->nchannels, 2); | |
127 | + le_store (hdr + 24, hw->freq, 4); | |
128 | + le_store (hdr + 28, hw->freq << (bits16 + stereo), 4); | |
129 | + le_store (hdr + 32, 1 << (bits16 + stereo), 2); | |
130 | + | |
131 | + wav->f = fopen (conf.wav_path, "wb"); | |
132 | + if (!wav->f) { | |
133 | + dolog ("failed to open wave file `%s'\nReason: %s\n", | |
134 | + conf.wav_path, strerror (errno)); | |
135 | + return -1; | |
136 | + } | |
137 | + | |
138 | + qemu_put_buffer (wav->f, hdr, sizeof (hdr)); | |
139 | + return 0; | |
140 | +} | |
141 | + | |
142 | +static void wav_hw_fini (HWVoice *hw) | |
143 | +{ | |
144 | + WAVVoice *wav = (WAVVoice *) hw; | |
145 | + int stereo = hw->nchannels == 2; | |
146 | + uint8_t rlen[4]; | |
147 | + uint8_t dlen[4]; | |
148 | + uint32_t rifflen = (wav->total_samples << stereo) + 36; | |
149 | + uint32_t datalen = wav->total_samples << stereo; | |
150 | + | |
151 | + if (!wav->f || !hw->active) | |
152 | + return; | |
153 | + | |
154 | + le_store (rlen, rifflen, 4); | |
155 | + le_store (dlen, datalen, 4); | |
156 | + | |
157 | + qemu_fseek (wav->f, 4, SEEK_SET); | |
158 | + qemu_put_buffer (wav->f, rlen, 4); | |
159 | + | |
160 | + qemu_fseek (wav->f, 32, SEEK_CUR); | |
161 | + qemu_put_buffer (wav->f, dlen, 4); | |
162 | + | |
163 | + fclose (wav->f); | |
164 | + wav->f = NULL; | |
165 | +} | |
166 | + | |
167 | +static int wav_hw_ctl (HWVoice *hw, int cmd, ...) | |
168 | +{ | |
169 | + (void) hw; | |
170 | + (void) cmd; | |
171 | + return 0; | |
172 | +} | |
173 | + | |
174 | +static void *wav_audio_init (void) | |
175 | +{ | |
176 | + return &conf; | |
177 | +} | |
178 | + | |
179 | +static void wav_audio_fini (void *opaque) | |
180 | +{ | |
181 | + ldebug ("wav_fini"); | |
182 | +} | |
183 | + | |
184 | +struct pcm_ops wav_pcm_ops = { | |
185 | + wav_hw_init, | |
186 | + wav_hw_fini, | |
187 | + wav_hw_run, | |
188 | + wav_hw_write, | |
189 | + wav_hw_ctl | |
190 | +}; | |
191 | + | |
192 | +struct audio_output_driver wav_output_driver = { | |
193 | + "wav", | |
194 | + wav_audio_init, | |
195 | + wav_audio_fini, | |
196 | + &wav_pcm_ops, | |
197 | + 1, | |
198 | + 1, | |
199 | + sizeof (WAVVoice) | |
200 | +}; | ... | ... |
audio/wavaudio.h
0 → 100644
1 | +/* | |
2 | + * QEMU WAV audio output driver header | |
3 | + * | |
4 | + * Copyright (c) 2004 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 | +#ifndef QEMU_WAVAUDIO_H | |
25 | +#define QEMU_WAVAUDIO_H | |
26 | + | |
27 | +typedef struct WAVVoice { | |
28 | + struct HWVoice hw; | |
29 | + QEMUFile *f; | |
30 | + int64_t old_ticks; | |
31 | + void *pcm_buf; | |
32 | + int total_samples; | |
33 | +} WAVVoice; | |
34 | + | |
35 | +extern struct pcm_ops wav_pcm_ops; | |
36 | +extern struct audio_output_driver wav_output_driver; | |
37 | + | |
38 | +#endif /* wavaudio.h */ | ... | ... |
hw/adlib.c
0 → 100644
1 | +/* | |
2 | + * QEMU Adlib emulation | |
3 | + * | |
4 | + * Copyright (c) 2004 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 "vl.h" | |
25 | + | |
26 | +#define AUDIO_CAP "adlib" | |
27 | +#include "audio/audio.h" | |
28 | + | |
29 | +#ifdef USE_YMF262 | |
30 | +#define HAS_YMF262 1 | |
31 | +#include "ymf262.h" | |
32 | +void YMF262UpdateOneQEMU(int which, INT16 *dst, int length); | |
33 | +#define SHIFT 2 | |
34 | +#else | |
35 | +#include "fmopl.h" | |
36 | +#define SHIFT 1 | |
37 | +#endif | |
38 | + | |
39 | +#ifdef _WIN32 | |
40 | +#include <windows.h> | |
41 | +#define small_delay() Sleep (1) | |
42 | +#else | |
43 | +#define small_delay() usleep (1) | |
44 | +#endif | |
45 | + | |
46 | +#define IO_READ_PROTO(name) \ | |
47 | + uint32_t name (void *opaque, uint32_t nport) | |
48 | +#define IO_WRITE_PROTO(name) \ | |
49 | + void name (void *opaque, uint32_t nport, uint32_t val) | |
50 | + | |
51 | +static struct { | |
52 | + int port; | |
53 | + int freq; | |
54 | +} conf = {0x220, 44100}; | |
55 | + | |
56 | +typedef struct { | |
57 | + int enabled; | |
58 | + int active; | |
59 | + int cparam; | |
60 | + int64_t ticks; | |
61 | + int bufpos; | |
62 | + int16_t *mixbuf; | |
63 | + double interval; | |
64 | + QEMUTimer *ts, *opl_ts; | |
65 | + SWVoice *voice; | |
66 | + int left, pos, samples, bytes_per_second, old_free; | |
67 | + int refcount; | |
68 | +#ifndef USE_YMF262 | |
69 | + FM_OPL *opl; | |
70 | +#endif | |
71 | +} AdlibState; | |
72 | + | |
73 | +static AdlibState adlib; | |
74 | + | |
75 | +static IO_WRITE_PROTO(adlib_write) | |
76 | +{ | |
77 | + AdlibState *s = opaque; | |
78 | + int a = nport & 3; | |
79 | + int status; | |
80 | + | |
81 | + s->ticks = qemu_get_clock (vm_clock); | |
82 | + s->active = 1; | |
83 | + AUD_enable (s->voice, 1); | |
84 | + | |
85 | +#ifdef USE_YMF262 | |
86 | + status = YMF262Write (0, a, val); | |
87 | +#else | |
88 | + status = OPLWrite (s->opl, a, val); | |
89 | +#endif | |
90 | +} | |
91 | + | |
92 | +static IO_READ_PROTO(adlib_read) | |
93 | +{ | |
94 | + AdlibState *s = opaque; | |
95 | + uint8_t data; | |
96 | + int a = nport & 3; | |
97 | + | |
98 | +#ifdef USE_YMF262 | |
99 | + (void) s; | |
100 | + data = YMF262Read (0, a); | |
101 | +#else | |
102 | + data = OPLRead (s->opl, a); | |
103 | +#endif | |
104 | + return data; | |
105 | +} | |
106 | + | |
107 | +static void OPL_timer (void *opaque) | |
108 | +{ | |
109 | + AdlibState *s = opaque; | |
110 | +#ifdef USE_YMF262 | |
111 | + YMF262TimerOver (s->cparam >> 1, s->cparam & 1); | |
112 | +#else | |
113 | + OPLTimerOver (s->opl, s->cparam); | |
114 | +#endif | |
115 | + qemu_mod_timer (s->opl_ts, qemu_get_clock (vm_clock) + s->interval); | |
116 | +} | |
117 | + | |
118 | +static void YMF262TimerHandler (int c, double interval_Sec) | |
119 | +{ | |
120 | + AdlibState *s = &adlib; | |
121 | + if (interval_Sec == 0.0) { | |
122 | + qemu_del_timer (s->opl_ts); | |
123 | + return; | |
124 | + } | |
125 | + s->cparam = c; | |
126 | + s->interval = ticks_per_sec * interval_Sec; | |
127 | + qemu_mod_timer (s->opl_ts, qemu_get_clock (vm_clock) + s->interval); | |
128 | + small_delay (); | |
129 | +} | |
130 | + | |
131 | +static int write_audio (AdlibState *s, int samples) | |
132 | +{ | |
133 | + int net = 0; | |
134 | + int ss = samples; | |
135 | + while (samples) { | |
136 | + int nbytes = samples << SHIFT; | |
137 | + int wbytes = AUD_write (s->voice, | |
138 | + s->mixbuf + (s->pos << (SHIFT - 1)), | |
139 | + nbytes); | |
140 | + int wsampl = wbytes >> SHIFT; | |
141 | + samples -= wsampl; | |
142 | + s->pos = (s->pos + wsampl) % s->samples; | |
143 | + net += wsampl; | |
144 | + if (!wbytes) | |
145 | + break; | |
146 | + } | |
147 | + if (net > ss) { | |
148 | + dolog ("WARNING: net > ss\n"); | |
149 | + } | |
150 | + return net; | |
151 | +} | |
152 | + | |
153 | +static void timer (void *opaque) | |
154 | +{ | |
155 | + AdlibState *s = opaque; | |
156 | + int elapsed, samples, net = 0; | |
157 | + | |
158 | + if (s->refcount) | |
159 | + dolog ("refcount=%d\n", s->refcount); | |
160 | + | |
161 | + s->refcount += 1; | |
162 | + if (!(s->active && s->enabled)) | |
163 | + goto reset; | |
164 | + | |
165 | + AUD_run (); | |
166 | + | |
167 | + while (s->left) { | |
168 | + int written = write_audio (s, s->left); | |
169 | + net += written; | |
170 | + if (!written) | |
171 | + goto reset2; | |
172 | + s->left -= written; | |
173 | + } | |
174 | + s->pos = 0; | |
175 | + | |
176 | + elapsed = AUD_calc_elapsed (s->voice); | |
177 | + if (!elapsed) | |
178 | + goto reset2; | |
179 | + | |
180 | + /* elapsed = AUD_get_free (s->voice); */ | |
181 | + samples = elapsed >> SHIFT; | |
182 | + if (!samples) | |
183 | + goto reset2; | |
184 | + | |
185 | + samples = audio_MIN (samples, s->samples - s->pos); | |
186 | + if (s->left) | |
187 | + dolog ("left=%d samples=%d elapsed=%d free=%d\n", | |
188 | + s->left, samples, elapsed, AUD_get_free (s->voice)); | |
189 | + | |
190 | + if (!samples) | |
191 | + goto reset2; | |
192 | + | |
193 | +#ifdef USE_YMF262 | |
194 | + YMF262UpdateOneQEMU (0, s->mixbuf + s->pos * 2, samples); | |
195 | +#else | |
196 | + YM3812UpdateOne (s->opl, s->mixbuf + s->pos, samples); | |
197 | +#endif | |
198 | + | |
199 | + while (samples) { | |
200 | + int written = write_audio (s, samples); | |
201 | + net += written; | |
202 | + if (!written) | |
203 | + break; | |
204 | + samples -= written; | |
205 | + } | |
206 | + if (!samples) | |
207 | + s->pos = 0; | |
208 | + s->left = samples; | |
209 | + | |
210 | +reset2: | |
211 | + AUD_adjust (s->voice, net << SHIFT); | |
212 | +reset: | |
213 | + qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + ticks_per_sec / 1024); | |
214 | + s->refcount -= 1; | |
215 | +} | |
216 | + | |
217 | +static void Adlib_fini (AdlibState *s) | |
218 | +{ | |
219 | +#ifdef USE_YMF262 | |
220 | + YMF262Shutdown (); | |
221 | +#else | |
222 | + if (s->opl) { | |
223 | + OPLDestroy (s->opl); | |
224 | + s->opl = NULL; | |
225 | + } | |
226 | +#endif | |
227 | + | |
228 | + if (s->opl_ts) | |
229 | + qemu_free_timer (s->opl_ts); | |
230 | + | |
231 | + if (s->ts) | |
232 | + qemu_free_timer (s->ts); | |
233 | + | |
234 | +#define maybe_free(p) if (p) qemu_free (p) | |
235 | + maybe_free (s->mixbuf); | |
236 | +#undef maybe_free | |
237 | + | |
238 | + s->active = 0; | |
239 | + s->enabled = 0; | |
240 | +} | |
241 | + | |
242 | +void Adlib_init (void) | |
243 | +{ | |
244 | + AdlibState *s = &adlib; | |
245 | + | |
246 | + memset (s, 0, sizeof (*s)); | |
247 | + | |
248 | +#ifdef USE_YMF262 | |
249 | + if (YMF262Init (1, 14318180, conf.freq)) { | |
250 | + dolog ("YMF262Init %d failed\n", conf.freq); | |
251 | + return; | |
252 | + } | |
253 | + else { | |
254 | + YMF262SetTimerHandler (0, YMF262TimerHandler, 0); | |
255 | + s->enabled = 1; | |
256 | + } | |
257 | +#else | |
258 | + s->opl = OPLCreate (OPL_TYPE_YM3812, 3579545, conf.freq); | |
259 | + if (!s->opl) { | |
260 | + dolog ("OPLCreate %d failed\n", conf.freq); | |
261 | + return; | |
262 | + } | |
263 | + else { | |
264 | + OPLSetTimerHandler (s->opl, YMF262TimerHandler, 0); | |
265 | + s->enabled = 1; | |
266 | + } | |
267 | +#endif | |
268 | + | |
269 | + s->opl_ts = qemu_new_timer (vm_clock, OPL_timer, s); | |
270 | + if (!s->opl_ts) { | |
271 | + dolog ("Can not get timer for adlib emulation\n"); | |
272 | + Adlib_fini (s); | |
273 | + return; | |
274 | + } | |
275 | + | |
276 | + s->ts = qemu_new_timer (vm_clock, timer, s); | |
277 | + if (!s->opl_ts) { | |
278 | + dolog ("Can not get timer for adlib emulation\n"); | |
279 | + Adlib_fini (s); | |
280 | + return; | |
281 | + } | |
282 | + | |
283 | + s->voice = AUD_open (s->voice, "adlib", conf.freq, SHIFT, AUD_FMT_S16); | |
284 | + if (!s->voice) { | |
285 | + Adlib_fini (s); | |
286 | + return; | |
287 | + } | |
288 | + | |
289 | + s->bytes_per_second = conf.freq << SHIFT; | |
290 | + s->samples = AUD_get_buffer_size (s->voice) >> SHIFT; | |
291 | + s->mixbuf = qemu_mallocz (s->samples << SHIFT); | |
292 | + | |
293 | + if (!s->mixbuf) { | |
294 | + dolog ("not enough memory for adlib mixing buffer (%d)\n", | |
295 | + s->samples << SHIFT); | |
296 | + Adlib_fini (s); | |
297 | + return; | |
298 | + } | |
299 | + register_ioport_read (0x388, 4, 1, adlib_read, s); | |
300 | + register_ioport_write (0x388, 4, 1, adlib_write, s); | |
301 | + | |
302 | + register_ioport_read (conf.port, 4, 1, adlib_read, s); | |
303 | + register_ioport_write (conf.port, 4, 1, adlib_write, s); | |
304 | + | |
305 | + register_ioport_read (conf.port + 8, 2, 1, adlib_read, s); | |
306 | + register_ioport_write (conf.port + 8, 2, 1, adlib_write, s); | |
307 | + | |
308 | + qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + 1); | |
309 | +} | ... | ... |
hw/dma.c
1 | 1 | /* |
2 | 2 | * QEMU DMA emulation |
3 | - * | |
4 | - * Copyright (c) 2003 Vassili Karpov (malc) | |
5 | - * | |
3 | + * | |
4 | + * Copyright (c) 2003-2004 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 |
... | ... | @@ -23,9 +23,9 @@ |
23 | 23 | */ |
24 | 24 | #include "vl.h" |
25 | 25 | |
26 | -//#define DEBUG_DMA | |
26 | +/* #define DEBUG_DMA */ | |
27 | 27 | |
28 | -#define log(...) fprintf (stderr, "dma: " __VA_ARGS__) | |
28 | +#define dolog(...) fprintf (stderr, "dma: " __VA_ARGS__) | |
29 | 29 | #ifdef DEBUG_DMA |
30 | 30 | #define lwarn(...) fprintf (stderr, "dma: " __VA_ARGS__) |
31 | 31 | #define linfo(...) fprintf (stderr, "dma: " __VA_ARGS__) |
... | ... | @@ -86,7 +86,7 @@ static void write_page (void *opaque, uint32_t nport, uint32_t data) |
86 | 86 | |
87 | 87 | ichan = channels[nport & 7]; |
88 | 88 | if (-1 == ichan) { |
89 | - log ("invalid channel %#x %#x\n", nport, data); | |
89 | + dolog ("invalid channel %#x %#x\n", nport, data); | |
90 | 90 | return; |
91 | 91 | } |
92 | 92 | d->regs[ichan].page = data; |
... | ... | @@ -99,7 +99,7 @@ static void write_pageh (void *opaque, uint32_t nport, uint32_t data) |
99 | 99 | |
100 | 100 | ichan = channels[nport & 7]; |
101 | 101 | if (-1 == ichan) { |
102 | - log ("invalid channel %#x %#x\n", nport, data); | |
102 | + dolog ("invalid channel %#x %#x\n", nport, data); | |
103 | 103 | return; |
104 | 104 | } |
105 | 105 | d->regs[ichan].pageh = data; |
... | ... | @@ -112,7 +112,7 @@ static uint32_t read_page (void *opaque, uint32_t nport) |
112 | 112 | |
113 | 113 | ichan = channels[nport & 7]; |
114 | 114 | if (-1 == ichan) { |
115 | - log ("invalid channel read %#x\n", nport); | |
115 | + dolog ("invalid channel read %#x\n", nport); | |
116 | 116 | return 0; |
117 | 117 | } |
118 | 118 | return d->regs[ichan].page; |
... | ... | @@ -125,7 +125,7 @@ static uint32_t read_pageh (void *opaque, uint32_t nport) |
125 | 125 | |
126 | 126 | ichan = channels[nport & 7]; |
127 | 127 | if (-1 == ichan) { |
128 | - log ("invalid channel read %#x\n", nport); | |
128 | + dolog ("invalid channel read %#x\n", nport); | |
129 | 129 | return 0; |
130 | 130 | } |
131 | 131 | return d->regs[ichan].pageh; |
... | ... | @@ -136,7 +136,7 @@ static inline void init_chan (struct dma_cont *d, int ichan) |
136 | 136 | struct dma_regs *r; |
137 | 137 | |
138 | 138 | r = d->regs + ichan; |
139 | - r->now[ADDR] = r->base[0] << d->dshift; | |
139 | + r->now[ADDR] = r->base[ADDR] << d->dshift; | |
140 | 140 | r->now[COUNT] = 0; |
141 | 141 | } |
142 | 142 | |
... | ... | @@ -152,7 +152,7 @@ static inline int getff (struct dma_cont *d) |
152 | 152 | static uint32_t read_chan (void *opaque, uint32_t nport) |
153 | 153 | { |
154 | 154 | struct dma_cont *d = opaque; |
155 | - int ichan, nreg, iport, ff, val; | |
155 | + int ichan, nreg, iport, ff, val, dir; | |
156 | 156 | struct dma_regs *r; |
157 | 157 | |
158 | 158 | iport = (nport >> d->dshift) & 0x0f; |
... | ... | @@ -160,12 +160,14 @@ static uint32_t read_chan (void *opaque, uint32_t nport) |
160 | 160 | nreg = iport & 1; |
161 | 161 | r = d->regs + ichan; |
162 | 162 | |
163 | + dir = ((r->mode >> 5) & 1) ? -1 : 1; | |
163 | 164 | ff = getff (d); |
164 | 165 | if (nreg) |
165 | 166 | val = (r->base[COUNT] << d->dshift) - r->now[COUNT]; |
166 | 167 | else |
167 | - val = r->now[ADDR] + r->now[COUNT]; | |
168 | + val = r->now[ADDR] + r->now[COUNT] * dir; | |
168 | 169 | |
170 | + ldebug ("read_chan %#x -> %d\n", iport, val); | |
169 | 171 | return (val >> (d->dshift + (ff << 3))) & 0xff; |
170 | 172 | } |
171 | 173 | |
... | ... | @@ -190,19 +192,19 @@ static void write_chan (void *opaque, uint32_t nport, uint32_t data) |
190 | 192 | static void write_cont (void *opaque, uint32_t nport, uint32_t data) |
191 | 193 | { |
192 | 194 | struct dma_cont *d = opaque; |
193 | - int iport, ichan; | |
195 | + int iport, ichan = 0; | |
194 | 196 | |
195 | 197 | iport = (nport >> d->dshift) & 0x0f; |
196 | 198 | switch (iport) { |
197 | - case 8: /* command */ | |
199 | + case 0x08: /* command */ | |
198 | 200 | if ((data != 0) && (data & CMD_NOT_SUPPORTED)) { |
199 | - log ("command %#x not supported\n", data); | |
201 | + dolog ("command %#x not supported\n", data); | |
200 | 202 | return; |
201 | 203 | } |
202 | 204 | d->command = data; |
203 | 205 | break; |
204 | 206 | |
205 | - case 9: | |
207 | + case 0x09: | |
206 | 208 | ichan = data & 3; |
207 | 209 | if (data & 4) { |
208 | 210 | d->status |= 1 << (ichan + 4); |
... | ... | @@ -213,22 +215,19 @@ static void write_cont (void *opaque, uint32_t nport, uint32_t data) |
213 | 215 | d->status &= ~(1 << ichan); |
214 | 216 | break; |
215 | 217 | |
216 | - case 0xa: /* single mask */ | |
218 | + case 0x0a: /* single mask */ | |
217 | 219 | if (data & 4) |
218 | 220 | d->mask |= 1 << (data & 3); |
219 | 221 | else |
220 | 222 | d->mask &= ~(1 << (data & 3)); |
221 | 223 | break; |
222 | 224 | |
223 | - case 0xb: /* mode */ | |
225 | + case 0x0b: /* mode */ | |
224 | 226 | { |
225 | 227 | ichan = data & 3; |
226 | 228 | #ifdef DEBUG_DMA |
227 | - int op; | |
228 | - int ai; | |
229 | - int dir; | |
230 | - int opmode; | |
231 | - | |
229 | + { | |
230 | + int op, ai, dir, opmode; | |
232 | 231 | op = (data >> 2) & 3; |
233 | 232 | ai = (data >> 4) & 1; |
234 | 233 | dir = (data >> 5) & 1; |
... | ... | @@ -236,39 +235,39 @@ static void write_cont (void *opaque, uint32_t nport, uint32_t data) |
236 | 235 | |
237 | 236 | linfo ("ichan %d, op %d, ai %d, dir %d, opmode %d\n", |
238 | 237 | ichan, op, ai, dir, opmode); |
238 | + } | |
239 | 239 | #endif |
240 | - | |
241 | 240 | d->regs[ichan].mode = data; |
242 | 241 | break; |
243 | 242 | } |
244 | 243 | |
245 | - case 0xc: /* clear flip flop */ | |
244 | + case 0x0c: /* clear flip flop */ | |
246 | 245 | d->flip_flop = 0; |
247 | 246 | break; |
248 | 247 | |
249 | - case 0xd: /* reset */ | |
248 | + case 0x0d: /* reset */ | |
250 | 249 | d->flip_flop = 0; |
251 | 250 | d->mask = ~0; |
252 | 251 | d->status = 0; |
253 | 252 | d->command = 0; |
254 | 253 | break; |
255 | 254 | |
256 | - case 0xe: /* clear mask for all channels */ | |
255 | + case 0x0e: /* clear mask for all channels */ | |
257 | 256 | d->mask = 0; |
258 | 257 | break; |
259 | 258 | |
260 | - case 0xf: /* write mask for all channels */ | |
259 | + case 0x0f: /* write mask for all channels */ | |
261 | 260 | d->mask = data; |
262 | 261 | break; |
263 | 262 | |
264 | 263 | default: |
265 | - log ("dma: unknown iport %#x\n", iport); | |
264 | + dolog ("unknown iport %#x\n", iport); | |
266 | 265 | break; |
267 | 266 | } |
268 | 267 | |
269 | 268 | #ifdef DEBUG_DMA |
270 | 269 | if (0xc != iport) { |
271 | - linfo ("nport %#06x, ichan % 2d, val %#06x\n", | |
270 | + linfo ("write_cont: nport %#06x, ichan % 2d, val %#06x\n", | |
272 | 271 | nport, ichan, data); |
273 | 272 | } |
274 | 273 | #endif |
... | ... | @@ -278,20 +277,22 @@ static uint32_t read_cont (void *opaque, uint32_t nport) |
278 | 277 | { |
279 | 278 | struct dma_cont *d = opaque; |
280 | 279 | int iport, val; |
281 | - | |
280 | + | |
282 | 281 | iport = (nport >> d->dshift) & 0x0f; |
283 | 282 | switch (iport) { |
284 | - case 0x08: /* status */ | |
283 | + case 0x08: /* status */ | |
285 | 284 | val = d->status; |
286 | 285 | d->status &= 0xf0; |
287 | 286 | break; |
288 | - case 0x0f: /* mask */ | |
287 | + case 0x0f: /* mask */ | |
289 | 288 | val = d->mask; |
290 | 289 | break; |
291 | 290 | default: |
292 | 291 | val = 0; |
293 | 292 | break; |
294 | 293 | } |
294 | + | |
295 | + ldebug ("read_cont: nport %#06x, iport %#04x val %#x\n", nport, iport, val); | |
295 | 296 | return val; |
296 | 297 | } |
297 | 298 | |
... | ... | @@ -322,23 +323,27 @@ void DMA_release_DREQ (int nchan) |
322 | 323 | |
323 | 324 | static void channel_run (int ncont, int ichan) |
324 | 325 | { |
325 | - struct dma_regs *r; | |
326 | 326 | int n; |
327 | - target_ulong addr; | |
328 | -/* int ai, dir; */ | |
327 | + struct dma_regs *r = &dma_controllers[ncont].regs[ichan]; | |
328 | +#ifdef DEBUG_DMA | |
329 | + int dir, opmode; | |
329 | 330 | |
330 | - r = dma_controllers[ncont].regs + ichan; | |
331 | -/* ai = r->mode & 16; */ | |
332 | -/* dir = r->mode & 32 ? -1 : 1; */ | |
331 | + dir = (r->mode >> 5) & 1; | |
332 | + opmode = (r->mode >> 6) & 3; | |
333 | 333 | |
334 | - /* NOTE: pageh is only used by PPC PREP */ | |
335 | - addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR]; | |
336 | - n = r->transfer_handler (r->opaque, addr, | |
337 | - (r->base[COUNT] << ncont) + (1 << ncont)); | |
338 | - r->now[COUNT] = n; | |
334 | + if (dir) { | |
335 | + dolog ("DMA in address decrement mode\n"); | |
336 | + } | |
337 | + if (opmode != 1) { | |
338 | + dolog ("DMA not in single mode select %#x\n", opmode); | |
339 | + } | |
340 | +#endif | |
339 | 341 | |
340 | - ldebug ("dma_pos %d size %d\n", | |
341 | - n, (r->base[1] << ncont) + (1 << ncont)); | |
342 | + r = dma_controllers[ncont].regs + ichan; | |
343 | + n = r->transfer_handler (r->opaque, ichan + (ncont << 2), | |
344 | + r->now[COUNT], (r->base[COUNT] + 1) << ncont); | |
345 | + r->now[COUNT] = n; | |
346 | + ldebug ("dma_pos %d size %d\n", n, (r->base[COUNT] + 1) << ncont); | |
342 | 347 | } |
343 | 348 | |
344 | 349 | void DMA_run (void) |
... | ... | @@ -361,7 +366,7 @@ void DMA_run (void) |
361 | 366 | } |
362 | 367 | |
363 | 368 | void DMA_register_channel (int nchan, |
364 | - DMA_transfer_handler transfer_handler, | |
369 | + DMA_transfer_handler transfer_handler, | |
365 | 370 | void *opaque) |
366 | 371 | { |
367 | 372 | struct dma_regs *r; |
... | ... | @@ -375,6 +380,50 @@ void DMA_register_channel (int nchan, |
375 | 380 | r->opaque = opaque; |
376 | 381 | } |
377 | 382 | |
383 | +int DMA_read_memory (int nchan, void *buf, int pos, int len) | |
384 | +{ | |
385 | + struct dma_regs *r = &dma_controllers[nchan > 3].regs[nchan & 3]; | |
386 | + target_ulong addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR]; | |
387 | + | |
388 | + if (r->mode & 0x20) { | |
389 | + int i; | |
390 | + uint8_t *p = buf; | |
391 | + | |
392 | + cpu_physical_memory_read (addr - pos - len, buf, len); | |
393 | + /* What about 16bit transfers? */ | |
394 | + for (i = 0; i < len >> 1; i++) { | |
395 | + uint8_t b = p[len - i - 1]; | |
396 | + p[i] = b; | |
397 | + } | |
398 | + } | |
399 | + else | |
400 | + cpu_physical_memory_read (addr + pos, buf, len); | |
401 | + | |
402 | + return len; | |
403 | +} | |
404 | + | |
405 | +int DMA_write_memory (int nchan, void *buf, int pos, int len) | |
406 | +{ | |
407 | + struct dma_regs *r = &dma_controllers[nchan > 3].regs[nchan & 3]; | |
408 | + target_ulong addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR]; | |
409 | + | |
410 | + if (r->mode & 0x20) { | |
411 | + int i; | |
412 | + uint8_t *p = buf; | |
413 | + | |
414 | + cpu_physical_memory_write (addr - pos - len, buf, len); | |
415 | + /* What about 16bit transfers? */ | |
416 | + for (i = 0; i < len; i++) { | |
417 | + uint8_t b = p[len - i - 1]; | |
418 | + p[i] = b; | |
419 | + } | |
420 | + } | |
421 | + else | |
422 | + cpu_physical_memory_write (addr + pos, buf, len); | |
423 | + | |
424 | + return len; | |
425 | +} | |
426 | + | |
378 | 427 | /* request the emulator to transfer a new DMA memory block ASAP */ |
379 | 428 | void DMA_schedule(int nchan) |
380 | 429 | { |
... | ... | @@ -388,7 +437,7 @@ static void dma_reset(void *opaque) |
388 | 437 | } |
389 | 438 | |
390 | 439 | /* dshift = 0: 8 bit DMA, 1 = 16 bit DMA */ |
391 | -static void dma_init2(struct dma_cont *d, int base, int dshift, | |
440 | +static void dma_init2(struct dma_cont *d, int base, int dshift, | |
392 | 441 | int page_base, int pageh_base) |
393 | 442 | { |
394 | 443 | const static int page_port_list[] = { 0x1, 0x2, 0x3, 0x7 }; |
... | ... | @@ -400,31 +449,87 @@ static void dma_init2(struct dma_cont *d, int base, int dshift, |
400 | 449 | register_ioport_read (base + (i << dshift), 1, 1, read_chan, d); |
401 | 450 | } |
402 | 451 | for (i = 0; i < LENOFA (page_port_list); i++) { |
403 | - register_ioport_write (page_base + page_port_list[i], 1, 1, | |
452 | + register_ioport_write (page_base + page_port_list[i], 1, 1, | |
404 | 453 | write_page, d); |
405 | - register_ioport_read (page_base + page_port_list[i], 1, 1, | |
454 | + register_ioport_read (page_base + page_port_list[i], 1, 1, | |
406 | 455 | read_page, d); |
407 | 456 | if (pageh_base >= 0) { |
408 | - register_ioport_write (pageh_base + page_port_list[i], 1, 1, | |
457 | + register_ioport_write (pageh_base + page_port_list[i], 1, 1, | |
409 | 458 | write_pageh, d); |
410 | - register_ioport_read (pageh_base + page_port_list[i], 1, 1, | |
459 | + register_ioport_read (pageh_base + page_port_list[i], 1, 1, | |
411 | 460 | read_pageh, d); |
412 | 461 | } |
413 | 462 | } |
414 | 463 | for (i = 0; i < 8; i++) { |
415 | - register_ioport_write (base + ((i + 8) << dshift), 1, 1, | |
464 | + register_ioport_write (base + ((i + 8) << dshift), 1, 1, | |
416 | 465 | write_cont, d); |
417 | - register_ioport_read (base + ((i + 8) << dshift), 1, 1, | |
466 | + register_ioport_read (base + ((i + 8) << dshift), 1, 1, | |
418 | 467 | read_cont, d); |
419 | 468 | } |
420 | 469 | qemu_register_reset(dma_reset, d); |
421 | 470 | dma_reset(d); |
422 | 471 | } |
423 | 472 | |
473 | +static void dma_save (QEMUFile *f, void *opaque) | |
474 | +{ | |
475 | + struct dma_cont *d = opaque; | |
476 | + int i; | |
477 | + | |
478 | + /* qemu_put_8s (f, &d->status); */ | |
479 | + qemu_put_8s (f, &d->command); | |
480 | + qemu_put_8s (f, &d->mask); | |
481 | + qemu_put_8s (f, &d->flip_flop); | |
482 | + qemu_put_be32s (f, &d->dshift); | |
483 | + | |
484 | + for (i = 0; i < 4; ++i) { | |
485 | + struct dma_regs *r = &d->regs[i]; | |
486 | + qemu_put_be32s (f, &r->now[0]); | |
487 | + qemu_put_be32s (f, &r->now[1]); | |
488 | + qemu_put_be16s (f, &r->base[0]); | |
489 | + qemu_put_be16s (f, &r->base[1]); | |
490 | + qemu_put_8s (f, &r->mode); | |
491 | + qemu_put_8s (f, &r->page); | |
492 | + qemu_put_8s (f, &r->pageh); | |
493 | + qemu_put_8s (f, &r->dack); | |
494 | + qemu_put_8s (f, &r->eop); | |
495 | + } | |
496 | +} | |
497 | + | |
498 | +static int dma_load (QEMUFile *f, void *opaque, int version_id) | |
499 | +{ | |
500 | + struct dma_cont *d = opaque; | |
501 | + int i; | |
502 | + | |
503 | + if (version_id != 1) | |
504 | + return -EINVAL; | |
505 | + | |
506 | + /* qemu_get_8s (f, &d->status); */ | |
507 | + qemu_get_8s (f, &d->command); | |
508 | + qemu_get_8s (f, &d->mask); | |
509 | + qemu_get_8s (f, &d->flip_flop); | |
510 | + qemu_get_be32s (f, &d->dshift); | |
511 | + | |
512 | + for (i = 0; i < 4; ++i) { | |
513 | + struct dma_regs *r = &d->regs[i]; | |
514 | + qemu_get_be32s (f, &r->now[0]); | |
515 | + qemu_get_be32s (f, &r->now[1]); | |
516 | + qemu_get_be16s (f, &r->base[0]); | |
517 | + qemu_get_be16s (f, &r->base[1]); | |
518 | + qemu_get_8s (f, &r->mode); | |
519 | + qemu_get_8s (f, &r->page); | |
520 | + qemu_get_8s (f, &r->pageh); | |
521 | + qemu_get_8s (f, &r->dack); | |
522 | + qemu_get_8s (f, &r->eop); | |
523 | + } | |
524 | + return 0; | |
525 | +} | |
526 | + | |
424 | 527 | void DMA_init (int high_page_enable) |
425 | 528 | { |
426 | - dma_init2(&dma_controllers[0], 0x00, 0, 0x80, | |
529 | + dma_init2(&dma_controllers[0], 0x00, 0, 0x80, | |
427 | 530 | high_page_enable ? 0x480 : -1); |
428 | 531 | dma_init2(&dma_controllers[1], 0xc0, 1, 0x88, |
429 | 532 | high_page_enable ? 0x488 : -1); |
533 | + register_savevm ("dma", 0, 1, dma_save, dma_load, &dma_controllers[0]); | |
534 | + register_savevm ("dma", 1, 1, dma_save, dma_load, &dma_controllers[1]); | |
430 | 535 | } | ... | ... |
hw/fdc.c
... | ... | @@ -313,7 +313,8 @@ static void fd_reset (fdrive_t *drv) |
313 | 313 | |
314 | 314 | static void fdctrl_reset (fdctrl_t *fdctrl, int do_irq); |
315 | 315 | static void fdctrl_reset_fifo (fdctrl_t *fdctrl); |
316 | -static int fdctrl_transfer_handler (void *opaque, target_ulong addr, int size); | |
316 | +static int fdctrl_transfer_handler (void *opaque, int nchan, | |
317 | + int dma_pos, int dma_len); | |
317 | 318 | static void fdctrl_raise_irq (fdctrl_t *fdctrl, uint8_t status); |
318 | 319 | static void fdctrl_result_timer(void *opaque); |
319 | 320 | |
... | ... | @@ -908,7 +909,8 @@ static void fdctrl_start_transfer_del (fdctrl_t *fdctrl, int direction) |
908 | 909 | } |
909 | 910 | |
910 | 911 | /* handlers for DMA transfers */ |
911 | -static int fdctrl_transfer_handler (void *opaque, target_ulong addr, int size) | |
912 | +static int fdctrl_transfer_handler (void *opaque, int nchan, | |
913 | + int dma_pos, int dma_len) | |
912 | 914 | { |
913 | 915 | fdctrl_t *fdctrl; |
914 | 916 | fdrive_t *cur_drv; |
... | ... | @@ -924,8 +926,8 @@ static int fdctrl_transfer_handler (void *opaque, target_ulong addr, int size) |
924 | 926 | if (fdctrl->data_dir == FD_DIR_SCANE || fdctrl->data_dir == FD_DIR_SCANL || |
925 | 927 | fdctrl->data_dir == FD_DIR_SCANH) |
926 | 928 | status2 = 0x04; |
927 | - if (size > fdctrl->data_len) | |
928 | - size = fdctrl->data_len; | |
929 | + if (dma_len > fdctrl->data_len) | |
930 | + dma_len = fdctrl->data_len; | |
929 | 931 | if (cur_drv->bs == NULL) { |
930 | 932 | if (fdctrl->data_dir == FD_DIR_WRITE) |
931 | 933 | fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00); |
... | ... | @@ -935,8 +937,8 @@ static int fdctrl_transfer_handler (void *opaque, target_ulong addr, int size) |
935 | 937 | goto transfer_error; |
936 | 938 | } |
937 | 939 | rel_pos = fdctrl->data_pos % FD_SECTOR_LEN; |
938 | - for (start_pos = fdctrl->data_pos; fdctrl->data_pos < size;) { | |
939 | - len = size - fdctrl->data_pos; | |
940 | + for (start_pos = fdctrl->data_pos; fdctrl->data_pos < dma_len;) { | |
941 | + len = dma_len - fdctrl->data_pos; | |
940 | 942 | if (len + rel_pos > FD_SECTOR_LEN) |
941 | 943 | len = FD_SECTOR_LEN - rel_pos; |
942 | 944 | FLOPPY_DPRINTF("copy %d bytes (%d %d %d) %d pos %d %02x %02x " |
... | ... | @@ -958,13 +960,17 @@ static int fdctrl_transfer_handler (void *opaque, target_ulong addr, int size) |
958 | 960 | switch (fdctrl->data_dir) { |
959 | 961 | case FD_DIR_READ: |
960 | 962 | /* READ commands */ |
961 | - cpu_physical_memory_write(addr + fdctrl->data_pos, | |
962 | - fdctrl->fifo + rel_pos, len); | |
963 | + DMA_write_memory (nchan, fdctrl->fifo + rel_pos, | |
964 | + fdctrl->data_pos, len); | |
965 | +/* cpu_physical_memory_write(addr + fdctrl->data_pos, */ | |
966 | +/* fdctrl->fifo + rel_pos, len); */ | |
963 | 967 | break; |
964 | 968 | case FD_DIR_WRITE: |
965 | 969 | /* WRITE commands */ |
966 | - cpu_physical_memory_read(addr + fdctrl->data_pos, | |
967 | - fdctrl->fifo + rel_pos, len); | |
970 | + DMA_read_memory (nchan, fdctrl->fifo + rel_pos, | |
971 | + fdctrl->data_pos, len); | |
972 | +/* cpu_physical_memory_read(addr + fdctrl->data_pos, */ | |
973 | +/* fdctrl->fifo + rel_pos, len); */ | |
968 | 974 | if (bdrv_write(cur_drv->bs, fd_sector(cur_drv), |
969 | 975 | fdctrl->fifo, 1) < 0) { |
970 | 976 | FLOPPY_ERROR("writting sector %d\n", fd_sector(cur_drv)); |
... | ... | @@ -977,8 +983,9 @@ static int fdctrl_transfer_handler (void *opaque, target_ulong addr, int size) |
977 | 983 | { |
978 | 984 | uint8_t tmpbuf[FD_SECTOR_LEN]; |
979 | 985 | int ret; |
980 | - cpu_physical_memory_read(addr + fdctrl->data_pos, | |
981 | - tmpbuf, len); | |
986 | + DMA_read_memory (nchan, tmpbuf, fdctrl->data_pos, len); | |
987 | +/* cpu_physical_memory_read(addr + fdctrl->data_pos, */ | |
988 | +/* tmpbuf, len); */ | |
982 | 989 | ret = memcmp(tmpbuf, fdctrl->fifo + rel_pos, len); |
983 | 990 | if (ret == 0) { |
984 | 991 | status2 = 0x08; | ... | ... |