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 | include config.mak | 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 | TARGET_PATH=$(SRC_PATH)/target-$(TARGET_ARCH) | 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 | DEFINES=-I. -I$(TARGET_PATH) -I$(SRC_PATH) | 15 | DEFINES=-I. -I$(TARGET_PATH) -I$(SRC_PATH) |
6 | ifdef CONFIG_USER_ONLY | 16 | ifdef CONFIG_USER_ONLY |
7 | VPATH+=:$(SRC_PATH)/linux-user | 17 | VPATH+=:$(SRC_PATH)/linux-user |
@@ -267,16 +277,31 @@ endif | @@ -267,16 +277,31 @@ endif | ||
267 | VL_OBJS=vl.o osdep.o block.o readline.o monitor.o pci.o console.o | 277 | VL_OBJS=vl.o osdep.o block.o readline.o monitor.o pci.o console.o |
268 | VL_OBJS+=block-cow.o block-qcow.o aes.o block-vmdk.o block-cloop.o | 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 | ifeq ($(TARGET_ARCH), i386) | 295 | ifeq ($(TARGET_ARCH), i386) |
271 | # Hardware support | 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 | endif | 300 | endif |
276 | ifeq ($(TARGET_ARCH), ppc) | 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 | VL_OBJS+= mc146818rtc.o serial.o i8259.o i8254.o fdc.o m48t59.o | 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 | endif | 305 | endif |
281 | ifeq ($(TARGET_ARCH), sparc) | 306 | ifeq ($(TARGET_ARCH), sparc) |
282 | VL_OBJS+= sun4m.o tcx.o lance.o iommu.o sched.o m48t08.o magic-load.o timer.o | 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,6 +385,8 @@ op.o: op.c op_template.h op_mem.h | ||
360 | op_helper.o: op_helper_mem.h | 385 | op_helper.o: op_helper_mem.h |
361 | endif | 386 | endif |
362 | 387 | ||
388 | +mixeng.o: mixeng.c mixeng.h mixeng_template.h | ||
389 | + | ||
363 | %.o: %.c | 390 | %.o: %.c |
364 | $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< | 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 | * QEMU DMA emulation | 2 | * QEMU DMA emulation |
3 | - * | ||
4 | - * Copyright (c) 2003 Vassili Karpov (malc) | ||
5 | - * | 3 | + * |
4 | + * Copyright (c) 2003-2004 Vassili Karpov (malc) | ||
5 | + * | ||
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy | 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 | 7 | * of this software and associated documentation files (the "Software"), to deal |
8 | * in the Software without restriction, including without limitation the rights | 8 | * in the Software without restriction, including without limitation the rights |
@@ -23,9 +23,9 @@ | @@ -23,9 +23,9 @@ | ||
23 | */ | 23 | */ |
24 | #include "vl.h" | 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 | #ifdef DEBUG_DMA | 29 | #ifdef DEBUG_DMA |
30 | #define lwarn(...) fprintf (stderr, "dma: " __VA_ARGS__) | 30 | #define lwarn(...) fprintf (stderr, "dma: " __VA_ARGS__) |
31 | #define linfo(...) fprintf (stderr, "dma: " __VA_ARGS__) | 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,7 +86,7 @@ static void write_page (void *opaque, uint32_t nport, uint32_t data) | ||
86 | 86 | ||
87 | ichan = channels[nport & 7]; | 87 | ichan = channels[nport & 7]; |
88 | if (-1 == ichan) { | 88 | if (-1 == ichan) { |
89 | - log ("invalid channel %#x %#x\n", nport, data); | 89 | + dolog ("invalid channel %#x %#x\n", nport, data); |
90 | return; | 90 | return; |
91 | } | 91 | } |
92 | d->regs[ichan].page = data; | 92 | d->regs[ichan].page = data; |
@@ -99,7 +99,7 @@ static void write_pageh (void *opaque, uint32_t nport, uint32_t data) | @@ -99,7 +99,7 @@ static void write_pageh (void *opaque, uint32_t nport, uint32_t data) | ||
99 | 99 | ||
100 | ichan = channels[nport & 7]; | 100 | ichan = channels[nport & 7]; |
101 | if (-1 == ichan) { | 101 | if (-1 == ichan) { |
102 | - log ("invalid channel %#x %#x\n", nport, data); | 102 | + dolog ("invalid channel %#x %#x\n", nport, data); |
103 | return; | 103 | return; |
104 | } | 104 | } |
105 | d->regs[ichan].pageh = data; | 105 | d->regs[ichan].pageh = data; |
@@ -112,7 +112,7 @@ static uint32_t read_page (void *opaque, uint32_t nport) | @@ -112,7 +112,7 @@ static uint32_t read_page (void *opaque, uint32_t nport) | ||
112 | 112 | ||
113 | ichan = channels[nport & 7]; | 113 | ichan = channels[nport & 7]; |
114 | if (-1 == ichan) { | 114 | if (-1 == ichan) { |
115 | - log ("invalid channel read %#x\n", nport); | 115 | + dolog ("invalid channel read %#x\n", nport); |
116 | return 0; | 116 | return 0; |
117 | } | 117 | } |
118 | return d->regs[ichan].page; | 118 | return d->regs[ichan].page; |
@@ -125,7 +125,7 @@ static uint32_t read_pageh (void *opaque, uint32_t nport) | @@ -125,7 +125,7 @@ static uint32_t read_pageh (void *opaque, uint32_t nport) | ||
125 | 125 | ||
126 | ichan = channels[nport & 7]; | 126 | ichan = channels[nport & 7]; |
127 | if (-1 == ichan) { | 127 | if (-1 == ichan) { |
128 | - log ("invalid channel read %#x\n", nport); | 128 | + dolog ("invalid channel read %#x\n", nport); |
129 | return 0; | 129 | return 0; |
130 | } | 130 | } |
131 | return d->regs[ichan].pageh; | 131 | return d->regs[ichan].pageh; |
@@ -136,7 +136,7 @@ static inline void init_chan (struct dma_cont *d, int ichan) | @@ -136,7 +136,7 @@ static inline void init_chan (struct dma_cont *d, int ichan) | ||
136 | struct dma_regs *r; | 136 | struct dma_regs *r; |
137 | 137 | ||
138 | r = d->regs + ichan; | 138 | r = d->regs + ichan; |
139 | - r->now[ADDR] = r->base[0] << d->dshift; | 139 | + r->now[ADDR] = r->base[ADDR] << d->dshift; |
140 | r->now[COUNT] = 0; | 140 | r->now[COUNT] = 0; |
141 | } | 141 | } |
142 | 142 | ||
@@ -152,7 +152,7 @@ static inline int getff (struct dma_cont *d) | @@ -152,7 +152,7 @@ static inline int getff (struct dma_cont *d) | ||
152 | static uint32_t read_chan (void *opaque, uint32_t nport) | 152 | static uint32_t read_chan (void *opaque, uint32_t nport) |
153 | { | 153 | { |
154 | struct dma_cont *d = opaque; | 154 | struct dma_cont *d = opaque; |
155 | - int ichan, nreg, iport, ff, val; | 155 | + int ichan, nreg, iport, ff, val, dir; |
156 | struct dma_regs *r; | 156 | struct dma_regs *r; |
157 | 157 | ||
158 | iport = (nport >> d->dshift) & 0x0f; | 158 | iport = (nport >> d->dshift) & 0x0f; |
@@ -160,12 +160,14 @@ static uint32_t read_chan (void *opaque, uint32_t nport) | @@ -160,12 +160,14 @@ static uint32_t read_chan (void *opaque, uint32_t nport) | ||
160 | nreg = iport & 1; | 160 | nreg = iport & 1; |
161 | r = d->regs + ichan; | 161 | r = d->regs + ichan; |
162 | 162 | ||
163 | + dir = ((r->mode >> 5) & 1) ? -1 : 1; | ||
163 | ff = getff (d); | 164 | ff = getff (d); |
164 | if (nreg) | 165 | if (nreg) |
165 | val = (r->base[COUNT] << d->dshift) - r->now[COUNT]; | 166 | val = (r->base[COUNT] << d->dshift) - r->now[COUNT]; |
166 | else | 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 | return (val >> (d->dshift + (ff << 3))) & 0xff; | 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,19 +192,19 @@ static void write_chan (void *opaque, uint32_t nport, uint32_t data) | ||
190 | static void write_cont (void *opaque, uint32_t nport, uint32_t data) | 192 | static void write_cont (void *opaque, uint32_t nport, uint32_t data) |
191 | { | 193 | { |
192 | struct dma_cont *d = opaque; | 194 | struct dma_cont *d = opaque; |
193 | - int iport, ichan; | 195 | + int iport, ichan = 0; |
194 | 196 | ||
195 | iport = (nport >> d->dshift) & 0x0f; | 197 | iport = (nport >> d->dshift) & 0x0f; |
196 | switch (iport) { | 198 | switch (iport) { |
197 | - case 8: /* command */ | 199 | + case 0x08: /* command */ |
198 | if ((data != 0) && (data & CMD_NOT_SUPPORTED)) { | 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 | return; | 202 | return; |
201 | } | 203 | } |
202 | d->command = data; | 204 | d->command = data; |
203 | break; | 205 | break; |
204 | 206 | ||
205 | - case 9: | 207 | + case 0x09: |
206 | ichan = data & 3; | 208 | ichan = data & 3; |
207 | if (data & 4) { | 209 | if (data & 4) { |
208 | d->status |= 1 << (ichan + 4); | 210 | d->status |= 1 << (ichan + 4); |
@@ -213,22 +215,19 @@ static void write_cont (void *opaque, uint32_t nport, uint32_t data) | @@ -213,22 +215,19 @@ static void write_cont (void *opaque, uint32_t nport, uint32_t data) | ||
213 | d->status &= ~(1 << ichan); | 215 | d->status &= ~(1 << ichan); |
214 | break; | 216 | break; |
215 | 217 | ||
216 | - case 0xa: /* single mask */ | 218 | + case 0x0a: /* single mask */ |
217 | if (data & 4) | 219 | if (data & 4) |
218 | d->mask |= 1 << (data & 3); | 220 | d->mask |= 1 << (data & 3); |
219 | else | 221 | else |
220 | d->mask &= ~(1 << (data & 3)); | 222 | d->mask &= ~(1 << (data & 3)); |
221 | break; | 223 | break; |
222 | 224 | ||
223 | - case 0xb: /* mode */ | 225 | + case 0x0b: /* mode */ |
224 | { | 226 | { |
225 | ichan = data & 3; | 227 | ichan = data & 3; |
226 | #ifdef DEBUG_DMA | 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 | op = (data >> 2) & 3; | 231 | op = (data >> 2) & 3; |
233 | ai = (data >> 4) & 1; | 232 | ai = (data >> 4) & 1; |
234 | dir = (data >> 5) & 1; | 233 | dir = (data >> 5) & 1; |
@@ -236,39 +235,39 @@ static void write_cont (void *opaque, uint32_t nport, uint32_t data) | @@ -236,39 +235,39 @@ static void write_cont (void *opaque, uint32_t nport, uint32_t data) | ||
236 | 235 | ||
237 | linfo ("ichan %d, op %d, ai %d, dir %d, opmode %d\n", | 236 | linfo ("ichan %d, op %d, ai %d, dir %d, opmode %d\n", |
238 | ichan, op, ai, dir, opmode); | 237 | ichan, op, ai, dir, opmode); |
238 | + } | ||
239 | #endif | 239 | #endif |
240 | - | ||
241 | d->regs[ichan].mode = data; | 240 | d->regs[ichan].mode = data; |
242 | break; | 241 | break; |
243 | } | 242 | } |
244 | 243 | ||
245 | - case 0xc: /* clear flip flop */ | 244 | + case 0x0c: /* clear flip flop */ |
246 | d->flip_flop = 0; | 245 | d->flip_flop = 0; |
247 | break; | 246 | break; |
248 | 247 | ||
249 | - case 0xd: /* reset */ | 248 | + case 0x0d: /* reset */ |
250 | d->flip_flop = 0; | 249 | d->flip_flop = 0; |
251 | d->mask = ~0; | 250 | d->mask = ~0; |
252 | d->status = 0; | 251 | d->status = 0; |
253 | d->command = 0; | 252 | d->command = 0; |
254 | break; | 253 | break; |
255 | 254 | ||
256 | - case 0xe: /* clear mask for all channels */ | 255 | + case 0x0e: /* clear mask for all channels */ |
257 | d->mask = 0; | 256 | d->mask = 0; |
258 | break; | 257 | break; |
259 | 258 | ||
260 | - case 0xf: /* write mask for all channels */ | 259 | + case 0x0f: /* write mask for all channels */ |
261 | d->mask = data; | 260 | d->mask = data; |
262 | break; | 261 | break; |
263 | 262 | ||
264 | default: | 263 | default: |
265 | - log ("dma: unknown iport %#x\n", iport); | 264 | + dolog ("unknown iport %#x\n", iport); |
266 | break; | 265 | break; |
267 | } | 266 | } |
268 | 267 | ||
269 | #ifdef DEBUG_DMA | 268 | #ifdef DEBUG_DMA |
270 | if (0xc != iport) { | 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 | nport, ichan, data); | 271 | nport, ichan, data); |
273 | } | 272 | } |
274 | #endif | 273 | #endif |
@@ -278,20 +277,22 @@ static uint32_t read_cont (void *opaque, uint32_t nport) | @@ -278,20 +277,22 @@ static uint32_t read_cont (void *opaque, uint32_t nport) | ||
278 | { | 277 | { |
279 | struct dma_cont *d = opaque; | 278 | struct dma_cont *d = opaque; |
280 | int iport, val; | 279 | int iport, val; |
281 | - | 280 | + |
282 | iport = (nport >> d->dshift) & 0x0f; | 281 | iport = (nport >> d->dshift) & 0x0f; |
283 | switch (iport) { | 282 | switch (iport) { |
284 | - case 0x08: /* status */ | 283 | + case 0x08: /* status */ |
285 | val = d->status; | 284 | val = d->status; |
286 | d->status &= 0xf0; | 285 | d->status &= 0xf0; |
287 | break; | 286 | break; |
288 | - case 0x0f: /* mask */ | 287 | + case 0x0f: /* mask */ |
289 | val = d->mask; | 288 | val = d->mask; |
290 | break; | 289 | break; |
291 | default: | 290 | default: |
292 | val = 0; | 291 | val = 0; |
293 | break; | 292 | break; |
294 | } | 293 | } |
294 | + | ||
295 | + ldebug ("read_cont: nport %#06x, iport %#04x val %#x\n", nport, iport, val); | ||
295 | return val; | 296 | return val; |
296 | } | 297 | } |
297 | 298 | ||
@@ -322,23 +323,27 @@ void DMA_release_DREQ (int nchan) | @@ -322,23 +323,27 @@ void DMA_release_DREQ (int nchan) | ||
322 | 323 | ||
323 | static void channel_run (int ncont, int ichan) | 324 | static void channel_run (int ncont, int ichan) |
324 | { | 325 | { |
325 | - struct dma_regs *r; | ||
326 | int n; | 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 | void DMA_run (void) | 349 | void DMA_run (void) |
@@ -361,7 +366,7 @@ void DMA_run (void) | @@ -361,7 +366,7 @@ void DMA_run (void) | ||
361 | } | 366 | } |
362 | 367 | ||
363 | void DMA_register_channel (int nchan, | 368 | void DMA_register_channel (int nchan, |
364 | - DMA_transfer_handler transfer_handler, | 369 | + DMA_transfer_handler transfer_handler, |
365 | void *opaque) | 370 | void *opaque) |
366 | { | 371 | { |
367 | struct dma_regs *r; | 372 | struct dma_regs *r; |
@@ -375,6 +380,50 @@ void DMA_register_channel (int nchan, | @@ -375,6 +380,50 @@ void DMA_register_channel (int nchan, | ||
375 | r->opaque = opaque; | 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 | /* request the emulator to transfer a new DMA memory block ASAP */ | 427 | /* request the emulator to transfer a new DMA memory block ASAP */ |
379 | void DMA_schedule(int nchan) | 428 | void DMA_schedule(int nchan) |
380 | { | 429 | { |
@@ -388,7 +437,7 @@ static void dma_reset(void *opaque) | @@ -388,7 +437,7 @@ static void dma_reset(void *opaque) | ||
388 | } | 437 | } |
389 | 438 | ||
390 | /* dshift = 0: 8 bit DMA, 1 = 16 bit DMA */ | 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 | int page_base, int pageh_base) | 441 | int page_base, int pageh_base) |
393 | { | 442 | { |
394 | const static int page_port_list[] = { 0x1, 0x2, 0x3, 0x7 }; | 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,31 +449,87 @@ static void dma_init2(struct dma_cont *d, int base, int dshift, | ||
400 | register_ioport_read (base + (i << dshift), 1, 1, read_chan, d); | 449 | register_ioport_read (base + (i << dshift), 1, 1, read_chan, d); |
401 | } | 450 | } |
402 | for (i = 0; i < LENOFA (page_port_list); i++) { | 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 | write_page, d); | 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 | read_page, d); | 455 | read_page, d); |
407 | if (pageh_base >= 0) { | 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 | write_pageh, d); | 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 | read_pageh, d); | 460 | read_pageh, d); |
412 | } | 461 | } |
413 | } | 462 | } |
414 | for (i = 0; i < 8; i++) { | 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 | write_cont, d); | 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 | read_cont, d); | 467 | read_cont, d); |
419 | } | 468 | } |
420 | qemu_register_reset(dma_reset, d); | 469 | qemu_register_reset(dma_reset, d); |
421 | dma_reset(d); | 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 | void DMA_init (int high_page_enable) | 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 | high_page_enable ? 0x480 : -1); | 530 | high_page_enable ? 0x480 : -1); |
428 | dma_init2(&dma_controllers[1], 0xc0, 1, 0x88, | 531 | dma_init2(&dma_controllers[1], 0xc0, 1, 0x88, |
429 | high_page_enable ? 0x488 : -1); | 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,7 +313,8 @@ static void fd_reset (fdrive_t *drv) | ||
313 | 313 | ||
314 | static void fdctrl_reset (fdctrl_t *fdctrl, int do_irq); | 314 | static void fdctrl_reset (fdctrl_t *fdctrl, int do_irq); |
315 | static void fdctrl_reset_fifo (fdctrl_t *fdctrl); | 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 | static void fdctrl_raise_irq (fdctrl_t *fdctrl, uint8_t status); | 318 | static void fdctrl_raise_irq (fdctrl_t *fdctrl, uint8_t status); |
318 | static void fdctrl_result_timer(void *opaque); | 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,7 +909,8 @@ static void fdctrl_start_transfer_del (fdctrl_t *fdctrl, int direction) | ||
908 | } | 909 | } |
909 | 910 | ||
910 | /* handlers for DMA transfers */ | 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 | fdctrl_t *fdctrl; | 915 | fdctrl_t *fdctrl; |
914 | fdrive_t *cur_drv; | 916 | fdrive_t *cur_drv; |
@@ -924,8 +926,8 @@ static int fdctrl_transfer_handler (void *opaque, target_ulong addr, int size) | @@ -924,8 +926,8 @@ static int fdctrl_transfer_handler (void *opaque, target_ulong addr, int size) | ||
924 | if (fdctrl->data_dir == FD_DIR_SCANE || fdctrl->data_dir == FD_DIR_SCANL || | 926 | if (fdctrl->data_dir == FD_DIR_SCANE || fdctrl->data_dir == FD_DIR_SCANL || |
925 | fdctrl->data_dir == FD_DIR_SCANH) | 927 | fdctrl->data_dir == FD_DIR_SCANH) |
926 | status2 = 0x04; | 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 | if (cur_drv->bs == NULL) { | 931 | if (cur_drv->bs == NULL) { |
930 | if (fdctrl->data_dir == FD_DIR_WRITE) | 932 | if (fdctrl->data_dir == FD_DIR_WRITE) |
931 | fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00); | 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,8 +937,8 @@ static int fdctrl_transfer_handler (void *opaque, target_ulong addr, int size) | ||
935 | goto transfer_error; | 937 | goto transfer_error; |
936 | } | 938 | } |
937 | rel_pos = fdctrl->data_pos % FD_SECTOR_LEN; | 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 | if (len + rel_pos > FD_SECTOR_LEN) | 942 | if (len + rel_pos > FD_SECTOR_LEN) |
941 | len = FD_SECTOR_LEN - rel_pos; | 943 | len = FD_SECTOR_LEN - rel_pos; |
942 | FLOPPY_DPRINTF("copy %d bytes (%d %d %d) %d pos %d %02x %02x " | 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,13 +960,17 @@ static int fdctrl_transfer_handler (void *opaque, target_ulong addr, int size) | ||
958 | switch (fdctrl->data_dir) { | 960 | switch (fdctrl->data_dir) { |
959 | case FD_DIR_READ: | 961 | case FD_DIR_READ: |
960 | /* READ commands */ | 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 | break; | 967 | break; |
964 | case FD_DIR_WRITE: | 968 | case FD_DIR_WRITE: |
965 | /* WRITE commands */ | 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 | if (bdrv_write(cur_drv->bs, fd_sector(cur_drv), | 974 | if (bdrv_write(cur_drv->bs, fd_sector(cur_drv), |
969 | fdctrl->fifo, 1) < 0) { | 975 | fdctrl->fifo, 1) < 0) { |
970 | FLOPPY_ERROR("writting sector %d\n", fd_sector(cur_drv)); | 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,8 +983,9 @@ static int fdctrl_transfer_handler (void *opaque, target_ulong addr, int size) | ||
977 | { | 983 | { |
978 | uint8_t tmpbuf[FD_SECTOR_LEN]; | 984 | uint8_t tmpbuf[FD_SECTOR_LEN]; |
979 | int ret; | 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 | ret = memcmp(tmpbuf, fdctrl->fifo + rel_pos, len); | 989 | ret = memcmp(tmpbuf, fdctrl->fifo + rel_pos, len); |
983 | if (ret == 0) { | 990 | if (ret == 0) { |
984 | status2 = 0x08; | 991 | status2 = 0x08; |