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
24 changed files
with
6038 additions
and
1674 deletions
Makefile.target
1 | 1 | include config.mak |
2 | 2 | |
3 | +#After enabling Adlib and/or FMOD rebuild QEMU from scratch | |
4 | +#Uncomment following for adlib support | |
5 | +#USE_ADLIB=1 | |
6 | + | |
7 | +#Uncomment following and specify proper paths/names for FMOD support | |
8 | +#USE_FMOD=1 | |
9 | +#FMOD_INCLUDE=/net/include/fmod | |
10 | +#FMOD_LIBPATH=/net/lib | |
11 | +#FMOD_VERSION=3.74 | |
12 | + | |
3 | 13 | TARGET_PATH=$(SRC_PATH)/target-$(TARGET_ARCH) |
4 | -VPATH=$(SRC_PATH):$(TARGET_PATH):$(SRC_PATH)/hw | |
14 | +VPATH=$(SRC_PATH):$(TARGET_PATH):$(SRC_PATH)/hw:$(SRC_PATH)/audio | |
5 | 15 | DEFINES=-I. -I$(TARGET_PATH) -I$(SRC_PATH) |
6 | 16 | ifdef CONFIG_USER_ONLY |
7 | 17 | VPATH+=:$(SRC_PATH)/linux-user |
... | ... | @@ -267,16 +277,31 @@ endif |
267 | 277 | VL_OBJS=vl.o osdep.o block.o readline.o monitor.o pci.o console.o |
268 | 278 | VL_OBJS+=block-cow.o block-qcow.o aes.o block-vmdk.o block-cloop.o |
269 | 279 | |
280 | +SOUND_HW = sb16.o | |
281 | +AUDIODRV = audio.o ossaudio.o sdlaudio.o wavaudio.o | |
282 | + | |
283 | +ifeq ($(USE_ADLIB),1) | |
284 | +SOUND_HW += fmopl.o adlib.o | |
285 | +audio.o: DEFINES := -DUSE_ADLIB $(DEFINES) | |
286 | +endif | |
287 | + | |
288 | +ifeq ($(USE_FMOD),1) | |
289 | +AUDIODRV += fmodaudio.o | |
290 | +audio.o fmodaudio.o: DEFINES := -DUSE_FMOD_AUDIO -I$(FMOD_INCLUDE) $(DEFINES) | |
291 | +LDFLAGS += -L$(FMOD_LIBPATH) -Wl,-rpath,$(FMOD_LIBPATH) | |
292 | +LIBS += -lfmod-$(FMOD_VERSION) | |
293 | +endif | |
294 | + | |
270 | 295 | ifeq ($(TARGET_ARCH), i386) |
271 | 296 | # Hardware support |
272 | -VL_OBJS+= ide.o ne2000.o pckbd.o vga.o sb16.o dma.o oss.o | |
273 | -VL_OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pc.o | |
274 | -VL_OBJS+= cirrus_vga.o | |
297 | +VL_OBJS+= ide.o ne2000.o pckbd.o vga.o $(SOUND_HW) dma.o $(AUDIODRV) | |
298 | +VL_OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pc.o | |
299 | +VL_OBJS+= cirrus_vga.o mixeng.o | |
275 | 300 | endif |
276 | 301 | ifeq ($(TARGET_ARCH), ppc) |
277 | -VL_OBJS+= ppc.o ide.o ne2000.o pckbd.o vga.o sb16.o dma.o oss.o | |
302 | +VL_OBJS+= ppc.o ide.o ne2000.o pckbd.o vga.o $(SOUND_HW) dma.o $(AUDIODRV) | |
278 | 303 | VL_OBJS+= mc146818rtc.o serial.o i8259.o i8254.o fdc.o m48t59.o |
279 | -VL_OBJS+= ppc_prep.o ppc_chrp.o cuda.o adb.o openpic.o | |
304 | +VL_OBJS+= ppc_prep.o ppc_chrp.o cuda.o adb.o openpic.o mixeng.o | |
280 | 305 | endif |
281 | 306 | ifeq ($(TARGET_ARCH), sparc) |
282 | 307 | VL_OBJS+= sun4m.o tcx.o lance.o iommu.o sched.o m48t08.o magic-load.o timer.o |
... | ... | @@ -360,6 +385,8 @@ op.o: op.c op_template.h op_mem.h |
360 | 385 | op_helper.o: op_helper_mem.h |
361 | 386 | endif |
362 | 387 | |
388 | +mixeng.o: mixeng.c mixeng.h mixeng_template.h | |
389 | + | |
363 | 390 | %.o: %.c |
364 | 391 | $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< |
365 | 392 | ... | ... |
audio/audio.c
0 → 100644
1 | +/* | |
2 | + * QEMU Audio subsystem | |
3 | + * | |
4 | + * Copyright (c) 2003-2004 Vassili Karpov (malc) | |
5 | + * | |
6 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | + * of this software and associated documentation files (the "Software"), to deal | |
8 | + * in the Software without restriction, including without limitation the rights | |
9 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | + * copies of the Software, and to permit persons to whom the Software is | |
11 | + * furnished to do so, subject to the following conditions: | |
12 | + * | |
13 | + * The above copyright notice and this permission notice shall be included in | |
14 | + * all copies or substantial portions of the Software. | |
15 | + * | |
16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | + * THE SOFTWARE. | |
23 | + */ | |
24 | +#include <assert.h> | |
25 | +#include <limits.h> | |
26 | +#include "vl.h" | |
27 | + | |
28 | +#define AUDIO_CAP "audio" | |
29 | +#include "audio/audio.h" | |
30 | + | |
31 | +#define USE_SDL_AUDIO | |
32 | +#define USE_WAV_AUDIO | |
33 | + | |
34 | +#if defined __linux__ || (defined _BSD && !defined __APPLE__) | |
35 | +#define USE_OSS_AUDIO | |
36 | +#endif | |
37 | + | |
38 | +#ifdef USE_OSS_AUDIO | |
39 | +#include "audio/ossaudio.h" | |
40 | +#endif | |
41 | + | |
42 | +#ifdef USE_SDL_AUDIO | |
43 | +#include "audio/sdlaudio.h" | |
44 | +#endif | |
45 | + | |
46 | +#ifdef USE_WAV_AUDIO | |
47 | +#include "audio/wavaudio.h" | |
48 | +#endif | |
49 | + | |
50 | +#ifdef USE_FMOD_AUDIO | |
51 | +#include "audio/fmodaudio.h" | |
52 | +#endif | |
53 | + | |
54 | +#define QC_AUDIO_DRV "QEMU_AUDIO_DRV" | |
55 | +#define QC_VOICES "QEMU_VOICES" | |
56 | +#define QC_FIXED_FORMAT "QEMU_FIXED_FORMAT" | |
57 | +#define QC_FIXED_FREQ "QEMU_FIXED_FREQ" | |
58 | + | |
59 | +extern void SB16_init (void); | |
60 | + | |
61 | +#ifdef USE_ADLIB | |
62 | +extern void Adlib_init (void); | |
63 | +#endif | |
64 | + | |
65 | +#ifdef USE_GUS | |
66 | +extern void GUS_init (void); | |
67 | +#endif | |
68 | + | |
69 | +static void (*hw_ctors[]) (void) = { | |
70 | + SB16_init, | |
71 | +#ifdef USE_ADLIB | |
72 | + Adlib_init, | |
73 | +#endif | |
74 | +#ifdef USE_GUS | |
75 | + GUS_init, | |
76 | +#endif | |
77 | + NULL | |
78 | +}; | |
79 | + | |
80 | +static HWVoice *hw_voice; | |
81 | + | |
82 | +AudioState audio_state = { | |
83 | + 1, /* use fixed settings */ | |
84 | + 44100, /* fixed frequency */ | |
85 | + 2, /* fixed channels */ | |
86 | + AUD_FMT_S16, /* fixed format */ | |
87 | + 1, /* number of hw voices */ | |
88 | + -1 /* voice size */ | |
89 | +}; | |
90 | + | |
91 | +/* http://www.df.lth.se/~john_e/gems/gem002d.html */ | |
92 | +/* http://www.multi-platforms.com/Tips/PopCount.htm */ | |
93 | +uint32_t popcount (uint32_t u) | |
94 | +{ | |
95 | + u = ((u&0x55555555) + ((u>>1)&0x55555555)); | |
96 | + u = ((u&0x33333333) + ((u>>2)&0x33333333)); | |
97 | + u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f)); | |
98 | + u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff)); | |
99 | + u = ( u&0x0000ffff) + (u>>16); | |
100 | + return u; | |
101 | +} | |
102 | + | |
103 | +inline uint32_t lsbindex (uint32_t u) | |
104 | +{ | |
105 | + return popcount ((u&-u)-1); | |
106 | +} | |
107 | + | |
108 | +int audio_get_conf_int (const char *key, int defval) | |
109 | +{ | |
110 | + int val = defval; | |
111 | + char *strval; | |
112 | + | |
113 | + strval = getenv (key); | |
114 | + if (strval) { | |
115 | + val = atoi (strval); | |
116 | + } | |
117 | + | |
118 | + return val; | |
119 | +} | |
120 | + | |
121 | +const char *audio_get_conf_str (const char *key, const char *defval) | |
122 | +{ | |
123 | + const char *val = getenv (key); | |
124 | + if (!val) | |
125 | + return defval; | |
126 | + else | |
127 | + return val; | |
128 | +} | |
129 | + | |
130 | +void audio_log (const char *fmt, ...) | |
131 | +{ | |
132 | + va_list ap; | |
133 | + va_start (ap, fmt); | |
134 | + vfprintf (stderr, fmt, ap); | |
135 | + va_end (ap); | |
136 | +} | |
137 | + | |
138 | +/* | |
139 | + * Soft Voice | |
140 | + */ | |
141 | +void pcm_sw_free_resources (SWVoice *sw) | |
142 | +{ | |
143 | + if (sw->buf) qemu_free (sw->buf); | |
144 | + if (sw->rate) st_rate_stop (sw->rate); | |
145 | + sw->buf = NULL; | |
146 | + sw->rate = NULL; | |
147 | +} | |
148 | + | |
149 | +int pcm_sw_alloc_resources (SWVoice *sw) | |
150 | +{ | |
151 | + sw->buf = qemu_mallocz (sw->hw->samples * sizeof (st_sample_t)); | |
152 | + if (!sw->buf) | |
153 | + return -1; | |
154 | + | |
155 | + sw->rate = st_rate_start (sw->freq, sw->hw->freq); | |
156 | + if (!sw->rate) { | |
157 | + qemu_free (sw->buf); | |
158 | + sw->buf = NULL; | |
159 | + return -1; | |
160 | + } | |
161 | + return 0; | |
162 | +} | |
163 | + | |
164 | +void pcm_sw_fini (SWVoice *sw) | |
165 | +{ | |
166 | + pcm_sw_free_resources (sw); | |
167 | +} | |
168 | + | |
169 | +int pcm_sw_init (SWVoice *sw, HWVoice *hw, int freq, | |
170 | + int nchannels, audfmt_e fmt) | |
171 | +{ | |
172 | + int bits = 8, sign = 0; | |
173 | + | |
174 | + switch (fmt) { | |
175 | + case AUD_FMT_S8: | |
176 | + sign = 1; | |
177 | + case AUD_FMT_U8: | |
178 | + break; | |
179 | + | |
180 | + case AUD_FMT_S16: | |
181 | + sign = 1; | |
182 | + case AUD_FMT_U16: | |
183 | + bits = 16; | |
184 | + break; | |
185 | + } | |
186 | + | |
187 | + sw->hw = hw; | |
188 | + sw->freq = freq; | |
189 | + sw->fmt = fmt; | |
190 | + sw->nchannels = nchannels; | |
191 | + sw->shift = (nchannels == 2) + (bits == 16); | |
192 | + sw->align = (1 << sw->shift) - 1; | |
193 | + sw->left = 0; | |
194 | + sw->pos = 0; | |
195 | + sw->wpos = 0; | |
196 | + sw->live = 0; | |
197 | + sw->ratio = (sw->hw->freq * ((int64_t) INT_MAX)) / sw->freq; | |
198 | + sw->bytes_per_second = sw->freq << sw->shift; | |
199 | + sw->conv = mixeng_conv[nchannels == 2][sign][bits == 16]; | |
200 | + | |
201 | + pcm_sw_free_resources (sw); | |
202 | + return pcm_sw_alloc_resources (sw); | |
203 | +} | |
204 | + | |
205 | +/* Hard voice */ | |
206 | +void pcm_hw_free_resources (HWVoice *hw) | |
207 | +{ | |
208 | + if (hw->mix_buf) | |
209 | + qemu_free (hw->mix_buf); | |
210 | + hw->mix_buf = NULL; | |
211 | +} | |
212 | + | |
213 | +int pcm_hw_alloc_resources (HWVoice *hw) | |
214 | +{ | |
215 | + hw->mix_buf = qemu_mallocz (hw->samples * sizeof (st_sample_t)); | |
216 | + if (!hw->mix_buf) | |
217 | + return -1; | |
218 | + return 0; | |
219 | +} | |
220 | + | |
221 | + | |
222 | +void pcm_hw_fini (HWVoice *hw) | |
223 | +{ | |
224 | + if (hw->active) { | |
225 | + ldebug ("pcm_hw_fini: %d %d %d\n", hw->freq, hw->nchannels, hw->fmt); | |
226 | + pcm_hw_free_resources (hw); | |
227 | + hw->pcm_ops->fini (hw); | |
228 | + memset (hw, 0, audio_state.drv->voice_size); | |
229 | + } | |
230 | +} | |
231 | + | |
232 | +void pcm_hw_gc (HWVoice *hw) | |
233 | +{ | |
234 | + if (hw->nb_voices) | |
235 | + return; | |
236 | + | |
237 | + pcm_hw_fini (hw); | |
238 | +} | |
239 | + | |
240 | +int pcm_hw_get_live (HWVoice *hw) | |
241 | +{ | |
242 | + int i, alive = 0, live = hw->samples; | |
243 | + | |
244 | + for (i = 0; i < hw->nb_voices; i++) { | |
245 | + if (hw->pvoice[i]->live) { | |
246 | + live = audio_MIN (hw->pvoice[i]->live, live); | |
247 | + alive += 1; | |
248 | + } | |
249 | + } | |
250 | + | |
251 | + if (alive) | |
252 | + return live; | |
253 | + else | |
254 | + return -1; | |
255 | +} | |
256 | + | |
257 | +int pcm_hw_get_live2 (HWVoice *hw, int *nb_active) | |
258 | +{ | |
259 | + int i, alive = 0, live = hw->samples; | |
260 | + | |
261 | + *nb_active = 0; | |
262 | + for (i = 0; i < hw->nb_voices; i++) { | |
263 | + if (hw->pvoice[i]->live) { | |
264 | + if (hw->pvoice[i]->live < live) { | |
265 | + *nb_active = hw->pvoice[i]->active != 0; | |
266 | + live = hw->pvoice[i]->live; | |
267 | + } | |
268 | + alive += 1; | |
269 | + } | |
270 | + } | |
271 | + | |
272 | + if (alive) | |
273 | + return live; | |
274 | + else | |
275 | + return -1; | |
276 | +} | |
277 | + | |
278 | +void pcm_hw_dec_live (HWVoice *hw, int decr) | |
279 | +{ | |
280 | + int i; | |
281 | + | |
282 | + for (i = 0; i < hw->nb_voices; i++) { | |
283 | + if (hw->pvoice[i]->live) { | |
284 | + hw->pvoice[i]->live -= decr; | |
285 | + } | |
286 | + } | |
287 | +} | |
288 | + | |
289 | +void pcm_hw_clear (HWVoice *hw, void *buf, int len) | |
290 | +{ | |
291 | + if (!len) | |
292 | + return; | |
293 | + | |
294 | + switch (hw->fmt) { | |
295 | + case AUD_FMT_S16: | |
296 | + case AUD_FMT_S8: | |
297 | + memset (buf, len << hw->shift, 0x00); | |
298 | + break; | |
299 | + | |
300 | + case AUD_FMT_U8: | |
301 | + memset (buf, len << hw->shift, 0x80); | |
302 | + break; | |
303 | + | |
304 | + case AUD_FMT_U16: | |
305 | + { | |
306 | + unsigned int i; | |
307 | + uint16_t *p = buf; | |
308 | + int shift = hw->nchannels - 1; | |
309 | + | |
310 | + for (i = 0; i < len << shift; i++) { | |
311 | + p[i] = INT16_MAX; | |
312 | + } | |
313 | + } | |
314 | + break; | |
315 | + } | |
316 | +} | |
317 | + | |
318 | +int pcm_hw_write (SWVoice *sw, void *buf, int size) | |
319 | +{ | |
320 | + int hwsamples, samples, isamp, osamp, wpos, live, dead, left, swlim, blck; | |
321 | + int ret = 0, pos = 0; | |
322 | + if (!sw) | |
323 | + return size; | |
324 | + | |
325 | + hwsamples = sw->hw->samples; | |
326 | + samples = size >> sw->shift; | |
327 | + | |
328 | + if (!sw->live) { | |
329 | + sw->wpos = sw->hw->rpos; | |
330 | + } | |
331 | + wpos = sw->wpos; | |
332 | + live = sw->live; | |
333 | + dead = hwsamples - live; | |
334 | + swlim = (dead * ((int64_t) INT_MAX)) / sw->ratio; | |
335 | + swlim = audio_MIN (swlim, samples); | |
336 | + | |
337 | + ldebug ("size=%d live=%d dead=%d swlim=%d wpos=%d\n", | |
338 | + size, live, dead, swlim, wpos); | |
339 | + if (swlim) | |
340 | + sw->conv (sw->buf, buf, swlim); | |
341 | + | |
342 | + while (swlim) { | |
343 | + dead = hwsamples - live; | |
344 | + left = hwsamples - wpos; | |
345 | + blck = audio_MIN (dead, left); | |
346 | + if (!blck) { | |
347 | + /* dolog ("swlim=%d\n", swlim); */ | |
348 | + break; | |
349 | + } | |
350 | + isamp = swlim; | |
351 | + osamp = blck; | |
352 | + st_rate_flow (sw->rate, sw->buf + pos, sw->hw->mix_buf + wpos, &isamp, &osamp); | |
353 | + ret += isamp; | |
354 | + swlim -= isamp; | |
355 | + pos += isamp; | |
356 | + live += osamp; | |
357 | + wpos = (wpos + osamp) % hwsamples; | |
358 | + } | |
359 | + | |
360 | + sw->wpos = wpos; | |
361 | + sw->live = live; | |
362 | + return ret << sw->shift; | |
363 | +} | |
364 | + | |
365 | +int pcm_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt) | |
366 | +{ | |
367 | + int sign = 0, bits = 8; | |
368 | + | |
369 | + pcm_hw_fini (hw); | |
370 | + ldebug ("pcm_hw_init: %d %d %d\n", freq, nchannels, fmt); | |
371 | + if (hw->pcm_ops->init (hw, freq, nchannels, fmt)) { | |
372 | + memset (hw, 0, audio_state.drv->voice_size); | |
373 | + return -1; | |
374 | + } | |
375 | + | |
376 | + switch (hw->fmt) { | |
377 | + case AUD_FMT_S8: | |
378 | + sign = 1; | |
379 | + case AUD_FMT_U8: | |
380 | + break; | |
381 | + | |
382 | + case AUD_FMT_S16: | |
383 | + sign = 1; | |
384 | + case AUD_FMT_U16: | |
385 | + bits = 16; | |
386 | + break; | |
387 | + } | |
388 | + | |
389 | + hw->nb_voices = 0; | |
390 | + hw->active = 1; | |
391 | + hw->shift = (hw->nchannels == 2) + (bits == 16); | |
392 | + hw->bytes_per_second = hw->freq << hw->shift; | |
393 | + hw->align = (1 << hw->shift) - 1; | |
394 | + hw->samples = hw->bufsize >> hw->shift; | |
395 | + hw->clip = mixeng_clip[hw->nchannels == 2][sign][bits == 16]; | |
396 | + if (pcm_hw_alloc_resources (hw)) { | |
397 | + pcm_hw_fini (hw); | |
398 | + return -1; | |
399 | + } | |
400 | + return 0; | |
401 | +} | |
402 | + | |
403 | +static int dist (void *hw) | |
404 | +{ | |
405 | + if (hw) { | |
406 | + return (((uint8_t *) hw - (uint8_t *) hw_voice) | |
407 | + / audio_state.voice_size) + 1; | |
408 | + } | |
409 | + else { | |
410 | + return 0; | |
411 | + } | |
412 | +} | |
413 | + | |
414 | +#define ADVANCE(hw) hw ? advance (hw, audio_state.voice_size) : hw_voice | |
415 | + | |
416 | +HWVoice *pcm_hw_find_any (HWVoice *hw) | |
417 | +{ | |
418 | + int i = dist (hw); | |
419 | + for (; i < audio_state.nb_hw_voices; i++) { | |
420 | + hw = ADVANCE (hw); | |
421 | + return hw; | |
422 | + } | |
423 | + return NULL; | |
424 | +} | |
425 | + | |
426 | +HWVoice *pcm_hw_find_any_active (HWVoice *hw) | |
427 | +{ | |
428 | + int i = dist (hw); | |
429 | + for (; i < audio_state.nb_hw_voices; i++) { | |
430 | + hw = ADVANCE (hw); | |
431 | + if (hw->active) | |
432 | + return hw; | |
433 | + } | |
434 | + return NULL; | |
435 | +} | |
436 | + | |
437 | +HWVoice *pcm_hw_find_any_active_enabled (HWVoice *hw) | |
438 | +{ | |
439 | + int i = dist (hw); | |
440 | + for (; i < audio_state.nb_hw_voices; i++) { | |
441 | + hw = ADVANCE (hw); | |
442 | + if (hw->active && hw->enabled) | |
443 | + return hw; | |
444 | + } | |
445 | + return NULL; | |
446 | +} | |
447 | + | |
448 | +HWVoice *pcm_hw_find_any_passive (HWVoice *hw) | |
449 | +{ | |
450 | + int i = dist (hw); | |
451 | + for (; i < audio_state.nb_hw_voices; i++) { | |
452 | + hw = ADVANCE (hw); | |
453 | + if (!hw->active) | |
454 | + return hw; | |
455 | + } | |
456 | + return NULL; | |
457 | +} | |
458 | + | |
459 | +HWVoice *pcm_hw_find_specific (HWVoice *hw, int freq, | |
460 | + int nchannels, audfmt_e fmt) | |
461 | +{ | |
462 | + while ((hw = pcm_hw_find_any_active (hw))) { | |
463 | + if (hw->freq == freq && | |
464 | + hw->nchannels == nchannels && | |
465 | + hw->fmt == fmt) | |
466 | + return hw; | |
467 | + } | |
468 | + return NULL; | |
469 | +} | |
470 | + | |
471 | +HWVoice *pcm_hw_add (int freq, int nchannels, audfmt_e fmt) | |
472 | +{ | |
473 | + HWVoice *hw; | |
474 | + | |
475 | + if (audio_state.fixed_format) { | |
476 | + freq = audio_state.fixed_freq; | |
477 | + nchannels = audio_state.fixed_channels; | |
478 | + fmt = audio_state.fixed_fmt; | |
479 | + } | |
480 | + | |
481 | + hw = pcm_hw_find_specific (NULL, freq, nchannels, fmt); | |
482 | + | |
483 | + if (hw) | |
484 | + return hw; | |
485 | + | |
486 | + hw = pcm_hw_find_any_passive (NULL); | |
487 | + if (hw) { | |
488 | + hw->pcm_ops = audio_state.drv->pcm_ops; | |
489 | + if (!hw->pcm_ops) | |
490 | + return NULL; | |
491 | + | |
492 | + if (pcm_hw_init (hw, freq, nchannels, fmt)) { | |
493 | + pcm_hw_gc (hw); | |
494 | + return NULL; | |
495 | + } | |
496 | + else | |
497 | + return hw; | |
498 | + } | |
499 | + | |
500 | + return pcm_hw_find_any (NULL); | |
501 | +} | |
502 | + | |
503 | +int pcm_hw_add_sw (HWVoice *hw, SWVoice *sw) | |
504 | +{ | |
505 | + SWVoice **pvoice = qemu_mallocz ((hw->nb_voices + 1) * sizeof (sw)); | |
506 | + if (!pvoice) | |
507 | + return -1; | |
508 | + | |
509 | + memcpy (pvoice, hw->pvoice, hw->nb_voices * sizeof (sw)); | |
510 | + qemu_free (hw->pvoice); | |
511 | + hw->pvoice = pvoice; | |
512 | + hw->pvoice[hw->nb_voices++] = sw; | |
513 | + return 0; | |
514 | +} | |
515 | + | |
516 | +int pcm_hw_del_sw (HWVoice *hw, SWVoice *sw) | |
517 | +{ | |
518 | + int i, j; | |
519 | + if (hw->nb_voices > 1) { | |
520 | + SWVoice **pvoice = qemu_mallocz ((hw->nb_voices - 1) * sizeof (sw)); | |
521 | + | |
522 | + if (!pvoice) { | |
523 | + dolog ("Can not maintain consistent state (not enough memory)\n"); | |
524 | + return -1; | |
525 | + } | |
526 | + | |
527 | + for (i = 0, j = 0; i < hw->nb_voices; i++) { | |
528 | + if (j >= hw->nb_voices - 1) { | |
529 | + dolog ("Can not maintain consistent state " | |
530 | + "(invariant violated)\n"); | |
531 | + return -1; | |
532 | + } | |
533 | + if (hw->pvoice[i] != sw) | |
534 | + pvoice[j++] = hw->pvoice[i]; | |
535 | + } | |
536 | + qemu_free (hw->pvoice); | |
537 | + hw->pvoice = pvoice; | |
538 | + hw->nb_voices -= 1; | |
539 | + } | |
540 | + else { | |
541 | + qemu_free (hw->pvoice); | |
542 | + hw->pvoice = NULL; | |
543 | + hw->nb_voices = 0; | |
544 | + } | |
545 | + return 0; | |
546 | +} | |
547 | + | |
548 | +SWVoice *pcm_create_voice_pair (int freq, int nchannels, audfmt_e fmt) | |
549 | +{ | |
550 | + SWVoice *sw; | |
551 | + HWVoice *hw; | |
552 | + | |
553 | + sw = qemu_mallocz (sizeof (*sw)); | |
554 | + if (!sw) | |
555 | + goto err1; | |
556 | + | |
557 | + hw = pcm_hw_add (freq, nchannels, fmt); | |
558 | + if (!hw) | |
559 | + goto err2; | |
560 | + | |
561 | + if (pcm_hw_add_sw (hw, sw)) | |
562 | + goto err3; | |
563 | + | |
564 | + if (pcm_sw_init (sw, hw, freq, nchannels, fmt)) | |
565 | + goto err4; | |
566 | + | |
567 | + return sw; | |
568 | + | |
569 | +err4: | |
570 | + pcm_hw_del_sw (hw, sw); | |
571 | +err3: | |
572 | + pcm_hw_gc (hw); | |
573 | +err2: | |
574 | + qemu_free (sw); | |
575 | +err1: | |
576 | + return NULL; | |
577 | +} | |
578 | + | |
579 | +SWVoice *AUD_open (SWVoice *sw, const char *name, | |
580 | + int freq, int nchannels, audfmt_e fmt) | |
581 | +{ | |
582 | + if (!audio_state.drv) { | |
583 | + return NULL; | |
584 | + } | |
585 | + | |
586 | + if (sw && freq == sw->freq && sw->nchannels == nchannels && sw->fmt == fmt) { | |
587 | + return sw; | |
588 | + } | |
589 | + | |
590 | + if (sw) { | |
591 | + ldebug ("Different format %s %d %d %d\n", | |
592 | + name, | |
593 | + sw->freq == freq, | |
594 | + sw->nchannels == nchannels, | |
595 | + sw->fmt == fmt); | |
596 | + } | |
597 | + | |
598 | + if (nchannels != 1 && nchannels != 2) { | |
599 | + dolog ("Bogus channel count %d for voice %s\n", nchannels, name); | |
600 | + return NULL; | |
601 | + } | |
602 | + | |
603 | + if (!audio_state.fixed_format && sw) { | |
604 | + pcm_sw_fini (sw); | |
605 | + pcm_hw_del_sw (sw->hw, sw); | |
606 | + pcm_hw_gc (sw->hw); | |
607 | + if (sw->name) { | |
608 | + qemu_free (sw->name); | |
609 | + sw->name = NULL; | |
610 | + } | |
611 | + qemu_free (sw); | |
612 | + sw = NULL; | |
613 | + } | |
614 | + | |
615 | + if (sw) { | |
616 | + HWVoice *hw = sw->hw; | |
617 | + if (!hw) { | |
618 | + dolog ("Internal logic error voice %s has no hardware store\n", | |
619 | + name); | |
620 | + return sw; | |
621 | + } | |
622 | + | |
623 | + if (pcm_sw_init (sw, hw, freq, nchannels, fmt)) { | |
624 | + pcm_sw_fini (sw); | |
625 | + pcm_hw_del_sw (hw, sw); | |
626 | + pcm_hw_gc (hw); | |
627 | + if (sw->name) { | |
628 | + qemu_free (sw->name); | |
629 | + sw->name = NULL; | |
630 | + } | |
631 | + qemu_free (sw); | |
632 | + return NULL; | |
633 | + } | |
634 | + } | |
635 | + else { | |
636 | + sw = pcm_create_voice_pair (freq, nchannels, fmt); | |
637 | + if (!sw) { | |
638 | + dolog ("Failed to create voice %s\n", name); | |
639 | + return NULL; | |
640 | + } | |
641 | + } | |
642 | + | |
643 | + if (sw->name) { | |
644 | + qemu_free (sw->name); | |
645 | + sw->name = NULL; | |
646 | + } | |
647 | + sw->name = qemu_strdup (name); | |
648 | + return sw; | |
649 | +} | |
650 | + | |
651 | +int AUD_write (SWVoice *sw, void *buf, int size) | |
652 | +{ | |
653 | + int bytes; | |
654 | + | |
655 | + if (!sw->hw->enabled) | |
656 | + dolog ("Writing to disabled voice %s\n", sw->name); | |
657 | + bytes = sw->hw->pcm_ops->write (sw, buf, size); | |
658 | + return bytes; | |
659 | +} | |
660 | + | |
661 | +void AUD_run (void) | |
662 | +{ | |
663 | + HWVoice *hw = NULL; | |
664 | + | |
665 | + while ((hw = pcm_hw_find_any_active_enabled (hw))) { | |
666 | + int i; | |
667 | + if (hw->pending_disable && pcm_hw_get_live (hw) <= 0) { | |
668 | + hw->enabled = 0; | |
669 | + hw->pcm_ops->ctl (hw, VOICE_DISABLE); | |
670 | + for (i = 0; i < hw->nb_voices; i++) { | |
671 | + hw->pvoice[i]->live = 0; | |
672 | + /* hw->pvoice[i]->old_ticks = 0; */ | |
673 | + } | |
674 | + continue; | |
675 | + } | |
676 | + | |
677 | + hw->pcm_ops->run (hw); | |
678 | + assert (hw->rpos < hw->samples); | |
679 | + for (i = 0; i < hw->nb_voices; i++) { | |
680 | + SWVoice *sw = hw->pvoice[i]; | |
681 | + if (!sw->active && !sw->live && sw->old_ticks) { | |
682 | + int64_t delta = qemu_get_clock (vm_clock) - sw->old_ticks; | |
683 | + if (delta > audio_state.ticks_threshold) { | |
684 | + ldebug ("resetting old_ticks for %s\n", sw->name); | |
685 | + sw->old_ticks = 0; | |
686 | + } | |
687 | + } | |
688 | + } | |
689 | + } | |
690 | +} | |
691 | + | |
692 | +int AUD_get_free (SWVoice *sw) | |
693 | +{ | |
694 | + int free; | |
695 | + | |
696 | + if (!sw) | |
697 | + return 4096; | |
698 | + | |
699 | + free = ((sw->hw->samples - sw->live) << sw->hw->shift) * sw->ratio | |
700 | + / INT_MAX; | |
701 | + | |
702 | + free &= ~sw->hw->align; | |
703 | + if (!free) return 0; | |
704 | + | |
705 | + return free; | |
706 | +} | |
707 | + | |
708 | +int AUD_get_buffer_size (SWVoice *sw) | |
709 | +{ | |
710 | + return sw->hw->bufsize; | |
711 | +} | |
712 | + | |
713 | +void AUD_adjust (SWVoice *sw, int bytes) | |
714 | +{ | |
715 | + if (!sw) | |
716 | + return; | |
717 | + sw->old_ticks += (ticks_per_sec * (int64_t) bytes) / sw->bytes_per_second; | |
718 | +} | |
719 | + | |
720 | +void AUD_reset (SWVoice *sw) | |
721 | +{ | |
722 | + sw->active = 0; | |
723 | + sw->old_ticks = 0; | |
724 | +} | |
725 | + | |
726 | +int AUD_calc_elapsed (SWVoice *sw) | |
727 | +{ | |
728 | + int64_t now, delta, bytes; | |
729 | + int dead, swlim; | |
730 | + | |
731 | + if (!sw) | |
732 | + return 0; | |
733 | + | |
734 | + now = qemu_get_clock (vm_clock); | |
735 | + delta = now - sw->old_ticks; | |
736 | + bytes = (delta * sw->bytes_per_second) / ticks_per_sec; | |
737 | + if (delta < 0) { | |
738 | + dolog ("whoops delta(<0)=%lld\n", delta); | |
739 | + return 0; | |
740 | + } | |
741 | + | |
742 | + dead = sw->hw->samples - sw->live; | |
743 | + swlim = ((dead * (int64_t) INT_MAX) / sw->ratio); | |
744 | + | |
745 | + if (bytes > swlim) { | |
746 | + return swlim; | |
747 | + } | |
748 | + else { | |
749 | + return bytes; | |
750 | + } | |
751 | +} | |
752 | + | |
753 | +void AUD_enable (SWVoice *sw, int on) | |
754 | +{ | |
755 | + int i; | |
756 | + HWVoice *hw; | |
757 | + | |
758 | + if (!sw) | |
759 | + return; | |
760 | + | |
761 | + hw = sw->hw; | |
762 | + if (on) { | |
763 | + if (!sw->live) | |
764 | + sw->wpos = sw->hw->rpos; | |
765 | + if (!sw->old_ticks) { | |
766 | + sw->old_ticks = qemu_get_clock (vm_clock); | |
767 | + } | |
768 | + } | |
769 | + | |
770 | + if (sw->active != on) { | |
771 | + if (on) { | |
772 | + hw->pending_disable = 0; | |
773 | + if (!hw->enabled) { | |
774 | + hw->enabled = 1; | |
775 | + for (i = 0; i < hw->nb_voices; i++) { | |
776 | + ldebug ("resetting voice\n"); | |
777 | + sw = hw->pvoice[i]; | |
778 | + sw->old_ticks = qemu_get_clock (vm_clock); | |
779 | + } | |
780 | + hw->pcm_ops->ctl (hw, VOICE_ENABLE); | |
781 | + } | |
782 | + } | |
783 | + else { | |
784 | + if (hw->enabled && !hw->pending_disable) { | |
785 | + int nb_active = 0; | |
786 | + for (i = 0; i < hw->nb_voices; i++) { | |
787 | + nb_active += hw->pvoice[i]->active != 0; | |
788 | + } | |
789 | + | |
790 | + if (nb_active == 1) { | |
791 | + hw->pending_disable = 1; | |
792 | + } | |
793 | + } | |
794 | + } | |
795 | + sw->active = on; | |
796 | + } | |
797 | +} | |
798 | + | |
799 | +static struct audio_output_driver *drvtab[] = { | |
800 | +#ifdef USE_OSS_AUDIO | |
801 | + &oss_output_driver, | |
802 | +#endif | |
803 | +#ifdef USE_FMOD_AUDIO | |
804 | + &fmod_output_driver, | |
805 | +#endif | |
806 | +#ifdef USE_SDL_AUDIO | |
807 | + &sdl_output_driver, | |
808 | +#endif | |
809 | +#ifdef USE_WAV_AUDIO | |
810 | + &wav_output_driver, | |
811 | +#endif | |
812 | +}; | |
813 | + | |
814 | +static int voice_init (struct audio_output_driver *drv) | |
815 | +{ | |
816 | + audio_state.opaque = drv->init (); | |
817 | + if (audio_state.opaque) { | |
818 | + if (audio_state.nb_hw_voices > drv->max_voices) { | |
819 | + dolog ("`%s' does not support %d multiple hardware channels\n" | |
820 | + "Resetting to %d\n", | |
821 | + drv->name, audio_state.nb_hw_voices, drv->max_voices); | |
822 | + audio_state.nb_hw_voices = drv->max_voices; | |
823 | + } | |
824 | + hw_voice = qemu_mallocz (audio_state.nb_hw_voices * drv->voice_size); | |
825 | + if (hw_voice) { | |
826 | + audio_state.drv = drv; | |
827 | + return 1; | |
828 | + } | |
829 | + else { | |
830 | + dolog ("Not enough memory for %d `%s' voices (each %d bytes)\n", | |
831 | + audio_state.nb_hw_voices, drv->name, drv->voice_size); | |
832 | + drv->fini (audio_state.opaque); | |
833 | + return 0; | |
834 | + } | |
835 | + } | |
836 | + else { | |
837 | + dolog ("Could not init `%s' audio\n", drv->name); | |
838 | + return 0; | |
839 | + } | |
840 | +} | |
841 | + | |
842 | +static void audio_vm_stop_handler (void *opaque, int reason) | |
843 | +{ | |
844 | + HWVoice *hw = NULL; | |
845 | + | |
846 | + while ((hw = pcm_hw_find_any (hw))) { | |
847 | + if (!hw->pcm_ops) | |
848 | + continue; | |
849 | + | |
850 | + hw->pcm_ops->ctl (hw, reason ? VOICE_ENABLE : VOICE_DISABLE); | |
851 | + } | |
852 | +} | |
853 | + | |
854 | +static void audio_atexit (void) | |
855 | +{ | |
856 | + HWVoice *hw = NULL; | |
857 | + | |
858 | + while ((hw = pcm_hw_find_any (hw))) { | |
859 | + if (!hw->pcm_ops) | |
860 | + continue; | |
861 | + | |
862 | + hw->pcm_ops->ctl (hw, VOICE_DISABLE); | |
863 | + hw->pcm_ops->fini (hw); | |
864 | + } | |
865 | + audio_state.drv->fini (audio_state.opaque); | |
866 | +} | |
867 | + | |
868 | +static void audio_save (QEMUFile *f, void *opaque) | |
869 | +{ | |
870 | +} | |
871 | + | |
872 | +static int audio_load (QEMUFile *f, void *opaque, int version_id) | |
873 | +{ | |
874 | + if (version_id != 1) | |
875 | + return -EINVAL; | |
876 | + | |
877 | + return 0; | |
878 | +} | |
879 | + | |
880 | +void AUD_init (void) | |
881 | +{ | |
882 | + int i; | |
883 | + int done = 0; | |
884 | + const char *drvname; | |
885 | + | |
886 | + audio_state.fixed_format = | |
887 | + !!audio_get_conf_int (QC_FIXED_FORMAT, audio_state.fixed_format); | |
888 | + audio_state.fixed_freq = | |
889 | + audio_get_conf_int (QC_FIXED_FREQ, audio_state.fixed_freq); | |
890 | + audio_state.nb_hw_voices = | |
891 | + audio_get_conf_int (QC_VOICES, audio_state.nb_hw_voices); | |
892 | + | |
893 | + if (audio_state.nb_hw_voices <= 0) { | |
894 | + dolog ("Bogus number of voices %d, resetting to 1\n", | |
895 | + audio_state.nb_hw_voices); | |
896 | + } | |
897 | + | |
898 | + drvname = audio_get_conf_str (QC_AUDIO_DRV, NULL); | |
899 | + if (drvname) { | |
900 | + int found = 0; | |
901 | + for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) { | |
902 | + if (!strcmp (drvname, drvtab[i]->name)) { | |
903 | + done = voice_init (drvtab[i]); | |
904 | + found = 1; | |
905 | + break; | |
906 | + } | |
907 | + } | |
908 | + if (!found) { | |
909 | + dolog ("Unknown audio driver `%s'\n", drvname); | |
910 | + } | |
911 | + } | |
912 | + | |
913 | + qemu_add_vm_stop_handler (audio_vm_stop_handler, NULL); | |
914 | + atexit (audio_atexit); | |
915 | + | |
916 | + if (!done) { | |
917 | + for (i = 0; !done && i < sizeof (drvtab) / sizeof (drvtab[0]); i++) { | |
918 | + if (drvtab[i]->can_be_default) | |
919 | + done = voice_init (drvtab[i]); | |
920 | + } | |
921 | + } | |
922 | + | |
923 | + audio_state.ticks_threshold = ticks_per_sec / 50; | |
924 | + audio_state.freq_threshold = 100; | |
925 | + | |
926 | + register_savevm ("audio", 0, 1, audio_save, audio_load, NULL); | |
927 | + if (!done) { | |
928 | + dolog ("Can not initialize audio subsystem\n"); | |
929 | + return; | |
930 | + } | |
931 | + | |
932 | + for (i = 0; hw_ctors[i]; i++) { | |
933 | + hw_ctors[i] (); | |
934 | + } | |
935 | +} | ... | ... |
audio/audio.h
0 → 100644
1 | +/* | |
2 | + * QEMU Audio subsystem header | |
3 | + * | |
4 | + * Copyright (c) 2003-2004 Vassili Karpov (malc) | |
5 | + * | |
6 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | + * of this software and associated documentation files (the "Software"), to deal | |
8 | + * in the Software without restriction, including without limitation the rights | |
9 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | + * copies of the Software, and to permit persons to whom the Software is | |
11 | + * furnished to do so, subject to the following conditions: | |
12 | + * | |
13 | + * The above copyright notice and this permission notice shall be included in | |
14 | + * all copies or substantial portions of the Software. | |
15 | + * | |
16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | + * THE SOFTWARE. | |
23 | + */ | |
24 | +#ifndef QEMU_AUDIO_H | |
25 | +#define QEMU_AUDIO_H | |
26 | + | |
27 | +#include "mixeng.h" | |
28 | + | |
29 | +#define dolog(...) fprintf (stderr, AUDIO_CAP ": " __VA_ARGS__) | |
30 | +#ifdef DEBUG | |
31 | +#define ldebug(...) dolog (__VA_ARGS__) | |
32 | +#else | |
33 | +#define ldebug(...) | |
34 | +#endif | |
35 | + | |
36 | +typedef enum { | |
37 | + AUD_FMT_U8, | |
38 | + AUD_FMT_S8, | |
39 | + AUD_FMT_U16, | |
40 | + AUD_FMT_S16 | |
41 | +} audfmt_e; | |
42 | + | |
43 | +typedef struct HWVoice HWVoice; | |
44 | +struct audio_output_driver; | |
45 | + | |
46 | +typedef struct AudioState { | |
47 | + int fixed_format; | |
48 | + int fixed_freq; | |
49 | + int fixed_channels; | |
50 | + int fixed_fmt; | |
51 | + int nb_hw_voices; | |
52 | + int voice_size; | |
53 | + int64_t ticks_threshold; | |
54 | + int freq_threshold; | |
55 | + void *opaque; | |
56 | + struct audio_output_driver *drv; | |
57 | +} AudioState; | |
58 | + | |
59 | +extern AudioState audio_state; | |
60 | + | |
61 | +typedef struct SWVoice { | |
62 | + int freq; | |
63 | + audfmt_e fmt; | |
64 | + int nchannels; | |
65 | + | |
66 | + int shift; | |
67 | + int align; | |
68 | + | |
69 | + t_sample *conv; | |
70 | + | |
71 | + int left; | |
72 | + int pos; | |
73 | + int bytes_per_second; | |
74 | + int64_t ratio; | |
75 | + st_sample_t *buf; | |
76 | + void *rate; | |
77 | + | |
78 | + int wpos; | |
79 | + int live; | |
80 | + int active; | |
81 | + int64_t old_ticks; | |
82 | + HWVoice *hw; | |
83 | + char *name; | |
84 | +} SWVoice; | |
85 | + | |
86 | +#define VOICE_ENABLE 1 | |
87 | +#define VOICE_DISABLE 2 | |
88 | + | |
89 | +struct pcm_ops { | |
90 | + int (*init) (HWVoice *hw, int freq, int nchannels, audfmt_e fmt); | |
91 | + void (*fini) (HWVoice *hw); | |
92 | + void (*run) (HWVoice *hw); | |
93 | + int (*write) (SWVoice *sw, void *buf, int size); | |
94 | + int (*ctl) (HWVoice *hw, int cmd, ...); | |
95 | +}; | |
96 | + | |
97 | +struct audio_output_driver { | |
98 | + const char *name; | |
99 | + void *(*init) (void); | |
100 | + void (*fini) (void *); | |
101 | + struct pcm_ops *pcm_ops; | |
102 | + int can_be_default; | |
103 | + int max_voices; | |
104 | + int voice_size; | |
105 | +}; | |
106 | + | |
107 | +struct HWVoice { | |
108 | + int active; | |
109 | + int enabled; | |
110 | + int pending_disable; | |
111 | + int valid; | |
112 | + int freq; | |
113 | + | |
114 | + f_sample *clip; | |
115 | + audfmt_e fmt; | |
116 | + int nchannels; | |
117 | + | |
118 | + int align; | |
119 | + int shift; | |
120 | + | |
121 | + int rpos; | |
122 | + int bufsize; | |
123 | + | |
124 | + int bytes_per_second; | |
125 | + st_sample_t *mix_buf; | |
126 | + | |
127 | + int samples; | |
128 | + int64_t old_ticks; | |
129 | + int nb_voices; | |
130 | + struct SWVoice **pvoice; | |
131 | + struct pcm_ops *pcm_ops; | |
132 | +}; | |
133 | + | |
134 | +void audio_log (const char *fmt, ...); | |
135 | +void pcm_sw_free_resources (SWVoice *sw); | |
136 | +int pcm_sw_alloc_resources (SWVoice *sw); | |
137 | +void pcm_sw_fini (SWVoice *sw); | |
138 | +int pcm_sw_init (SWVoice *sw, HWVoice *hw, int freq, | |
139 | + int nchannels, audfmt_e fmt); | |
140 | + | |
141 | +void pcm_hw_clear (HWVoice *hw, void *buf, int len); | |
142 | +HWVoice * pcm_hw_find_any (HWVoice *hw); | |
143 | +HWVoice * pcm_hw_find_any_active (HWVoice *hw); | |
144 | +HWVoice * pcm_hw_find_any_passive (HWVoice *hw); | |
145 | +HWVoice * pcm_hw_find_specific (HWVoice *hw, int freq, | |
146 | + int nchannels, audfmt_e fmt); | |
147 | +HWVoice * pcm_hw_add (int freq, int nchannels, audfmt_e fmt); | |
148 | +int pcm_hw_add_sw (HWVoice *hw, SWVoice *sw); | |
149 | +int pcm_hw_del_sw (HWVoice *hw, SWVoice *sw); | |
150 | +SWVoice * pcm_create_voice_pair (int freq, int nchannels, audfmt_e fmt); | |
151 | + | |
152 | +void pcm_hw_free_resources (HWVoice *hw); | |
153 | +int pcm_hw_alloc_resources (HWVoice *hw); | |
154 | +void pcm_hw_fini (HWVoice *hw); | |
155 | +void pcm_hw_gc (HWVoice *hw); | |
156 | +int pcm_hw_get_live (HWVoice *hw); | |
157 | +int pcm_hw_get_live2 (HWVoice *hw, int *nb_active); | |
158 | +void pcm_hw_dec_live (HWVoice *hw, int decr); | |
159 | +int pcm_hw_write (SWVoice *sw, void *buf, int len); | |
160 | + | |
161 | +int audio_get_conf_int (const char *key, int defval); | |
162 | +const char *audio_get_conf_str (const char *key, const char *defval); | |
163 | + | |
164 | +/* Public API */ | |
165 | +SWVoice * AUD_open (SWVoice *sw, const char *name, int freq, | |
166 | + int nchannels, audfmt_e fmt); | |
167 | +int AUD_write (SWVoice *sw, void *pcm_buf, int size); | |
168 | +void AUD_adjust (SWVoice *sw, int leftover); | |
169 | +void AUD_reset (SWVoice *sw); | |
170 | +int AUD_get_free (SWVoice *sw); | |
171 | +int AUD_get_buffer_size (SWVoice *sw); | |
172 | +void AUD_run (void); | |
173 | +void AUD_enable (SWVoice *sw, int on); | |
174 | +int AUD_calc_elapsed (SWVoice *sw); | |
175 | + | |
176 | +static inline void *advance (void *p, int incr) | |
177 | +{ | |
178 | + uint8_t *d = p; | |
179 | + return (d + incr); | |
180 | +} | |
181 | + | |
182 | +uint32_t popcount (uint32_t u); | |
183 | +inline uint32_t lsbindex (uint32_t u); | |
184 | + | |
185 | +#define audio_MIN(a, b) ((a)>(b)?(b):(a)) | |
186 | +#define audio_MAX(a, b) ((a)<(b)?(b):(a)) | |
187 | + | |
188 | +#endif /* audio.h */ | ... | ... |
audio/fmodaudio.c
0 → 100644
1 | +/* | |
2 | + * QEMU FMOD audio output driver | |
3 | + * | |
4 | + * Copyright (c) 2004 Vassili Karpov (malc) | |
5 | + * | |
6 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | + * of this software and associated documentation files (the "Software"), to deal | |
8 | + * in the Software without restriction, including without limitation the rights | |
9 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | + * copies of the Software, and to permit persons to whom the Software is | |
11 | + * furnished to do so, subject to the following conditions: | |
12 | + * | |
13 | + * The above copyright notice and this permission notice shall be included in | |
14 | + * all copies or substantial portions of the Software. | |
15 | + * | |
16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | + * THE SOFTWARE. | |
23 | + */ | |
24 | +#include <fmod.h> | |
25 | +#include <fmod_errors.h> | |
26 | +#include "vl.h" | |
27 | + | |
28 | +#define AUDIO_CAP "fmod" | |
29 | +#include "audio/audio.h" | |
30 | +#include "audio/fmodaudio.h" | |
31 | + | |
32 | +#define QC_FMOD_DRV "QEMU_FMOD_DRV" | |
33 | +#define QC_FMOD_FREQ "QEMU_FMOD_FREQ" | |
34 | +#define QC_FMOD_SAMPLES "QEMU_FMOD_SAMPLES" | |
35 | +#define QC_FMOD_CHANNELS "QEMU_FMOD_CHANNELS" | |
36 | +#define QC_FMOD_BUFSIZE "QEMU_FMOD_BUFSIZE" | |
37 | +#define QC_FMOD_THRESHOLD "QEMU_FMOD_THRESHOLD" | |
38 | + | |
39 | +static struct { | |
40 | + int nb_samples; | |
41 | + int freq; | |
42 | + int nb_channels; | |
43 | + int bufsize; | |
44 | + int threshold; | |
45 | +} conf = { | |
46 | + 2048, | |
47 | + 44100, | |
48 | + 1, | |
49 | + 0, | |
50 | + 128 | |
51 | +}; | |
52 | + | |
53 | +#define errstr() FMOD_ErrorString (FSOUND_GetError ()) | |
54 | + | |
55 | +static int fmod_hw_write (SWVoice *sw, void *buf, int len) | |
56 | +{ | |
57 | + return pcm_hw_write (sw, buf, len); | |
58 | +} | |
59 | + | |
60 | +static void fmod_clear_sample (FMODVoice *fmd) | |
61 | +{ | |
62 | + HWVoice *hw = &fmd->hw; | |
63 | + int status; | |
64 | + void *p1 = 0, *p2 = 0; | |
65 | + unsigned int len1 = 0, len2 = 0; | |
66 | + | |
67 | + status = FSOUND_Sample_Lock ( | |
68 | + fmd->fmod_sample, | |
69 | + 0, | |
70 | + hw->samples << hw->shift, | |
71 | + &p1, | |
72 | + &p2, | |
73 | + &len1, | |
74 | + &len2 | |
75 | + ); | |
76 | + | |
77 | + if (!status) { | |
78 | + dolog ("Failed to lock sample\nReason: %s\n", errstr ()); | |
79 | + return; | |
80 | + } | |
81 | + | |
82 | + if ((len1 & hw->align) || (len2 & hw->align)) { | |
83 | + dolog ("Locking sample returned unaligned length %d, %d\n", | |
84 | + len1, len2); | |
85 | + goto fail; | |
86 | + } | |
87 | + | |
88 | + if (len1 + len2 != hw->samples << hw->shift) { | |
89 | + dolog ("Locking sample returned incomplete length %d, %d\n", | |
90 | + len1 + len2, hw->samples << hw->shift); | |
91 | + goto fail; | |
92 | + } | |
93 | + pcm_hw_clear (hw, p1, hw->samples); | |
94 | + | |
95 | + fail: | |
96 | + status = FSOUND_Sample_Unlock (fmd->fmod_sample, p1, p2, len1, len2); | |
97 | + if (!status) { | |
98 | + dolog ("Failed to unlock sample\nReason: %s\n", errstr ()); | |
99 | + } | |
100 | +} | |
101 | + | |
102 | +static int fmod_write_sample (HWVoice *hw, uint8_t *dst, st_sample_t *src, | |
103 | + int src_size, int src_pos, int dst_len) | |
104 | +{ | |
105 | + int src_len1 = dst_len, src_len2 = 0, pos = src_pos + dst_len; | |
106 | + st_sample_t *src1 = src + src_pos, *src2 = 0; | |
107 | + | |
108 | + if (src_pos + dst_len > src_size) { | |
109 | + src_len1 = src_size - src_pos; | |
110 | + src2 = src; | |
111 | + src_len2 = dst_len - src_len1; | |
112 | + pos = src_len2; | |
113 | + } | |
114 | + | |
115 | + if (src_len1) { | |
116 | + hw->clip (dst, src1, src_len1); | |
117 | + memset (src1, 0, src_len1 * sizeof (st_sample_t)); | |
118 | + advance (dst, src_len1); | |
119 | + } | |
120 | + | |
121 | + if (src_len2) { | |
122 | + hw->clip (dst, src2, src_len2); | |
123 | + memset (src2, 0, src_len2 * sizeof (st_sample_t)); | |
124 | + } | |
125 | + return pos; | |
126 | +} | |
127 | + | |
128 | +static int fmod_unlock_sample (FMODVoice *fmd, void *p1, void *p2, | |
129 | + unsigned int blen1, unsigned int blen2) | |
130 | +{ | |
131 | + int status = FSOUND_Sample_Unlock (fmd->fmod_sample, p1, p2, blen1, blen2); | |
132 | + if (!status) { | |
133 | + dolog ("Failed to unlock sample\nReason: %s\n", errstr ()); | |
134 | + return -1; | |
135 | + } | |
136 | + return 0; | |
137 | +} | |
138 | + | |
139 | +static int fmod_lock_sample (FMODVoice *fmd, int pos, int len, | |
140 | + void **p1, void **p2, | |
141 | + unsigned int *blen1, unsigned int *blen2) | |
142 | +{ | |
143 | + HWVoice *hw = &fmd->hw; | |
144 | + int status; | |
145 | + | |
146 | + status = FSOUND_Sample_Lock ( | |
147 | + fmd->fmod_sample, | |
148 | + pos << hw->shift, | |
149 | + len << hw->shift, | |
150 | + p1, | |
151 | + p2, | |
152 | + blen1, | |
153 | + blen2 | |
154 | + ); | |
155 | + | |
156 | + if (!status) { | |
157 | + dolog ("Failed to lock sample\nReason: %s\n", errstr ()); | |
158 | + return -1; | |
159 | + } | |
160 | + | |
161 | + if ((*blen1 & hw->align) || (*blen2 & hw->align)) { | |
162 | + dolog ("Locking sample returned unaligned length %d, %d\n", | |
163 | + *blen1, *blen2); | |
164 | + fmod_unlock_sample (fmd, *p1, *p2, *blen1, *blen2); | |
165 | + return -1; | |
166 | + } | |
167 | + return 0; | |
168 | +} | |
169 | + | |
170 | +static void fmod_hw_run (HWVoice *hw) | |
171 | +{ | |
172 | + FMODVoice *fmd = (FMODVoice *) hw; | |
173 | + int rpos, live, decr; | |
174 | + void *p1 = 0, *p2 = 0; | |
175 | + unsigned int blen1 = 0, blen2 = 0; | |
176 | + unsigned int len1 = 0, len2 = 0; | |
177 | + int nb_active; | |
178 | + | |
179 | + live = pcm_hw_get_live2 (hw, &nb_active); | |
180 | + if (live <= 0) { | |
181 | + return; | |
182 | + } | |
183 | + | |
184 | + if (!hw->pending_disable | |
185 | + && nb_active | |
186 | + && conf.threshold | |
187 | + && live <= conf.threshold) { | |
188 | + ldebug ("live=%d nb_active=%d\n", live, nb_active); | |
189 | + return; | |
190 | + } | |
191 | + | |
192 | + decr = live; | |
193 | + | |
194 | +#if 1 | |
195 | + if (fmd->channel >= 0) { | |
196 | + int pos2 = (fmd->old_pos + decr) % hw->samples; | |
197 | + int pos = FSOUND_GetCurrentPosition (fmd->channel); | |
198 | + | |
199 | + if (fmd->old_pos < pos && pos2 >= pos) { | |
200 | + decr = pos - fmd->old_pos - (pos2 == pos) - 1; | |
201 | + } | |
202 | + else if (fmd->old_pos > pos && pos2 >= pos && pos2 < fmd->old_pos) { | |
203 | + decr = (hw->samples - fmd->old_pos) + pos - (pos2 == pos) - 1; | |
204 | + } | |
205 | +/* ldebug ("pos=%d pos2=%d old=%d live=%d decr=%d\n", */ | |
206 | +/* pos, pos2, fmd->old_pos, live, decr); */ | |
207 | + } | |
208 | +#endif | |
209 | + | |
210 | + if (decr <= 0) { | |
211 | + return; | |
212 | + } | |
213 | + | |
214 | + if (fmod_lock_sample (fmd, fmd->old_pos, decr, &p1, &p2, &blen1, &blen2)) { | |
215 | + return; | |
216 | + } | |
217 | + | |
218 | + len1 = blen1 >> hw->shift; | |
219 | + len2 = blen2 >> hw->shift; | |
220 | + ldebug ("%p %p %d %d %d %d\n", p1, p2, len1, len2, blen1, blen2); | |
221 | + decr = len1 + len2; | |
222 | + rpos = hw->rpos; | |
223 | + | |
224 | + if (len1) { | |
225 | + rpos = fmod_write_sample (hw, p1, hw->mix_buf, hw->samples, rpos, len1); | |
226 | + } | |
227 | + | |
228 | + if (len2) { | |
229 | + rpos = fmod_write_sample (hw, p2, hw->mix_buf, hw->samples, rpos, len2); | |
230 | + } | |
231 | + | |
232 | + fmod_unlock_sample (fmd, p1, p2, blen1, blen2); | |
233 | + | |
234 | + pcm_hw_dec_live (hw, decr); | |
235 | + hw->rpos = rpos % hw->samples; | |
236 | + fmd->old_pos = (fmd->old_pos + decr) % hw->samples; | |
237 | +} | |
238 | + | |
239 | +static int AUD_to_fmodfmt (audfmt_e fmt, int stereo) | |
240 | +{ | |
241 | + int mode = FSOUND_LOOP_NORMAL; | |
242 | + | |
243 | + switch (fmt) { | |
244 | + case AUD_FMT_S8: | |
245 | + mode |= FSOUND_SIGNED | FSOUND_8BITS; | |
246 | + break; | |
247 | + | |
248 | + case AUD_FMT_U8: | |
249 | + mode |= FSOUND_UNSIGNED | FSOUND_8BITS; | |
250 | + break; | |
251 | + | |
252 | + case AUD_FMT_S16: | |
253 | + mode |= FSOUND_SIGNED | FSOUND_16BITS; | |
254 | + break; | |
255 | + | |
256 | + case AUD_FMT_U16: | |
257 | + mode |= FSOUND_UNSIGNED | FSOUND_16BITS; | |
258 | + break; | |
259 | + | |
260 | + default: | |
261 | + dolog ("Internal logic error: Bad audio format %d\nAborting\n", fmt); | |
262 | + exit (EXIT_FAILURE); | |
263 | + } | |
264 | + mode |= stereo ? FSOUND_STEREO : FSOUND_MONO; | |
265 | + return mode; | |
266 | +} | |
267 | + | |
268 | +static void fmod_hw_fini (HWVoice *hw) | |
269 | +{ | |
270 | + FMODVoice *fmd = (FMODVoice *) hw; | |
271 | + | |
272 | + if (fmd->fmod_sample) { | |
273 | + FSOUND_Sample_Free (fmd->fmod_sample); | |
274 | + fmd->fmod_sample = 0; | |
275 | + | |
276 | + if (fmd->channel >= 0) { | |
277 | + FSOUND_StopSound (fmd->channel); | |
278 | + } | |
279 | + } | |
280 | +} | |
281 | + | |
282 | +static int fmod_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt) | |
283 | +{ | |
284 | + int bits16, mode, channel; | |
285 | + FMODVoice *fmd = (FMODVoice *) hw; | |
286 | + | |
287 | + mode = AUD_to_fmodfmt (fmt, nchannels == 2 ? 1 : 0); | |
288 | + fmd->fmod_sample = FSOUND_Sample_Alloc ( | |
289 | + FSOUND_FREE, /* index */ | |
290 | + conf.nb_samples, /* length */ | |
291 | + mode, /* mode */ | |
292 | + freq, /* freq */ | |
293 | + 255, /* volume */ | |
294 | + 128, /* pan */ | |
295 | + 255 /* priority */ | |
296 | + ); | |
297 | + | |
298 | + if (!fmd->fmod_sample) { | |
299 | + dolog ("Failed to allocate FMOD sample\nReason: %s\n", errstr ()); | |
300 | + return -1; | |
301 | + } | |
302 | + | |
303 | + channel = FSOUND_PlaySoundEx (FSOUND_FREE, fmd->fmod_sample, 0, 1); | |
304 | + if (channel < 0) { | |
305 | + dolog ("Failed to start playing sound\nReason: %s\n", errstr ()); | |
306 | + FSOUND_Sample_Free (fmd->fmod_sample); | |
307 | + return -1; | |
308 | + } | |
309 | + fmd->channel = channel; | |
310 | + | |
311 | + hw->freq = freq; | |
312 | + hw->fmt = fmt; | |
313 | + hw->nchannels = nchannels; | |
314 | + bits16 = fmt == AUD_FMT_U16 || fmt == AUD_FMT_S16; | |
315 | + hw->bufsize = conf.nb_samples << (nchannels == 2) << bits16; | |
316 | + return 0; | |
317 | +} | |
318 | + | |
319 | +static int fmod_hw_ctl (HWVoice *hw, int cmd, ...) | |
320 | +{ | |
321 | + int status; | |
322 | + FMODVoice *fmd = (FMODVoice *) hw; | |
323 | + | |
324 | + switch (cmd) { | |
325 | + case VOICE_ENABLE: | |
326 | + fmod_clear_sample (fmd); | |
327 | + status = FSOUND_SetPaused (fmd->channel, 0); | |
328 | + if (!status) { | |
329 | + dolog ("Failed to resume channel %d\nReason: %s\n", | |
330 | + fmd->channel, errstr ()); | |
331 | + } | |
332 | + break; | |
333 | + | |
334 | + case VOICE_DISABLE: | |
335 | + status = FSOUND_SetPaused (fmd->channel, 1); | |
336 | + if (!status) { | |
337 | + dolog ("Failed to pause channel %d\nReason: %s\n", | |
338 | + fmd->channel, errstr ()); | |
339 | + } | |
340 | + break; | |
341 | + } | |
342 | + return 0; | |
343 | +} | |
344 | + | |
345 | +static struct { | |
346 | + const char *name; | |
347 | + int type; | |
348 | +} drvtab[] = { | |
349 | + {"none", FSOUND_OUTPUT_NOSOUND}, | |
350 | +#ifdef _WIN32 | |
351 | + {"winmm", FSOUND_OUTPUT_WINMM}, | |
352 | + {"dsound", FSOUND_OUTPUT_DSOUND}, | |
353 | + {"a3d", FSOUND_OUTPUT_A3D}, | |
354 | + {"asio", FSOUND_OUTPUT_ASIO}, | |
355 | +#endif | |
356 | +#ifdef __linux__ | |
357 | + {"oss", FSOUND_OUTPUT_OSS}, | |
358 | + {"alsa", FSOUND_OUTPUT_ALSA}, | |
359 | + {"esd", FSOUND_OUTPUT_ESD}, | |
360 | +#endif | |
361 | +#ifdef __APPLE__ | |
362 | + {"mac", FSOUND_OUTPUT_MAC}, | |
363 | +#endif | |
364 | +#if 0 | |
365 | + {"xbox", FSOUND_OUTPUT_XBOX}, | |
366 | + {"ps2", FSOUND_OUTPUT_PS2}, | |
367 | + {"gcube", FSOUND_OUTPUT_GC}, | |
368 | +#endif | |
369 | + {"nort", FSOUND_OUTPUT_NOSOUND_NONREALTIME} | |
370 | +}; | |
371 | + | |
372 | +static void *fmod_audio_init (void) | |
373 | +{ | |
374 | + int i; | |
375 | + double ver; | |
376 | + int status; | |
377 | + int output_type = -1; | |
378 | + const char *drv = audio_get_conf_str (QC_FMOD_DRV, NULL); | |
379 | + | |
380 | + ver = FSOUND_GetVersion (); | |
381 | + if (ver < FMOD_VERSION) { | |
382 | + dolog ("Wrong FMOD version %f, need at least %f\n", ver, FMOD_VERSION); | |
383 | + return NULL; | |
384 | + } | |
385 | + | |
386 | + if (drv) { | |
387 | + int found = 0; | |
388 | + for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) { | |
389 | + if (!strcmp (drv, drvtab[i].name)) { | |
390 | + output_type = drvtab[i].type; | |
391 | + found = 1; | |
392 | + break; | |
393 | + } | |
394 | + } | |
395 | + if (!found) { | |
396 | + dolog ("Unknown FMOD output driver `%s'\n", drv); | |
397 | + } | |
398 | + } | |
399 | + | |
400 | + if (output_type != -1) { | |
401 | + status = FSOUND_SetOutput (output_type); | |
402 | + if (!status) { | |
403 | + dolog ("FSOUND_SetOutput(%d) failed\nReason: %s\n", | |
404 | + output_type, errstr ()); | |
405 | + return NULL; | |
406 | + } | |
407 | + } | |
408 | + | |
409 | + conf.freq = audio_get_conf_int (QC_FMOD_FREQ, conf.freq); | |
410 | + conf.nb_samples = audio_get_conf_int (QC_FMOD_SAMPLES, conf.nb_samples); | |
411 | + conf.nb_channels = | |
412 | + audio_get_conf_int (QC_FMOD_CHANNELS, | |
413 | + (audio_state.nb_hw_voices > 1 | |
414 | + ? audio_state.nb_hw_voices | |
415 | + : conf.nb_channels)); | |
416 | + conf.bufsize = audio_get_conf_int (QC_FMOD_BUFSIZE, conf.bufsize); | |
417 | + conf.threshold = audio_get_conf_int (QC_FMOD_THRESHOLD, conf.threshold); | |
418 | + | |
419 | + if (conf.bufsize) { | |
420 | + status = FSOUND_SetBufferSize (conf.bufsize); | |
421 | + if (!status) { | |
422 | + dolog ("FSOUND_SetBufferSize (%d) failed\nReason: %s\n", | |
423 | + conf.bufsize, errstr ()); | |
424 | + } | |
425 | + } | |
426 | + | |
427 | + status = FSOUND_Init (conf.freq, conf.nb_channels, 0); | |
428 | + if (!status) { | |
429 | + dolog ("FSOUND_Init failed\nReason: %s\n", errstr ()); | |
430 | + return NULL; | |
431 | + } | |
432 | + | |
433 | + return &conf; | |
434 | +} | |
435 | + | |
436 | +static void fmod_audio_fini (void *opaque) | |
437 | +{ | |
438 | + FSOUND_Close (); | |
439 | +} | |
440 | + | |
441 | +struct pcm_ops fmod_pcm_ops = { | |
442 | + fmod_hw_init, | |
443 | + fmod_hw_fini, | |
444 | + fmod_hw_run, | |
445 | + fmod_hw_write, | |
446 | + fmod_hw_ctl | |
447 | +}; | |
448 | + | |
449 | +struct audio_output_driver fmod_output_driver = { | |
450 | + "fmod", | |
451 | + fmod_audio_init, | |
452 | + fmod_audio_fini, | |
453 | + &fmod_pcm_ops, | |
454 | + 1, | |
455 | + INT_MAX, | |
456 | + sizeof (FMODVoice) | |
457 | +}; | ... | ... |
audio/fmodaudio.h
0 → 100644
1 | +/* | |
2 | + * QEMU FMOD audio output driver header | |
3 | + * | |
4 | + * Copyright (c) 2004 Vassili Karpov (malc) | |
5 | + * | |
6 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | + * of this software and associated documentation files (the "Software"), to deal | |
8 | + * in the Software without restriction, including without limitation the rights | |
9 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | + * copies of the Software, and to permit persons to whom the Software is | |
11 | + * furnished to do so, subject to the following conditions: | |
12 | + * | |
13 | + * The above copyright notice and this permission notice shall be included in | |
14 | + * all copies or substantial portions of the Software. | |
15 | + * | |
16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | + * THE SOFTWARE. | |
23 | + */ | |
24 | +#ifndef QEMU_FMODAUDIO_H | |
25 | +#define QEMU_FMODAUDIO_H | |
26 | + | |
27 | +#include <fmod.h> | |
28 | + | |
29 | +typedef struct FMODVoice { | |
30 | + struct HWVoice hw; | |
31 | + unsigned int old_pos; | |
32 | + FSOUND_SAMPLE *fmod_sample; | |
33 | + int channel; | |
34 | +} FMODVoice; | |
35 | + | |
36 | +extern struct pcm_ops fmod_pcm_ops; | |
37 | +extern struct audio_output_driver fmod_output_driver; | |
38 | + | |
39 | +#endif /* fmodaudio.h */ | ... | ... |
audio/mixeng.c
0 → 100644
1 | +/* | |
2 | + * QEMU Mixing engine | |
3 | + * | |
4 | + * Copyright (c) 2004 Vassili Karpov (malc) | |
5 | + * Copyright (c) 1998 Fabrice Bellard | |
6 | + * | |
7 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
8 | + * of this software and associated documentation files (the "Software"), to deal | |
9 | + * in the Software without restriction, including without limitation the rights | |
10 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
11 | + * copies of the Software, and to permit persons to whom the Software is | |
12 | + * furnished to do so, subject to the following conditions: | |
13 | + * | |
14 | + * The above copyright notice and this permission notice shall be included in | |
15 | + * all copies or substantial portions of the Software. | |
16 | + * | |
17 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
18 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
19 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
20 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
21 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
22 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
23 | + * THE SOFTWARE. | |
24 | + */ | |
25 | +#include "vl.h" | |
26 | +//#define DEBUG_FP | |
27 | +#include "audio/mixeng.h" | |
28 | + | |
29 | +#define IN_T int8_t | |
30 | +#define IN_MIN CHAR_MIN | |
31 | +#define IN_MAX CHAR_MAX | |
32 | +#define SIGNED | |
33 | +#include "mixeng_template.h" | |
34 | +#undef SIGNED | |
35 | +#undef IN_MAX | |
36 | +#undef IN_MIN | |
37 | +#undef IN_T | |
38 | + | |
39 | +#define IN_T uint8_t | |
40 | +#define IN_MIN 0 | |
41 | +#define IN_MAX UCHAR_MAX | |
42 | +#include "mixeng_template.h" | |
43 | +#undef IN_MAX | |
44 | +#undef IN_MIN | |
45 | +#undef IN_T | |
46 | + | |
47 | +#define IN_T int16_t | |
48 | +#define IN_MIN SHRT_MIN | |
49 | +#define IN_MAX SHRT_MAX | |
50 | +#define SIGNED | |
51 | +#include "mixeng_template.h" | |
52 | +#undef SIGNED | |
53 | +#undef IN_MAX | |
54 | +#undef IN_MIN | |
55 | +#undef IN_T | |
56 | + | |
57 | +#define IN_T uint16_t | |
58 | +#define IN_MIN 0 | |
59 | +#define IN_MAX USHRT_MAX | |
60 | +#include "mixeng_template.h" | |
61 | +#undef IN_MAX | |
62 | +#undef IN_MIN | |
63 | +#undef IN_T | |
64 | + | |
65 | +t_sample *mixeng_conv[2][2][2] = { | |
66 | + { | |
67 | + { | |
68 | + conv_uint8_t_to_mono, | |
69 | + conv_uint16_t_to_mono | |
70 | + }, | |
71 | + { | |
72 | + conv_int8_t_to_mono, | |
73 | + conv_int16_t_to_mono | |
74 | + } | |
75 | + }, | |
76 | + { | |
77 | + { | |
78 | + conv_uint8_t_to_stereo, | |
79 | + conv_uint16_t_to_stereo | |
80 | + }, | |
81 | + { | |
82 | + conv_int8_t_to_stereo, | |
83 | + conv_int16_t_to_stereo | |
84 | + } | |
85 | + } | |
86 | +}; | |
87 | + | |
88 | +f_sample *mixeng_clip[2][2][2] = { | |
89 | + { | |
90 | + { | |
91 | + clip_uint8_t_from_mono, | |
92 | + clip_uint16_t_from_mono | |
93 | + }, | |
94 | + { | |
95 | + clip_int8_t_from_mono, | |
96 | + clip_int16_t_from_mono | |
97 | + } | |
98 | + }, | |
99 | + { | |
100 | + { | |
101 | + clip_uint8_t_from_stereo, | |
102 | + clip_uint16_t_from_stereo | |
103 | + }, | |
104 | + { | |
105 | + clip_int8_t_from_stereo, | |
106 | + clip_int16_t_from_stereo | |
107 | + } | |
108 | + } | |
109 | +}; | |
110 | + | |
111 | +/* | |
112 | + * August 21, 1998 | |
113 | + * Copyright 1998 Fabrice Bellard. | |
114 | + * | |
115 | + * [Rewrote completly the code of Lance Norskog And Sundry | |
116 | + * Contributors with a more efficient algorithm.] | |
117 | + * | |
118 | + * This source code is freely redistributable and may be used for | |
119 | + * any purpose. This copyright notice must be maintained. | |
120 | + * Lance Norskog And Sundry Contributors are not responsible for | |
121 | + * the consequences of using this software. | |
122 | + */ | |
123 | + | |
124 | +/* | |
125 | + * Sound Tools rate change effect file. | |
126 | + */ | |
127 | +/* | |
128 | + * Linear Interpolation. | |
129 | + * | |
130 | + * The use of fractional increment allows us to use no buffer. It | |
131 | + * avoid the problems at the end of the buffer we had with the old | |
132 | + * method which stored a possibly big buffer of size | |
133 | + * lcm(in_rate,out_rate). | |
134 | + * | |
135 | + * Limited to 16 bit samples and sampling frequency <= 65535 Hz. If | |
136 | + * the input & output frequencies are equal, a delay of one sample is | |
137 | + * introduced. Limited to processing 32-bit count worth of samples. | |
138 | + * | |
139 | + * 1 << FRAC_BITS evaluating to zero in several places. Changed with | |
140 | + * an (unsigned long) cast to make it safe. MarkMLl 2/1/99 | |
141 | + */ | |
142 | + | |
143 | +/* Private data */ | |
144 | +typedef struct ratestuff { | |
145 | + uint64_t opos; | |
146 | + uint64_t opos_inc; | |
147 | + uint32_t ipos; /* position in the input stream (integer) */ | |
148 | + st_sample_t ilast; /* last sample in the input stream */ | |
149 | +} *rate_t; | |
150 | + | |
151 | +/* | |
152 | + * Prepare processing. | |
153 | + */ | |
154 | +void *st_rate_start (int inrate, int outrate) | |
155 | +{ | |
156 | + rate_t rate = (rate_t) qemu_mallocz (sizeof (struct ratestuff)); | |
157 | + | |
158 | + if (!rate) { | |
159 | + exit (EXIT_FAILURE); | |
160 | + } | |
161 | + | |
162 | + if (inrate == outrate) { | |
163 | + // exit (EXIT_FAILURE); | |
164 | + } | |
165 | + | |
166 | + if (inrate >= 65535 || outrate >= 65535) { | |
167 | + // exit (EXIT_FAILURE); | |
168 | + } | |
169 | + | |
170 | + rate->opos = 0; | |
171 | + | |
172 | + /* increment */ | |
173 | + rate->opos_inc = (inrate * ((int64_t) UINT_MAX)) / outrate; | |
174 | + | |
175 | + rate->ipos = 0; | |
176 | + rate->ilast.l = 0; | |
177 | + rate->ilast.r = 0; | |
178 | + return rate; | |
179 | +} | |
180 | + | |
181 | +/* | |
182 | + * Processed signed long samples from ibuf to obuf. | |
183 | + * Return number of samples processed. | |
184 | + */ | |
185 | +void st_rate_flow (void *opaque, st_sample_t *ibuf, st_sample_t *obuf, | |
186 | + int *isamp, int *osamp) | |
187 | +{ | |
188 | + rate_t rate = (rate_t) opaque; | |
189 | + st_sample_t *istart, *iend; | |
190 | + st_sample_t *ostart, *oend; | |
191 | + st_sample_t ilast, icur, out; | |
192 | + int64_t t; | |
193 | + | |
194 | + ilast = rate->ilast; | |
195 | + | |
196 | + istart = ibuf; | |
197 | + iend = ibuf + *isamp; | |
198 | + | |
199 | + ostart = obuf; | |
200 | + oend = obuf + *osamp; | |
201 | + | |
202 | + if (rate->opos_inc == 1ULL << 32) { | |
203 | + int i, n = *isamp > *osamp ? *osamp : *isamp; | |
204 | + for (i = 0; i < n; i++) { | |
205 | + obuf[i].l += ibuf[i].r; | |
206 | + obuf[i].r += ibuf[i].r; | |
207 | + } | |
208 | + *isamp = n; | |
209 | + *osamp = n; | |
210 | + return; | |
211 | + } | |
212 | + | |
213 | + while (obuf < oend) { | |
214 | + | |
215 | + /* Safety catch to make sure we have input samples. */ | |
216 | + if (ibuf >= iend) | |
217 | + break; | |
218 | + | |
219 | + /* read as many input samples so that ipos > opos */ | |
220 | + | |
221 | + while (rate->ipos <= (rate->opos >> 32)) { | |
222 | + ilast = *ibuf++; | |
223 | + rate->ipos++; | |
224 | + /* See if we finished the input buffer yet */ | |
225 | + if (ibuf >= iend) goto the_end; | |
226 | + } | |
227 | + | |
228 | + icur = *ibuf; | |
229 | + | |
230 | + /* interpolate */ | |
231 | + t = rate->opos & 0xffffffff; | |
232 | + out.l = (ilast.l * (INT_MAX - t) + icur.l * t) / INT_MAX; | |
233 | + out.r = (ilast.r * (INT_MAX - t) + icur.r * t) / INT_MAX; | |
234 | + | |
235 | + /* output sample & increment position */ | |
236 | +#if 0 | |
237 | + *obuf++ = out; | |
238 | +#else | |
239 | + obuf->l += out.l; | |
240 | + obuf->r += out.r; | |
241 | + obuf += 1; | |
242 | +#endif | |
243 | + rate->opos += rate->opos_inc; | |
244 | + } | |
245 | + | |
246 | +the_end: | |
247 | + *isamp = ibuf - istart; | |
248 | + *osamp = obuf - ostart; | |
249 | + rate->ilast = ilast; | |
250 | +} | |
251 | + | |
252 | +void st_rate_stop (void *opaque) | |
253 | +{ | |
254 | + qemu_free (opaque); | |
255 | +} | ... | ... |
audio/mixeng.h
0 → 100644
1 | +/* | |
2 | + * QEMU Mixing engine header | |
3 | + * | |
4 | + * Copyright (c) 2004 Vassili Karpov (malc) | |
5 | + * | |
6 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | + * of this software and associated documentation files (the "Software"), to deal | |
8 | + * in the Software without restriction, including without limitation the rights | |
9 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | + * copies of the Software, and to permit persons to whom the Software is | |
11 | + * furnished to do so, subject to the following conditions: | |
12 | + * | |
13 | + * The above copyright notice and this permission notice shall be included in | |
14 | + * all copies or substantial portions of the Software. | |
15 | + * | |
16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | + * THE SOFTWARE. | |
23 | + */ | |
24 | +#ifndef QEMU_MIXENG_H | |
25 | +#define QEMU_MIXENG_H | |
26 | + | |
27 | +typedef void (t_sample) (void *dst, const void *src, int samples); | |
28 | +typedef void (f_sample) (void *dst, const void *src, int samples); | |
29 | +typedef struct { int64_t l; int64_t r; } st_sample_t; | |
30 | + | |
31 | +extern t_sample *mixeng_conv[2][2][2]; | |
32 | +extern f_sample *mixeng_clip[2][2][2]; | |
33 | + | |
34 | +void *st_rate_start (int inrate, int outrate); | |
35 | +void st_rate_flow (void *opaque, st_sample_t *ibuf, st_sample_t *obuf, | |
36 | + int *isamp, int *osamp); | |
37 | +void st_rate_stop (void *opaque); | |
38 | + | |
39 | +#endif /* mixeng.h */ | ... | ... |
audio/mixeng_template.h
0 → 100644
1 | +/* | |
2 | + * QEMU Mixing engine | |
3 | + * | |
4 | + * Copyright (c) 2004 Vassili Karpov (malc) | |
5 | + * | |
6 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | + * of this software and associated documentation files (the "Software"), to deal | |
8 | + * in the Software without restriction, including without limitation the rights | |
9 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | + * copies of the Software, and to permit persons to whom the Software is | |
11 | + * furnished to do so, subject to the following conditions: | |
12 | + * | |
13 | + * The above copyright notice and this permission notice shall be included in | |
14 | + * all copies or substantial portions of the Software. | |
15 | + * | |
16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | + * THE SOFTWARE. | |
23 | + */ | |
24 | + | |
25 | +/* | |
26 | + * Tusen tack till Mike Nordell | |
27 | + * dec++'ified by Dscho | |
28 | + */ | |
29 | + | |
30 | +#ifdef SIGNED | |
31 | +#define HALFT IN_MAX | |
32 | +#define HALF IN_MAX | |
33 | +#else | |
34 | +#define HALFT ((IN_MAX)>>1) | |
35 | +#define HALF HALFT | |
36 | +#endif | |
37 | + | |
38 | +static int64_t inline glue(conv_,IN_T) (IN_T v) | |
39 | +{ | |
40 | +#ifdef SIGNED | |
41 | + return (INT_MAX*(int64_t)v)/HALF; | |
42 | +#else | |
43 | + return (INT_MAX*((int64_t)v-HALFT))/HALF; | |
44 | +#endif | |
45 | +} | |
46 | + | |
47 | +static IN_T inline glue(clip_,IN_T) (int64_t v) | |
48 | +{ | |
49 | + if (v >= INT_MAX) | |
50 | + return IN_MAX; | |
51 | + else if (v < -INT_MAX) | |
52 | + return IN_MIN; | |
53 | + | |
54 | +#ifdef SIGNED | |
55 | + return (IN_T) (v*HALF/INT_MAX); | |
56 | +#else | |
57 | + return (IN_T) (v+INT_MAX/2)*HALF/INT_MAX; | |
58 | +#endif | |
59 | +} | |
60 | + | |
61 | +static void glue(glue(conv_,IN_T),_to_stereo) (void *dst, const void *src, | |
62 | + int samples) | |
63 | +{ | |
64 | + st_sample_t *out = (st_sample_t *) dst; | |
65 | + IN_T *in = (IN_T *) src; | |
66 | + while (samples--) { | |
67 | + out->l = glue(conv_,IN_T) (*in++); | |
68 | + out->r = glue(conv_,IN_T) (*in++); | |
69 | + out += 1; | |
70 | + } | |
71 | +} | |
72 | + | |
73 | +static void glue(glue(conv_,IN_T),_to_mono) (void *dst, const void *src, | |
74 | + int samples) | |
75 | +{ | |
76 | + st_sample_t *out = (st_sample_t *) dst; | |
77 | + IN_T *in = (IN_T *) src; | |
78 | + while (samples--) { | |
79 | + out->l = glue(conv_,IN_T) (in[0]); | |
80 | + out->r = out->l; | |
81 | + out += 1; | |
82 | + in += 1; | |
83 | + } | |
84 | +} | |
85 | + | |
86 | +static void glue(glue(clip_,IN_T),_from_stereo) (void *dst, const void *src, | |
87 | + int samples) | |
88 | +{ | |
89 | + st_sample_t *in = (st_sample_t *) src; | |
90 | + IN_T *out = (IN_T *) dst; | |
91 | + while (samples--) { | |
92 | + *out++ = glue(clip_,IN_T) (in->l); | |
93 | + *out++ = glue(clip_,IN_T) (in->r); | |
94 | + in += 1; | |
95 | + } | |
96 | +} | |
97 | + | |
98 | +static void glue(glue(clip_,IN_T),_from_mono) (void *dst, const void *src, | |
99 | + int samples) | |
100 | +{ | |
101 | + st_sample_t *in = (st_sample_t *) src; | |
102 | + IN_T *out = (IN_T *) dst; | |
103 | + while (samples--) { | |
104 | + *out++ = glue(clip_,IN_T) (in->l + in->r); | |
105 | + in += 1; | |
106 | + } | |
107 | +} | |
108 | + | |
109 | +#undef HALF | |
110 | +#undef HALFT | |
111 | + | ... | ... |
audio/ossaudio.c
0 → 100644
1 | +/* | |
2 | + * QEMU OSS audio output driver | |
3 | + * | |
4 | + * Copyright (c) 2003-2004 Vassili Karpov (malc) | |
5 | + * | |
6 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | + * of this software and associated documentation files (the "Software"), to deal | |
8 | + * in the Software without restriction, including without limitation the rights | |
9 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | + * copies of the Software, and to permit persons to whom the Software is | |
11 | + * furnished to do so, subject to the following conditions: | |
12 | + * | |
13 | + * The above copyright notice and this permission notice shall be included in | |
14 | + * all copies or substantial portions of the Software. | |
15 | + * | |
16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | + * THE SOFTWARE. | |
23 | + */ | |
24 | + | |
25 | +/* Temporary kludge */ | |
26 | +#if defined __linux__ || (defined _BSD && !defined __APPLE__) | |
27 | +#include <assert.h> | |
28 | +#include "vl.h" | |
29 | + | |
30 | +#include <sys/mman.h> | |
31 | +#include <sys/types.h> | |
32 | +#include <sys/ioctl.h> | |
33 | +#include <sys/soundcard.h> | |
34 | + | |
35 | +#define AUDIO_CAP "oss" | |
36 | +#include "audio/audio.h" | |
37 | +#include "audio/ossaudio.h" | |
38 | + | |
39 | +#define QC_OSS_FRAGSIZE "QEMU_OSS_FRAGSIZE" | |
40 | +#define QC_OSS_NFRAGS "QEMU_OSS_NFRAGS" | |
41 | +#define QC_OSS_MMAP "QEMU_OSS_MMAP" | |
42 | +#define QC_OSS_DEV "QEMU_OSS_DEV" | |
43 | + | |
44 | +#define errstr() strerror (errno) | |
45 | + | |
46 | +static struct { | |
47 | + int try_mmap; | |
48 | + int nfrags; | |
49 | + int fragsize; | |
50 | + const char *dspname; | |
51 | +} conf = { | |
52 | + .try_mmap = 0, | |
53 | + .nfrags = 4, | |
54 | + .fragsize = 4096, | |
55 | + .dspname = "/dev/dsp" | |
56 | +}; | |
57 | + | |
58 | +struct oss_params { | |
59 | + int freq; | |
60 | + audfmt_e fmt; | |
61 | + int nchannels; | |
62 | + int nfrags; | |
63 | + int fragsize; | |
64 | +}; | |
65 | + | |
66 | +static int oss_hw_write (SWVoice *sw, void *buf, int len) | |
67 | +{ | |
68 | + return pcm_hw_write (sw, buf, len); | |
69 | +} | |
70 | + | |
71 | +static int AUD_to_ossfmt (audfmt_e fmt) | |
72 | +{ | |
73 | + switch (fmt) { | |
74 | + case AUD_FMT_S8: return AFMT_S8; | |
75 | + case AUD_FMT_U8: return AFMT_U8; | |
76 | + case AUD_FMT_S16: return AFMT_S16_LE; | |
77 | + case AUD_FMT_U16: return AFMT_U16_LE; | |
78 | + default: | |
79 | + dolog ("Internal logic error: Bad audio format %d\nAborting\n", fmt); | |
80 | + exit (EXIT_FAILURE); | |
81 | + } | |
82 | +} | |
83 | + | |
84 | +static int oss_to_audfmt (int fmt) | |
85 | +{ | |
86 | + switch (fmt) { | |
87 | + case AFMT_S8: return AUD_FMT_S8; | |
88 | + case AFMT_U8: return AUD_FMT_U8; | |
89 | + case AFMT_S16_LE: return AUD_FMT_S16; | |
90 | + case AFMT_U16_LE: return AUD_FMT_U16; | |
91 | + default: | |
92 | + dolog ("Internal logic error: Unrecognized OSS audio format %d\n" | |
93 | + "Aborting\n", | |
94 | + fmt); | |
95 | + exit (EXIT_FAILURE); | |
96 | + } | |
97 | +} | |
98 | + | |
99 | +#ifdef DEBUG_PCM | |
100 | +static void oss_dump_pcm_info (struct oss_params *req, struct oss_params *obt) | |
101 | +{ | |
102 | + dolog ("parameter | requested value | obtained value\n"); | |
103 | + dolog ("format | %10d | %10d\n", req->fmt, obt->fmt); | |
104 | + dolog ("channels | %10d | %10d\n", req->nchannels, obt->nchannels); | |
105 | + dolog ("frequency | %10d | %10d\n", req->freq, obt->freq); | |
106 | + dolog ("nfrags | %10d | %10d\n", req->nfrags, obt->nfrags); | |
107 | + dolog ("fragsize | %10d | %10d\n", req->fragsize, obt->fragsize); | |
108 | +} | |
109 | +#endif | |
110 | + | |
111 | +static int oss_open (struct oss_params *req, struct oss_params *obt, int *pfd) | |
112 | +{ | |
113 | + int fd; | |
114 | + int mmmmssss; | |
115 | + audio_buf_info abinfo; | |
116 | + int fmt, freq, nchannels; | |
117 | + const char *dspname = conf.dspname; | |
118 | + | |
119 | + fd = open (dspname, O_RDWR | O_NONBLOCK); | |
120 | + if (-1 == fd) { | |
121 | + dolog ("Could not initialize audio hardware. Failed to open `%s':\n" | |
122 | + "Reason:%s\n", | |
123 | + dspname, | |
124 | + errstr ()); | |
125 | + return -1; | |
126 | + } | |
127 | + | |
128 | + freq = req->freq; | |
129 | + nchannels = req->nchannels; | |
130 | + fmt = req->fmt; | |
131 | + | |
132 | + if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) { | |
133 | + dolog ("Could not initialize audio hardware\n" | |
134 | + "Failed to set sample size\n" | |
135 | + "Reason: %s\n", | |
136 | + errstr ()); | |
137 | + goto err; | |
138 | + } | |
139 | + | |
140 | + if (ioctl (fd, SNDCTL_DSP_CHANNELS, &nchannels)) { | |
141 | + dolog ("Could not initialize audio hardware\n" | |
142 | + "Failed to set number of channels\n" | |
143 | + "Reason: %s\n", | |
144 | + errstr ()); | |
145 | + goto err; | |
146 | + } | |
147 | + | |
148 | + if (ioctl (fd, SNDCTL_DSP_SPEED, &freq)) { | |
149 | + dolog ("Could not initialize audio hardware\n" | |
150 | + "Failed to set frequency\n" | |
151 | + "Reason: %s\n", | |
152 | + errstr ()); | |
153 | + goto err; | |
154 | + } | |
155 | + | |
156 | + if (ioctl (fd, SNDCTL_DSP_NONBLOCK)) { | |
157 | + dolog ("Could not initialize audio hardware\n" | |
158 | + "Failed to set non-blocking mode\n" | |
159 | + "Reason: %s\n", | |
160 | + errstr ()); | |
161 | + goto err; | |
162 | + } | |
163 | + | |
164 | + mmmmssss = (req->nfrags << 16) | lsbindex (req->fragsize); | |
165 | + if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss)) { | |
166 | + dolog ("Could not initialize audio hardware\n" | |
167 | + "Failed to set buffer length (%d, %d)\n" | |
168 | + "Reason:%s\n", | |
169 | + conf.nfrags, conf.fragsize, | |
170 | + errstr ()); | |
171 | + goto err; | |
172 | + } | |
173 | + | |
174 | + if (ioctl (fd, SNDCTL_DSP_GETOSPACE, &abinfo)) { | |
175 | + dolog ("Could not initialize audio hardware\n" | |
176 | + "Failed to get buffer length\n" | |
177 | + "Reason:%s\n", | |
178 | + errstr ()); | |
179 | + goto err; | |
180 | + } | |
181 | + | |
182 | + obt->fmt = fmt; | |
183 | + obt->nchannels = nchannels; | |
184 | + obt->freq = freq; | |
185 | + obt->nfrags = abinfo.fragstotal; | |
186 | + obt->fragsize = abinfo.fragsize; | |
187 | + *pfd = fd; | |
188 | + | |
189 | + if ((req->fmt != obt->fmt) || | |
190 | + (req->nchannels != obt->nchannels) || | |
191 | + (req->freq != obt->freq) || | |
192 | + (req->fragsize != obt->fragsize) || | |
193 | + (req->nfrags != obt->nfrags)) { | |
194 | +#ifdef DEBUG_PCM | |
195 | + dolog ("Audio parameters mismatch\n"); | |
196 | + oss_dump_pcm_info (req, obt); | |
197 | +#endif | |
198 | + } | |
199 | + | |
200 | +#ifdef DEBUG_PCM | |
201 | + oss_dump_pcm_info (req, obt); | |
202 | +#endif | |
203 | + return 0; | |
204 | + | |
205 | +err: | |
206 | + close (fd); | |
207 | + return -1; | |
208 | +} | |
209 | + | |
210 | +static void oss_hw_run (HWVoice *hw) | |
211 | +{ | |
212 | + OSSVoice *oss = (OSSVoice *) hw; | |
213 | + int err, rpos, live, decr; | |
214 | + int samples; | |
215 | + uint8_t *dst; | |
216 | + st_sample_t *src; | |
217 | + struct audio_buf_info abinfo; | |
218 | + struct count_info cntinfo; | |
219 | + | |
220 | + live = pcm_hw_get_live (hw); | |
221 | + if (live <= 0) | |
222 | + return; | |
223 | + | |
224 | + if (oss->mmapped) { | |
225 | + int bytes; | |
226 | + | |
227 | + err = ioctl (oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo); | |
228 | + if (err < 0) { | |
229 | + dolog ("SNDCTL_DSP_GETOPTR failed\nReason: %s\n", errstr ()); | |
230 | + return; | |
231 | + } | |
232 | + | |
233 | + if (cntinfo.ptr == oss->old_optr) { | |
234 | + if (abs (hw->samples - live) < 64) | |
235 | + dolog ("overrun\n"); | |
236 | + return; | |
237 | + } | |
238 | + | |
239 | + if (cntinfo.ptr > oss->old_optr) { | |
240 | + bytes = cntinfo.ptr - oss->old_optr; | |
241 | + } | |
242 | + else { | |
243 | + bytes = hw->bufsize + cntinfo.ptr - oss->old_optr; | |
244 | + } | |
245 | + | |
246 | + decr = audio_MIN (bytes >> hw->shift, live); | |
247 | + } | |
248 | + else { | |
249 | + err = ioctl (oss->fd, SNDCTL_DSP_GETOSPACE, &abinfo); | |
250 | + if (err < 0) { | |
251 | + dolog ("SNDCTL_DSP_GETOSPACE failed\nReason: %s\n", errstr ()); | |
252 | + return; | |
253 | + } | |
254 | + | |
255 | + decr = audio_MIN (abinfo.bytes >> hw->shift, live); | |
256 | + if (decr <= 0) | |
257 | + return; | |
258 | + } | |
259 | + | |
260 | + samples = decr; | |
261 | + rpos = hw->rpos; | |
262 | + while (samples) { | |
263 | + int left_till_end_samples = hw->samples - rpos; | |
264 | + int convert_samples = audio_MIN (samples, left_till_end_samples); | |
265 | + | |
266 | + src = advance (hw->mix_buf, rpos * sizeof (st_sample_t)); | |
267 | + dst = advance (oss->pcm_buf, rpos << hw->shift); | |
268 | + | |
269 | + hw->clip (dst, src, convert_samples); | |
270 | + if (!oss->mmapped) { | |
271 | + int written; | |
272 | + | |
273 | + written = write (oss->fd, dst, convert_samples << hw->shift); | |
274 | + /* XXX: follow errno recommendations ? */ | |
275 | + if (written == -1) { | |
276 | + dolog ("Failed to write audio\nReason: %s\n", errstr ()); | |
277 | + continue; | |
278 | + } | |
279 | + | |
280 | + if (written != convert_samples << hw->shift) { | |
281 | + int wsamples = written >> hw->shift; | |
282 | + int wbytes = wsamples << hw->shift; | |
283 | + if (wbytes != written) { | |
284 | + dolog ("Unaligned write %d, %d\n", wbytes, written); | |
285 | + } | |
286 | + memset (src, 0, wbytes); | |
287 | + decr -= samples; | |
288 | + rpos = (rpos + wsamples) % hw->samples; | |
289 | + break; | |
290 | + } | |
291 | + } | |
292 | + memset (src, 0, convert_samples * sizeof (st_sample_t)); | |
293 | + | |
294 | + rpos = (rpos + convert_samples) % hw->samples; | |
295 | + samples -= convert_samples; | |
296 | + } | |
297 | + if (oss->mmapped) { | |
298 | + oss->old_optr = cntinfo.ptr; | |
299 | + } | |
300 | + | |
301 | + pcm_hw_dec_live (hw, decr); | |
302 | + hw->rpos = rpos; | |
303 | +} | |
304 | + | |
305 | +static void oss_hw_fini (HWVoice *hw) | |
306 | +{ | |
307 | + int err; | |
308 | + OSSVoice *oss = (OSSVoice *) hw; | |
309 | + | |
310 | + ldebug ("oss_hw_fini\n"); | |
311 | + err = close (oss->fd); | |
312 | + if (err) { | |
313 | + dolog ("Failed to close OSS descriptor\nReason: %s\n", errstr ()); | |
314 | + } | |
315 | + oss->fd = -1; | |
316 | + | |
317 | + if (oss->pcm_buf) { | |
318 | + if (oss->mmapped) { | |
319 | + err = munmap (oss->pcm_buf, hw->bufsize); | |
320 | + if (err) { | |
321 | + dolog ("Failed to unmap OSS buffer\nReason: %s\n", | |
322 | + errstr ()); | |
323 | + } | |
324 | + } | |
325 | + else { | |
326 | + qemu_free (oss->pcm_buf); | |
327 | + } | |
328 | + oss->pcm_buf = NULL; | |
329 | + } | |
330 | +} | |
331 | + | |
332 | +static int oss_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt) | |
333 | +{ | |
334 | + OSSVoice *oss = (OSSVoice *) hw; | |
335 | + struct oss_params req, obt; | |
336 | + | |
337 | + assert (!oss->fd); | |
338 | + req.fmt = AUD_to_ossfmt (fmt); | |
339 | + req.freq = freq; | |
340 | + req.nchannels = nchannels; | |
341 | + req.fragsize = conf.fragsize; | |
342 | + req.nfrags = conf.nfrags; | |
343 | + | |
344 | + if (oss_open (&req, &obt, &oss->fd)) | |
345 | + return -1; | |
346 | + | |
347 | + hw->freq = obt.freq; | |
348 | + hw->fmt = oss_to_audfmt (obt.fmt); | |
349 | + hw->nchannels = obt.nchannels; | |
350 | + | |
351 | + oss->nfrags = obt.nfrags; | |
352 | + oss->fragsize = obt.fragsize; | |
353 | + hw->bufsize = obt.nfrags * obt.fragsize; | |
354 | + | |
355 | + oss->mmapped = 0; | |
356 | + if (conf.try_mmap) { | |
357 | + oss->pcm_buf = mmap (0, hw->bufsize, PROT_READ | PROT_WRITE, | |
358 | + MAP_SHARED, oss->fd, 0); | |
359 | + if (oss->pcm_buf == MAP_FAILED) { | |
360 | + dolog ("Failed to mmap OSS device\nReason: %s\n", | |
361 | + errstr ()); | |
362 | + } | |
363 | + | |
364 | + for (;;) { | |
365 | + int err; | |
366 | + int trig = 0; | |
367 | + if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { | |
368 | + dolog ("SNDCTL_DSP_SETTRIGGER 0 failed\nReason: %s\n", | |
369 | + errstr ()); | |
370 | + goto fail; | |
371 | + } | |
372 | + | |
373 | + trig = PCM_ENABLE_OUTPUT; | |
374 | + if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { | |
375 | + dolog ("SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n" | |
376 | + "Reason: %s\n", errstr ()); | |
377 | + goto fail; | |
378 | + } | |
379 | + oss->mmapped = 1; | |
380 | + break; | |
381 | + | |
382 | + fail: | |
383 | + err = munmap (oss->pcm_buf, hw->bufsize); | |
384 | + if (err) { | |
385 | + dolog ("Failed to unmap OSS device\nReason: %s\n", | |
386 | + errstr ()); | |
387 | + } | |
388 | + } | |
389 | + } | |
390 | + | |
391 | + if (!oss->mmapped) { | |
392 | + oss->pcm_buf = qemu_mallocz (hw->bufsize); | |
393 | + if (!oss->pcm_buf) { | |
394 | + close (oss->fd); | |
395 | + oss->fd = -1; | |
396 | + return -1; | |
397 | + } | |
398 | + } | |
399 | + | |
400 | + return 0; | |
401 | +} | |
402 | + | |
403 | +static int oss_hw_ctl (HWVoice *hw, int cmd, ...) | |
404 | +{ | |
405 | + int trig; | |
406 | + OSSVoice *oss = (OSSVoice *) hw; | |
407 | + | |
408 | + if (!oss->mmapped) | |
409 | + return 0; | |
410 | + | |
411 | + switch (cmd) { | |
412 | + case VOICE_ENABLE: | |
413 | + ldebug ("enabling voice\n"); | |
414 | + pcm_hw_clear (hw, oss->pcm_buf, hw->samples); | |
415 | + trig = PCM_ENABLE_OUTPUT; | |
416 | + if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { | |
417 | + dolog ("SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n" | |
418 | + "Reason: %s\n", errstr ()); | |
419 | + return -1; | |
420 | + } | |
421 | + break; | |
422 | + | |
423 | + case VOICE_DISABLE: | |
424 | + ldebug ("disabling voice\n"); | |
425 | + trig = 0; | |
426 | + if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { | |
427 | + dolog ("SNDCTL_DSP_SETTRIGGER 0 failed\nReason: %s\n", | |
428 | + errstr ()); | |
429 | + return -1; | |
430 | + } | |
431 | + break; | |
432 | + } | |
433 | + return 0; | |
434 | +} | |
435 | + | |
436 | +static void *oss_audio_init (void) | |
437 | +{ | |
438 | + conf.fragsize = audio_get_conf_int (QC_OSS_FRAGSIZE, conf.fragsize); | |
439 | + conf.nfrags = audio_get_conf_int (QC_OSS_NFRAGS, conf.nfrags); | |
440 | + conf.try_mmap = audio_get_conf_int (QC_OSS_MMAP, conf.try_mmap); | |
441 | + conf.dspname = audio_get_conf_str (QC_OSS_DEV, conf.dspname); | |
442 | + return &conf; | |
443 | +} | |
444 | + | |
445 | +static void oss_audio_fini (void *opaque) | |
446 | +{ | |
447 | +} | |
448 | + | |
449 | +struct pcm_ops oss_pcm_ops = { | |
450 | + oss_hw_init, | |
451 | + oss_hw_fini, | |
452 | + oss_hw_run, | |
453 | + oss_hw_write, | |
454 | + oss_hw_ctl | |
455 | +}; | |
456 | + | |
457 | +struct audio_output_driver oss_output_driver = { | |
458 | + "oss", | |
459 | + oss_audio_init, | |
460 | + oss_audio_fini, | |
461 | + &oss_pcm_ops, | |
462 | + 1, | |
463 | + INT_MAX, | |
464 | + sizeof (OSSVoice) | |
465 | +}; | |
466 | +#endif | ... | ... |
audio/ossaudio.h
0 → 100644
1 | +/* | |
2 | + * QEMU OSS audio output driver header | |
3 | + * | |
4 | + * Copyright (c) 2003-2004 Vassili Karpov (malc) | |
5 | + * | |
6 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | + * of this software and associated documentation files (the "Software"), to deal | |
8 | + * in the Software without restriction, including without limitation the rights | |
9 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | + * copies of the Software, and to permit persons to whom the Software is | |
11 | + * furnished to do so, subject to the following conditions: | |
12 | + * | |
13 | + * The above copyright notice and this permission notice shall be included in | |
14 | + * all copies or substantial portions of the Software. | |
15 | + * | |
16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | + * THE SOFTWARE. | |
23 | + */ | |
24 | +#ifndef QEMU_OSSAUDIO_H | |
25 | +#define QEMU_OSSAUDIO_H | |
26 | + | |
27 | +typedef struct OSSVoice { | |
28 | + struct HWVoice hw; | |
29 | + void *pcm_buf; | |
30 | + int fd; | |
31 | + int nfrags; | |
32 | + int fragsize; | |
33 | + int mmapped; | |
34 | + int old_optr; | |
35 | +} OSSVoice; | |
36 | + | |
37 | +extern struct pcm_ops oss_pcm_ops; | |
38 | +extern struct audio_output_driver oss_output_driver; | |
39 | + | |
40 | +#endif /* ossaudio.h */ | ... | ... |
audio/sdlaudio.c
0 → 100644
1 | +/* | |
2 | + * QEMU SDL audio output driver | |
3 | + * | |
4 | + * Copyright (c) 2004 Vassili Karpov (malc) | |
5 | + * | |
6 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | + * of this software and associated documentation files (the "Software"), to deal | |
8 | + * in the Software without restriction, including without limitation the rights | |
9 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | + * copies of the Software, and to permit persons to whom the Software is | |
11 | + * furnished to do so, subject to the following conditions: | |
12 | + * | |
13 | + * The above copyright notice and this permission notice shall be included in | |
14 | + * all copies or substantial portions of the Software. | |
15 | + * | |
16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | + * THE SOFTWARE. | |
23 | + */ | |
24 | +#include <SDL/SDL.h> | |
25 | +#include <SDL/SDL_thread.h> | |
26 | +#include "vl.h" | |
27 | + | |
28 | +#define AUDIO_CAP "sdl" | |
29 | +#include "audio/audio.h" | |
30 | +#include "audio/sdlaudio.h" | |
31 | + | |
32 | +#define QC_SDL_SAMPLES "QEMU_SDL_SAMPLES" | |
33 | + | |
34 | +#define errstr() SDL_GetError () | |
35 | + | |
36 | +static struct { | |
37 | + int nb_samples; | |
38 | +} conf = { | |
39 | + 1024 | |
40 | +}; | |
41 | + | |
42 | +struct SDLAudioState { | |
43 | + int exit; | |
44 | + SDL_mutex *mutex; | |
45 | + SDL_sem *sem; | |
46 | + int initialized; | |
47 | +} glob_sdl; | |
48 | +typedef struct SDLAudioState SDLAudioState; | |
49 | + | |
50 | +static void sdl_hw_run (HWVoice *hw) | |
51 | +{ | |
52 | + (void) hw; | |
53 | +} | |
54 | + | |
55 | +static int sdl_lock (SDLAudioState *s) | |
56 | +{ | |
57 | + if (SDL_LockMutex (s->mutex)) { | |
58 | + dolog ("SDL_LockMutex failed\nReason: %s\n", errstr ()); | |
59 | + return -1; | |
60 | + } | |
61 | + return 0; | |
62 | +} | |
63 | + | |
64 | +static int sdl_unlock (SDLAudioState *s) | |
65 | +{ | |
66 | + if (SDL_UnlockMutex (s->mutex)) { | |
67 | + dolog ("SDL_UnlockMutex failed\nReason: %s\n", errstr ()); | |
68 | + return -1; | |
69 | + } | |
70 | + return 0; | |
71 | +} | |
72 | + | |
73 | +static int sdl_post (SDLAudioState *s) | |
74 | +{ | |
75 | + if (SDL_SemPost (s->sem)) { | |
76 | + dolog ("SDL_SemPost failed\nReason: %s\n", errstr ()); | |
77 | + return -1; | |
78 | + } | |
79 | + return 0; | |
80 | +} | |
81 | + | |
82 | +static int sdl_wait (SDLAudioState *s) | |
83 | +{ | |
84 | + if (SDL_SemWait (s->sem)) { | |
85 | + dolog ("SDL_SemWait failed\nReason: %s\n", errstr ()); | |
86 | + return -1; | |
87 | + } | |
88 | + return 0; | |
89 | +} | |
90 | + | |
91 | +static int sdl_unlock_and_post (SDLAudioState *s) | |
92 | +{ | |
93 | + if (sdl_unlock (s)) | |
94 | + return -1; | |
95 | + | |
96 | + return sdl_post (s); | |
97 | +} | |
98 | + | |
99 | +static int sdl_hw_write (SWVoice *sw, void *buf, int len) | |
100 | +{ | |
101 | + int ret; | |
102 | + SDLAudioState *s = &glob_sdl; | |
103 | + sdl_lock (s); | |
104 | + ret = pcm_hw_write (sw, buf, len); | |
105 | + sdl_unlock_and_post (s); | |
106 | + return ret; | |
107 | +} | |
108 | + | |
109 | +static int AUD_to_sdlfmt (audfmt_e fmt, int *shift) | |
110 | +{ | |
111 | + *shift = 0; | |
112 | + switch (fmt) { | |
113 | + case AUD_FMT_S8: return AUDIO_S8; | |
114 | + case AUD_FMT_U8: return AUDIO_U8; | |
115 | + case AUD_FMT_S16: *shift = 1; return AUDIO_S16LSB; | |
116 | + case AUD_FMT_U16: *shift = 1; return AUDIO_U16LSB; | |
117 | + default: | |
118 | + dolog ("Internal logic error: Bad audio format %d\nAborting\n", fmt); | |
119 | + exit (EXIT_FAILURE); | |
120 | + } | |
121 | +} | |
122 | + | |
123 | +static int sdl_to_audfmt (int fmt) | |
124 | +{ | |
125 | + switch (fmt) { | |
126 | + case AUDIO_S8: return AUD_FMT_S8; | |
127 | + case AUDIO_U8: return AUD_FMT_U8; | |
128 | + case AUDIO_S16LSB: return AUD_FMT_S16; | |
129 | + case AUDIO_U16LSB: return AUD_FMT_U16; | |
130 | + default: | |
131 | + dolog ("Internal logic error: Unrecognized SDL audio format %d\n" | |
132 | + "Aborting\n", fmt); | |
133 | + exit (EXIT_FAILURE); | |
134 | + } | |
135 | +} | |
136 | + | |
137 | +static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt) | |
138 | +{ | |
139 | + int status; | |
140 | + | |
141 | + status = SDL_OpenAudio (req, obt); | |
142 | + if (status) { | |
143 | + dolog ("SDL_OpenAudio failed\nReason: %s\n", errstr ()); | |
144 | + } | |
145 | + return status; | |
146 | +} | |
147 | + | |
148 | +static void sdl_close (SDLAudioState *s) | |
149 | +{ | |
150 | + if (s->initialized) { | |
151 | + sdl_lock (s); | |
152 | + s->exit = 1; | |
153 | + sdl_unlock_and_post (s); | |
154 | + SDL_PauseAudio (1); | |
155 | + SDL_CloseAudio (); | |
156 | + s->initialized = 0; | |
157 | + } | |
158 | +} | |
159 | + | |
160 | +static void sdl_callback (void *opaque, Uint8 *buf, int len) | |
161 | +{ | |
162 | + SDLVoice *sdl = opaque; | |
163 | + SDLAudioState *s = &glob_sdl; | |
164 | + HWVoice *hw = &sdl->hw; | |
165 | + int samples = len >> hw->shift; | |
166 | + | |
167 | + if (s->exit) { | |
168 | + return; | |
169 | + } | |
170 | + | |
171 | + while (samples) { | |
172 | + int to_mix, live, decr; | |
173 | + | |
174 | + /* dolog ("in callback samples=%d\n", samples); */ | |
175 | + sdl_wait (s); | |
176 | + if (s->exit) { | |
177 | + return; | |
178 | + } | |
179 | + | |
180 | + sdl_lock (s); | |
181 | + live = pcm_hw_get_live (hw); | |
182 | + if (live <= 0) | |
183 | + goto again; | |
184 | + | |
185 | + /* dolog ("in callback live=%d\n", live); */ | |
186 | + to_mix = audio_MIN (samples, live); | |
187 | + decr = to_mix; | |
188 | + while (to_mix) { | |
189 | + int chunk = audio_MIN (to_mix, hw->samples - hw->rpos); | |
190 | + st_sample_t *src = hw->mix_buf + hw->rpos; | |
191 | + | |
192 | + /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */ | |
193 | + hw->clip (buf, src, chunk); | |
194 | + memset (src, 0, chunk * sizeof (st_sample_t)); | |
195 | + hw->rpos = (hw->rpos + chunk) % hw->samples; | |
196 | + to_mix -= chunk; | |
197 | + buf += chunk << hw->shift; | |
198 | + } | |
199 | + samples -= decr; | |
200 | + pcm_hw_dec_live (hw, decr); | |
201 | + | |
202 | + again: | |
203 | + sdl_unlock (s); | |
204 | + } | |
205 | + /* dolog ("done len=%d\n", len); */ | |
206 | +} | |
207 | + | |
208 | +static void sdl_hw_fini (HWVoice *hw) | |
209 | +{ | |
210 | + ldebug ("sdl_hw_fini %d fixed=%d\n", | |
211 | + glob_sdl.initialized, audio_conf.fixed_format); | |
212 | + sdl_close (&glob_sdl); | |
213 | +} | |
214 | + | |
215 | +static int sdl_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt) | |
216 | +{ | |
217 | + SDLVoice *sdl = (SDLVoice *) hw; | |
218 | + SDLAudioState *s = &glob_sdl; | |
219 | + SDL_AudioSpec req, obt; | |
220 | + int shift; | |
221 | + | |
222 | + ldebug ("sdl_hw_init %d freq=%d fixed=%d\n", | |
223 | + s->initialized, freq, audio_conf.fixed_format); | |
224 | + | |
225 | + if (nchannels != 2) { | |
226 | + dolog ("Bogus channel count %d\n", nchannels); | |
227 | + return -1; | |
228 | + } | |
229 | + | |
230 | + req.freq = freq; | |
231 | + req.format = AUD_to_sdlfmt (fmt, &shift); | |
232 | + req.channels = nchannels; | |
233 | + req.samples = conf.nb_samples; | |
234 | + shift <<= nchannels == 2; | |
235 | + | |
236 | + req.callback = sdl_callback; | |
237 | + req.userdata = sdl; | |
238 | + | |
239 | + if (sdl_open (&req, &obt)) | |
240 | + return -1; | |
241 | + | |
242 | + hw->freq = obt.freq; | |
243 | + hw->fmt = sdl_to_audfmt (obt.format); | |
244 | + hw->nchannels = obt.channels; | |
245 | + hw->bufsize = obt.samples << shift; | |
246 | + | |
247 | + s->initialized = 1; | |
248 | + s->exit = 0; | |
249 | + SDL_PauseAudio (0); | |
250 | + return 0; | |
251 | +} | |
252 | + | |
253 | +static int sdl_hw_ctl (HWVoice *hw, int cmd, ...) | |
254 | +{ | |
255 | + (void) hw; | |
256 | + | |
257 | + switch (cmd) { | |
258 | + case VOICE_ENABLE: | |
259 | + SDL_PauseAudio (0); | |
260 | + break; | |
261 | + | |
262 | + case VOICE_DISABLE: | |
263 | + SDL_PauseAudio (1); | |
264 | + break; | |
265 | + } | |
266 | + return 0; | |
267 | +} | |
268 | + | |
269 | +static void *sdl_audio_init (void) | |
270 | +{ | |
271 | + SDLAudioState *s = &glob_sdl; | |
272 | + conf.nb_samples = audio_get_conf_int (QC_SDL_SAMPLES, conf.nb_samples); | |
273 | + | |
274 | + if (SDL_InitSubSystem (SDL_INIT_AUDIO)) { | |
275 | + dolog ("SDL failed to initialize audio subsystem\nReason: %s\n", | |
276 | + errstr ()); | |
277 | + return NULL; | |
278 | + } | |
279 | + | |
280 | + s->mutex = SDL_CreateMutex (); | |
281 | + if (!s->mutex) { | |
282 | + dolog ("Failed to create SDL mutex\nReason: %s\n", errstr ()); | |
283 | + SDL_QuitSubSystem (SDL_INIT_AUDIO); | |
284 | + return NULL; | |
285 | + } | |
286 | + | |
287 | + s->sem = SDL_CreateSemaphore (0); | |
288 | + if (!s->sem) { | |
289 | + dolog ("Failed to create SDL semaphore\nReason: %s\n", errstr ()); | |
290 | + SDL_DestroyMutex (s->mutex); | |
291 | + SDL_QuitSubSystem (SDL_INIT_AUDIO); | |
292 | + return NULL; | |
293 | + } | |
294 | + | |
295 | + return s; | |
296 | +} | |
297 | + | |
298 | +static void sdl_audio_fini (void *opaque) | |
299 | +{ | |
300 | + SDLAudioState *s = opaque; | |
301 | + sdl_close (s); | |
302 | + SDL_DestroySemaphore (s->sem); | |
303 | + SDL_DestroyMutex (s->mutex); | |
304 | + SDL_QuitSubSystem (SDL_INIT_AUDIO); | |
305 | +} | |
306 | + | |
307 | +struct pcm_ops sdl_pcm_ops = { | |
308 | + sdl_hw_init, | |
309 | + sdl_hw_fini, | |
310 | + sdl_hw_run, | |
311 | + sdl_hw_write, | |
312 | + sdl_hw_ctl | |
313 | +}; | |
314 | + | |
315 | +struct audio_output_driver sdl_output_driver = { | |
316 | + "sdl", | |
317 | + sdl_audio_init, | |
318 | + sdl_audio_fini, | |
319 | + &sdl_pcm_ops, | |
320 | + 1, | |
321 | + 1, | |
322 | + sizeof (SDLVoice) | |
323 | +}; | ... | ... |
audio/sdlaudio.h
0 → 100644
1 | +/* | |
2 | + * QEMU SDL audio output driver header | |
3 | + * | |
4 | + * Copyright (c) 2004 Vassili Karpov (malc) | |
5 | + * | |
6 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | + * of this software and associated documentation files (the "Software"), to deal | |
8 | + * in the Software without restriction, including without limitation the rights | |
9 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | + * copies of the Software, and to permit persons to whom the Software is | |
11 | + * furnished to do so, subject to the following conditions: | |
12 | + * | |
13 | + * The above copyright notice and this permission notice shall be included in | |
14 | + * all copies or substantial portions of the Software. | |
15 | + * | |
16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | + * THE SOFTWARE. | |
23 | + */ | |
24 | +#ifndef QEMU_SDLAUDIO_H | |
25 | +#define QEMU_SDLAUDIO_H | |
26 | + | |
27 | +typedef struct SDLVoice { | |
28 | + struct HWVoice hw; | |
29 | +} SDLVoice; | |
30 | + | |
31 | +extern struct pcm_ops sdl_pcm_ops; | |
32 | +extern struct audio_output_driver sdl_output_driver; | |
33 | + | |
34 | +#endif /* sdlaudio.h */ | ... | ... |
audio/wavaudio.c
0 → 100644
1 | +/* | |
2 | + * QEMU WAV audio output driver | |
3 | + * | |
4 | + * Copyright (c) 2004 Vassili Karpov (malc) | |
5 | + * | |
6 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | + * of this software and associated documentation files (the "Software"), to deal | |
8 | + * in the Software without restriction, including without limitation the rights | |
9 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | + * copies of the Software, and to permit persons to whom the Software is | |
11 | + * furnished to do so, subject to the following conditions: | |
12 | + * | |
13 | + * The above copyright notice and this permission notice shall be included in | |
14 | + * all copies or substantial portions of the Software. | |
15 | + * | |
16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | + * THE SOFTWARE. | |
23 | + */ | |
24 | +#include "vl.h" | |
25 | + | |
26 | +#define AUDIO_CAP "wav" | |
27 | +#include "audio/audio.h" | |
28 | +#include "audio/wavaudio.h" | |
29 | + | |
30 | +static struct { | |
31 | + const char *wav_path; | |
32 | +} conf = { | |
33 | + .wav_path = "qemu.wav" | |
34 | +}; | |
35 | + | |
36 | +static void wav_hw_run (HWVoice *hw) | |
37 | +{ | |
38 | + WAVVoice *wav = (WAVVoice *) hw; | |
39 | + int rpos, live, decr, samples; | |
40 | + uint8_t *dst; | |
41 | + st_sample_t *src; | |
42 | + int64_t now = qemu_get_clock (vm_clock); | |
43 | + int64_t ticks = now - wav->old_ticks; | |
44 | + int64_t bytes = (ticks * hw->bytes_per_second) / ticks_per_sec; | |
45 | + wav->old_ticks = now; | |
46 | + | |
47 | + if (bytes > INT_MAX) | |
48 | + samples = INT_MAX >> hw->shift; | |
49 | + else | |
50 | + samples = bytes >> hw->shift; | |
51 | + | |
52 | + live = pcm_hw_get_live (hw); | |
53 | + if (live <= 0) | |
54 | + return; | |
55 | + | |
56 | + decr = audio_MIN (live, samples); | |
57 | + samples = decr; | |
58 | + rpos = hw->rpos; | |
59 | + while (samples) { | |
60 | + int left_till_end_samples = hw->samples - rpos; | |
61 | + int convert_samples = audio_MIN (samples, left_till_end_samples); | |
62 | + | |
63 | + src = advance (hw->mix_buf, rpos * sizeof (st_sample_t)); | |
64 | + dst = advance (wav->pcm_buf, rpos << hw->shift); | |
65 | + | |
66 | + hw->clip (dst, src, convert_samples); | |
67 | + qemu_put_buffer (wav->f, dst, convert_samples << hw->shift); | |
68 | + memset (src, 0, convert_samples * sizeof (st_sample_t)); | |
69 | + | |
70 | + rpos = (rpos + convert_samples) % hw->samples; | |
71 | + samples -= convert_samples; | |
72 | + wav->total_samples += convert_samples; | |
73 | + } | |
74 | + | |
75 | + pcm_hw_dec_live (hw, decr); | |
76 | + hw->rpos = rpos; | |
77 | +} | |
78 | + | |
79 | +static int wav_hw_write (SWVoice *sw, void *buf, int len) | |
80 | +{ | |
81 | + return pcm_hw_write (sw, buf, len); | |
82 | +} | |
83 | + | |
84 | + | |
85 | +/* VICE code: Store number as little endian. */ | |
86 | +static void le_store (uint8_t *buf, uint32_t val, int len) | |
87 | +{ | |
88 | + int i; | |
89 | + for (i = 0; i < len; i++) { | |
90 | + buf[i] = (uint8_t) (val & 0xff); | |
91 | + val >>= 8; | |
92 | + } | |
93 | +} | |
94 | + | |
95 | +static int wav_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt) | |
96 | +{ | |
97 | + WAVVoice *wav = (WAVVoice *) hw; | |
98 | + int bits16 = 0, stereo = audio_state.fixed_channels == 2; | |
99 | + uint8_t hdr[] = { | |
100 | + 0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56, | |
101 | + 0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, | |
102 | + 0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04, | |
103 | + 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00 | |
104 | + }; | |
105 | + | |
106 | + switch (audio_state.fixed_fmt) { | |
107 | + case AUD_FMT_S8: | |
108 | + case AUD_FMT_U8: | |
109 | + break; | |
110 | + | |
111 | + case AUD_FMT_S16: | |
112 | + case AUD_FMT_U16: | |
113 | + bits16 = 1; | |
114 | + break; | |
115 | + } | |
116 | + | |
117 | + hdr[34] = bits16 ? 0x10 : 0x08; | |
118 | + hw->freq = 44100; | |
119 | + hw->nchannels = stereo ? 2 : 1; | |
120 | + hw->fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8; | |
121 | + hw->bufsize = 4096; | |
122 | + wav->pcm_buf = qemu_mallocz (hw->bufsize); | |
123 | + if (!wav->pcm_buf) | |
124 | + return -1; | |
125 | + | |
126 | + le_store (hdr + 22, hw->nchannels, 2); | |
127 | + le_store (hdr + 24, hw->freq, 4); | |
128 | + le_store (hdr + 28, hw->freq << (bits16 + stereo), 4); | |
129 | + le_store (hdr + 32, 1 << (bits16 + stereo), 2); | |
130 | + | |
131 | + wav->f = fopen (conf.wav_path, "wb"); | |
132 | + if (!wav->f) { | |
133 | + dolog ("failed to open wave file `%s'\nReason: %s\n", | |
134 | + conf.wav_path, strerror (errno)); | |
135 | + return -1; | |
136 | + } | |
137 | + | |
138 | + qemu_put_buffer (wav->f, hdr, sizeof (hdr)); | |
139 | + return 0; | |
140 | +} | |
141 | + | |
142 | +static void wav_hw_fini (HWVoice *hw) | |
143 | +{ | |
144 | + WAVVoice *wav = (WAVVoice *) hw; | |
145 | + int stereo = hw->nchannels == 2; | |
146 | + uint8_t rlen[4]; | |
147 | + uint8_t dlen[4]; | |
148 | + uint32_t rifflen = (wav->total_samples << stereo) + 36; | |
149 | + uint32_t datalen = wav->total_samples << stereo; | |
150 | + | |
151 | + if (!wav->f || !hw->active) | |
152 | + return; | |
153 | + | |
154 | + le_store (rlen, rifflen, 4); | |
155 | + le_store (dlen, datalen, 4); | |
156 | + | |
157 | + qemu_fseek (wav->f, 4, SEEK_SET); | |
158 | + qemu_put_buffer (wav->f, rlen, 4); | |
159 | + | |
160 | + qemu_fseek (wav->f, 32, SEEK_CUR); | |
161 | + qemu_put_buffer (wav->f, dlen, 4); | |
162 | + | |
163 | + fclose (wav->f); | |
164 | + wav->f = NULL; | |
165 | +} | |
166 | + | |
167 | +static int wav_hw_ctl (HWVoice *hw, int cmd, ...) | |
168 | +{ | |
169 | + (void) hw; | |
170 | + (void) cmd; | |
171 | + return 0; | |
172 | +} | |
173 | + | |
174 | +static void *wav_audio_init (void) | |
175 | +{ | |
176 | + return &conf; | |
177 | +} | |
178 | + | |
179 | +static void wav_audio_fini (void *opaque) | |
180 | +{ | |
181 | + ldebug ("wav_fini"); | |
182 | +} | |
183 | + | |
184 | +struct pcm_ops wav_pcm_ops = { | |
185 | + wav_hw_init, | |
186 | + wav_hw_fini, | |
187 | + wav_hw_run, | |
188 | + wav_hw_write, | |
189 | + wav_hw_ctl | |
190 | +}; | |
191 | + | |
192 | +struct audio_output_driver wav_output_driver = { | |
193 | + "wav", | |
194 | + wav_audio_init, | |
195 | + wav_audio_fini, | |
196 | + &wav_pcm_ops, | |
197 | + 1, | |
198 | + 1, | |
199 | + sizeof (WAVVoice) | |
200 | +}; | ... | ... |
audio/wavaudio.h
0 → 100644
1 | +/* | |
2 | + * QEMU WAV audio output driver header | |
3 | + * | |
4 | + * Copyright (c) 2004 Vassili Karpov (malc) | |
5 | + * | |
6 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | + * of this software and associated documentation files (the "Software"), to deal | |
8 | + * in the Software without restriction, including without limitation the rights | |
9 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | + * copies of the Software, and to permit persons to whom the Software is | |
11 | + * furnished to do so, subject to the following conditions: | |
12 | + * | |
13 | + * The above copyright notice and this permission notice shall be included in | |
14 | + * all copies or substantial portions of the Software. | |
15 | + * | |
16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | + * THE SOFTWARE. | |
23 | + */ | |
24 | +#ifndef QEMU_WAVAUDIO_H | |
25 | +#define QEMU_WAVAUDIO_H | |
26 | + | |
27 | +typedef struct WAVVoice { | |
28 | + struct HWVoice hw; | |
29 | + QEMUFile *f; | |
30 | + int64_t old_ticks; | |
31 | + void *pcm_buf; | |
32 | + int total_samples; | |
33 | +} WAVVoice; | |
34 | + | |
35 | +extern struct pcm_ops wav_pcm_ops; | |
36 | +extern struct audio_output_driver wav_output_driver; | |
37 | + | |
38 | +#endif /* wavaudio.h */ | ... | ... |
hw/adlib.c
0 → 100644
1 | +/* | |
2 | + * QEMU Adlib emulation | |
3 | + * | |
4 | + * Copyright (c) 2004 Vassili Karpov (malc) | |
5 | + * | |
6 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | + * of this software and associated documentation files (the "Software"), to deal | |
8 | + * in the Software without restriction, including without limitation the rights | |
9 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | + * copies of the Software, and to permit persons to whom the Software is | |
11 | + * furnished to do so, subject to the following conditions: | |
12 | + * | |
13 | + * The above copyright notice and this permission notice shall be included in | |
14 | + * all copies or substantial portions of the Software. | |
15 | + * | |
16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | + * THE SOFTWARE. | |
23 | + */ | |
24 | +#include "vl.h" | |
25 | + | |
26 | +#define AUDIO_CAP "adlib" | |
27 | +#include "audio/audio.h" | |
28 | + | |
29 | +#ifdef USE_YMF262 | |
30 | +#define HAS_YMF262 1 | |
31 | +#include "ymf262.h" | |
32 | +void YMF262UpdateOneQEMU(int which, INT16 *dst, int length); | |
33 | +#define SHIFT 2 | |
34 | +#else | |
35 | +#include "fmopl.h" | |
36 | +#define SHIFT 1 | |
37 | +#endif | |
38 | + | |
39 | +#ifdef _WIN32 | |
40 | +#include <windows.h> | |
41 | +#define small_delay() Sleep (1) | |
42 | +#else | |
43 | +#define small_delay() usleep (1) | |
44 | +#endif | |
45 | + | |
46 | +#define IO_READ_PROTO(name) \ | |
47 | + uint32_t name (void *opaque, uint32_t nport) | |
48 | +#define IO_WRITE_PROTO(name) \ | |
49 | + void name (void *opaque, uint32_t nport, uint32_t val) | |
50 | + | |
51 | +static struct { | |
52 | + int port; | |
53 | + int freq; | |
54 | +} conf = {0x220, 44100}; | |
55 | + | |
56 | +typedef struct { | |
57 | + int enabled; | |
58 | + int active; | |
59 | + int cparam; | |
60 | + int64_t ticks; | |
61 | + int bufpos; | |
62 | + int16_t *mixbuf; | |
63 | + double interval; | |
64 | + QEMUTimer *ts, *opl_ts; | |
65 | + SWVoice *voice; | |
66 | + int left, pos, samples, bytes_per_second, old_free; | |
67 | + int refcount; | |
68 | +#ifndef USE_YMF262 | |
69 | + FM_OPL *opl; | |
70 | +#endif | |
71 | +} AdlibState; | |
72 | + | |
73 | +static AdlibState adlib; | |
74 | + | |
75 | +static IO_WRITE_PROTO(adlib_write) | |
76 | +{ | |
77 | + AdlibState *s = opaque; | |
78 | + int a = nport & 3; | |
79 | + int status; | |
80 | + | |
81 | + s->ticks = qemu_get_clock (vm_clock); | |
82 | + s->active = 1; | |
83 | + AUD_enable (s->voice, 1); | |
84 | + | |
85 | +#ifdef USE_YMF262 | |
86 | + status = YMF262Write (0, a, val); | |
87 | +#else | |
88 | + status = OPLWrite (s->opl, a, val); | |
89 | +#endif | |
90 | +} | |
91 | + | |
92 | +static IO_READ_PROTO(adlib_read) | |
93 | +{ | |
94 | + AdlibState *s = opaque; | |
95 | + uint8_t data; | |
96 | + int a = nport & 3; | |
97 | + | |
98 | +#ifdef USE_YMF262 | |
99 | + (void) s; | |
100 | + data = YMF262Read (0, a); | |
101 | +#else | |
102 | + data = OPLRead (s->opl, a); | |
103 | +#endif | |
104 | + return data; | |
105 | +} | |
106 | + | |
107 | +static void OPL_timer (void *opaque) | |
108 | +{ | |
109 | + AdlibState *s = opaque; | |
110 | +#ifdef USE_YMF262 | |
111 | + YMF262TimerOver (s->cparam >> 1, s->cparam & 1); | |
112 | +#else | |
113 | + OPLTimerOver (s->opl, s->cparam); | |
114 | +#endif | |
115 | + qemu_mod_timer (s->opl_ts, qemu_get_clock (vm_clock) + s->interval); | |
116 | +} | |
117 | + | |
118 | +static void YMF262TimerHandler (int c, double interval_Sec) | |
119 | +{ | |
120 | + AdlibState *s = &adlib; | |
121 | + if (interval_Sec == 0.0) { | |
122 | + qemu_del_timer (s->opl_ts); | |
123 | + return; | |
124 | + } | |
125 | + s->cparam = c; | |
126 | + s->interval = ticks_per_sec * interval_Sec; | |
127 | + qemu_mod_timer (s->opl_ts, qemu_get_clock (vm_clock) + s->interval); | |
128 | + small_delay (); | |
129 | +} | |
130 | + | |
131 | +static int write_audio (AdlibState *s, int samples) | |
132 | +{ | |
133 | + int net = 0; | |
134 | + int ss = samples; | |
135 | + while (samples) { | |
136 | + int nbytes = samples << SHIFT; | |
137 | + int wbytes = AUD_write (s->voice, | |
138 | + s->mixbuf + (s->pos << (SHIFT - 1)), | |
139 | + nbytes); | |
140 | + int wsampl = wbytes >> SHIFT; | |
141 | + samples -= wsampl; | |
142 | + s->pos = (s->pos + wsampl) % s->samples; | |
143 | + net += wsampl; | |
144 | + if (!wbytes) | |
145 | + break; | |
146 | + } | |
147 | + if (net > ss) { | |
148 | + dolog ("WARNING: net > ss\n"); | |
149 | + } | |
150 | + return net; | |
151 | +} | |
152 | + | |
153 | +static void timer (void *opaque) | |
154 | +{ | |
155 | + AdlibState *s = opaque; | |
156 | + int elapsed, samples, net = 0; | |
157 | + | |
158 | + if (s->refcount) | |
159 | + dolog ("refcount=%d\n", s->refcount); | |
160 | + | |
161 | + s->refcount += 1; | |
162 | + if (!(s->active && s->enabled)) | |
163 | + goto reset; | |
164 | + | |
165 | + AUD_run (); | |
166 | + | |
167 | + while (s->left) { | |
168 | + int written = write_audio (s, s->left); | |
169 | + net += written; | |
170 | + if (!written) | |
171 | + goto reset2; | |
172 | + s->left -= written; | |
173 | + } | |
174 | + s->pos = 0; | |
175 | + | |
176 | + elapsed = AUD_calc_elapsed (s->voice); | |
177 | + if (!elapsed) | |
178 | + goto reset2; | |
179 | + | |
180 | + /* elapsed = AUD_get_free (s->voice); */ | |
181 | + samples = elapsed >> SHIFT; | |
182 | + if (!samples) | |
183 | + goto reset2; | |
184 | + | |
185 | + samples = audio_MIN (samples, s->samples - s->pos); | |
186 | + if (s->left) | |
187 | + dolog ("left=%d samples=%d elapsed=%d free=%d\n", | |
188 | + s->left, samples, elapsed, AUD_get_free (s->voice)); | |
189 | + | |
190 | + if (!samples) | |
191 | + goto reset2; | |
192 | + | |
193 | +#ifdef USE_YMF262 | |
194 | + YMF262UpdateOneQEMU (0, s->mixbuf + s->pos * 2, samples); | |
195 | +#else | |
196 | + YM3812UpdateOne (s->opl, s->mixbuf + s->pos, samples); | |
197 | +#endif | |
198 | + | |
199 | + while (samples) { | |
200 | + int written = write_audio (s, samples); | |
201 | + net += written; | |
202 | + if (!written) | |
203 | + break; | |
204 | + samples -= written; | |
205 | + } | |
206 | + if (!samples) | |
207 | + s->pos = 0; | |
208 | + s->left = samples; | |
209 | + | |
210 | +reset2: | |
211 | + AUD_adjust (s->voice, net << SHIFT); | |
212 | +reset: | |
213 | + qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + ticks_per_sec / 1024); | |
214 | + s->refcount -= 1; | |
215 | +} | |
216 | + | |
217 | +static void Adlib_fini (AdlibState *s) | |
218 | +{ | |
219 | +#ifdef USE_YMF262 | |
220 | + YMF262Shutdown (); | |
221 | +#else | |
222 | + if (s->opl) { | |
223 | + OPLDestroy (s->opl); | |
224 | + s->opl = NULL; | |
225 | + } | |
226 | +#endif | |
227 | + | |
228 | + if (s->opl_ts) | |
229 | + qemu_free_timer (s->opl_ts); | |
230 | + | |
231 | + if (s->ts) | |
232 | + qemu_free_timer (s->ts); | |
233 | + | |
234 | +#define maybe_free(p) if (p) qemu_free (p) | |
235 | + maybe_free (s->mixbuf); | |
236 | +#undef maybe_free | |
237 | + | |
238 | + s->active = 0; | |
239 | + s->enabled = 0; | |
240 | +} | |
241 | + | |
242 | +void Adlib_init (void) | |
243 | +{ | |
244 | + AdlibState *s = &adlib; | |
245 | + | |
246 | + memset (s, 0, sizeof (*s)); | |
247 | + | |
248 | +#ifdef USE_YMF262 | |
249 | + if (YMF262Init (1, 14318180, conf.freq)) { | |
250 | + dolog ("YMF262Init %d failed\n", conf.freq); | |
251 | + return; | |
252 | + } | |
253 | + else { | |
254 | + YMF262SetTimerHandler (0, YMF262TimerHandler, 0); | |
255 | + s->enabled = 1; | |
256 | + } | |
257 | +#else | |
258 | + s->opl = OPLCreate (OPL_TYPE_YM3812, 3579545, conf.freq); | |
259 | + if (!s->opl) { | |
260 | + dolog ("OPLCreate %d failed\n", conf.freq); | |
261 | + return; | |
262 | + } | |
263 | + else { | |
264 | + OPLSetTimerHandler (s->opl, YMF262TimerHandler, 0); | |
265 | + s->enabled = 1; | |
266 | + } | |
267 | +#endif | |
268 | + | |
269 | + s->opl_ts = qemu_new_timer (vm_clock, OPL_timer, s); | |
270 | + if (!s->opl_ts) { | |
271 | + dolog ("Can not get timer for adlib emulation\n"); | |
272 | + Adlib_fini (s); | |
273 | + return; | |
274 | + } | |
275 | + | |
276 | + s->ts = qemu_new_timer (vm_clock, timer, s); | |
277 | + if (!s->opl_ts) { | |
278 | + dolog ("Can not get timer for adlib emulation\n"); | |
279 | + Adlib_fini (s); | |
280 | + return; | |
281 | + } | |
282 | + | |
283 | + s->voice = AUD_open (s->voice, "adlib", conf.freq, SHIFT, AUD_FMT_S16); | |
284 | + if (!s->voice) { | |
285 | + Adlib_fini (s); | |
286 | + return; | |
287 | + } | |
288 | + | |
289 | + s->bytes_per_second = conf.freq << SHIFT; | |
290 | + s->samples = AUD_get_buffer_size (s->voice) >> SHIFT; | |
291 | + s->mixbuf = qemu_mallocz (s->samples << SHIFT); | |
292 | + | |
293 | + if (!s->mixbuf) { | |
294 | + dolog ("not enough memory for adlib mixing buffer (%d)\n", | |
295 | + s->samples << SHIFT); | |
296 | + Adlib_fini (s); | |
297 | + return; | |
298 | + } | |
299 | + register_ioport_read (0x388, 4, 1, adlib_read, s); | |
300 | + register_ioport_write (0x388, 4, 1, adlib_write, s); | |
301 | + | |
302 | + register_ioport_read (conf.port, 4, 1, adlib_read, s); | |
303 | + register_ioport_write (conf.port, 4, 1, adlib_write, s); | |
304 | + | |
305 | + register_ioport_read (conf.port + 8, 2, 1, adlib_read, s); | |
306 | + register_ioport_write (conf.port + 8, 2, 1, adlib_write, s); | |
307 | + | |
308 | + qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + 1); | |
309 | +} | ... | ... |
hw/dma.c
1 | 1 | /* |
2 | 2 | * QEMU DMA emulation |
3 | - * | |
4 | - * Copyright (c) 2003 Vassili Karpov (malc) | |
5 | - * | |
3 | + * | |
4 | + * Copyright (c) 2003-2004 Vassili Karpov (malc) | |
5 | + * | |
6 | 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
7 | 7 | * of this software and associated documentation files (the "Software"), to deal |
8 | 8 | * in the Software without restriction, including without limitation the rights |
... | ... | @@ -23,9 +23,9 @@ |
23 | 23 | */ |
24 | 24 | #include "vl.h" |
25 | 25 | |
26 | -//#define DEBUG_DMA | |
26 | +/* #define DEBUG_DMA */ | |
27 | 27 | |
28 | -#define log(...) fprintf (stderr, "dma: " __VA_ARGS__) | |
28 | +#define dolog(...) fprintf (stderr, "dma: " __VA_ARGS__) | |
29 | 29 | #ifdef DEBUG_DMA |
30 | 30 | #define lwarn(...) fprintf (stderr, "dma: " __VA_ARGS__) |
31 | 31 | #define linfo(...) fprintf (stderr, "dma: " __VA_ARGS__) |
... | ... | @@ -86,7 +86,7 @@ static void write_page (void *opaque, uint32_t nport, uint32_t data) |
86 | 86 | |
87 | 87 | ichan = channels[nport & 7]; |
88 | 88 | if (-1 == ichan) { |
89 | - log ("invalid channel %#x %#x\n", nport, data); | |
89 | + dolog ("invalid channel %#x %#x\n", nport, data); | |
90 | 90 | return; |
91 | 91 | } |
92 | 92 | d->regs[ichan].page = data; |
... | ... | @@ -99,7 +99,7 @@ static void write_pageh (void *opaque, uint32_t nport, uint32_t data) |
99 | 99 | |
100 | 100 | ichan = channels[nport & 7]; |
101 | 101 | if (-1 == ichan) { |
102 | - log ("invalid channel %#x %#x\n", nport, data); | |
102 | + dolog ("invalid channel %#x %#x\n", nport, data); | |
103 | 103 | return; |
104 | 104 | } |
105 | 105 | d->regs[ichan].pageh = data; |
... | ... | @@ -112,7 +112,7 @@ static uint32_t read_page (void *opaque, uint32_t nport) |
112 | 112 | |
113 | 113 | ichan = channels[nport & 7]; |
114 | 114 | if (-1 == ichan) { |
115 | - log ("invalid channel read %#x\n", nport); | |
115 | + dolog ("invalid channel read %#x\n", nport); | |
116 | 116 | return 0; |
117 | 117 | } |
118 | 118 | return d->regs[ichan].page; |
... | ... | @@ -125,7 +125,7 @@ static uint32_t read_pageh (void *opaque, uint32_t nport) |
125 | 125 | |
126 | 126 | ichan = channels[nport & 7]; |
127 | 127 | if (-1 == ichan) { |
128 | - log ("invalid channel read %#x\n", nport); | |
128 | + dolog ("invalid channel read %#x\n", nport); | |
129 | 129 | return 0; |
130 | 130 | } |
131 | 131 | return d->regs[ichan].pageh; |
... | ... | @@ -136,7 +136,7 @@ static inline void init_chan (struct dma_cont *d, int ichan) |
136 | 136 | struct dma_regs *r; |
137 | 137 | |
138 | 138 | r = d->regs + ichan; |
139 | - r->now[ADDR] = r->base[0] << d->dshift; | |
139 | + r->now[ADDR] = r->base[ADDR] << d->dshift; | |
140 | 140 | r->now[COUNT] = 0; |
141 | 141 | } |
142 | 142 | |
... | ... | @@ -152,7 +152,7 @@ static inline int getff (struct dma_cont *d) |
152 | 152 | static uint32_t read_chan (void *opaque, uint32_t nport) |
153 | 153 | { |
154 | 154 | struct dma_cont *d = opaque; |
155 | - int ichan, nreg, iport, ff, val; | |
155 | + int ichan, nreg, iport, ff, val, dir; | |
156 | 156 | struct dma_regs *r; |
157 | 157 | |
158 | 158 | iport = (nport >> d->dshift) & 0x0f; |
... | ... | @@ -160,12 +160,14 @@ static uint32_t read_chan (void *opaque, uint32_t nport) |
160 | 160 | nreg = iport & 1; |
161 | 161 | r = d->regs + ichan; |
162 | 162 | |
163 | + dir = ((r->mode >> 5) & 1) ? -1 : 1; | |
163 | 164 | ff = getff (d); |
164 | 165 | if (nreg) |
165 | 166 | val = (r->base[COUNT] << d->dshift) - r->now[COUNT]; |
166 | 167 | else |
167 | - val = r->now[ADDR] + r->now[COUNT]; | |
168 | + val = r->now[ADDR] + r->now[COUNT] * dir; | |
168 | 169 | |
170 | + ldebug ("read_chan %#x -> %d\n", iport, val); | |
169 | 171 | return (val >> (d->dshift + (ff << 3))) & 0xff; |
170 | 172 | } |
171 | 173 | |
... | ... | @@ -190,19 +192,19 @@ static void write_chan (void *opaque, uint32_t nport, uint32_t data) |
190 | 192 | static void write_cont (void *opaque, uint32_t nport, uint32_t data) |
191 | 193 | { |
192 | 194 | struct dma_cont *d = opaque; |
193 | - int iport, ichan; | |
195 | + int iport, ichan = 0; | |
194 | 196 | |
195 | 197 | iport = (nport >> d->dshift) & 0x0f; |
196 | 198 | switch (iport) { |
197 | - case 8: /* command */ | |
199 | + case 0x08: /* command */ | |
198 | 200 | if ((data != 0) && (data & CMD_NOT_SUPPORTED)) { |
199 | - log ("command %#x not supported\n", data); | |
201 | + dolog ("command %#x not supported\n", data); | |
200 | 202 | return; |
201 | 203 | } |
202 | 204 | d->command = data; |
203 | 205 | break; |
204 | 206 | |
205 | - case 9: | |
207 | + case 0x09: | |
206 | 208 | ichan = data & 3; |
207 | 209 | if (data & 4) { |
208 | 210 | d->status |= 1 << (ichan + 4); |
... | ... | @@ -213,22 +215,19 @@ static void write_cont (void *opaque, uint32_t nport, uint32_t data) |
213 | 215 | d->status &= ~(1 << ichan); |
214 | 216 | break; |
215 | 217 | |
216 | - case 0xa: /* single mask */ | |
218 | + case 0x0a: /* single mask */ | |
217 | 219 | if (data & 4) |
218 | 220 | d->mask |= 1 << (data & 3); |
219 | 221 | else |
220 | 222 | d->mask &= ~(1 << (data & 3)); |
221 | 223 | break; |
222 | 224 | |
223 | - case 0xb: /* mode */ | |
225 | + case 0x0b: /* mode */ | |
224 | 226 | { |
225 | 227 | ichan = data & 3; |
226 | 228 | #ifdef DEBUG_DMA |
227 | - int op; | |
228 | - int ai; | |
229 | - int dir; | |
230 | - int opmode; | |
231 | - | |
229 | + { | |
230 | + int op, ai, dir, opmode; | |
232 | 231 | op = (data >> 2) & 3; |
233 | 232 | ai = (data >> 4) & 1; |
234 | 233 | dir = (data >> 5) & 1; |
... | ... | @@ -236,39 +235,39 @@ static void write_cont (void *opaque, uint32_t nport, uint32_t data) |
236 | 235 | |
237 | 236 | linfo ("ichan %d, op %d, ai %d, dir %d, opmode %d\n", |
238 | 237 | ichan, op, ai, dir, opmode); |
238 | + } | |
239 | 239 | #endif |
240 | - | |
241 | 240 | d->regs[ichan].mode = data; |
242 | 241 | break; |
243 | 242 | } |
244 | 243 | |
245 | - case 0xc: /* clear flip flop */ | |
244 | + case 0x0c: /* clear flip flop */ | |
246 | 245 | d->flip_flop = 0; |
247 | 246 | break; |
248 | 247 | |
249 | - case 0xd: /* reset */ | |
248 | + case 0x0d: /* reset */ | |
250 | 249 | d->flip_flop = 0; |
251 | 250 | d->mask = ~0; |
252 | 251 | d->status = 0; |
253 | 252 | d->command = 0; |
254 | 253 | break; |
255 | 254 | |
256 | - case 0xe: /* clear mask for all channels */ | |
255 | + case 0x0e: /* clear mask for all channels */ | |
257 | 256 | d->mask = 0; |
258 | 257 | break; |
259 | 258 | |
260 | - case 0xf: /* write mask for all channels */ | |
259 | + case 0x0f: /* write mask for all channels */ | |
261 | 260 | d->mask = data; |
262 | 261 | break; |
263 | 262 | |
264 | 263 | default: |
265 | - log ("dma: unknown iport %#x\n", iport); | |
264 | + dolog ("unknown iport %#x\n", iport); | |
266 | 265 | break; |
267 | 266 | } |
268 | 267 | |
269 | 268 | #ifdef DEBUG_DMA |
270 | 269 | if (0xc != iport) { |
271 | - linfo ("nport %#06x, ichan % 2d, val %#06x\n", | |
270 | + linfo ("write_cont: nport %#06x, ichan % 2d, val %#06x\n", | |
272 | 271 | nport, ichan, data); |
273 | 272 | } |
274 | 273 | #endif |
... | ... | @@ -278,20 +277,22 @@ static uint32_t read_cont (void *opaque, uint32_t nport) |
278 | 277 | { |
279 | 278 | struct dma_cont *d = opaque; |
280 | 279 | int iport, val; |
281 | - | |
280 | + | |
282 | 281 | iport = (nport >> d->dshift) & 0x0f; |
283 | 282 | switch (iport) { |
284 | - case 0x08: /* status */ | |
283 | + case 0x08: /* status */ | |
285 | 284 | val = d->status; |
286 | 285 | d->status &= 0xf0; |
287 | 286 | break; |
288 | - case 0x0f: /* mask */ | |
287 | + case 0x0f: /* mask */ | |
289 | 288 | val = d->mask; |
290 | 289 | break; |
291 | 290 | default: |
292 | 291 | val = 0; |
293 | 292 | break; |
294 | 293 | } |
294 | + | |
295 | + ldebug ("read_cont: nport %#06x, iport %#04x val %#x\n", nport, iport, val); | |
295 | 296 | return val; |
296 | 297 | } |
297 | 298 | |
... | ... | @@ -322,23 +323,27 @@ void DMA_release_DREQ (int nchan) |
322 | 323 | |
323 | 324 | static void channel_run (int ncont, int ichan) |
324 | 325 | { |
325 | - struct dma_regs *r; | |
326 | 326 | int n; |
327 | - target_ulong addr; | |
328 | -/* int ai, dir; */ | |
327 | + struct dma_regs *r = &dma_controllers[ncont].regs[ichan]; | |
328 | +#ifdef DEBUG_DMA | |
329 | + int dir, opmode; | |
329 | 330 | |
330 | - r = dma_controllers[ncont].regs + ichan; | |
331 | -/* ai = r->mode & 16; */ | |
332 | -/* dir = r->mode & 32 ? -1 : 1; */ | |
331 | + dir = (r->mode >> 5) & 1; | |
332 | + opmode = (r->mode >> 6) & 3; | |
333 | 333 | |
334 | - /* NOTE: pageh is only used by PPC PREP */ | |
335 | - addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR]; | |
336 | - n = r->transfer_handler (r->opaque, addr, | |
337 | - (r->base[COUNT] << ncont) + (1 << ncont)); | |
338 | - r->now[COUNT] = n; | |
334 | + if (dir) { | |
335 | + dolog ("DMA in address decrement mode\n"); | |
336 | + } | |
337 | + if (opmode != 1) { | |
338 | + dolog ("DMA not in single mode select %#x\n", opmode); | |
339 | + } | |
340 | +#endif | |
339 | 341 | |
340 | - ldebug ("dma_pos %d size %d\n", | |
341 | - n, (r->base[1] << ncont) + (1 << ncont)); | |
342 | + r = dma_controllers[ncont].regs + ichan; | |
343 | + n = r->transfer_handler (r->opaque, ichan + (ncont << 2), | |
344 | + r->now[COUNT], (r->base[COUNT] + 1) << ncont); | |
345 | + r->now[COUNT] = n; | |
346 | + ldebug ("dma_pos %d size %d\n", n, (r->base[COUNT] + 1) << ncont); | |
342 | 347 | } |
343 | 348 | |
344 | 349 | void DMA_run (void) |
... | ... | @@ -361,7 +366,7 @@ void DMA_run (void) |
361 | 366 | } |
362 | 367 | |
363 | 368 | void DMA_register_channel (int nchan, |
364 | - DMA_transfer_handler transfer_handler, | |
369 | + DMA_transfer_handler transfer_handler, | |
365 | 370 | void *opaque) |
366 | 371 | { |
367 | 372 | struct dma_regs *r; |
... | ... | @@ -375,6 +380,50 @@ void DMA_register_channel (int nchan, |
375 | 380 | r->opaque = opaque; |
376 | 381 | } |
377 | 382 | |
383 | +int DMA_read_memory (int nchan, void *buf, int pos, int len) | |
384 | +{ | |
385 | + struct dma_regs *r = &dma_controllers[nchan > 3].regs[nchan & 3]; | |
386 | + target_ulong addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR]; | |
387 | + | |
388 | + if (r->mode & 0x20) { | |
389 | + int i; | |
390 | + uint8_t *p = buf; | |
391 | + | |
392 | + cpu_physical_memory_read (addr - pos - len, buf, len); | |
393 | + /* What about 16bit transfers? */ | |
394 | + for (i = 0; i < len >> 1; i++) { | |
395 | + uint8_t b = p[len - i - 1]; | |
396 | + p[i] = b; | |
397 | + } | |
398 | + } | |
399 | + else | |
400 | + cpu_physical_memory_read (addr + pos, buf, len); | |
401 | + | |
402 | + return len; | |
403 | +} | |
404 | + | |
405 | +int DMA_write_memory (int nchan, void *buf, int pos, int len) | |
406 | +{ | |
407 | + struct dma_regs *r = &dma_controllers[nchan > 3].regs[nchan & 3]; | |
408 | + target_ulong addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR]; | |
409 | + | |
410 | + if (r->mode & 0x20) { | |
411 | + int i; | |
412 | + uint8_t *p = buf; | |
413 | + | |
414 | + cpu_physical_memory_write (addr - pos - len, buf, len); | |
415 | + /* What about 16bit transfers? */ | |
416 | + for (i = 0; i < len; i++) { | |
417 | + uint8_t b = p[len - i - 1]; | |
418 | + p[i] = b; | |
419 | + } | |
420 | + } | |
421 | + else | |
422 | + cpu_physical_memory_write (addr + pos, buf, len); | |
423 | + | |
424 | + return len; | |
425 | +} | |
426 | + | |
378 | 427 | /* request the emulator to transfer a new DMA memory block ASAP */ |
379 | 428 | void DMA_schedule(int nchan) |
380 | 429 | { |
... | ... | @@ -388,7 +437,7 @@ static void dma_reset(void *opaque) |
388 | 437 | } |
389 | 438 | |
390 | 439 | /* dshift = 0: 8 bit DMA, 1 = 16 bit DMA */ |
391 | -static void dma_init2(struct dma_cont *d, int base, int dshift, | |
440 | +static void dma_init2(struct dma_cont *d, int base, int dshift, | |
392 | 441 | int page_base, int pageh_base) |
393 | 442 | { |
394 | 443 | const static int page_port_list[] = { 0x1, 0x2, 0x3, 0x7 }; |
... | ... | @@ -400,31 +449,87 @@ static void dma_init2(struct dma_cont *d, int base, int dshift, |
400 | 449 | register_ioport_read (base + (i << dshift), 1, 1, read_chan, d); |
401 | 450 | } |
402 | 451 | for (i = 0; i < LENOFA (page_port_list); i++) { |
403 | - register_ioport_write (page_base + page_port_list[i], 1, 1, | |
452 | + register_ioport_write (page_base + page_port_list[i], 1, 1, | |
404 | 453 | write_page, d); |
405 | - register_ioport_read (page_base + page_port_list[i], 1, 1, | |
454 | + register_ioport_read (page_base + page_port_list[i], 1, 1, | |
406 | 455 | read_page, d); |
407 | 456 | if (pageh_base >= 0) { |
408 | - register_ioport_write (pageh_base + page_port_list[i], 1, 1, | |
457 | + register_ioport_write (pageh_base + page_port_list[i], 1, 1, | |
409 | 458 | write_pageh, d); |
410 | - register_ioport_read (pageh_base + page_port_list[i], 1, 1, | |
459 | + register_ioport_read (pageh_base + page_port_list[i], 1, 1, | |
411 | 460 | read_pageh, d); |
412 | 461 | } |
413 | 462 | } |
414 | 463 | for (i = 0; i < 8; i++) { |
415 | - register_ioport_write (base + ((i + 8) << dshift), 1, 1, | |
464 | + register_ioport_write (base + ((i + 8) << dshift), 1, 1, | |
416 | 465 | write_cont, d); |
417 | - register_ioport_read (base + ((i + 8) << dshift), 1, 1, | |
466 | + register_ioport_read (base + ((i + 8) << dshift), 1, 1, | |
418 | 467 | read_cont, d); |
419 | 468 | } |
420 | 469 | qemu_register_reset(dma_reset, d); |
421 | 470 | dma_reset(d); |
422 | 471 | } |
423 | 472 | |
473 | +static void dma_save (QEMUFile *f, void *opaque) | |
474 | +{ | |
475 | + struct dma_cont *d = opaque; | |
476 | + int i; | |
477 | + | |
478 | + /* qemu_put_8s (f, &d->status); */ | |
479 | + qemu_put_8s (f, &d->command); | |
480 | + qemu_put_8s (f, &d->mask); | |
481 | + qemu_put_8s (f, &d->flip_flop); | |
482 | + qemu_put_be32s (f, &d->dshift); | |
483 | + | |
484 | + for (i = 0; i < 4; ++i) { | |
485 | + struct dma_regs *r = &d->regs[i]; | |
486 | + qemu_put_be32s (f, &r->now[0]); | |
487 | + qemu_put_be32s (f, &r->now[1]); | |
488 | + qemu_put_be16s (f, &r->base[0]); | |
489 | + qemu_put_be16s (f, &r->base[1]); | |
490 | + qemu_put_8s (f, &r->mode); | |
491 | + qemu_put_8s (f, &r->page); | |
492 | + qemu_put_8s (f, &r->pageh); | |
493 | + qemu_put_8s (f, &r->dack); | |
494 | + qemu_put_8s (f, &r->eop); | |
495 | + } | |
496 | +} | |
497 | + | |
498 | +static int dma_load (QEMUFile *f, void *opaque, int version_id) | |
499 | +{ | |
500 | + struct dma_cont *d = opaque; | |
501 | + int i; | |
502 | + | |
503 | + if (version_id != 1) | |
504 | + return -EINVAL; | |
505 | + | |
506 | + /* qemu_get_8s (f, &d->status); */ | |
507 | + qemu_get_8s (f, &d->command); | |
508 | + qemu_get_8s (f, &d->mask); | |
509 | + qemu_get_8s (f, &d->flip_flop); | |
510 | + qemu_get_be32s (f, &d->dshift); | |
511 | + | |
512 | + for (i = 0; i < 4; ++i) { | |
513 | + struct dma_regs *r = &d->regs[i]; | |
514 | + qemu_get_be32s (f, &r->now[0]); | |
515 | + qemu_get_be32s (f, &r->now[1]); | |
516 | + qemu_get_be16s (f, &r->base[0]); | |
517 | + qemu_get_be16s (f, &r->base[1]); | |
518 | + qemu_get_8s (f, &r->mode); | |
519 | + qemu_get_8s (f, &r->page); | |
520 | + qemu_get_8s (f, &r->pageh); | |
521 | + qemu_get_8s (f, &r->dack); | |
522 | + qemu_get_8s (f, &r->eop); | |
523 | + } | |
524 | + return 0; | |
525 | +} | |
526 | + | |
424 | 527 | void DMA_init (int high_page_enable) |
425 | 528 | { |
426 | - dma_init2(&dma_controllers[0], 0x00, 0, 0x80, | |
529 | + dma_init2(&dma_controllers[0], 0x00, 0, 0x80, | |
427 | 530 | high_page_enable ? 0x480 : -1); |
428 | 531 | dma_init2(&dma_controllers[1], 0xc0, 1, 0x88, |
429 | 532 | high_page_enable ? 0x488 : -1); |
533 | + register_savevm ("dma", 0, 1, dma_save, dma_load, &dma_controllers[0]); | |
534 | + register_savevm ("dma", 1, 1, dma_save, dma_load, &dma_controllers[1]); | |
430 | 535 | } | ... | ... |
hw/fdc.c
... | ... | @@ -313,7 +313,8 @@ static void fd_reset (fdrive_t *drv) |
313 | 313 | |
314 | 314 | static void fdctrl_reset (fdctrl_t *fdctrl, int do_irq); |
315 | 315 | static void fdctrl_reset_fifo (fdctrl_t *fdctrl); |
316 | -static int fdctrl_transfer_handler (void *opaque, target_ulong addr, int size); | |
316 | +static int fdctrl_transfer_handler (void *opaque, int nchan, | |
317 | + int dma_pos, int dma_len); | |
317 | 318 | static void fdctrl_raise_irq (fdctrl_t *fdctrl, uint8_t status); |
318 | 319 | static void fdctrl_result_timer(void *opaque); |
319 | 320 | |
... | ... | @@ -908,7 +909,8 @@ static void fdctrl_start_transfer_del (fdctrl_t *fdctrl, int direction) |
908 | 909 | } |
909 | 910 | |
910 | 911 | /* handlers for DMA transfers */ |
911 | -static int fdctrl_transfer_handler (void *opaque, target_ulong addr, int size) | |
912 | +static int fdctrl_transfer_handler (void *opaque, int nchan, | |
913 | + int dma_pos, int dma_len) | |
912 | 914 | { |
913 | 915 | fdctrl_t *fdctrl; |
914 | 916 | fdrive_t *cur_drv; |
... | ... | @@ -924,8 +926,8 @@ static int fdctrl_transfer_handler (void *opaque, target_ulong addr, int size) |
924 | 926 | if (fdctrl->data_dir == FD_DIR_SCANE || fdctrl->data_dir == FD_DIR_SCANL || |
925 | 927 | fdctrl->data_dir == FD_DIR_SCANH) |
926 | 928 | status2 = 0x04; |
927 | - if (size > fdctrl->data_len) | |
928 | - size = fdctrl->data_len; | |
929 | + if (dma_len > fdctrl->data_len) | |
930 | + dma_len = fdctrl->data_len; | |
929 | 931 | if (cur_drv->bs == NULL) { |
930 | 932 | if (fdctrl->data_dir == FD_DIR_WRITE) |
931 | 933 | fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00); |
... | ... | @@ -935,8 +937,8 @@ static int fdctrl_transfer_handler (void *opaque, target_ulong addr, int size) |
935 | 937 | goto transfer_error; |
936 | 938 | } |
937 | 939 | rel_pos = fdctrl->data_pos % FD_SECTOR_LEN; |
938 | - for (start_pos = fdctrl->data_pos; fdctrl->data_pos < size;) { | |
939 | - len = size - fdctrl->data_pos; | |
940 | + for (start_pos = fdctrl->data_pos; fdctrl->data_pos < dma_len;) { | |
941 | + len = dma_len - fdctrl->data_pos; | |
940 | 942 | if (len + rel_pos > FD_SECTOR_LEN) |
941 | 943 | len = FD_SECTOR_LEN - rel_pos; |
942 | 944 | FLOPPY_DPRINTF("copy %d bytes (%d %d %d) %d pos %d %02x %02x " |
... | ... | @@ -958,13 +960,17 @@ static int fdctrl_transfer_handler (void *opaque, target_ulong addr, int size) |
958 | 960 | switch (fdctrl->data_dir) { |
959 | 961 | case FD_DIR_READ: |
960 | 962 | /* READ commands */ |
961 | - cpu_physical_memory_write(addr + fdctrl->data_pos, | |
962 | - fdctrl->fifo + rel_pos, len); | |
963 | + DMA_write_memory (nchan, fdctrl->fifo + rel_pos, | |
964 | + fdctrl->data_pos, len); | |
965 | +/* cpu_physical_memory_write(addr + fdctrl->data_pos, */ | |
966 | +/* fdctrl->fifo + rel_pos, len); */ | |
963 | 967 | break; |
964 | 968 | case FD_DIR_WRITE: |
965 | 969 | /* WRITE commands */ |
966 | - cpu_physical_memory_read(addr + fdctrl->data_pos, | |
967 | - fdctrl->fifo + rel_pos, len); | |
970 | + DMA_read_memory (nchan, fdctrl->fifo + rel_pos, | |
971 | + fdctrl->data_pos, len); | |
972 | +/* cpu_physical_memory_read(addr + fdctrl->data_pos, */ | |
973 | +/* fdctrl->fifo + rel_pos, len); */ | |
968 | 974 | if (bdrv_write(cur_drv->bs, fd_sector(cur_drv), |
969 | 975 | fdctrl->fifo, 1) < 0) { |
970 | 976 | FLOPPY_ERROR("writting sector %d\n", fd_sector(cur_drv)); |
... | ... | @@ -977,8 +983,9 @@ static int fdctrl_transfer_handler (void *opaque, target_ulong addr, int size) |
977 | 983 | { |
978 | 984 | uint8_t tmpbuf[FD_SECTOR_LEN]; |
979 | 985 | int ret; |
980 | - cpu_physical_memory_read(addr + fdctrl->data_pos, | |
981 | - tmpbuf, len); | |
986 | + DMA_read_memory (nchan, tmpbuf, fdctrl->data_pos, len); | |
987 | +/* cpu_physical_memory_read(addr + fdctrl->data_pos, */ | |
988 | +/* tmpbuf, len); */ | |
982 | 989 | ret = memcmp(tmpbuf, fdctrl->fifo + rel_pos, len); |
983 | 990 | if (ret == 0) { |
984 | 991 | status2 = 0x08; | ... | ... |
hw/fmopl.c
0 → 100644
1 | +/* | |
2 | +** | |
3 | +** File: fmopl.c -- software implementation of FM sound generator | |
4 | +** | |
5 | +** Copyright (C) 1999,2000 Tatsuyuki Satoh , MultiArcadeMachineEmurator development | |
6 | +** | |
7 | +** Version 0.37a | |
8 | +** | |
9 | +*/ | |
10 | + | |
11 | +/* | |
12 | + preliminary : | |
13 | + Problem : | |
14 | + note: | |
15 | +*/ | |
16 | + | |
17 | +/* This version of fmopl.c is a fork of the MAME one, relicensed under the LGPL. | |
18 | + * | |
19 | + * This library is free software; you can redistribute it and/or | |
20 | + * modify it under the terms of the GNU Lesser General Public | |
21 | + * License as published by the Free Software Foundation; either | |
22 | + * version 2.1 of the License, or (at your option) any later version. | |
23 | + * | |
24 | + * This library is distributed in the hope that it will be useful, | |
25 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
26 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
27 | + * Lesser General Public License for more details. | |
28 | + * | |
29 | + * You should have received a copy of the GNU Lesser General Public | |
30 | + * License along with this library; if not, write to the Free Software | |
31 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
32 | + */ | |
33 | + | |
34 | +#define INLINE __inline | |
35 | +#define HAS_YM3812 1 | |
36 | + | |
37 | +#include <stdio.h> | |
38 | +#include <stdlib.h> | |
39 | +#include <string.h> | |
40 | +#include <stdarg.h> | |
41 | +#include <math.h> | |
42 | +//#include "driver.h" /* use M.A.M.E. */ | |
43 | +#include "fmopl.h" | |
44 | + | |
45 | +#ifndef PI | |
46 | +#define PI 3.14159265358979323846 | |
47 | +#endif | |
48 | + | |
49 | +/* -------------------- for debug --------------------- */ | |
50 | +/* #define OPL_OUTPUT_LOG */ | |
51 | +#ifdef OPL_OUTPUT_LOG | |
52 | +static FILE *opl_dbg_fp = NULL; | |
53 | +static FM_OPL *opl_dbg_opl[16]; | |
54 | +static int opl_dbg_maxchip,opl_dbg_chip; | |
55 | +#endif | |
56 | + | |
57 | +/* -------------------- preliminary define section --------------------- */ | |
58 | +/* attack/decay rate time rate */ | |
59 | +#define OPL_ARRATE 141280 /* RATE 4 = 2826.24ms @ 3.6MHz */ | |
60 | +#define OPL_DRRATE 1956000 /* RATE 4 = 39280.64ms @ 3.6MHz */ | |
61 | + | |
62 | +#define DELTAT_MIXING_LEVEL (1) /* DELTA-T ADPCM MIXING LEVEL */ | |
63 | + | |
64 | +#define FREQ_BITS 24 /* frequency turn */ | |
65 | + | |
66 | +/* counter bits = 20 , octerve 7 */ | |
67 | +#define FREQ_RATE (1<<(FREQ_BITS-20)) | |
68 | +#define TL_BITS (FREQ_BITS+2) | |
69 | + | |
70 | +/* final output shift , limit minimum and maximum */ | |
71 | +#define OPL_OUTSB (TL_BITS+3-16) /* OPL output final shift 16bit */ | |
72 | +#define OPL_MAXOUT (0x7fff<<OPL_OUTSB) | |
73 | +#define OPL_MINOUT (-0x8000<<OPL_OUTSB) | |
74 | + | |
75 | +/* -------------------- quality selection --------------------- */ | |
76 | + | |
77 | +/* sinwave entries */ | |
78 | +/* used static memory = SIN_ENT * 4 (byte) */ | |
79 | +#define SIN_ENT 2048 | |
80 | + | |
81 | +/* output level entries (envelope,sinwave) */ | |
82 | +/* envelope counter lower bits */ | |
83 | +#define ENV_BITS 16 | |
84 | +/* envelope output entries */ | |
85 | +#define EG_ENT 4096 | |
86 | +/* used dynamic memory = EG_ENT*4*4(byte)or EG_ENT*6*4(byte) */ | |
87 | +/* used static memory = EG_ENT*4 (byte) */ | |
88 | + | |
89 | +#define EG_OFF ((2*EG_ENT)<<ENV_BITS) /* OFF */ | |
90 | +#define EG_DED EG_OFF | |
91 | +#define EG_DST (EG_ENT<<ENV_BITS) /* DECAY START */ | |
92 | +#define EG_AED EG_DST | |
93 | +#define EG_AST 0 /* ATTACK START */ | |
94 | + | |
95 | +#define EG_STEP (96.0/EG_ENT) /* OPL is 0.1875 dB step */ | |
96 | + | |
97 | +/* LFO table entries */ | |
98 | +#define VIB_ENT 512 | |
99 | +#define VIB_SHIFT (32-9) | |
100 | +#define AMS_ENT 512 | |
101 | +#define AMS_SHIFT (32-9) | |
102 | + | |
103 | +#define VIB_RATE 256 | |
104 | + | |
105 | +/* -------------------- local defines , macros --------------------- */ | |
106 | + | |
107 | +/* register number to channel number , slot offset */ | |
108 | +#define SLOT1 0 | |
109 | +#define SLOT2 1 | |
110 | + | |
111 | +/* envelope phase */ | |
112 | +#define ENV_MOD_RR 0x00 | |
113 | +#define ENV_MOD_DR 0x01 | |
114 | +#define ENV_MOD_AR 0x02 | |
115 | + | |
116 | +/* -------------------- tables --------------------- */ | |
117 | +static const int slot_array[32]= | |
118 | +{ | |
119 | + 0, 2, 4, 1, 3, 5,-1,-1, | |
120 | + 6, 8,10, 7, 9,11,-1,-1, | |
121 | + 12,14,16,13,15,17,-1,-1, | |
122 | + -1,-1,-1,-1,-1,-1,-1,-1 | |
123 | +}; | |
124 | + | |
125 | +/* key scale level */ | |
126 | +/* table is 3dB/OCT , DV converts this in TL step at 6dB/OCT */ | |
127 | +#define DV (EG_STEP/2) | |
128 | +static const UINT32 KSL_TABLE[8*16]= | |
129 | +{ | |
130 | + /* OCT 0 */ | |
131 | + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, | |
132 | + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, | |
133 | + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, | |
134 | + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, | |
135 | + /* OCT 1 */ | |
136 | + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, | |
137 | + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, | |
138 | + 0.000/DV, 0.750/DV, 1.125/DV, 1.500/DV, | |
139 | + 1.875/DV, 2.250/DV, 2.625/DV, 3.000/DV, | |
140 | + /* OCT 2 */ | |
141 | + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, | |
142 | + 0.000/DV, 1.125/DV, 1.875/DV, 2.625/DV, | |
143 | + 3.000/DV, 3.750/DV, 4.125/DV, 4.500/DV, | |
144 | + 4.875/DV, 5.250/DV, 5.625/DV, 6.000/DV, | |
145 | + /* OCT 3 */ | |
146 | + 0.000/DV, 0.000/DV, 0.000/DV, 1.875/DV, | |
147 | + 3.000/DV, 4.125/DV, 4.875/DV, 5.625/DV, | |
148 | + 6.000/DV, 6.750/DV, 7.125/DV, 7.500/DV, | |
149 | + 7.875/DV, 8.250/DV, 8.625/DV, 9.000/DV, | |
150 | + /* OCT 4 */ | |
151 | + 0.000/DV, 0.000/DV, 3.000/DV, 4.875/DV, | |
152 | + 6.000/DV, 7.125/DV, 7.875/DV, 8.625/DV, | |
153 | + 9.000/DV, 9.750/DV,10.125/DV,10.500/DV, | |
154 | + 10.875/DV,11.250/DV,11.625/DV,12.000/DV, | |
155 | + /* OCT 5 */ | |
156 | + 0.000/DV, 3.000/DV, 6.000/DV, 7.875/DV, | |
157 | + 9.000/DV,10.125/DV,10.875/DV,11.625/DV, | |
158 | + 12.000/DV,12.750/DV,13.125/DV,13.500/DV, | |
159 | + 13.875/DV,14.250/DV,14.625/DV,15.000/DV, | |
160 | + /* OCT 6 */ | |
161 | + 0.000/DV, 6.000/DV, 9.000/DV,10.875/DV, | |
162 | + 12.000/DV,13.125/DV,13.875/DV,14.625/DV, | |
163 | + 15.000/DV,15.750/DV,16.125/DV,16.500/DV, | |
164 | + 16.875/DV,17.250/DV,17.625/DV,18.000/DV, | |
165 | + /* OCT 7 */ | |
166 | + 0.000/DV, 9.000/DV,12.000/DV,13.875/DV, | |
167 | + 15.000/DV,16.125/DV,16.875/DV,17.625/DV, | |
168 | + 18.000/DV,18.750/DV,19.125/DV,19.500/DV, | |
169 | + 19.875/DV,20.250/DV,20.625/DV,21.000/DV | |
170 | +}; | |
171 | +#undef DV | |
172 | + | |
173 | +/* sustain lebel table (3db per step) */ | |
174 | +/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/ | |
175 | +#define SC(db) (db*((3/EG_STEP)*(1<<ENV_BITS)))+EG_DST | |
176 | +static const INT32 SL_TABLE[16]={ | |
177 | + SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7), | |
178 | + SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31) | |
179 | +}; | |
180 | +#undef SC | |
181 | + | |
182 | +#define TL_MAX (EG_ENT*2) /* limit(tl + ksr + envelope) + sinwave */ | |
183 | +/* TotalLevel : 48 24 12 6 3 1.5 0.75 (dB) */ | |
184 | +/* TL_TABLE[ 0 to TL_MAX ] : plus section */ | |
185 | +/* TL_TABLE[ TL_MAX to TL_MAX+TL_MAX-1 ] : minus section */ | |
186 | +static INT32 *TL_TABLE; | |
187 | + | |
188 | +/* pointers to TL_TABLE with sinwave output offset */ | |
189 | +static INT32 **SIN_TABLE; | |
190 | + | |
191 | +/* LFO table */ | |
192 | +static INT32 *AMS_TABLE; | |
193 | +static INT32 *VIB_TABLE; | |
194 | + | |
195 | +/* envelope output curve table */ | |
196 | +/* attack + decay + OFF */ | |
197 | +static INT32 ENV_CURVE[2*EG_ENT+1]; | |
198 | + | |
199 | +/* multiple table */ | |
200 | +#define ML 2 | |
201 | +static const UINT32 MUL_TABLE[16]= { | |
202 | +/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 */ | |
203 | + 0.50*ML, 1.00*ML, 2.00*ML, 3.00*ML, 4.00*ML, 5.00*ML, 6.00*ML, 7.00*ML, | |
204 | + 8.00*ML, 9.00*ML,10.00*ML,10.00*ML,12.00*ML,12.00*ML,15.00*ML,15.00*ML | |
205 | +}; | |
206 | +#undef ML | |
207 | + | |
208 | +/* dummy attack / decay rate ( when rate == 0 ) */ | |
209 | +static INT32 RATE_0[16]= | |
210 | +{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; | |
211 | + | |
212 | +/* -------------------- static state --------------------- */ | |
213 | + | |
214 | +/* lock level of common table */ | |
215 | +static int num_lock = 0; | |
216 | + | |
217 | +/* work table */ | |
218 | +static void *cur_chip = NULL; /* current chip point */ | |
219 | +/* currenct chip state */ | |
220 | +/* static OPLSAMPLE *bufL,*bufR; */ | |
221 | +static OPL_CH *S_CH; | |
222 | +static OPL_CH *E_CH; | |
223 | +OPL_SLOT *SLOT7_1,*SLOT7_2,*SLOT8_1,*SLOT8_2; | |
224 | + | |
225 | +static INT32 outd[1]; | |
226 | +static INT32 ams; | |
227 | +static INT32 vib; | |
228 | +INT32 *ams_table; | |
229 | +INT32 *vib_table; | |
230 | +static INT32 amsIncr; | |
231 | +static INT32 vibIncr; | |
232 | +static INT32 feedback2; /* connect for SLOT 2 */ | |
233 | + | |
234 | +/* log output level */ | |
235 | +#define LOG_ERR 3 /* ERROR */ | |
236 | +#define LOG_WAR 2 /* WARNING */ | |
237 | +#define LOG_INF 1 /* INFORMATION */ | |
238 | + | |
239 | +//#define LOG_LEVEL LOG_INF | |
240 | +#define LOG_LEVEL LOG_ERR | |
241 | + | |
242 | +//#define LOG(n,x) if( (n)>=LOG_LEVEL ) logerror x | |
243 | +#define LOG(n,x) | |
244 | + | |
245 | +/* --------------------- subroutines --------------------- */ | |
246 | + | |
247 | +INLINE int Limit( int val, int max, int min ) { | |
248 | + if ( val > max ) | |
249 | + val = max; | |
250 | + else if ( val < min ) | |
251 | + val = min; | |
252 | + | |
253 | + return val; | |
254 | +} | |
255 | + | |
256 | +/* status set and IRQ handling */ | |
257 | +INLINE void OPL_STATUS_SET(FM_OPL *OPL,int flag) | |
258 | +{ | |
259 | + /* set status flag */ | |
260 | + OPL->status |= flag; | |
261 | + if(!(OPL->status & 0x80)) | |
262 | + { | |
263 | + if(OPL->status & OPL->statusmask) | |
264 | + { /* IRQ on */ | |
265 | + OPL->status |= 0x80; | |
266 | + /* callback user interrupt handler (IRQ is OFF to ON) */ | |
267 | + if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,1); | |
268 | + } | |
269 | + } | |
270 | +} | |
271 | + | |
272 | +/* status reset and IRQ handling */ | |
273 | +INLINE void OPL_STATUS_RESET(FM_OPL *OPL,int flag) | |
274 | +{ | |
275 | + /* reset status flag */ | |
276 | + OPL->status &=~flag; | |
277 | + if((OPL->status & 0x80)) | |
278 | + { | |
279 | + if (!(OPL->status & OPL->statusmask) ) | |
280 | + { | |
281 | + OPL->status &= 0x7f; | |
282 | + /* callback user interrupt handler (IRQ is ON to OFF) */ | |
283 | + if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,0); | |
284 | + } | |
285 | + } | |
286 | +} | |
287 | + | |
288 | +/* IRQ mask set */ | |
289 | +INLINE void OPL_STATUSMASK_SET(FM_OPL *OPL,int flag) | |
290 | +{ | |
291 | + OPL->statusmask = flag; | |
292 | + /* IRQ handling check */ | |
293 | + OPL_STATUS_SET(OPL,0); | |
294 | + OPL_STATUS_RESET(OPL,0); | |
295 | +} | |
296 | + | |
297 | +/* ----- key on ----- */ | |
298 | +INLINE void OPL_KEYON(OPL_SLOT *SLOT) | |
299 | +{ | |
300 | + /* sin wave restart */ | |
301 | + SLOT->Cnt = 0; | |
302 | + /* set attack */ | |
303 | + SLOT->evm = ENV_MOD_AR; | |
304 | + SLOT->evs = SLOT->evsa; | |
305 | + SLOT->evc = EG_AST; | |
306 | + SLOT->eve = EG_AED; | |
307 | +} | |
308 | +/* ----- key off ----- */ | |
309 | +INLINE void OPL_KEYOFF(OPL_SLOT *SLOT) | |
310 | +{ | |
311 | + if( SLOT->evm > ENV_MOD_RR) | |
312 | + { | |
313 | + /* set envelope counter from envleope output */ | |
314 | + SLOT->evm = ENV_MOD_RR; | |
315 | + if( !(SLOT->evc&EG_DST) ) | |
316 | + //SLOT->evc = (ENV_CURVE[SLOT->evc>>ENV_BITS]<<ENV_BITS) + EG_DST; | |
317 | + SLOT->evc = EG_DST; | |
318 | + SLOT->eve = EG_DED; | |
319 | + SLOT->evs = SLOT->evsr; | |
320 | + } | |
321 | +} | |
322 | + | |
323 | +/* ---------- calcrate Envelope Generator & Phase Generator ---------- */ | |
324 | +/* return : envelope output */ | |
325 | +INLINE UINT32 OPL_CALC_SLOT( OPL_SLOT *SLOT ) | |
326 | +{ | |
327 | + /* calcrate envelope generator */ | |
328 | + if( (SLOT->evc+=SLOT->evs) >= SLOT->eve ) | |
329 | + { | |
330 | + switch( SLOT->evm ){ | |
331 | + case ENV_MOD_AR: /* ATTACK -> DECAY1 */ | |
332 | + /* next DR */ | |
333 | + SLOT->evm = ENV_MOD_DR; | |
334 | + SLOT->evc = EG_DST; | |
335 | + SLOT->eve = SLOT->SL; | |
336 | + SLOT->evs = SLOT->evsd; | |
337 | + break; | |
338 | + case ENV_MOD_DR: /* DECAY -> SL or RR */ | |
339 | + SLOT->evc = SLOT->SL; | |
340 | + SLOT->eve = EG_DED; | |
341 | + if(SLOT->eg_typ) | |
342 | + { | |
343 | + SLOT->evs = 0; | |
344 | + } | |
345 | + else | |
346 | + { | |
347 | + SLOT->evm = ENV_MOD_RR; | |
348 | + SLOT->evs = SLOT->evsr; | |
349 | + } | |
350 | + break; | |
351 | + case ENV_MOD_RR: /* RR -> OFF */ | |
352 | + SLOT->evc = EG_OFF; | |
353 | + SLOT->eve = EG_OFF+1; | |
354 | + SLOT->evs = 0; | |
355 | + break; | |
356 | + } | |
357 | + } | |
358 | + /* calcrate envelope */ | |
359 | + return SLOT->TLL+ENV_CURVE[SLOT->evc>>ENV_BITS]+(SLOT->ams ? ams : 0); | |
360 | +} | |
361 | + | |
362 | +/* set algorythm connection */ | |
363 | +static void set_algorythm( OPL_CH *CH) | |
364 | +{ | |
365 | + INT32 *carrier = &outd[0]; | |
366 | + CH->connect1 = CH->CON ? carrier : &feedback2; | |
367 | + CH->connect2 = carrier; | |
368 | +} | |
369 | + | |
370 | +/* ---------- frequency counter for operater update ---------- */ | |
371 | +INLINE void CALC_FCSLOT(OPL_CH *CH,OPL_SLOT *SLOT) | |
372 | +{ | |
373 | + int ksr; | |
374 | + | |
375 | + /* frequency step counter */ | |
376 | + SLOT->Incr = CH->fc * SLOT->mul; | |
377 | + ksr = CH->kcode >> SLOT->KSR; | |
378 | + | |
379 | + if( SLOT->ksr != ksr ) | |
380 | + { | |
381 | + SLOT->ksr = ksr; | |
382 | + /* attack , decay rate recalcration */ | |
383 | + SLOT->evsa = SLOT->AR[ksr]; | |
384 | + SLOT->evsd = SLOT->DR[ksr]; | |
385 | + SLOT->evsr = SLOT->RR[ksr]; | |
386 | + } | |
387 | + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); | |
388 | +} | |
389 | + | |
390 | +/* set multi,am,vib,EG-TYP,KSR,mul */ | |
391 | +INLINE void set_mul(FM_OPL *OPL,int slot,int v) | |
392 | +{ | |
393 | + OPL_CH *CH = &OPL->P_CH[slot/2]; | |
394 | + OPL_SLOT *SLOT = &CH->SLOT[slot&1]; | |
395 | + | |
396 | + SLOT->mul = MUL_TABLE[v&0x0f]; | |
397 | + SLOT->KSR = (v&0x10) ? 0 : 2; | |
398 | + SLOT->eg_typ = (v&0x20)>>5; | |
399 | + SLOT->vib = (v&0x40); | |
400 | + SLOT->ams = (v&0x80); | |
401 | + CALC_FCSLOT(CH,SLOT); | |
402 | +} | |
403 | + | |
404 | +/* set ksl & tl */ | |
405 | +INLINE void set_ksl_tl(FM_OPL *OPL,int slot,int v) | |
406 | +{ | |
407 | + OPL_CH *CH = &OPL->P_CH[slot/2]; | |
408 | + OPL_SLOT *SLOT = &CH->SLOT[slot&1]; | |
409 | + int ksl = v>>6; /* 0 / 1.5 / 3 / 6 db/OCT */ | |
410 | + | |
411 | + SLOT->ksl = ksl ? 3-ksl : 31; | |
412 | + SLOT->TL = (v&0x3f)*(0.75/EG_STEP); /* 0.75db step */ | |
413 | + | |
414 | + if( !(OPL->mode&0x80) ) | |
415 | + { /* not CSM latch total level */ | |
416 | + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); | |
417 | + } | |
418 | +} | |
419 | + | |
420 | +/* set attack rate & decay rate */ | |
421 | +INLINE void set_ar_dr(FM_OPL *OPL,int slot,int v) | |
422 | +{ | |
423 | + OPL_CH *CH = &OPL->P_CH[slot/2]; | |
424 | + OPL_SLOT *SLOT = &CH->SLOT[slot&1]; | |
425 | + int ar = v>>4; | |
426 | + int dr = v&0x0f; | |
427 | + | |
428 | + SLOT->AR = ar ? &OPL->AR_TABLE[ar<<2] : RATE_0; | |
429 | + SLOT->evsa = SLOT->AR[SLOT->ksr]; | |
430 | + if( SLOT->evm == ENV_MOD_AR ) SLOT->evs = SLOT->evsa; | |
431 | + | |
432 | + SLOT->DR = dr ? &OPL->DR_TABLE[dr<<2] : RATE_0; | |
433 | + SLOT->evsd = SLOT->DR[SLOT->ksr]; | |
434 | + if( SLOT->evm == ENV_MOD_DR ) SLOT->evs = SLOT->evsd; | |
435 | +} | |
436 | + | |
437 | +/* set sustain level & release rate */ | |
438 | +INLINE void set_sl_rr(FM_OPL *OPL,int slot,int v) | |
439 | +{ | |
440 | + OPL_CH *CH = &OPL->P_CH[slot/2]; | |
441 | + OPL_SLOT *SLOT = &CH->SLOT[slot&1]; | |
442 | + int sl = v>>4; | |
443 | + int rr = v & 0x0f; | |
444 | + | |
445 | + SLOT->SL = SL_TABLE[sl]; | |
446 | + if( SLOT->evm == ENV_MOD_DR ) SLOT->eve = SLOT->SL; | |
447 | + SLOT->RR = &OPL->DR_TABLE[rr<<2]; | |
448 | + SLOT->evsr = SLOT->RR[SLOT->ksr]; | |
449 | + if( SLOT->evm == ENV_MOD_RR ) SLOT->evs = SLOT->evsr; | |
450 | +} | |
451 | + | |
452 | +/* operator output calcrator */ | |
453 | +#define OP_OUT(slot,env,con) slot->wavetable[((slot->Cnt+con)/(0x1000000/SIN_ENT))&(SIN_ENT-1)][env] | |
454 | +/* ---------- calcrate one of channel ---------- */ | |
455 | +INLINE void OPL_CALC_CH( OPL_CH *CH ) | |
456 | +{ | |
457 | + UINT32 env_out; | |
458 | + OPL_SLOT *SLOT; | |
459 | + | |
460 | + feedback2 = 0; | |
461 | + /* SLOT 1 */ | |
462 | + SLOT = &CH->SLOT[SLOT1]; | |
463 | + env_out=OPL_CALC_SLOT(SLOT); | |
464 | + if( env_out < EG_ENT-1 ) | |
465 | + { | |
466 | + /* PG */ | |
467 | + if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE); | |
468 | + else SLOT->Cnt += SLOT->Incr; | |
469 | + /* connectoion */ | |
470 | + if(CH->FB) | |
471 | + { | |
472 | + int feedback1 = (CH->op1_out[0]+CH->op1_out[1])>>CH->FB; | |
473 | + CH->op1_out[1] = CH->op1_out[0]; | |
474 | + *CH->connect1 += CH->op1_out[0] = OP_OUT(SLOT,env_out,feedback1); | |
475 | + } | |
476 | + else | |
477 | + { | |
478 | + *CH->connect1 += OP_OUT(SLOT,env_out,0); | |
479 | + } | |
480 | + }else | |
481 | + { | |
482 | + CH->op1_out[1] = CH->op1_out[0]; | |
483 | + CH->op1_out[0] = 0; | |
484 | + } | |
485 | + /* SLOT 2 */ | |
486 | + SLOT = &CH->SLOT[SLOT2]; | |
487 | + env_out=OPL_CALC_SLOT(SLOT); | |
488 | + if( env_out < EG_ENT-1 ) | |
489 | + { | |
490 | + /* PG */ | |
491 | + if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE); | |
492 | + else SLOT->Cnt += SLOT->Incr; | |
493 | + /* connectoion */ | |
494 | + outd[0] += OP_OUT(SLOT,env_out, feedback2); | |
495 | + } | |
496 | +} | |
497 | + | |
498 | +/* ---------- calcrate rythm block ---------- */ | |
499 | +#define WHITE_NOISE_db 6.0 | |
500 | +INLINE void OPL_CALC_RH( OPL_CH *CH ) | |
501 | +{ | |
502 | + UINT32 env_tam,env_sd,env_top,env_hh; | |
503 | + int whitenoise = (rand()&1)*(WHITE_NOISE_db/EG_STEP); | |
504 | + INT32 tone8; | |
505 | + | |
506 | + OPL_SLOT *SLOT; | |
507 | + int env_out; | |
508 | + | |
509 | + /* BD : same as FM serial mode and output level is large */ | |
510 | + feedback2 = 0; | |
511 | + /* SLOT 1 */ | |
512 | + SLOT = &CH[6].SLOT[SLOT1]; | |
513 | + env_out=OPL_CALC_SLOT(SLOT); | |
514 | + if( env_out < EG_ENT-1 ) | |
515 | + { | |
516 | + /* PG */ | |
517 | + if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE); | |
518 | + else SLOT->Cnt += SLOT->Incr; | |
519 | + /* connectoion */ | |
520 | + if(CH[6].FB) | |
521 | + { | |
522 | + int feedback1 = (CH[6].op1_out[0]+CH[6].op1_out[1])>>CH[6].FB; | |
523 | + CH[6].op1_out[1] = CH[6].op1_out[0]; | |
524 | + feedback2 = CH[6].op1_out[0] = OP_OUT(SLOT,env_out,feedback1); | |
525 | + } | |
526 | + else | |
527 | + { | |
528 | + feedback2 = OP_OUT(SLOT,env_out,0); | |
529 | + } | |
530 | + }else | |
531 | + { | |
532 | + feedback2 = 0; | |
533 | + CH[6].op1_out[1] = CH[6].op1_out[0]; | |
534 | + CH[6].op1_out[0] = 0; | |
535 | + } | |
536 | + /* SLOT 2 */ | |
537 | + SLOT = &CH[6].SLOT[SLOT2]; | |
538 | + env_out=OPL_CALC_SLOT(SLOT); | |
539 | + if( env_out < EG_ENT-1 ) | |
540 | + { | |
541 | + /* PG */ | |
542 | + if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE); | |
543 | + else SLOT->Cnt += SLOT->Incr; | |
544 | + /* connectoion */ | |
545 | + outd[0] += OP_OUT(SLOT,env_out, feedback2)*2; | |
546 | + } | |
547 | + | |
548 | + // SD (17) = mul14[fnum7] + white noise | |
549 | + // TAM (15) = mul15[fnum8] | |
550 | + // TOP (18) = fnum6(mul18[fnum8]+whitenoise) | |
551 | + // HH (14) = fnum7(mul18[fnum8]+whitenoise) + white noise | |
552 | + env_sd =OPL_CALC_SLOT(SLOT7_2) + whitenoise; | |
553 | + env_tam=OPL_CALC_SLOT(SLOT8_1); | |
554 | + env_top=OPL_CALC_SLOT(SLOT8_2); | |
555 | + env_hh =OPL_CALC_SLOT(SLOT7_1) + whitenoise; | |
556 | + | |
557 | + /* PG */ | |
558 | + if(SLOT7_1->vib) SLOT7_1->Cnt += (2*SLOT7_1->Incr*vib/VIB_RATE); | |
559 | + else SLOT7_1->Cnt += 2*SLOT7_1->Incr; | |
560 | + if(SLOT7_2->vib) SLOT7_2->Cnt += ((CH[7].fc*8)*vib/VIB_RATE); | |
561 | + else SLOT7_2->Cnt += (CH[7].fc*8); | |
562 | + if(SLOT8_1->vib) SLOT8_1->Cnt += (SLOT8_1->Incr*vib/VIB_RATE); | |
563 | + else SLOT8_1->Cnt += SLOT8_1->Incr; | |
564 | + if(SLOT8_2->vib) SLOT8_2->Cnt += ((CH[8].fc*48)*vib/VIB_RATE); | |
565 | + else SLOT8_2->Cnt += (CH[8].fc*48); | |
566 | + | |
567 | + tone8 = OP_OUT(SLOT8_2,whitenoise,0 ); | |
568 | + | |
569 | + /* SD */ | |
570 | + if( env_sd < EG_ENT-1 ) | |
571 | + outd[0] += OP_OUT(SLOT7_1,env_sd, 0)*8; | |
572 | + /* TAM */ | |
573 | + if( env_tam < EG_ENT-1 ) | |
574 | + outd[0] += OP_OUT(SLOT8_1,env_tam, 0)*2; | |
575 | + /* TOP-CY */ | |
576 | + if( env_top < EG_ENT-1 ) | |
577 | + outd[0] += OP_OUT(SLOT7_2,env_top,tone8)*2; | |
578 | + /* HH */ | |
579 | + if( env_hh < EG_ENT-1 ) | |
580 | + outd[0] += OP_OUT(SLOT7_2,env_hh,tone8)*2; | |
581 | +} | |
582 | + | |
583 | +/* ----------- initialize time tabls ----------- */ | |
584 | +static void init_timetables( FM_OPL *OPL , int ARRATE , int DRRATE ) | |
585 | +{ | |
586 | + int i; | |
587 | + double rate; | |
588 | + | |
589 | + /* make attack rate & decay rate tables */ | |
590 | + for (i = 0;i < 4;i++) OPL->AR_TABLE[i] = OPL->DR_TABLE[i] = 0; | |
591 | + for (i = 4;i <= 60;i++){ | |
592 | + rate = OPL->freqbase; /* frequency rate */ | |
593 | + if( i < 60 ) rate *= 1.0+(i&3)*0.25; /* b0-1 : x1 , x1.25 , x1.5 , x1.75 */ | |
594 | + rate *= 1<<((i>>2)-1); /* b2-5 : shift bit */ | |
595 | + rate *= (double)(EG_ENT<<ENV_BITS); | |
596 | + OPL->AR_TABLE[i] = rate / ARRATE; | |
597 | + OPL->DR_TABLE[i] = rate / DRRATE; | |
598 | + } | |
599 | + for (i = 60;i < 76;i++) | |
600 | + { | |
601 | + OPL->AR_TABLE[i] = EG_AED-1; | |
602 | + OPL->DR_TABLE[i] = OPL->DR_TABLE[60]; | |
603 | + } | |
604 | +#if 0 | |
605 | + for (i = 0;i < 64 ;i++){ /* make for overflow area */ | |
606 | + LOG(LOG_WAR,("rate %2d , ar %f ms , dr %f ms \n",i, | |
607 | + ((double)(EG_ENT<<ENV_BITS) / OPL->AR_TABLE[i]) * (1000.0 / OPL->rate), | |
608 | + ((double)(EG_ENT<<ENV_BITS) / OPL->DR_TABLE[i]) * (1000.0 / OPL->rate) )); | |
609 | + } | |
610 | +#endif | |
611 | +} | |
612 | + | |
613 | +/* ---------- generic table initialize ---------- */ | |
614 | +static int OPLOpenTable( void ) | |
615 | +{ | |
616 | + int s,t; | |
617 | + double rate; | |
618 | + int i,j; | |
619 | + double pom; | |
620 | + | |
621 | + /* allocate dynamic tables */ | |
622 | + if( (TL_TABLE = malloc(TL_MAX*2*sizeof(INT32))) == NULL) | |
623 | + return 0; | |
624 | + if( (SIN_TABLE = malloc(SIN_ENT*4 *sizeof(INT32 *))) == NULL) | |
625 | + { | |
626 | + free(TL_TABLE); | |
627 | + return 0; | |
628 | + } | |
629 | + if( (AMS_TABLE = malloc(AMS_ENT*2 *sizeof(INT32))) == NULL) | |
630 | + { | |
631 | + free(TL_TABLE); | |
632 | + free(SIN_TABLE); | |
633 | + return 0; | |
634 | + } | |
635 | + if( (VIB_TABLE = malloc(VIB_ENT*2 *sizeof(INT32))) == NULL) | |
636 | + { | |
637 | + free(TL_TABLE); | |
638 | + free(SIN_TABLE); | |
639 | + free(AMS_TABLE); | |
640 | + return 0; | |
641 | + } | |
642 | + /* make total level table */ | |
643 | + for (t = 0;t < EG_ENT-1 ;t++){ | |
644 | + rate = ((1<<TL_BITS)-1)/pow(10,EG_STEP*t/20); /* dB -> voltage */ | |
645 | + TL_TABLE[ t] = (int)rate; | |
646 | + TL_TABLE[TL_MAX+t] = -TL_TABLE[t]; | |
647 | +/* LOG(LOG_INF,("TotalLevel(%3d) = %x\n",t,TL_TABLE[t]));*/ | |
648 | + } | |
649 | + /* fill volume off area */ | |
650 | + for ( t = EG_ENT-1; t < TL_MAX ;t++){ | |
651 | + TL_TABLE[t] = TL_TABLE[TL_MAX+t] = 0; | |
652 | + } | |
653 | + | |
654 | + /* make sinwave table (total level offet) */ | |
655 | + /* degree 0 = degree 180 = off */ | |
656 | + SIN_TABLE[0] = SIN_TABLE[SIN_ENT/2] = &TL_TABLE[EG_ENT-1]; | |
657 | + for (s = 1;s <= SIN_ENT/4;s++){ | |
658 | + pom = sin(2*PI*s/SIN_ENT); /* sin */ | |
659 | + pom = 20*log10(1/pom); /* decibel */ | |
660 | + j = pom / EG_STEP; /* TL_TABLE steps */ | |
661 | + | |
662 | + /* degree 0 - 90 , degree 180 - 90 : plus section */ | |
663 | + SIN_TABLE[ s] = SIN_TABLE[SIN_ENT/2-s] = &TL_TABLE[j]; | |
664 | + /* degree 180 - 270 , degree 360 - 270 : minus section */ | |
665 | + SIN_TABLE[SIN_ENT/2+s] = SIN_TABLE[SIN_ENT -s] = &TL_TABLE[TL_MAX+j]; | |
666 | +/* LOG(LOG_INF,("sin(%3d) = %f:%f db\n",s,pom,(double)j * EG_STEP));*/ | |
667 | + } | |
668 | + for (s = 0;s < SIN_ENT;s++) | |
669 | + { | |
670 | + SIN_TABLE[SIN_ENT*1+s] = s<(SIN_ENT/2) ? SIN_TABLE[s] : &TL_TABLE[EG_ENT]; | |
671 | + SIN_TABLE[SIN_ENT*2+s] = SIN_TABLE[s % (SIN_ENT/2)]; | |
672 | + SIN_TABLE[SIN_ENT*3+s] = (s/(SIN_ENT/4))&1 ? &TL_TABLE[EG_ENT] : SIN_TABLE[SIN_ENT*2+s]; | |
673 | + } | |
674 | + | |
675 | + /* envelope counter -> envelope output table */ | |
676 | + for (i=0; i<EG_ENT; i++) | |
677 | + { | |
678 | + /* ATTACK curve */ | |
679 | + pom = pow( ((double)(EG_ENT-1-i)/EG_ENT) , 8 ) * EG_ENT; | |
680 | + /* if( pom >= EG_ENT ) pom = EG_ENT-1; */ | |
681 | + ENV_CURVE[i] = (int)pom; | |
682 | + /* DECAY ,RELEASE curve */ | |
683 | + ENV_CURVE[(EG_DST>>ENV_BITS)+i]= i; | |
684 | + } | |
685 | + /* off */ | |
686 | + ENV_CURVE[EG_OFF>>ENV_BITS]= EG_ENT-1; | |
687 | + /* make LFO ams table */ | |
688 | + for (i=0; i<AMS_ENT; i++) | |
689 | + { | |
690 | + pom = (1.0+sin(2*PI*i/AMS_ENT))/2; /* sin */ | |
691 | + AMS_TABLE[i] = (1.0/EG_STEP)*pom; /* 1dB */ | |
692 | + AMS_TABLE[AMS_ENT+i] = (4.8/EG_STEP)*pom; /* 4.8dB */ | |
693 | + } | |
694 | + /* make LFO vibrate table */ | |
695 | + for (i=0; i<VIB_ENT; i++) | |
696 | + { | |
697 | + /* 100cent = 1seminote = 6% ?? */ | |
698 | + pom = (double)VIB_RATE*0.06*sin(2*PI*i/VIB_ENT); /* +-100sect step */ | |
699 | + VIB_TABLE[i] = VIB_RATE + (pom*0.07); /* +- 7cent */ | |
700 | + VIB_TABLE[VIB_ENT+i] = VIB_RATE + (pom*0.14); /* +-14cent */ | |
701 | + /* LOG(LOG_INF,("vib %d=%d\n",i,VIB_TABLE[VIB_ENT+i])); */ | |
702 | + } | |
703 | + return 1; | |
704 | +} | |
705 | + | |
706 | + | |
707 | +static void OPLCloseTable( void ) | |
708 | +{ | |
709 | + free(TL_TABLE); | |
710 | + free(SIN_TABLE); | |
711 | + free(AMS_TABLE); | |
712 | + free(VIB_TABLE); | |
713 | +} | |
714 | + | |
715 | +/* CSM Key Controll */ | |
716 | +INLINE void CSMKeyControll(OPL_CH *CH) | |
717 | +{ | |
718 | + OPL_SLOT *slot1 = &CH->SLOT[SLOT1]; | |
719 | + OPL_SLOT *slot2 = &CH->SLOT[SLOT2]; | |
720 | + /* all key off */ | |
721 | + OPL_KEYOFF(slot1); | |
722 | + OPL_KEYOFF(slot2); | |
723 | + /* total level latch */ | |
724 | + slot1->TLL = slot1->TL + (CH->ksl_base>>slot1->ksl); | |
725 | + slot1->TLL = slot1->TL + (CH->ksl_base>>slot1->ksl); | |
726 | + /* key on */ | |
727 | + CH->op1_out[0] = CH->op1_out[1] = 0; | |
728 | + OPL_KEYON(slot1); | |
729 | + OPL_KEYON(slot2); | |
730 | +} | |
731 | + | |
732 | +/* ---------- opl initialize ---------- */ | |
733 | +static void OPL_initalize(FM_OPL *OPL) | |
734 | +{ | |
735 | + int fn; | |
736 | + | |
737 | + /* frequency base */ | |
738 | + OPL->freqbase = (OPL->rate) ? ((double)OPL->clock / OPL->rate) / 72 : 0; | |
739 | + /* Timer base time */ | |
740 | + OPL->TimerBase = 1.0/((double)OPL->clock / 72.0 ); | |
741 | + /* make time tables */ | |
742 | + init_timetables( OPL , OPL_ARRATE , OPL_DRRATE ); | |
743 | + /* make fnumber -> increment counter table */ | |
744 | + for( fn=0 ; fn < 1024 ; fn++ ) | |
745 | + { | |
746 | + OPL->FN_TABLE[fn] = OPL->freqbase * fn * FREQ_RATE * (1<<7) / 2; | |
747 | + } | |
748 | + /* LFO freq.table */ | |
749 | + OPL->amsIncr = OPL->rate ? (double)AMS_ENT*(1<<AMS_SHIFT) / OPL->rate * 3.7 * ((double)OPL->clock/3600000) : 0; | |
750 | + OPL->vibIncr = OPL->rate ? (double)VIB_ENT*(1<<VIB_SHIFT) / OPL->rate * 6.4 * ((double)OPL->clock/3600000) : 0; | |
751 | +} | |
752 | + | |
753 | +/* ---------- write a OPL registers ---------- */ | |
754 | +static void OPLWriteReg(FM_OPL *OPL, int r, int v) | |
755 | +{ | |
756 | + OPL_CH *CH; | |
757 | + int slot; | |
758 | + int block_fnum; | |
759 | + | |
760 | + switch(r&0xe0) | |
761 | + { | |
762 | + case 0x00: /* 00-1f:controll */ | |
763 | + switch(r&0x1f) | |
764 | + { | |
765 | + case 0x01: | |
766 | + /* wave selector enable */ | |
767 | + if(OPL->type&OPL_TYPE_WAVESEL) | |
768 | + { | |
769 | + OPL->wavesel = v&0x20; | |
770 | + if(!OPL->wavesel) | |
771 | + { | |
772 | + /* preset compatible mode */ | |
773 | + int c; | |
774 | + for(c=0;c<OPL->max_ch;c++) | |
775 | + { | |
776 | + OPL->P_CH[c].SLOT[SLOT1].wavetable = &SIN_TABLE[0]; | |
777 | + OPL->P_CH[c].SLOT[SLOT2].wavetable = &SIN_TABLE[0]; | |
778 | + } | |
779 | + } | |
780 | + } | |
781 | + return; | |
782 | + case 0x02: /* Timer 1 */ | |
783 | + OPL->T[0] = (256-v)*4; | |
784 | + break; | |
785 | + case 0x03: /* Timer 2 */ | |
786 | + OPL->T[1] = (256-v)*16; | |
787 | + return; | |
788 | + case 0x04: /* IRQ clear / mask and Timer enable */ | |
789 | + if(v&0x80) | |
790 | + { /* IRQ flag clear */ | |
791 | + OPL_STATUS_RESET(OPL,0x7f); | |
792 | + } | |
793 | + else | |
794 | + { /* set IRQ mask ,timer enable*/ | |
795 | + UINT8 st1 = v&1; | |
796 | + UINT8 st2 = (v>>1)&1; | |
797 | + /* IRQRST,T1MSK,t2MSK,EOSMSK,BRMSK,x,ST2,ST1 */ | |
798 | + OPL_STATUS_RESET(OPL,v&0x78); | |
799 | + OPL_STATUSMASK_SET(OPL,((~v)&0x78)|0x01); | |
800 | + /* timer 2 */ | |
801 | + if(OPL->st[1] != st2) | |
802 | + { | |
803 | + double interval = st2 ? (double)OPL->T[1]*OPL->TimerBase : 0.0; | |
804 | + OPL->st[1] = st2; | |
805 | + if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+1,interval); | |
806 | + } | |
807 | + /* timer 1 */ | |
808 | + if(OPL->st[0] != st1) | |
809 | + { | |
810 | + double interval = st1 ? (double)OPL->T[0]*OPL->TimerBase : 0.0; | |
811 | + OPL->st[0] = st1; | |
812 | + if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+0,interval); | |
813 | + } | |
814 | + } | |
815 | + return; | |
816 | +#if BUILD_Y8950 | |
817 | + case 0x06: /* Key Board OUT */ | |
818 | + if(OPL->type&OPL_TYPE_KEYBOARD) | |
819 | + { | |
820 | + if(OPL->keyboardhandler_w) | |
821 | + OPL->keyboardhandler_w(OPL->keyboard_param,v); | |
822 | + else | |
823 | + LOG(LOG_WAR,("OPL:write unmapped KEYBOARD port\n")); | |
824 | + } | |
825 | + return; | |
826 | + case 0x07: /* DELTA-T controll : START,REC,MEMDATA,REPT,SPOFF,x,x,RST */ | |
827 | + if(OPL->type&OPL_TYPE_ADPCM) | |
828 | + YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v); | |
829 | + return; | |
830 | + case 0x08: /* MODE,DELTA-T : CSM,NOTESEL,x,x,smpl,da/ad,64k,rom */ | |
831 | + OPL->mode = v; | |
832 | + v&=0x1f; /* for DELTA-T unit */ | |
833 | + case 0x09: /* START ADD */ | |
834 | + case 0x0a: | |
835 | + case 0x0b: /* STOP ADD */ | |
836 | + case 0x0c: | |
837 | + case 0x0d: /* PRESCALE */ | |
838 | + case 0x0e: | |
839 | + case 0x0f: /* ADPCM data */ | |
840 | + case 0x10: /* DELTA-N */ | |
841 | + case 0x11: /* DELTA-N */ | |
842 | + case 0x12: /* EG-CTRL */ | |
843 | + if(OPL->type&OPL_TYPE_ADPCM) | |
844 | + YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v); | |
845 | + return; | |
846 | +#if 0 | |
847 | + case 0x15: /* DAC data */ | |
848 | + case 0x16: | |
849 | + case 0x17: /* SHIFT */ | |
850 | + return; | |
851 | + case 0x18: /* I/O CTRL (Direction) */ | |
852 | + if(OPL->type&OPL_TYPE_IO) | |
853 | + OPL->portDirection = v&0x0f; | |
854 | + return; | |
855 | + case 0x19: /* I/O DATA */ | |
856 | + if(OPL->type&OPL_TYPE_IO) | |
857 | + { | |
858 | + OPL->portLatch = v; | |
859 | + if(OPL->porthandler_w) | |
860 | + OPL->porthandler_w(OPL->port_param,v&OPL->portDirection); | |
861 | + } | |
862 | + return; | |
863 | + case 0x1a: /* PCM data */ | |
864 | + return; | |
865 | +#endif | |
866 | +#endif | |
867 | + } | |
868 | + break; | |
869 | + case 0x20: /* am,vib,ksr,eg type,mul */ | |
870 | + slot = slot_array[r&0x1f]; | |
871 | + if(slot == -1) return; | |
872 | + set_mul(OPL,slot,v); | |
873 | + return; | |
874 | + case 0x40: | |
875 | + slot = slot_array[r&0x1f]; | |
876 | + if(slot == -1) return; | |
877 | + set_ksl_tl(OPL,slot,v); | |
878 | + return; | |
879 | + case 0x60: | |
880 | + slot = slot_array[r&0x1f]; | |
881 | + if(slot == -1) return; | |
882 | + set_ar_dr(OPL,slot,v); | |
883 | + return; | |
884 | + case 0x80: | |
885 | + slot = slot_array[r&0x1f]; | |
886 | + if(slot == -1) return; | |
887 | + set_sl_rr(OPL,slot,v); | |
888 | + return; | |
889 | + case 0xa0: | |
890 | + switch(r) | |
891 | + { | |
892 | + case 0xbd: | |
893 | + /* amsep,vibdep,r,bd,sd,tom,tc,hh */ | |
894 | + { | |
895 | + UINT8 rkey = OPL->rythm^v; | |
896 | + OPL->ams_table = &AMS_TABLE[v&0x80 ? AMS_ENT : 0]; | |
897 | + OPL->vib_table = &VIB_TABLE[v&0x40 ? VIB_ENT : 0]; | |
898 | + OPL->rythm = v&0x3f; | |
899 | + if(OPL->rythm&0x20) | |
900 | + { | |
901 | +#if 0 | |
902 | + usrintf_showmessage("OPL Rythm mode select"); | |
903 | +#endif | |
904 | + /* BD key on/off */ | |
905 | + if(rkey&0x10) | |
906 | + { | |
907 | + if(v&0x10) | |
908 | + { | |
909 | + OPL->P_CH[6].op1_out[0] = OPL->P_CH[6].op1_out[1] = 0; | |
910 | + OPL_KEYON(&OPL->P_CH[6].SLOT[SLOT1]); | |
911 | + OPL_KEYON(&OPL->P_CH[6].SLOT[SLOT2]); | |
912 | + } | |
913 | + else | |
914 | + { | |
915 | + OPL_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1]); | |
916 | + OPL_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2]); | |
917 | + } | |
918 | + } | |
919 | + /* SD key on/off */ | |
920 | + if(rkey&0x08) | |
921 | + { | |
922 | + if(v&0x08) OPL_KEYON(&OPL->P_CH[7].SLOT[SLOT2]); | |
923 | + else OPL_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2]); | |
924 | + }/* TAM key on/off */ | |
925 | + if(rkey&0x04) | |
926 | + { | |
927 | + if(v&0x04) OPL_KEYON(&OPL->P_CH[8].SLOT[SLOT1]); | |
928 | + else OPL_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1]); | |
929 | + } | |
930 | + /* TOP-CY key on/off */ | |
931 | + if(rkey&0x02) | |
932 | + { | |
933 | + if(v&0x02) OPL_KEYON(&OPL->P_CH[8].SLOT[SLOT2]); | |
934 | + else OPL_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2]); | |
935 | + } | |
936 | + /* HH key on/off */ | |
937 | + if(rkey&0x01) | |
938 | + { | |
939 | + if(v&0x01) OPL_KEYON(&OPL->P_CH[7].SLOT[SLOT1]); | |
940 | + else OPL_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1]); | |
941 | + } | |
942 | + } | |
943 | + } | |
944 | + return; | |
945 | + } | |
946 | + /* keyon,block,fnum */ | |
947 | + if( (r&0x0f) > 8) return; | |
948 | + CH = &OPL->P_CH[r&0x0f]; | |
949 | + if(!(r&0x10)) | |
950 | + { /* a0-a8 */ | |
951 | + block_fnum = (CH->block_fnum&0x1f00) | v; | |
952 | + } | |
953 | + else | |
954 | + { /* b0-b8 */ | |
955 | + int keyon = (v>>5)&1; | |
956 | + block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff); | |
957 | + if(CH->keyon != keyon) | |
958 | + { | |
959 | + if( (CH->keyon=keyon) ) | |
960 | + { | |
961 | + CH->op1_out[0] = CH->op1_out[1] = 0; | |
962 | + OPL_KEYON(&CH->SLOT[SLOT1]); | |
963 | + OPL_KEYON(&CH->SLOT[SLOT2]); | |
964 | + } | |
965 | + else | |
966 | + { | |
967 | + OPL_KEYOFF(&CH->SLOT[SLOT1]); | |
968 | + OPL_KEYOFF(&CH->SLOT[SLOT2]); | |
969 | + } | |
970 | + } | |
971 | + } | |
972 | + /* update */ | |
973 | + if(CH->block_fnum != block_fnum) | |
974 | + { | |
975 | + int blockRv = 7-(block_fnum>>10); | |
976 | + int fnum = block_fnum&0x3ff; | |
977 | + CH->block_fnum = block_fnum; | |
978 | + | |
979 | + CH->ksl_base = KSL_TABLE[block_fnum>>6]; | |
980 | + CH->fc = OPL->FN_TABLE[fnum]>>blockRv; | |
981 | + CH->kcode = CH->block_fnum>>9; | |
982 | + if( (OPL->mode&0x40) && CH->block_fnum&0x100) CH->kcode |=1; | |
983 | + CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); | |
984 | + CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); | |
985 | + } | |
986 | + return; | |
987 | + case 0xc0: | |
988 | + /* FB,C */ | |
989 | + if( (r&0x0f) > 8) return; | |
990 | + CH = &OPL->P_CH[r&0x0f]; | |
991 | + { | |
992 | + int feedback = (v>>1)&7; | |
993 | + CH->FB = feedback ? (8+1) - feedback : 0; | |
994 | + CH->CON = v&1; | |
995 | + set_algorythm(CH); | |
996 | + } | |
997 | + return; | |
998 | + case 0xe0: /* wave type */ | |
999 | + slot = slot_array[r&0x1f]; | |
1000 | + if(slot == -1) return; | |
1001 | + CH = &OPL->P_CH[slot/2]; | |
1002 | + if(OPL->wavesel) | |
1003 | + { | |
1004 | + /* LOG(LOG_INF,("OPL SLOT %d wave select %d\n",slot,v&3)); */ | |
1005 | + CH->SLOT[slot&1].wavetable = &SIN_TABLE[(v&0x03)*SIN_ENT]; | |
1006 | + } | |
1007 | + return; | |
1008 | + } | |
1009 | +} | |
1010 | + | |
1011 | +/* lock/unlock for common table */ | |
1012 | +static int OPL_LockTable(void) | |
1013 | +{ | |
1014 | + num_lock++; | |
1015 | + if(num_lock>1) return 0; | |
1016 | + /* first time */ | |
1017 | + cur_chip = NULL; | |
1018 | + /* allocate total level table (128kb space) */ | |
1019 | + if( !OPLOpenTable() ) | |
1020 | + { | |
1021 | + num_lock--; | |
1022 | + return -1; | |
1023 | + } | |
1024 | + return 0; | |
1025 | +} | |
1026 | + | |
1027 | +static void OPL_UnLockTable(void) | |
1028 | +{ | |
1029 | + if(num_lock) num_lock--; | |
1030 | + if(num_lock) return; | |
1031 | + /* last time */ | |
1032 | + cur_chip = NULL; | |
1033 | + OPLCloseTable(); | |
1034 | +} | |
1035 | + | |
1036 | +#if (BUILD_YM3812 || BUILD_YM3526) | |
1037 | +/*******************************************************************************/ | |
1038 | +/* YM3812 local section */ | |
1039 | +/*******************************************************************************/ | |
1040 | + | |
1041 | +/* ---------- update one of chip ----------- */ | |
1042 | +void YM3812UpdateOne(FM_OPL *OPL, INT16 *buffer, int length) | |
1043 | +{ | |
1044 | + int i; | |
1045 | + int data; | |
1046 | + OPLSAMPLE *buf = buffer; | |
1047 | + UINT32 amsCnt = OPL->amsCnt; | |
1048 | + UINT32 vibCnt = OPL->vibCnt; | |
1049 | + UINT8 rythm = OPL->rythm&0x20; | |
1050 | + OPL_CH *CH,*R_CH; | |
1051 | + | |
1052 | + if( (void *)OPL != cur_chip ){ | |
1053 | + cur_chip = (void *)OPL; | |
1054 | + /* channel pointers */ | |
1055 | + S_CH = OPL->P_CH; | |
1056 | + E_CH = &S_CH[9]; | |
1057 | + /* rythm slot */ | |
1058 | + SLOT7_1 = &S_CH[7].SLOT[SLOT1]; | |
1059 | + SLOT7_2 = &S_CH[7].SLOT[SLOT2]; | |
1060 | + SLOT8_1 = &S_CH[8].SLOT[SLOT1]; | |
1061 | + SLOT8_2 = &S_CH[8].SLOT[SLOT2]; | |
1062 | + /* LFO state */ | |
1063 | + amsIncr = OPL->amsIncr; | |
1064 | + vibIncr = OPL->vibIncr; | |
1065 | + ams_table = OPL->ams_table; | |
1066 | + vib_table = OPL->vib_table; | |
1067 | + } | |
1068 | + R_CH = rythm ? &S_CH[6] : E_CH; | |
1069 | + for( i=0; i < length ; i++ ) | |
1070 | + { | |
1071 | + /* channel A channel B channel C */ | |
1072 | + /* LFO */ | |
1073 | + ams = ams_table[(amsCnt+=amsIncr)>>AMS_SHIFT]; | |
1074 | + vib = vib_table[(vibCnt+=vibIncr)>>VIB_SHIFT]; | |
1075 | + outd[0] = 0; | |
1076 | + /* FM part */ | |
1077 | + for(CH=S_CH ; CH < R_CH ; CH++) | |
1078 | + OPL_CALC_CH(CH); | |
1079 | + /* Rythn part */ | |
1080 | + if(rythm) | |
1081 | + OPL_CALC_RH(S_CH); | |
1082 | + /* limit check */ | |
1083 | + data = Limit( outd[0] , OPL_MAXOUT, OPL_MINOUT ); | |
1084 | + /* store to sound buffer */ | |
1085 | + buf[i] = data >> OPL_OUTSB; | |
1086 | + } | |
1087 | + | |
1088 | + OPL->amsCnt = amsCnt; | |
1089 | + OPL->vibCnt = vibCnt; | |
1090 | +#ifdef OPL_OUTPUT_LOG | |
1091 | + if(opl_dbg_fp) | |
1092 | + { | |
1093 | + for(opl_dbg_chip=0;opl_dbg_chip<opl_dbg_maxchip;opl_dbg_chip++) | |
1094 | + if( opl_dbg_opl[opl_dbg_chip] == OPL) break; | |
1095 | + fprintf(opl_dbg_fp,"%c%c%c",0x20+opl_dbg_chip,length&0xff,length/256); | |
1096 | + } | |
1097 | +#endif | |
1098 | +} | |
1099 | +#endif /* (BUILD_YM3812 || BUILD_YM3526) */ | |
1100 | + | |
1101 | +#if BUILD_Y8950 | |
1102 | + | |
1103 | +void Y8950UpdateOne(FM_OPL *OPL, INT16 *buffer, int length) | |
1104 | +{ | |
1105 | + int i; | |
1106 | + int data; | |
1107 | + OPLSAMPLE *buf = buffer; | |
1108 | + UINT32 amsCnt = OPL->amsCnt; | |
1109 | + UINT32 vibCnt = OPL->vibCnt; | |
1110 | + UINT8 rythm = OPL->rythm&0x20; | |
1111 | + OPL_CH *CH,*R_CH; | |
1112 | + YM_DELTAT *DELTAT = OPL->deltat; | |
1113 | + | |
1114 | + /* setup DELTA-T unit */ | |
1115 | + YM_DELTAT_DECODE_PRESET(DELTAT); | |
1116 | + | |
1117 | + if( (void *)OPL != cur_chip ){ | |
1118 | + cur_chip = (void *)OPL; | |
1119 | + /* channel pointers */ | |
1120 | + S_CH = OPL->P_CH; | |
1121 | + E_CH = &S_CH[9]; | |
1122 | + /* rythm slot */ | |
1123 | + SLOT7_1 = &S_CH[7].SLOT[SLOT1]; | |
1124 | + SLOT7_2 = &S_CH[7].SLOT[SLOT2]; | |
1125 | + SLOT8_1 = &S_CH[8].SLOT[SLOT1]; | |
1126 | + SLOT8_2 = &S_CH[8].SLOT[SLOT2]; | |
1127 | + /* LFO state */ | |
1128 | + amsIncr = OPL->amsIncr; | |
1129 | + vibIncr = OPL->vibIncr; | |
1130 | + ams_table = OPL->ams_table; | |
1131 | + vib_table = OPL->vib_table; | |
1132 | + } | |
1133 | + R_CH = rythm ? &S_CH[6] : E_CH; | |
1134 | + for( i=0; i < length ; i++ ) | |
1135 | + { | |
1136 | + /* channel A channel B channel C */ | |
1137 | + /* LFO */ | |
1138 | + ams = ams_table[(amsCnt+=amsIncr)>>AMS_SHIFT]; | |
1139 | + vib = vib_table[(vibCnt+=vibIncr)>>VIB_SHIFT]; | |
1140 | + outd[0] = 0; | |
1141 | + /* deltaT ADPCM */ | |
1142 | + if( DELTAT->portstate ) | |
1143 | + YM_DELTAT_ADPCM_CALC(DELTAT); | |
1144 | + /* FM part */ | |
1145 | + for(CH=S_CH ; CH < R_CH ; CH++) | |
1146 | + OPL_CALC_CH(CH); | |
1147 | + /* Rythn part */ | |
1148 | + if(rythm) | |
1149 | + OPL_CALC_RH(S_CH); | |
1150 | + /* limit check */ | |
1151 | + data = Limit( outd[0] , OPL_MAXOUT, OPL_MINOUT ); | |
1152 | + /* store to sound buffer */ | |
1153 | + buf[i] = data >> OPL_OUTSB; | |
1154 | + } | |
1155 | + OPL->amsCnt = amsCnt; | |
1156 | + OPL->vibCnt = vibCnt; | |
1157 | + /* deltaT START flag */ | |
1158 | + if( !DELTAT->portstate ) | |
1159 | + OPL->status &= 0xfe; | |
1160 | +} | |
1161 | +#endif | |
1162 | + | |
1163 | +/* ---------- reset one of chip ---------- */ | |
1164 | +void OPLResetChip(FM_OPL *OPL) | |
1165 | +{ | |
1166 | + int c,s; | |
1167 | + int i; | |
1168 | + | |
1169 | + /* reset chip */ | |
1170 | + OPL->mode = 0; /* normal mode */ | |
1171 | + OPL_STATUS_RESET(OPL,0x7f); | |
1172 | + /* reset with register write */ | |
1173 | + OPLWriteReg(OPL,0x01,0); /* wabesel disable */ | |
1174 | + OPLWriteReg(OPL,0x02,0); /* Timer1 */ | |
1175 | + OPLWriteReg(OPL,0x03,0); /* Timer2 */ | |
1176 | + OPLWriteReg(OPL,0x04,0); /* IRQ mask clear */ | |
1177 | + for(i = 0xff ; i >= 0x20 ; i-- ) OPLWriteReg(OPL,i,0); | |
1178 | + /* reset OPerator paramater */ | |
1179 | + for( c = 0 ; c < OPL->max_ch ; c++ ) | |
1180 | + { | |
1181 | + OPL_CH *CH = &OPL->P_CH[c]; | |
1182 | + /* OPL->P_CH[c].PAN = OPN_CENTER; */ | |
1183 | + for(s = 0 ; s < 2 ; s++ ) | |
1184 | + { | |
1185 | + /* wave table */ | |
1186 | + CH->SLOT[s].wavetable = &SIN_TABLE[0]; | |
1187 | + /* CH->SLOT[s].evm = ENV_MOD_RR; */ | |
1188 | + CH->SLOT[s].evc = EG_OFF; | |
1189 | + CH->SLOT[s].eve = EG_OFF+1; | |
1190 | + CH->SLOT[s].evs = 0; | |
1191 | + } | |
1192 | + } | |
1193 | +#if BUILD_Y8950 | |
1194 | + if(OPL->type&OPL_TYPE_ADPCM) | |
1195 | + { | |
1196 | + YM_DELTAT *DELTAT = OPL->deltat; | |
1197 | + | |
1198 | + DELTAT->freqbase = OPL->freqbase; | |
1199 | + DELTAT->output_pointer = outd; | |
1200 | + DELTAT->portshift = 5; | |
1201 | + DELTAT->output_range = DELTAT_MIXING_LEVEL<<TL_BITS; | |
1202 | + YM_DELTAT_ADPCM_Reset(DELTAT,0); | |
1203 | + } | |
1204 | +#endif | |
1205 | +} | |
1206 | + | |
1207 | +/* ---------- Create one of vietual YM3812 ---------- */ | |
1208 | +/* 'rate' is sampling rate and 'bufsiz' is the size of the */ | |
1209 | +FM_OPL *OPLCreate(int type, int clock, int rate) | |
1210 | +{ | |
1211 | + char *ptr; | |
1212 | + FM_OPL *OPL; | |
1213 | + int state_size; | |
1214 | + int max_ch = 9; /* normaly 9 channels */ | |
1215 | + | |
1216 | + if( OPL_LockTable() ==-1) return NULL; | |
1217 | + /* allocate OPL state space */ | |
1218 | + state_size = sizeof(FM_OPL); | |
1219 | + state_size += sizeof(OPL_CH)*max_ch; | |
1220 | +#if BUILD_Y8950 | |
1221 | + if(type&OPL_TYPE_ADPCM) state_size+= sizeof(YM_DELTAT); | |
1222 | +#endif | |
1223 | + /* allocate memory block */ | |
1224 | + ptr = malloc(state_size); | |
1225 | + if(ptr==NULL) return NULL; | |
1226 | + /* clear */ | |
1227 | + memset(ptr,0,state_size); | |
1228 | + OPL = (FM_OPL *)ptr; ptr+=sizeof(FM_OPL); | |
1229 | + OPL->P_CH = (OPL_CH *)ptr; ptr+=sizeof(OPL_CH)*max_ch; | |
1230 | +#if BUILD_Y8950 | |
1231 | + if(type&OPL_TYPE_ADPCM) OPL->deltat = (YM_DELTAT *)ptr; ptr+=sizeof(YM_DELTAT); | |
1232 | +#endif | |
1233 | + /* set channel state pointer */ | |
1234 | + OPL->type = type; | |
1235 | + OPL->clock = clock; | |
1236 | + OPL->rate = rate; | |
1237 | + OPL->max_ch = max_ch; | |
1238 | + /* init grobal tables */ | |
1239 | + OPL_initalize(OPL); | |
1240 | + /* reset chip */ | |
1241 | + OPLResetChip(OPL); | |
1242 | +#ifdef OPL_OUTPUT_LOG | |
1243 | + if(!opl_dbg_fp) | |
1244 | + { | |
1245 | + opl_dbg_fp = fopen("opllog.opl","wb"); | |
1246 | + opl_dbg_maxchip = 0; | |
1247 | + } | |
1248 | + if(opl_dbg_fp) | |
1249 | + { | |
1250 | + opl_dbg_opl[opl_dbg_maxchip] = OPL; | |
1251 | + fprintf(opl_dbg_fp,"%c%c%c%c%c%c",0x00+opl_dbg_maxchip, | |
1252 | + type, | |
1253 | + clock&0xff, | |
1254 | + (clock/0x100)&0xff, | |
1255 | + (clock/0x10000)&0xff, | |
1256 | + (clock/0x1000000)&0xff); | |
1257 | + opl_dbg_maxchip++; | |
1258 | + } | |
1259 | +#endif | |
1260 | + return OPL; | |
1261 | +} | |
1262 | + | |
1263 | +/* ---------- Destroy one of vietual YM3812 ---------- */ | |
1264 | +void OPLDestroy(FM_OPL *OPL) | |
1265 | +{ | |
1266 | +#ifdef OPL_OUTPUT_LOG | |
1267 | + if(opl_dbg_fp) | |
1268 | + { | |
1269 | + fclose(opl_dbg_fp); | |
1270 | + opl_dbg_fp = NULL; | |
1271 | + } | |
1272 | +#endif | |
1273 | + OPL_UnLockTable(); | |
1274 | + free(OPL); | |
1275 | +} | |
1276 | + | |
1277 | +/* ---------- Option handlers ---------- */ | |
1278 | + | |
1279 | +void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER TimerHandler,int channelOffset) | |
1280 | +{ | |
1281 | + OPL->TimerHandler = TimerHandler; | |
1282 | + OPL->TimerParam = channelOffset; | |
1283 | +} | |
1284 | +void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,int param) | |
1285 | +{ | |
1286 | + OPL->IRQHandler = IRQHandler; | |
1287 | + OPL->IRQParam = param; | |
1288 | +} | |
1289 | +void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,int param) | |
1290 | +{ | |
1291 | + OPL->UpdateHandler = UpdateHandler; | |
1292 | + OPL->UpdateParam = param; | |
1293 | +} | |
1294 | +#if BUILD_Y8950 | |
1295 | +void OPLSetPortHandler(FM_OPL *OPL,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,int param) | |
1296 | +{ | |
1297 | + OPL->porthandler_w = PortHandler_w; | |
1298 | + OPL->porthandler_r = PortHandler_r; | |
1299 | + OPL->port_param = param; | |
1300 | +} | |
1301 | + | |
1302 | +void OPLSetKeyboardHandler(FM_OPL *OPL,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,int param) | |
1303 | +{ | |
1304 | + OPL->keyboardhandler_w = KeyboardHandler_w; | |
1305 | + OPL->keyboardhandler_r = KeyboardHandler_r; | |
1306 | + OPL->keyboard_param = param; | |
1307 | +} | |
1308 | +#endif | |
1309 | +/* ---------- YM3812 I/O interface ---------- */ | |
1310 | +int OPLWrite(FM_OPL *OPL,int a,int v) | |
1311 | +{ | |
1312 | + if( !(a&1) ) | |
1313 | + { /* address port */ | |
1314 | + OPL->address = v & 0xff; | |
1315 | + } | |
1316 | + else | |
1317 | + { /* data port */ | |
1318 | + if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0); | |
1319 | +#ifdef OPL_OUTPUT_LOG | |
1320 | + if(opl_dbg_fp) | |
1321 | + { | |
1322 | + for(opl_dbg_chip=0;opl_dbg_chip<opl_dbg_maxchip;opl_dbg_chip++) | |
1323 | + if( opl_dbg_opl[opl_dbg_chip] == OPL) break; | |
1324 | + fprintf(opl_dbg_fp,"%c%c%c",0x10+opl_dbg_chip,OPL->address,v); | |
1325 | + } | |
1326 | +#endif | |
1327 | + OPLWriteReg(OPL,OPL->address,v); | |
1328 | + } | |
1329 | + return OPL->status>>7; | |
1330 | +} | |
1331 | + | |
1332 | +unsigned char OPLRead(FM_OPL *OPL,int a) | |
1333 | +{ | |
1334 | + if( !(a&1) ) | |
1335 | + { /* status port */ | |
1336 | + return OPL->status & (OPL->statusmask|0x80); | |
1337 | + } | |
1338 | + /* data port */ | |
1339 | + switch(OPL->address) | |
1340 | + { | |
1341 | + case 0x05: /* KeyBoard IN */ | |
1342 | + if(OPL->type&OPL_TYPE_KEYBOARD) | |
1343 | + { | |
1344 | + if(OPL->keyboardhandler_r) | |
1345 | + return OPL->keyboardhandler_r(OPL->keyboard_param); | |
1346 | + else | |
1347 | + LOG(LOG_WAR,("OPL:read unmapped KEYBOARD port\n")); | |
1348 | + } | |
1349 | + return 0; | |
1350 | +#if 0 | |
1351 | + case 0x0f: /* ADPCM-DATA */ | |
1352 | + return 0; | |
1353 | +#endif | |
1354 | + case 0x19: /* I/O DATA */ | |
1355 | + if(OPL->type&OPL_TYPE_IO) | |
1356 | + { | |
1357 | + if(OPL->porthandler_r) | |
1358 | + return OPL->porthandler_r(OPL->port_param); | |
1359 | + else | |
1360 | + LOG(LOG_WAR,("OPL:read unmapped I/O port\n")); | |
1361 | + } | |
1362 | + return 0; | |
1363 | + case 0x1a: /* PCM-DATA */ | |
1364 | + return 0; | |
1365 | + } | |
1366 | + return 0; | |
1367 | +} | |
1368 | + | |
1369 | +int OPLTimerOver(FM_OPL *OPL,int c) | |
1370 | +{ | |
1371 | + if( c ) | |
1372 | + { /* Timer B */ | |
1373 | + OPL_STATUS_SET(OPL,0x20); | |
1374 | + } | |
1375 | + else | |
1376 | + { /* Timer A */ | |
1377 | + OPL_STATUS_SET(OPL,0x40); | |
1378 | + /* CSM mode key,TL controll */ | |
1379 | + if( OPL->mode & 0x80 ) | |
1380 | + { /* CSM mode total level latch and auto key on */ | |
1381 | + int ch; | |
1382 | + if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0); | |
1383 | + for(ch=0;ch<9;ch++) | |
1384 | + CSMKeyControll( &OPL->P_CH[ch] ); | |
1385 | + } | |
1386 | + } | |
1387 | + /* reload timer */ | |
1388 | + if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+c,(double)OPL->T[c]*OPL->TimerBase); | |
1389 | + return OPL->status>>7; | |
1390 | +} | ... | ... |
hw/fmopl.h
0 → 100644
1 | +#ifndef __FMOPL_H_ | |
2 | +#define __FMOPL_H_ | |
3 | + | |
4 | +/* --- select emulation chips --- */ | |
5 | +#define BUILD_YM3812 (HAS_YM3812) | |
6 | +//#define BUILD_YM3526 (HAS_YM3526) | |
7 | +//#define BUILD_Y8950 (HAS_Y8950) | |
8 | + | |
9 | +/* --- system optimize --- */ | |
10 | +/* select bit size of output : 8 or 16 */ | |
11 | +#define OPL_OUTPUT_BIT 16 | |
12 | + | |
13 | +/* compiler dependence */ | |
14 | +#ifndef OSD_CPU_H | |
15 | +#define OSD_CPU_H | |
16 | +typedef unsigned char UINT8; /* unsigned 8bit */ | |
17 | +typedef unsigned short UINT16; /* unsigned 16bit */ | |
18 | +typedef unsigned int UINT32; /* unsigned 32bit */ | |
19 | +typedef signed char INT8; /* signed 8bit */ | |
20 | +typedef signed short INT16; /* signed 16bit */ | |
21 | +typedef signed int INT32; /* signed 32bit */ | |
22 | +#endif | |
23 | + | |
24 | +#if (OPL_OUTPUT_BIT==16) | |
25 | +typedef INT16 OPLSAMPLE; | |
26 | +#endif | |
27 | +#if (OPL_OUTPUT_BIT==8) | |
28 | +typedef unsigned char OPLSAMPLE; | |
29 | +#endif | |
30 | + | |
31 | + | |
32 | +#if BUILD_Y8950 | |
33 | +#include "ymdeltat.h" | |
34 | +#endif | |
35 | + | |
36 | +typedef void (*OPL_TIMERHANDLER)(int channel,double interval_Sec); | |
37 | +typedef void (*OPL_IRQHANDLER)(int param,int irq); | |
38 | +typedef void (*OPL_UPDATEHANDLER)(int param,int min_interval_us); | |
39 | +typedef void (*OPL_PORTHANDLER_W)(int param,unsigned char data); | |
40 | +typedef unsigned char (*OPL_PORTHANDLER_R)(int param); | |
41 | + | |
42 | +/* !!!!! here is private section , do not access there member direct !!!!! */ | |
43 | + | |
44 | +#define OPL_TYPE_WAVESEL 0x01 /* waveform select */ | |
45 | +#define OPL_TYPE_ADPCM 0x02 /* DELTA-T ADPCM unit */ | |
46 | +#define OPL_TYPE_KEYBOARD 0x04 /* keyboard interface */ | |
47 | +#define OPL_TYPE_IO 0x08 /* I/O port */ | |
48 | + | |
49 | +/* Saving is necessary for member of the 'R' mark for suspend/resume */ | |
50 | +/* ---------- OPL one of slot ---------- */ | |
51 | +typedef struct fm_opl_slot { | |
52 | + INT32 TL; /* total level :TL << 8 */ | |
53 | + INT32 TLL; /* adjusted now TL */ | |
54 | + UINT8 KSR; /* key scale rate :(shift down bit) */ | |
55 | + INT32 *AR; /* attack rate :&AR_TABLE[AR<<2] */ | |
56 | + INT32 *DR; /* decay rate :&DR_TALBE[DR<<2] */ | |
57 | + INT32 SL; /* sustin level :SL_TALBE[SL] */ | |
58 | + INT32 *RR; /* release rate :&DR_TABLE[RR<<2] */ | |
59 | + UINT8 ksl; /* keyscale level :(shift down bits) */ | |
60 | + UINT8 ksr; /* key scale rate :kcode>>KSR */ | |
61 | + UINT32 mul; /* multiple :ML_TABLE[ML] */ | |
62 | + UINT32 Cnt; /* frequency count : */ | |
63 | + UINT32 Incr; /* frequency step : */ | |
64 | + /* envelope generator state */ | |
65 | + UINT8 eg_typ; /* envelope type flag */ | |
66 | + UINT8 evm; /* envelope phase */ | |
67 | + INT32 evc; /* envelope counter */ | |
68 | + INT32 eve; /* envelope counter end point */ | |
69 | + INT32 evs; /* envelope counter step */ | |
70 | + INT32 evsa; /* envelope step for AR :AR[ksr] */ | |
71 | + INT32 evsd; /* envelope step for DR :DR[ksr] */ | |
72 | + INT32 evsr; /* envelope step for RR :RR[ksr] */ | |
73 | + /* LFO */ | |
74 | + UINT8 ams; /* ams flag */ | |
75 | + UINT8 vib; /* vibrate flag */ | |
76 | + /* wave selector */ | |
77 | + INT32 **wavetable; | |
78 | +}OPL_SLOT; | |
79 | + | |
80 | +/* ---------- OPL one of channel ---------- */ | |
81 | +typedef struct fm_opl_channel { | |
82 | + OPL_SLOT SLOT[2]; | |
83 | + UINT8 CON; /* connection type */ | |
84 | + UINT8 FB; /* feed back :(shift down bit) */ | |
85 | + INT32 *connect1; /* slot1 output pointer */ | |
86 | + INT32 *connect2; /* slot2 output pointer */ | |
87 | + INT32 op1_out[2]; /* slot1 output for selfeedback */ | |
88 | + /* phase generator state */ | |
89 | + UINT32 block_fnum; /* block+fnum : */ | |
90 | + UINT8 kcode; /* key code : KeyScaleCode */ | |
91 | + UINT32 fc; /* Freq. Increment base */ | |
92 | + UINT32 ksl_base; /* KeyScaleLevel Base step */ | |
93 | + UINT8 keyon; /* key on/off flag */ | |
94 | +} OPL_CH; | |
95 | + | |
96 | +/* OPL state */ | |
97 | +typedef struct fm_opl_f { | |
98 | + UINT8 type; /* chip type */ | |
99 | + int clock; /* master clock (Hz) */ | |
100 | + int rate; /* sampling rate (Hz) */ | |
101 | + double freqbase; /* frequency base */ | |
102 | + double TimerBase; /* Timer base time (==sampling time) */ | |
103 | + UINT8 address; /* address register */ | |
104 | + UINT8 status; /* status flag */ | |
105 | + UINT8 statusmask; /* status mask */ | |
106 | + UINT32 mode; /* Reg.08 : CSM , notesel,etc. */ | |
107 | + /* Timer */ | |
108 | + int T[2]; /* timer counter */ | |
109 | + UINT8 st[2]; /* timer enable */ | |
110 | + /* FM channel slots */ | |
111 | + OPL_CH *P_CH; /* pointer of CH */ | |
112 | + int max_ch; /* maximum channel */ | |
113 | + /* Rythm sention */ | |
114 | + UINT8 rythm; /* Rythm mode , key flag */ | |
115 | +#if BUILD_Y8950 | |
116 | + /* Delta-T ADPCM unit (Y8950) */ | |
117 | + YM_DELTAT *deltat; /* DELTA-T ADPCM */ | |
118 | +#endif | |
119 | + /* Keyboard / I/O interface unit (Y8950) */ | |
120 | + UINT8 portDirection; | |
121 | + UINT8 portLatch; | |
122 | + OPL_PORTHANDLER_R porthandler_r; | |
123 | + OPL_PORTHANDLER_W porthandler_w; | |
124 | + int port_param; | |
125 | + OPL_PORTHANDLER_R keyboardhandler_r; | |
126 | + OPL_PORTHANDLER_W keyboardhandler_w; | |
127 | + int keyboard_param; | |
128 | + /* time tables */ | |
129 | + INT32 AR_TABLE[75]; /* atttack rate tables */ | |
130 | + INT32 DR_TABLE[75]; /* decay rate tables */ | |
131 | + UINT32 FN_TABLE[1024]; /* fnumber -> increment counter */ | |
132 | + /* LFO */ | |
133 | + INT32 *ams_table; | |
134 | + INT32 *vib_table; | |
135 | + INT32 amsCnt; | |
136 | + INT32 amsIncr; | |
137 | + INT32 vibCnt; | |
138 | + INT32 vibIncr; | |
139 | + /* wave selector enable flag */ | |
140 | + UINT8 wavesel; | |
141 | + /* external event callback handler */ | |
142 | + OPL_TIMERHANDLER TimerHandler; /* TIMER handler */ | |
143 | + int TimerParam; /* TIMER parameter */ | |
144 | + OPL_IRQHANDLER IRQHandler; /* IRQ handler */ | |
145 | + int IRQParam; /* IRQ parameter */ | |
146 | + OPL_UPDATEHANDLER UpdateHandler; /* stream update handler */ | |
147 | + int UpdateParam; /* stream update parameter */ | |
148 | +} FM_OPL; | |
149 | + | |
150 | +/* ---------- Generic interface section ---------- */ | |
151 | +#define OPL_TYPE_YM3526 (0) | |
152 | +#define OPL_TYPE_YM3812 (OPL_TYPE_WAVESEL) | |
153 | +#define OPL_TYPE_Y8950 (OPL_TYPE_ADPCM|OPL_TYPE_KEYBOARD|OPL_TYPE_IO) | |
154 | + | |
155 | +FM_OPL *OPLCreate(int type, int clock, int rate); | |
156 | +void OPLDestroy(FM_OPL *OPL); | |
157 | +void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER TimerHandler,int channelOffset); | |
158 | +void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,int param); | |
159 | +void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,int param); | |
160 | +/* Y8950 port handlers */ | |
161 | +void OPLSetPortHandler(FM_OPL *OPL,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,int param); | |
162 | +void OPLSetKeyboardHandler(FM_OPL *OPL,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,int param); | |
163 | + | |
164 | +void OPLResetChip(FM_OPL *OPL); | |
165 | +int OPLWrite(FM_OPL *OPL,int a,int v); | |
166 | +unsigned char OPLRead(FM_OPL *OPL,int a); | |
167 | +int OPLTimerOver(FM_OPL *OPL,int c); | |
168 | + | |
169 | +/* YM3626/YM3812 local section */ | |
170 | +void YM3812UpdateOne(FM_OPL *OPL, INT16 *buffer, int length); | |
171 | + | |
172 | +void Y8950UpdateOne(FM_OPL *OPL, INT16 *buffer, int length); | |
173 | + | |
174 | +#endif | ... | ... |
hw/pc.c
... | ... | @@ -554,13 +554,10 @@ void pc_init(int ram_size, int vga_ram_size, int boot_device, |
554 | 554 | kbd_init(); |
555 | 555 | DMA_init(0); |
556 | 556 | |
557 | -#ifndef _WIN32 | |
558 | 557 | if (audio_enabled) { |
559 | 558 | /* no audio supported yet for win32 */ |
560 | 559 | AUD_init(); |
561 | - SB16_init(); | |
562 | 560 | } |
563 | -#endif | |
564 | 561 | |
565 | 562 | floppy_controller = fdctrl_init(6, 2, 0, 0x3f0, fd_table); |
566 | 563 | ... | ... |
hw/sb16.c
1 | 1 | /* |
2 | 2 | * QEMU Soundblaster 16 emulation |
3 | 3 | * |
4 | - * Copyright (c) 2003 Vassili Karpov (malc) | |
4 | + * Copyright (c) 2003-2004 Vassili Karpov (malc) | |
5 | 5 | * |
6 | 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
7 | 7 | * of this software and associated documentation files (the "Software"), to deal |
... | ... | @@ -23,32 +23,20 @@ |
23 | 23 | */ |
24 | 24 | #include "vl.h" |
25 | 25 | |
26 | -#define MIN(a, b) ((a)>(b)?(b):(a)) | |
27 | -#define LENOFA(a) ((int) (sizeof(a)/sizeof(a[0]))) | |
28 | - | |
29 | -#define dolog(...) fprintf (stderr, "sb16: " __VA_ARGS__); | |
26 | +/* #define DEBUG */ | |
27 | +#define AUDIO_CAP "sb16" | |
28 | +#include "audio/audio.h" | |
30 | 29 | |
31 | -/* #define DEBUG_SB16 */ | |
30 | +#define LENOFA(a) ((int) (sizeof(a)/sizeof(a[0]))) | |
32 | 31 | |
33 | -#ifdef DEBUG_SB16 | |
34 | -#define lwarn(...) fprintf (stderr, "sb16: " __VA_ARGS__) | |
35 | -#define linfo(...) fprintf (stderr, "sb16: " __VA_ARGS__) | |
36 | -#define ldebug(...) fprintf (stderr, "sb16: " __VA_ARGS__) | |
37 | -#else | |
38 | -#define lwarn(...) | |
39 | -#define linfo(...) | |
40 | -#define ldebug(...) | |
41 | -#endif | |
32 | +/* #define DEBUG_SB16_MOST */ | |
42 | 33 | |
43 | -#define IO_READ_PROTO(name) \ | |
34 | +#define IO_READ_PROTO(name) \ | |
44 | 35 | uint32_t name (void *opaque, uint32_t nport) |
45 | -#define IO_WRITE_PROTO(name) \ | |
36 | +#define IO_WRITE_PROTO(name) \ | |
46 | 37 | void name (void *opaque, uint32_t nport, uint32_t val) |
47 | 38 | |
48 | -static const char e3[] = | |
49 | - "COPYRIGHT (C) CREATIVE TECHNOLOGY LTD, 1992\0" | |
50 | - "COPYRIGHT (C) CREATIVE TECHNOLOGY LTD, 1994-1997"; | |
51 | - /* "COPYRIGHT (C) CREATIVE TECHNOLOGY LTD, 1994."; */ | |
39 | +static const char e3[] = "COPYRIGHT (C) CREATIVE TECHNOLOGY LTD, 1992."; | |
52 | 40 | |
53 | 41 | static struct { |
54 | 42 | int ver_lo; |
... | ... | @@ -57,38 +45,58 @@ static struct { |
57 | 45 | int dma; |
58 | 46 | int hdma; |
59 | 47 | int port; |
60 | - int mix_block; | |
61 | -} sb = {5, 4, 5, 1, 5, 0x220, -1}; | |
62 | - | |
63 | -static int mix_block, noirq; | |
48 | +} conf = {5, 4, 5, 1, 5, 0x220}; | |
64 | 49 | |
65 | 50 | typedef struct SB16State { |
51 | + int irq; | |
52 | + int dma; | |
53 | + int hdma; | |
54 | + int port; | |
55 | + int ver; | |
56 | + | |
66 | 57 | int in_index; |
67 | 58 | int out_data_len; |
68 | 59 | int fmt_stereo; |
69 | 60 | int fmt_signed; |
70 | 61 | int fmt_bits; |
62 | + audfmt_e fmt; | |
71 | 63 | int dma_auto; |
72 | - int dma_buffer_size; | |
64 | + int block_size; | |
73 | 65 | int fifo; |
74 | 66 | int freq; |
75 | 67 | int time_const; |
76 | 68 | int speaker; |
77 | 69 | int needed_bytes; |
78 | 70 | int cmd; |
79 | - int dma_pos; | |
80 | 71 | int use_hdma; |
72 | + int highspeed; | |
73 | + int can_write; | |
81 | 74 | |
82 | 75 | int v2x6; |
83 | 76 | |
77 | + uint8_t csp_param; | |
78 | + uint8_t csp_value; | |
79 | + uint8_t csp_mode; | |
80 | + uint8_t csp_regs[256]; | |
81 | + uint8_t csp_index; | |
82 | + uint8_t csp_reg83[4]; | |
83 | + int csp_reg83r; | |
84 | + int csp_reg83w; | |
85 | + | |
84 | 86 | uint8_t in2_data[10]; |
85 | - uint8_t out_data[1024]; | |
87 | + uint8_t out_data[50]; | |
88 | + uint8_t test_reg; | |
89 | + uint8_t last_read_byte; | |
90 | + int nzero; | |
86 | 91 | |
87 | 92 | int left_till_irq; |
88 | - uint64_t nzero; | |
89 | - uint8_t last_read_byte; | |
90 | - uint8_t test_reg; | |
91 | 93 | |
94 | + int dma_running; | |
95 | + int bytes_per_second; | |
96 | + int align; | |
97 | + SWVoice *voice; | |
98 | + | |
99 | + QEMUTimer *ts, *aux_ts; | |
92 | 100 | /* mixer state */ |
93 | 101 | int mixer_nreg; |
94 | 102 | uint8_t mixer_regs[256]; |
... | ... | @@ -97,664 +105,853 @@ typedef struct SB16State { |
97 | 105 | /* XXX: suppress that and use a context */ |
98 | 106 | static struct SB16State dsp; |
99 | 107 | |
108 | +static int magic_of_irq (int irq) | |
109 | +{ | |
110 | + switch (irq) { | |
111 | + case 5: | |
112 | + return 2; | |
113 | + case 7: | |
114 | + return 4; | |
115 | + case 9: | |
116 | + return 1; | |
117 | + case 10: | |
118 | + return 8; | |
119 | + default: | |
120 | + dolog ("bad irq %d\n", irq); | |
121 | + return 2; | |
122 | + } | |
123 | +} | |
124 | + | |
125 | +static int irq_of_magic (int magic) | |
126 | +{ | |
127 | + switch (magic) { | |
128 | + case 1: | |
129 | + return 9; | |
130 | + case 2: | |
131 | + return 5; | |
132 | + case 4: | |
133 | + return 7; | |
134 | + case 8: | |
135 | + return 10; | |
136 | + default: | |
137 | + dolog ("bad irq magic %d\n", magic); | |
138 | + return -1; | |
139 | + } | |
140 | +} | |
141 | + | |
142 | +#if 0 | |
100 | 143 | static void log_dsp (SB16State *dsp) |
101 | 144 | { |
102 | - ldebug ("%c:%c:%d:%c:dmabuf=%d:pos=%d:freq=%d:timeconst=%d:speaker=%d\n", | |
103 | - dsp->fmt_stereo ? 'S' : 'M', | |
104 | - dsp->fmt_signed ? 'S' : 'U', | |
105 | - dsp->fmt_bits, | |
106 | - dsp->dma_auto ? 'a' : 's', | |
107 | - dsp->dma_buffer_size, | |
108 | - dsp->dma_pos, | |
109 | - dsp->freq, | |
110 | - dsp->time_const, | |
111 | - dsp->speaker); | |
145 | + ldebug ("%s:%s:%d:%s:dmasize=%d:freq=%d:const=%d:speaker=%d\n", | |
146 | + dsp->fmt_stereo ? "Stereo" : "Mono", | |
147 | + dsp->fmt_signed ? "Signed" : "Unsigned", | |
148 | + dsp->fmt_bits, | |
149 | + dsp->dma_auto ? "Auto" : "Single", | |
150 | + dsp->block_size, | |
151 | + dsp->freq, | |
152 | + dsp->time_const, | |
153 | + dsp->speaker); | |
154 | +} | |
155 | +#endif | |
156 | + | |
157 | +static void speaker (SB16State *s, int on) | |
158 | +{ | |
159 | + s->speaker = on; | |
160 | + /* AUD_enable (s->voice, on); */ | |
112 | 161 | } |
113 | 162 | |
114 | -static void control (int hold) | |
163 | +static void control (SB16State *s, int hold) | |
115 | 164 | { |
116 | - linfo ("%d high %d\n", hold, dsp.use_hdma); | |
165 | + int dma = s->use_hdma ? s->hdma : s->dma; | |
166 | + s->dma_running = hold; | |
167 | + | |
168 | + ldebug ("hold %d high %d dma %d\n", hold, s->use_hdma, dma); | |
169 | + | |
117 | 170 | if (hold) { |
118 | - if (dsp.use_hdma) | |
119 | - DMA_hold_DREQ (sb.hdma); | |
120 | - else | |
121 | - DMA_hold_DREQ (sb.dma); | |
171 | + DMA_hold_DREQ (dma); | |
172 | + AUD_enable (s->voice, 1); | |
122 | 173 | } |
123 | 174 | else { |
124 | - if (dsp.use_hdma) | |
125 | - DMA_release_DREQ (sb.hdma); | |
126 | - else | |
127 | - DMA_release_DREQ (sb.dma); | |
175 | + DMA_release_DREQ (dma); | |
176 | + AUD_enable (s->voice, 0); | |
128 | 177 | } |
129 | 178 | } |
130 | 179 | |
131 | -static void dma_cmd (uint8_t cmd, uint8_t d0, int dma_len) | |
180 | +static void aux_timer (void *opaque) | |
132 | 181 | { |
133 | - int bps; | |
134 | - audfmt_e fmt; | |
182 | + SB16State *s = opaque; | |
183 | + s->can_write = 1; | |
184 | + pic_set_irq (s->irq, 1); | |
185 | +} | |
186 | + | |
187 | +#define DMA8_AUTO 1 | |
188 | +#define DMA8_HIGH 2 | |
189 | + | |
190 | +static void dma_cmd8 (SB16State *s, int mask, int dma_len) | |
191 | +{ | |
192 | + s->fmt = AUD_FMT_U8; | |
193 | + s->use_hdma = 0; | |
194 | + s->fmt_bits = 8; | |
195 | + s->fmt_signed = 0; | |
196 | + s->fmt_stereo = (s->mixer_regs[0x0e] & 2) != 0; | |
197 | + if (-1 == s->time_const) { | |
198 | + s->freq = 11025; | |
199 | + } | |
200 | + else { | |
201 | + int tmp = (256 - s->time_const); | |
202 | + s->freq = (1000000 + (tmp / 2)) / tmp; | |
203 | + } | |
204 | + | |
205 | + if (-1 != dma_len) | |
206 | + s->block_size = dma_len + 1; | |
207 | + | |
208 | + s->freq >>= s->fmt_stereo; | |
209 | + s->left_till_irq = s->block_size; | |
210 | + s->bytes_per_second = (s->freq << s->fmt_stereo); | |
211 | + /* s->highspeed = (mask & DMA8_HIGH) != 0; */ | |
212 | + s->dma_auto = (mask & DMA8_AUTO) != 0; | |
213 | + s->align = (1 << s->fmt_stereo) - 1; | |
214 | + | |
215 | + ldebug ("freq %d, stereo %d, sign %d, bits %d, " | |
216 | + "dma %d, auto %d, fifo %d, high %d\n", | |
217 | + s->freq, s->fmt_stereo, s->fmt_signed, s->fmt_bits, | |
218 | + s->block_size, s->dma_auto, s->fifo, s->highspeed); | |
219 | + | |
220 | + if (s->freq) | |
221 | + s->voice = AUD_open (s->voice, "sb16", s->freq, | |
222 | + 1 << s->fmt_stereo, s->fmt); | |
223 | + | |
224 | + control (s, 1); | |
225 | + speaker (s, 1); | |
226 | +} | |
135 | 227 | |
136 | - dsp.use_hdma = cmd < 0xc0; | |
137 | - dsp.fifo = (cmd >> 1) & 1; | |
138 | - dsp.dma_auto = (cmd >> 2) & 1; | |
228 | +static void dma_cmd (SB16State *s, uint8_t cmd, uint8_t d0, int dma_len) | |
229 | +{ | |
230 | + s->use_hdma = cmd < 0xc0; | |
231 | + s->fifo = (cmd >> 1) & 1; | |
232 | + s->dma_auto = (cmd >> 2) & 1; | |
233 | + s->fmt_signed = (d0 >> 4) & 1; | |
234 | + s->fmt_stereo = (d0 >> 5) & 1; | |
139 | 235 | |
140 | 236 | switch (cmd >> 4) { |
141 | 237 | case 11: |
142 | - dsp.fmt_bits = 16; | |
238 | + s->fmt_bits = 16; | |
143 | 239 | break; |
144 | 240 | |
145 | 241 | case 12: |
146 | - dsp.fmt_bits = 8; | |
242 | + s->fmt_bits = 8; | |
147 | 243 | break; |
148 | 244 | } |
149 | 245 | |
150 | - dsp.fmt_signed = (d0 >> 4) & 1; | |
151 | - dsp.fmt_stereo = (d0 >> 5) & 1; | |
152 | - | |
153 | - if (-1 != dsp.time_const) { | |
154 | - int tmp; | |
155 | - | |
156 | - tmp = 256 - dsp.time_const; | |
157 | - dsp.freq = (1000000 + (tmp / 2)) / tmp; | |
246 | + if (-1 != s->time_const) { | |
247 | +#if 1 | |
248 | + int tmp = 256 - s->time_const; | |
249 | + s->freq = (1000000 + (tmp / 2)) / tmp; | |
250 | +#else | |
251 | + /* s->freq = 1000000 / ((255 - s->time_const) << s->fmt_stereo); */ | |
252 | + s->freq = 1000000 / ((255 - s->time_const)); | |
253 | +#endif | |
254 | + s->time_const = -1; | |
158 | 255 | } |
159 | - bps = 1 << (16 == dsp.fmt_bits); | |
160 | 256 | |
161 | - if (-1 != dma_len) | |
162 | - dsp.dma_buffer_size = (dma_len + 1) * bps; | |
257 | + s->block_size = dma_len + 1; | |
258 | + s->block_size <<= (s->fmt_bits == 16); | |
259 | + if (!s->dma_auto) /* Miles Sound System ? */ | |
260 | + s->block_size <<= s->fmt_stereo; | |
163 | 261 | |
164 | - linfo ("frequency %d, stereo %d, signed %d, bits %d, size %d, auto %d\n", | |
165 | - dsp.freq, dsp.fmt_stereo, dsp.fmt_signed, dsp.fmt_bits, | |
166 | - dsp.dma_buffer_size, dsp.dma_auto); | |
262 | + ldebug ("freq %d, stereo %d, sign %d, bits %d, " | |
263 | + "dma %d, auto %d, fifo %d, high %d\n", | |
264 | + s->freq, s->fmt_stereo, s->fmt_signed, s->fmt_bits, | |
265 | + s->block_size, s->dma_auto, s->fifo, s->highspeed); | |
167 | 266 | |
168 | - if (16 == dsp.fmt_bits) { | |
169 | - if (dsp.fmt_signed) { | |
170 | - fmt = AUD_FMT_S16; | |
267 | + if (16 == s->fmt_bits) { | |
268 | + if (s->fmt_signed) { | |
269 | + s->fmt = AUD_FMT_S16; | |
171 | 270 | } |
172 | 271 | else { |
173 | - fmt = AUD_FMT_U16; | |
272 | + s->fmt = AUD_FMT_U16; | |
174 | 273 | } |
175 | 274 | } |
176 | 275 | else { |
177 | - if (dsp.fmt_signed) { | |
178 | - fmt = AUD_FMT_S8; | |
276 | + if (s->fmt_signed) { | |
277 | + s->fmt = AUD_FMT_S8; | |
179 | 278 | } |
180 | 279 | else { |
181 | - fmt = AUD_FMT_U8; | |
280 | + s->fmt = AUD_FMT_U8; | |
182 | 281 | } |
183 | 282 | } |
184 | 283 | |
185 | - dsp.dma_pos = 0; | |
186 | - dsp.left_till_irq = dsp.dma_buffer_size; | |
284 | + s->left_till_irq = s->block_size; | |
187 | 285 | |
188 | - if (sb.mix_block) { | |
189 | - mix_block = sb.mix_block; | |
190 | - } | |
191 | - else { | |
192 | - int align; | |
286 | + s->bytes_per_second = (s->freq << s->fmt_stereo) << (s->fmt_bits == 16); | |
287 | + s->highspeed = 0; | |
288 | + s->align = (1 << (s->fmt_stereo + (s->fmt_bits == 16))) - 1; | |
193 | 289 | |
194 | - align = bps << dsp.fmt_stereo; | |
195 | - mix_block = ((dsp.freq * align) / 100) & ~(align - 1); | |
196 | - } | |
290 | + if (s->freq) | |
291 | + s->voice = AUD_open (s->voice, "sb16", s->freq, | |
292 | + 1 << s->fmt_stereo, s->fmt); | |
197 | 293 | |
198 | - if (dsp.freq) | |
199 | - AUD_reset (dsp.freq, 1 << dsp.fmt_stereo, fmt); | |
200 | - control (1); | |
201 | - dsp.speaker = 1; | |
294 | + control (s, 1); | |
295 | + speaker (s, 1); | |
202 | 296 | } |
203 | 297 | |
204 | -static inline void dsp_out_data(SB16State *dsp, int val) | |
298 | +static inline void dsp_out_data (SB16State *s, uint8_t val) | |
205 | 299 | { |
206 | - if (dsp->out_data_len < sizeof(dsp->out_data)) | |
207 | - dsp->out_data[dsp->out_data_len++] = val; | |
300 | + ldebug ("outdata %#x\n", val); | |
301 | + if (s->out_data_len < sizeof (s->out_data)) | |
302 | + s->out_data[s->out_data_len++] = val; | |
208 | 303 | } |
209 | 304 | |
210 | -static inline uint8_t dsp_get_data(SB16State *dsp) | |
305 | +static inline uint8_t dsp_get_data (SB16State *s) | |
211 | 306 | { |
212 | - if (dsp->in_index) | |
213 | - return dsp->in2_data[--dsp->in_index]; | |
214 | - else | |
307 | + if (s->in_index) | |
308 | + return s->in2_data[--s->in_index]; | |
309 | + else { | |
310 | + dolog ("buffer underflow\n"); | |
215 | 311 | return 0; |
312 | + } | |
216 | 313 | } |
217 | 314 | |
218 | -static void command (SB16State *dsp, uint8_t cmd) | |
315 | +static void command (SB16State *s, uint8_t cmd) | |
219 | 316 | { |
220 | - linfo ("command: %#x\n", cmd); | |
317 | + ldebug ("command %#x\n", cmd); | |
221 | 318 | |
222 | 319 | if (cmd > 0xaf && cmd < 0xd0) { |
223 | - if (cmd & 8) | |
224 | - goto error; | |
320 | + if (cmd & 8) { | |
321 | + dolog ("ADC not yet supported (command %#x)\n", cmd); | |
322 | + } | |
225 | 323 | |
226 | 324 | switch (cmd >> 4) { |
227 | 325 | case 11: |
228 | 326 | case 12: |
229 | 327 | break; |
230 | 328 | default: |
231 | - dolog ("command: %#x wrong bits specification\n", cmd); | |
232 | - goto error; | |
329 | + dolog ("%#x wrong bits\n", cmd); | |
233 | 330 | } |
234 | - dsp->needed_bytes = 3; | |
331 | + s->needed_bytes = 3; | |
235 | 332 | } |
236 | 333 | else { |
237 | 334 | switch (cmd) { |
238 | - case 0x00: | |
239 | - case 0xe7: | |
240 | - /* IMS uses those when probing for sound devices */ | |
241 | - return; | |
242 | - | |
243 | 335 | case 0x03: |
336 | + dsp_out_data (s, 0x10); /* s->csp_param); */ | |
337 | + goto warn; | |
338 | + | |
244 | 339 | case 0x04: |
245 | - dsp_out_data (dsp, 0); | |
246 | - return; | |
340 | + s->needed_bytes = 1; | |
341 | + goto warn; | |
247 | 342 | |
248 | 343 | case 0x05: |
249 | - dsp->needed_bytes = 2; | |
250 | - break; | |
344 | + s->needed_bytes = 2; | |
345 | + goto warn; | |
346 | + | |
347 | + case 0x08: | |
348 | + /* __asm__ ("int3"); */ | |
349 | + goto warn; | |
251 | 350 | |
252 | 351 | case 0x0e: |
253 | - dsp->needed_bytes = 2; | |
254 | - break; | |
352 | + s->needed_bytes = 2; | |
353 | + goto warn; | |
354 | + | |
355 | + case 0x09: | |
356 | + dsp_out_data (s, 0xf8); | |
357 | + goto warn; | |
255 | 358 | |
256 | 359 | case 0x0f: |
257 | - dsp->needed_bytes = 1; | |
258 | - break; | |
360 | + s->needed_bytes = 1; | |
361 | + goto warn; | |
259 | 362 | |
260 | 363 | case 0x10: |
261 | - dsp->needed_bytes = 1; | |
262 | - break; | |
364 | + s->needed_bytes = 1; | |
365 | + goto warn; | |
263 | 366 | |
264 | 367 | case 0x14: |
265 | - dsp->needed_bytes = 2; | |
266 | - dsp->dma_buffer_size = 0; | |
368 | + s->needed_bytes = 2; | |
369 | + s->block_size = 0; | |
267 | 370 | break; |
268 | 371 | |
269 | - case 0x20: | |
270 | - dsp_out_data(dsp, 0xff); | |
271 | - break; | |
372 | + case 0x20: /* Direct ADC, Juice/PL */ | |
373 | + dsp_out_data (s, 0xff); | |
374 | + goto warn; | |
272 | 375 | |
273 | 376 | case 0x35: |
274 | - lwarn ("MIDI commands not implemented\n"); | |
377 | + dolog ("MIDI command(0x35) not implemented\n"); | |
275 | 378 | break; |
276 | 379 | |
277 | 380 | case 0x40: |
278 | - dsp->freq = -1; | |
279 | - dsp->time_const = -1; | |
280 | - dsp->needed_bytes = 1; | |
381 | + s->freq = -1; | |
382 | + s->time_const = -1; | |
383 | + s->needed_bytes = 1; | |
281 | 384 | break; |
282 | 385 | |
283 | 386 | case 0x41: |
284 | - case 0x42: | |
285 | - dsp->freq = -1; | |
286 | - dsp->time_const = -1; | |
287 | - dsp->needed_bytes = 2; | |
387 | + s->freq = -1; | |
388 | + s->time_const = -1; | |
389 | + s->needed_bytes = 2; | |
288 | 390 | break; |
289 | 391 | |
392 | + case 0x42: | |
393 | + s->freq = -1; | |
394 | + s->time_const = -1; | |
395 | + s->needed_bytes = 2; | |
396 | + goto warn; | |
397 | + | |
290 | 398 | case 0x45: |
291 | - dsp_out_data (dsp, 0xaa); | |
399 | + dsp_out_data (s, 0xaa); | |
400 | + goto warn; | |
401 | + | |
292 | 402 | case 0x47: /* Continue Auto-Initialize DMA 16bit */ |
293 | 403 | break; |
294 | 404 | |
295 | 405 | case 0x48: |
296 | - dsp->needed_bytes = 2; | |
406 | + s->needed_bytes = 2; | |
297 | 407 | break; |
298 | 408 | |
299 | - case 0x27: /* ????????? */ | |
300 | - case 0x4e: | |
301 | - return; | |
302 | - | |
303 | 409 | case 0x80: |
304 | - cmd = -1; | |
410 | + s->needed_bytes = 2; | |
305 | 411 | break; |
306 | 412 | |
307 | 413 | case 0x90: |
308 | 414 | case 0x91: |
309 | - { | |
310 | - uint8_t d0; | |
311 | - | |
312 | - d0 = 4; | |
313 | - /* if (dsp->fmt_signed) d0 |= 16; */ | |
314 | - /* if (dsp->fmt_stereo) d0 |= 32; */ | |
315 | - dma_cmd (cmd == 0x90 ? 0xc4 : 0xc0, d0, -1); | |
316 | - cmd = -1; | |
317 | - break; | |
318 | - } | |
415 | + dma_cmd8 (s, ((cmd & 1) == 0) | DMA8_HIGH, -1); | |
416 | + break; | |
319 | 417 | |
320 | - case 0xd0: /* XXX */ | |
321 | - control (0); | |
322 | - return; | |
418 | + case 0xd0: /* halt DMA operation. 8bit */ | |
419 | + control (s, 0); | |
420 | + break; | |
323 | 421 | |
324 | - case 0xd1: | |
325 | - dsp->speaker = 1; | |
422 | + case 0xd1: /* speaker on */ | |
423 | + speaker (s, 1); | |
326 | 424 | break; |
327 | 425 | |
328 | - case 0xd3: | |
329 | - dsp->speaker = 0; | |
330 | - return; | |
426 | + case 0xd3: /* speaker off */ | |
427 | + speaker (s, 0); | |
428 | + break; | |
331 | 429 | |
332 | - case 0xd4: | |
333 | - control (1); | |
430 | + case 0xd4: /* continue DMA operation. 8bit */ | |
431 | + control (s, 1); | |
334 | 432 | break; |
335 | 433 | |
336 | - case 0xd5: | |
337 | - control (0); | |
434 | + case 0xd5: /* halt DMA operation. 16bit */ | |
435 | + control (s, 0); | |
338 | 436 | break; |
339 | 437 | |
340 | - case 0xd6: | |
341 | - control (1); | |
438 | + case 0xd6: /* continue DMA operation. 16bit */ | |
439 | + control (s, 1); | |
342 | 440 | break; |
343 | 441 | |
344 | - case 0xd9: | |
345 | - control (0); | |
346 | - dsp->dma_auto = 0; | |
347 | - return; | |
442 | + case 0xd9: /* exit auto-init DMA after this block. 16bit */ | |
443 | + s->dma_auto = 0; | |
444 | + break; | |
348 | 445 | |
349 | - case 0xda: | |
350 | - control (0); | |
351 | - dsp->dma_auto = 0; | |
446 | + case 0xda: /* exit auto-init DMA after this block. 8bit */ | |
447 | + s->dma_auto = 0; | |
352 | 448 | break; |
353 | 449 | |
354 | 450 | case 0xe0: |
355 | - dsp->needed_bytes = 1; | |
356 | - break; | |
451 | + s->needed_bytes = 1; | |
452 | + goto warn; | |
357 | 453 | |
358 | 454 | case 0xe1: |
359 | - dsp_out_data(dsp, sb.ver_lo); | |
360 | - dsp_out_data(dsp, sb.ver_hi); | |
361 | - return; | |
455 | + dsp_out_data (s, s->ver & 0xff); | |
456 | + dsp_out_data (s, s->ver >> 8); | |
457 | + break; | |
458 | + | |
459 | + case 0xe2: | |
460 | + s->needed_bytes = 1; | |
461 | + goto warn; | |
362 | 462 | |
363 | 463 | case 0xe3: |
364 | 464 | { |
365 | 465 | int i; |
366 | - for (i = sizeof (e3) - 1; i >= 0; i--) | |
367 | - dsp_out_data (dsp, e3[i]); | |
368 | - return; | |
466 | + for (i = sizeof (e3) - 1; i >= 0; --i) | |
467 | + dsp_out_data (s, e3[i]); | |
369 | 468 | } |
469 | + break; | |
370 | 470 | |
371 | 471 | case 0xe4: /* write test reg */ |
372 | - dsp->needed_bytes = 1; | |
472 | + s->needed_bytes = 1; | |
373 | 473 | break; |
374 | 474 | |
475 | + case 0xe7: | |
476 | + dolog ("Attempt to probe for ESS (0xe7)?\n"); | |
477 | + return; | |
478 | + | |
375 | 479 | case 0xe8: /* read test reg */ |
376 | - dsp_out_data (dsp, dsp->test_reg); | |
480 | + dsp_out_data (s, s->test_reg); | |
377 | 481 | break; |
378 | 482 | |
379 | 483 | case 0xf2: |
380 | - dsp_out_data (dsp, 0xaa); | |
381 | - dsp->mixer_regs[0x82] |= dsp->mixer_regs[0x80]; | |
382 | - pic_set_irq (sb.irq, 1); | |
383 | - return; | |
484 | + case 0xf3: | |
485 | + dsp_out_data (s, 0xaa); | |
486 | + s->mixer_regs[0x82] |= (cmd == 0xf2) ? 1 : 2; | |
487 | + pic_set_irq (s->irq, 1); | |
488 | + break; | |
384 | 489 | |
385 | 490 | case 0xf9: |
386 | - dsp->needed_bytes = 1; | |
387 | - break; | |
491 | + s->needed_bytes = 1; | |
492 | + goto warn; | |
388 | 493 | |
389 | 494 | case 0xfa: |
390 | - dsp_out_data (dsp, 0); | |
391 | - break; | |
495 | + dsp_out_data (s, 0); | |
496 | + goto warn; | |
392 | 497 | |
393 | 498 | case 0xfc: /* FIXME */ |
394 | - dsp_out_data (dsp, 0); | |
395 | - break; | |
499 | + dsp_out_data (s, 0); | |
500 | + goto warn; | |
396 | 501 | |
397 | 502 | default: |
398 | 503 | dolog ("unrecognized command %#x\n", cmd); |
399 | - goto error; | |
504 | + return; | |
400 | 505 | } |
401 | 506 | } |
402 | - dsp->cmd = cmd; | |
507 | + | |
508 | + s->cmd = cmd; | |
509 | + if (!s->needed_bytes) | |
510 | + ldebug ("\n"); | |
403 | 511 | return; |
404 | 512 | |
405 | - error: | |
513 | + warn: | |
514 | + dolog ("warning command %#x,%d is not trully understood yet\n", | |
515 | + cmd, s->needed_bytes); | |
516 | + s->cmd = cmd; | |
406 | 517 | return; |
407 | 518 | } |
408 | 519 | |
409 | -static void complete (SB16State *dsp) | |
520 | +static uint16_t dsp_get_lohi (SB16State *s) | |
521 | +{ | |
522 | + uint8_t hi = dsp_get_data (s); | |
523 | + uint8_t lo = dsp_get_data (s); | |
524 | + return (hi << 8) | lo; | |
525 | +} | |
526 | + | |
527 | +static uint16_t dsp_get_hilo (SB16State *s) | |
528 | +{ | |
529 | + uint8_t lo = dsp_get_data (s); | |
530 | + uint8_t hi = dsp_get_data (s); | |
531 | + return (hi << 8) | lo; | |
532 | +} | |
533 | + | |
534 | +static void complete (SB16State *s) | |
410 | 535 | { |
411 | 536 | int d0, d1, d2; |
412 | - linfo ("complete command %#x, in_index %d, needed_bytes %d\n", | |
413 | - dsp->cmd, dsp->in_index, dsp->needed_bytes); | |
537 | + ldebug ("complete command %#x, in_index %d, needed_bytes %d\n", | |
538 | + s->cmd, s->in_index, s->needed_bytes); | |
414 | 539 | |
415 | - if (dsp->cmd > 0xaf && dsp->cmd < 0xd0) { | |
416 | - d2 = dsp_get_data (dsp); | |
417 | - d1 = dsp_get_data (dsp); | |
418 | - d0 = dsp_get_data (dsp); | |
540 | + if (s->cmd > 0xaf && s->cmd < 0xd0) { | |
541 | + d2 = dsp_get_data (s); | |
542 | + d1 = dsp_get_data (s); | |
543 | + d0 = dsp_get_data (s); | |
419 | 544 | |
420 | - ldebug ("d0 = %d, d1 = %d, d2 = %d\n", | |
421 | - d0, d1, d2); | |
422 | - dma_cmd (dsp->cmd, d0, d1 + (d2 << 8)); | |
545 | + if (s->cmd & 8) { | |
546 | + dolog ("ADC params cmd = %#x d0 = %d, d1 = %d, d2 = %d\n", | |
547 | + s->cmd, d0, d1, d2); | |
548 | + } | |
549 | + else { | |
550 | + ldebug ("cmd = %#x d0 = %d, d1 = %d, d2 = %d\n", | |
551 | + s->cmd, d0, d1, d2); | |
552 | + dma_cmd (s, s->cmd, d0, d1 + (d2 << 8)); | |
553 | + } | |
423 | 554 | } |
424 | 555 | else { |
425 | - switch (dsp->cmd) { | |
556 | + switch (s->cmd) { | |
426 | 557 | case 0x04: |
427 | - case 0x10: | |
428 | - dsp_get_data (dsp); | |
558 | + s->csp_mode = dsp_get_data (s); | |
559 | + s->csp_reg83r = 0; | |
560 | + s->csp_reg83w = 0; | |
561 | + ldebug ("CSP command 0x04: mode=%#x\n", s->csp_mode); | |
429 | 562 | break; |
430 | 563 | |
431 | - case 0x0f: | |
432 | - d0 = dsp_get_data (dsp); | |
433 | - dsp_out_data (dsp, 0xf8); | |
564 | + case 0x05: | |
565 | + s->csp_param = dsp_get_data (s); | |
566 | + s->csp_value = dsp_get_data (s); | |
567 | + ldebug ("CSP command 0x05: param=%#x value=%#x\n", | |
568 | + s->csp_param, | |
569 | + s->csp_value); | |
434 | 570 | break; |
435 | 571 | |
436 | - case 0x05: | |
437 | 572 | case 0x0e: |
438 | - dsp_get_data (dsp); | |
439 | - dsp_get_data (dsp); | |
573 | + d0 = dsp_get_data (s); | |
574 | + d1 = dsp_get_data (s); | |
575 | + ldebug ("write CSP register %d <- %#x\n", d1, d0); | |
576 | + if (d1 == 0x83) { | |
577 | + ldebug ("0x83[%d] <- %#x\n", s->csp_reg83r, d0); | |
578 | + s->csp_reg83[s->csp_reg83r % 4] = d0; | |
579 | + s->csp_reg83r += 1; | |
580 | + } | |
581 | + else | |
582 | + s->csp_regs[d1] = d0; | |
440 | 583 | break; |
441 | 584 | |
442 | - case 0x14: | |
443 | - { | |
444 | - int save_left; | |
445 | - int save_pos; | |
446 | - | |
447 | - d1 = dsp_get_data (dsp); | |
448 | - d0 = dsp_get_data (dsp); | |
585 | + case 0x0f: | |
586 | + d0 = dsp_get_data (s); | |
587 | + ldebug ("read CSP register %#x -> %#x, mode=%#x\n", | |
588 | + d0, s->csp_regs[d0], s->csp_mode); | |
589 | + if (d0 == 0x83) { | |
590 | + ldebug ("0x83[%d] -> %#x\n", | |
591 | + s->csp_reg83w, | |
592 | + s->csp_reg83[s->csp_reg83w % 4]); | |
593 | + dsp_out_data (s, s->csp_reg83[s->csp_reg83w % 4]); | |
594 | + s->csp_reg83w += 1; | |
595 | + } | |
596 | + else | |
597 | + dsp_out_data (s, s->csp_regs[d0]); | |
598 | + break; | |
449 | 599 | |
450 | - save_left = dsp->left_till_irq; | |
451 | - save_pos = dsp->dma_pos; | |
452 | - dma_cmd (0xc0, 0, d0 + (d1 << 8)); | |
453 | - dsp->left_till_irq = save_left; | |
454 | - dsp->dma_pos = save_pos; | |
600 | + case 0x10: | |
601 | + d0 = dsp_get_data (s); | |
602 | + dolog ("cmd 0x10 d0=%#x\n", d0); | |
603 | + break; | |
455 | 604 | |
456 | - linfo ("set buffer size data[%d, %d] %d pos %d\n", | |
457 | - d0, d1, dsp->dma_buffer_size, dsp->dma_pos); | |
458 | - break; | |
459 | - } | |
605 | + case 0x14: | |
606 | + dma_cmd8 (s, 0, dsp_get_lohi (s)); | |
607 | + /* s->can_write = 0; */ | |
608 | + /* qemu_mod_timer (s->aux_ts, qemu_get_clock (vm_clock) + (ticks_per_sec * 320) / 1000000); */ | |
609 | + break; | |
460 | 610 | |
461 | 611 | case 0x40: |
462 | - dsp->time_const = dsp_get_data (dsp); | |
463 | - linfo ("set time const %d\n", dsp->time_const); | |
612 | + s->time_const = dsp_get_data (s); | |
613 | + ldebug ("set time const %d\n", s->time_const); | |
464 | 614 | break; |
465 | 615 | |
466 | - case 0x41: | |
467 | - case 0x42: | |
468 | - d1 = dsp_get_data (dsp); | |
469 | - d0 = dsp_get_data (dsp); | |
616 | + case 0x42: /* FT2 sets output freq with this, go figure */ | |
617 | + dolog ("cmd 0x42 might not do what it think it should\n"); | |
470 | 618 | |
471 | - dsp->freq = d1 + (d0 << 8); | |
472 | - linfo ("set freq %#x, %#x = %d\n", d1, d0, dsp->freq); | |
619 | + case 0x41: | |
620 | + s->freq = dsp_get_hilo (s); | |
621 | + ldebug ("set freq %d\n", s->freq); | |
473 | 622 | break; |
474 | 623 | |
475 | 624 | case 0x48: |
476 | - d1 = dsp_get_data (dsp); | |
477 | - d0 = dsp_get_data (dsp); | |
478 | - dsp->dma_buffer_size = d1 + (d0 << 8); | |
479 | - linfo ("set dma len %#x, %#x = %d\n", | |
480 | - d1, d0, dsp->dma_buffer_size); | |
625 | + s->block_size = dsp_get_lohi (s); | |
626 | + /* s->highspeed = 1; */ | |
627 | + ldebug ("set dma block len %d\n", s->block_size); | |
628 | + break; | |
629 | + | |
630 | + case 0x80: | |
631 | + { | |
632 | + int samples, bytes; | |
633 | + int64_t ticks; | |
634 | + | |
635 | + if (-1 == s->freq) | |
636 | + s->freq = 11025; | |
637 | + samples = dsp_get_lohi (s); | |
638 | + bytes = samples << s->fmt_stereo << (s->fmt_bits == 16); | |
639 | + ticks = ticks_per_sec / (s->freq / bytes); | |
640 | + if (ticks < ticks_per_sec / 1024) | |
641 | + pic_set_irq (s->irq, 1); | |
642 | + else | |
643 | + qemu_mod_timer (s->aux_ts, qemu_get_clock (vm_clock) + ticks); | |
644 | + ldebug ("mix silence %d %d %lld\n", samples, bytes, ticks); | |
645 | + } | |
481 | 646 | break; |
482 | 647 | |
483 | 648 | case 0xe0: |
484 | - d0 = dsp_get_data (dsp); | |
485 | - dsp->out_data_len = 0; | |
486 | - linfo ("data = %#x\n", d0); | |
487 | - dsp_out_data (dsp, d0 ^ 0xff); | |
649 | + d0 = dsp_get_data (s); | |
650 | + s->out_data_len = 0; | |
651 | + ldebug ("E0 data = %#x\n", d0); | |
652 | + dsp_out_data(s, ~d0); | |
488 | 653 | break; |
489 | 654 | |
490 | - case 0xe4: | |
491 | - dsp->test_reg = dsp_get_data (dsp); | |
655 | + case 0xe2: | |
656 | + d0 = dsp_get_data (s); | |
657 | + dolog ("E2 = %#x\n", d0); | |
492 | 658 | break; |
493 | 659 | |
660 | + case 0xe4: | |
661 | + s->test_reg = dsp_get_data (s); | |
662 | + break; | |
494 | 663 | |
495 | 664 | case 0xf9: |
496 | - d0 = dsp_get_data (dsp); | |
497 | - ldebug ("f9 <- %#x\n", d0); | |
665 | + d0 = dsp_get_data (s); | |
666 | + ldebug ("command 0xf9 with %#x\n", d0); | |
498 | 667 | switch (d0) { |
499 | - case 0x0e: dsp_out_data (dsp, 0xff); break; | |
500 | - case 0x0f: dsp_out_data (dsp, 0x07); break; | |
501 | - case 0xf9: dsp_out_data (dsp, 0x00); break; | |
668 | + case 0x0e: | |
669 | + dsp_out_data (s, 0xff); | |
670 | + break; | |
671 | + | |
672 | + case 0x0f: | |
673 | + dsp_out_data (s, 0x07); | |
674 | + break; | |
675 | + | |
502 | 676 | case 0x37: |
503 | - dsp_out_data (dsp, 0x38); break; | |
677 | + dsp_out_data (s, 0x38); | |
678 | + break; | |
679 | + | |
504 | 680 | default: |
505 | - dsp_out_data (dsp, 0); | |
681 | + dsp_out_data (s, 0x00); | |
682 | + break; | |
506 | 683 | } |
507 | 684 | break; |
508 | 685 | |
509 | 686 | default: |
510 | - dolog ("complete: unrecognized command %#x\n", dsp->cmd); | |
687 | + dolog ("complete: unrecognized command %#x\n", s->cmd); | |
511 | 688 | return; |
512 | 689 | } |
513 | 690 | } |
514 | 691 | |
515 | - dsp->needed_bytes = 0; | |
516 | - dsp->cmd = -1; | |
692 | + ldebug ("\n"); | |
693 | + s->cmd = -1; | |
517 | 694 | return; |
518 | 695 | } |
519 | 696 | |
697 | +static void reset (SB16State *s) | |
698 | +{ | |
699 | + pic_set_irq (s->irq, 0); | |
700 | + if (s->dma_auto) { | |
701 | + pic_set_irq (s->irq, 1); | |
702 | + pic_set_irq (s->irq, 0); | |
703 | + } | |
704 | + | |
705 | + s->mixer_regs[0x82] = 0; | |
706 | + s->dma_auto = 0; | |
707 | + s->in_index = 0; | |
708 | + s->out_data_len = 0; | |
709 | + s->left_till_irq = 0; | |
710 | + s->needed_bytes = 0; | |
711 | + s->block_size = -1; | |
712 | + s->nzero = 0; | |
713 | + s->highspeed = 0; | |
714 | + s->v2x6 = 0; | |
715 | + | |
716 | + dsp_out_data(s, 0xaa); | |
717 | + speaker (s, 0); | |
718 | + control (s, 0); | |
719 | +} | |
720 | + | |
520 | 721 | static IO_WRITE_PROTO (dsp_write) |
521 | 722 | { |
522 | - SB16State *dsp = opaque; | |
723 | + SB16State *s = opaque; | |
523 | 724 | int iport; |
524 | 725 | |
525 | - iport = nport - sb.port; | |
726 | + iport = nport - s->port; | |
526 | 727 | |
527 | - ldebug ("dsp_write %#x <- %#x\n", nport, val); | |
728 | + ldebug ("write %#x <- %#x\n", nport, val); | |
528 | 729 | switch (iport) { |
529 | - case 0x6: | |
530 | - control (0); | |
531 | - if (0 == val) | |
532 | - dsp->v2x6 = 0; | |
533 | - else if ((1 == val) && (0 == dsp->v2x6)) { | |
534 | - dsp->v2x6 = 1; | |
535 | - dsp->dma_pos = 0; | |
536 | - dsp->dma_auto = 0; | |
537 | - dsp->in_index = 0; | |
538 | - dsp->out_data_len = 0; | |
539 | - dsp->left_till_irq = 0; | |
540 | - dsp->speaker = 0; | |
541 | - dsp->needed_bytes = 0; | |
542 | - pic_set_irq (sb.irq, 0); | |
543 | - dsp_out_data(dsp, 0xaa); | |
730 | + case 0x06: | |
731 | + switch (val) { | |
732 | + case 0x00: | |
733 | + if (s->v2x6 == 1) { | |
734 | + if (0 && s->highspeed) { | |
735 | + s->highspeed = 0; | |
736 | + pic_set_irq (s->irq, 0); | |
737 | + control (s, 0); | |
738 | + } | |
739 | + else | |
740 | + reset (s); | |
741 | + } | |
742 | + s->v2x6 = 0; | |
743 | + break; | |
744 | + | |
745 | + case 0x01: | |
746 | + case 0x03: /* FreeBSD kludge */ | |
747 | + s->v2x6 = 1; | |
748 | + break; | |
749 | + | |
750 | + case 0xc6: | |
751 | + s->v2x6 = 0; /* Prince of Persia, csp.sys, diagnose.exe */ | |
752 | + break; | |
753 | + | |
754 | + case 0xb8: /* Panic */ | |
755 | + reset (s); | |
756 | + break; | |
757 | + | |
758 | + case 0x39: | |
759 | + dsp_out_data (s, 0x38); | |
760 | + reset (s); | |
761 | + s->v2x6 = 0x39; | |
762 | + break; | |
763 | + | |
764 | + default: | |
765 | + s->v2x6 = val; | |
766 | + break; | |
544 | 767 | } |
545 | - else | |
546 | - dsp->v2x6 = ~0; | |
547 | 768 | break; |
548 | 769 | |
549 | - case 0xc: /* write data or command | write status */ | |
550 | - if (0 == dsp->needed_bytes) { | |
551 | - command (dsp, val); | |
552 | - if (0 == dsp->needed_bytes) { | |
553 | - log_dsp (dsp); | |
770 | + case 0x0c: /* write data or command | write status */ | |
771 | +/* if (s->highspeed) */ | |
772 | +/* break; */ | |
773 | + | |
774 | + if (0 == s->needed_bytes) { | |
775 | + command (s, val); | |
776 | +#if 0 | |
777 | + if (0 == s->needed_bytes) { | |
778 | + log_dsp (s); | |
554 | 779 | } |
780 | +#endif | |
555 | 781 | } |
556 | 782 | else { |
557 | - if (dsp->in_index == sizeof (dsp->in2_data)) { | |
783 | + if (s->in_index == sizeof (s->in2_data)) { | |
558 | 784 | dolog ("in data overrun\n"); |
559 | 785 | } |
560 | 786 | else { |
561 | - dsp->in2_data[dsp->in_index++] = val; | |
562 | - if (dsp->in_index == dsp->needed_bytes) { | |
563 | - dsp->needed_bytes = 0; | |
564 | - complete (dsp); | |
565 | - log_dsp (dsp); | |
787 | + s->in2_data[s->in_index++] = val; | |
788 | + if (s->in_index == s->needed_bytes) { | |
789 | + s->needed_bytes = 0; | |
790 | + complete (s); | |
791 | +#if 0 | |
792 | + log_dsp (s); | |
793 | +#endif | |
794 | + } | |
566 | 795 | } |
567 | 796 | } |
568 | - } | |
569 | 797 | break; |
570 | 798 | |
571 | 799 | default: |
572 | - dolog ("dsp_write (nport=%#x, val=%#x)\n", nport, val); | |
800 | + ldebug ("(nport=%#x, val=%#x)\n", nport, val); | |
573 | 801 | break; |
574 | 802 | } |
575 | 803 | } |
576 | 804 | |
577 | 805 | static IO_READ_PROTO (dsp_read) |
578 | 806 | { |
579 | - SB16State *dsp = opaque; | |
580 | - int iport, retval; | |
807 | + SB16State *s = opaque; | |
808 | + int iport, retval, ack = 0; | |
581 | 809 | |
582 | - iport = nport - sb.port; | |
810 | + iport = nport - s->port; | |
583 | 811 | |
584 | 812 | switch (iport) { |
585 | - case 0x6: /* reset */ | |
586 | - control (0); | |
587 | - retval = 0; | |
588 | - dsp->speaker = 0; | |
813 | + case 0x06: /* reset */ | |
814 | + retval = 0xff; | |
589 | 815 | break; |
590 | 816 | |
591 | - case 0xa: /* read data */ | |
592 | - if (dsp->out_data_len) { | |
593 | - retval = dsp->out_data[--dsp->out_data_len]; | |
594 | - dsp->last_read_byte = retval; | |
595 | - } else { | |
596 | - retval = dsp->last_read_byte; | |
817 | + case 0x0a: /* read data */ | |
818 | + if (s->out_data_len) { | |
819 | + retval = s->out_data[--s->out_data_len]; | |
820 | + s->last_read_byte = retval; | |
821 | + } | |
822 | + else { | |
597 | 823 | dolog ("empty output buffer\n"); |
824 | + retval = s->last_read_byte; | |
598 | 825 | /* goto error; */ |
599 | 826 | } |
600 | 827 | break; |
601 | 828 | |
602 | - case 0xc: /* 0xxxxxxx can write */ | |
603 | - retval = 0; | |
604 | - if (dsp->out_data_len == sizeof (dsp->out_data)) retval |= 0x80; | |
829 | + case 0x0c: /* 0 can write */ | |
830 | + retval = s->can_write ? 0 : 0x80; | |
605 | 831 | break; |
606 | 832 | |
607 | - case 0xd: /* timer interrupt clear */ | |
608 | - dolog ("timer interrupt clear\n"); | |
609 | - goto error; | |
833 | + case 0x0d: /* timer interrupt clear */ | |
834 | + /* dolog ("timer interrupt clear\n"); */ | |
835 | + retval = 0; | |
836 | + break; | |
610 | 837 | |
611 | - case 0xe: /* data available status | irq 8 ack */ | |
612 | - /* XXX drop pic irq line here? */ | |
613 | - /* ldebug ("8 ack\n"); */ | |
614 | - retval = dsp->out_data_len ? 0x80 : 0; | |
615 | - dsp->mixer_regs[0x82] &= ~dsp->mixer_regs[0x80]; | |
616 | - pic_set_irq (sb.irq, 0); | |
838 | + case 0x0e: /* data available status | irq 8 ack */ | |
839 | + retval = (!s->out_data_len || s->highspeed) ? 0 : 0x80; | |
840 | + if (s->mixer_regs[0x82] & 1) { | |
841 | + ack = 1; | |
842 | + s->mixer_regs[0x82] &= 1; | |
843 | + pic_set_irq (s->irq, 0); | |
844 | + } | |
617 | 845 | break; |
618 | 846 | |
619 | - case 0xf: /* irq 16 ack */ | |
620 | - /* XXX drop pic irq line here? */ | |
621 | - ldebug ("16 ack\n"); | |
847 | + case 0x0f: /* irq 16 ack */ | |
622 | 848 | retval = 0xff; |
623 | - dsp->mixer_regs[0x82] &= ~dsp->mixer_regs[0x80]; | |
624 | - pic_set_irq (sb.irq, 0); | |
849 | + if (s->mixer_regs[0x82] & 2) { | |
850 | + ack = 1; | |
851 | + s->mixer_regs[0x82] &= 2; | |
852 | + pic_set_irq (s->irq, 0); | |
853 | + } | |
625 | 854 | break; |
626 | 855 | |
627 | 856 | default: |
628 | 857 | goto error; |
629 | 858 | } |
630 | 859 | |
631 | - if (0xe == iport) { | |
632 | - if (0 == retval) { | |
633 | - if (!dsp->nzero) { | |
634 | - ldebug ("dsp_read (nport=%#x iport %#x) = %#x, %lld\n", | |
635 | - nport, iport, retval, dsp->nzero); | |
636 | - } | |
637 | - dsp->nzero += 1; | |
638 | - } | |
639 | - else { | |
640 | - ldebug ("dsp_read (nport=%#x iport %#x) = %#x, %lld\n", | |
641 | - nport, iport, retval, dsp->nzero); | |
642 | - dsp->nzero = 0; | |
643 | - } | |
644 | - } | |
645 | - else { | |
646 | - ldebug ("dsp_read (nport=%#x iport %#x) = %#x\n", | |
647 | - nport, iport, retval); | |
648 | - } | |
860 | + if (!ack) | |
861 | + ldebug ("read %#x -> %#x\n", nport, retval); | |
649 | 862 | |
650 | 863 | return retval; |
651 | 864 | |
652 | 865 | error: |
653 | - printf ("dsp_read error %#x\n", nport); | |
866 | + dolog ("WARNING dsp_read %#x error\n", nport); | |
654 | 867 | return 0xff; |
655 | 868 | } |
656 | 869 | |
870 | +static void reset_mixer (SB16State *s) | |
871 | +{ | |
872 | + int i; | |
873 | + | |
874 | + memset (s->mixer_regs, 0xff, 0x7f); | |
875 | + memset (s->mixer_regs + 0x83, 0xff, sizeof (s->mixer_regs) - 0x83); | |
876 | + | |
877 | + s->mixer_regs[0x02] = 4; /* master volume 3bits */ | |
878 | + s->mixer_regs[0x06] = 4; /* MIDI volume 3bits */ | |
879 | + s->mixer_regs[0x08] = 0; /* CD volume 3bits */ | |
880 | + s->mixer_regs[0x0a] = 0; /* voice volume 2bits */ | |
881 | + | |
882 | + /* d5=input filt, d3=lowpass filt, d1,d2=input source */ | |
883 | + s->mixer_regs[0x0c] = 0; | |
884 | + | |
885 | + /* d5=output filt, d1=stereo switch */ | |
886 | + s->mixer_regs[0x0e] = 0; | |
887 | + | |
888 | + /* voice volume L d5,d7, R d1,d3 */ | |
889 | + s->mixer_regs[0x04] = (4 << 5) | (4 << 1); | |
890 | + /* master ... */ | |
891 | + s->mixer_regs[0x22] = (4 << 5) | (4 << 1); | |
892 | + /* MIDI ... */ | |
893 | + s->mixer_regs[0x26] = (4 << 5) | (4 << 1); | |
894 | + | |
895 | + for (i = 0x30; i < 0x48; i++) { | |
896 | + s->mixer_regs[i] = 0x20; | |
897 | + } | |
898 | +} | |
899 | + | |
657 | 900 | static IO_WRITE_PROTO(mixer_write_indexb) |
658 | 901 | { |
659 | - SB16State *dsp = opaque; | |
660 | - dsp->mixer_nreg = val; | |
902 | + SB16State *s = opaque; | |
903 | + s->mixer_nreg = val; | |
661 | 904 | } |
662 | 905 | |
663 | 906 | static IO_WRITE_PROTO(mixer_write_datab) |
664 | 907 | { |
665 | - SB16State *dsp = opaque; | |
666 | - int i; | |
908 | + SB16State *s = opaque; | |
909 | + | |
910 | + ldebug ("mixer_write [%#x] <- %#x\n", s->mixer_nreg, val); | |
911 | + if (s->mixer_nreg > sizeof (s->mixer_regs)) | |
912 | + return; | |
667 | 913 | |
668 | - linfo ("mixer [%#x] <- %#x\n", dsp->mixer_nreg, val); | |
669 | - switch (dsp->mixer_nreg) { | |
914 | + switch (s->mixer_nreg) { | |
670 | 915 | case 0x00: |
671 | - /* Bochs */ | |
672 | - dsp->mixer_regs[0x04] = 0xcc; | |
673 | - dsp->mixer_regs[0x0a] = 0x00; | |
674 | - dsp->mixer_regs[0x22] = 0xcc; | |
675 | - dsp->mixer_regs[0x26] = 0xcc; | |
676 | - dsp->mixer_regs[0x28] = 0x00; | |
677 | - dsp->mixer_regs[0x2e] = 0x00; | |
678 | - dsp->mixer_regs[0x3c] = 0x1f; | |
679 | - dsp->mixer_regs[0x3d] = 0x15; | |
680 | - dsp->mixer_regs[0x3e] = 0x0b; | |
681 | - | |
682 | - for (i = 0x30; i <= 0x35; i++) | |
683 | - dsp->mixer_regs[i] = 0xc0; | |
684 | - | |
685 | - for (i = 0x36; i <= 0x3b; i++) | |
686 | - dsp->mixer_regs[i] = 0x00; | |
687 | - | |
688 | - for (i = 0x3f; i <= 0x43; i++) | |
689 | - dsp->mixer_regs[i] = 0x00; | |
690 | - | |
691 | - for (i = 0x44; i <= 0x47; i++) | |
692 | - dsp->mixer_regs[i] = 0x80; | |
693 | - | |
694 | - for (i = 0x30; i < 0x48; i++) { | |
695 | - dsp->mixer_regs[i] = 0x20; | |
696 | - } | |
916 | + reset_mixer (s); | |
697 | 917 | break; |
698 | 918 | |
699 | - case 0x04: | |
700 | - case 0x0a: | |
701 | - case 0x22: | |
702 | - case 0x26: | |
703 | - case 0x28: | |
704 | - case 0x2e: | |
705 | - case 0x30: | |
706 | - case 0x31: | |
707 | - case 0x32: | |
708 | - case 0x33: | |
709 | - case 0x34: | |
710 | - case 0x35: | |
711 | - case 0x36: | |
712 | - case 0x37: | |
713 | - case 0x38: | |
714 | - case 0x39: | |
715 | - case 0x3a: | |
716 | - case 0x3b: | |
717 | - case 0x3c: | |
718 | - case 0x3d: | |
719 | - case 0x3e: | |
720 | - case 0x3f: | |
721 | - case 0x40: | |
722 | - case 0x41: | |
723 | - case 0x42: | |
724 | - case 0x43: | |
725 | - case 0x44: | |
726 | - case 0x45: | |
727 | - case 0x46: | |
728 | - case 0x47: | |
729 | 919 | case 0x80: |
730 | - case 0x81: | |
920 | + { | |
921 | + int irq = irq_of_magic (val); | |
922 | + ldebug ("setting irq to %d (val=%#x)\n", irq, val); | |
923 | + if (irq > 0) | |
924 | + s->irq = irq; | |
925 | + } | |
731 | 926 | break; |
732 | - default: | |
733 | - return; | |
734 | - } | |
735 | - dsp->mixer_regs[dsp->mixer_nreg] = val; | |
736 | -} | |
737 | 927 | |
738 | -static IO_WRITE_PROTO(mpu_write) | |
739 | -{ | |
740 | - linfo ("mpu: %#x\n", val); | |
741 | -} | |
928 | + case 0x81: | |
929 | + { | |
930 | + int dma, hdma; | |
742 | 931 | |
743 | -static IO_WRITE_PROTO(adlib_write) | |
744 | -{ | |
745 | - linfo ("adlib: %#x\n", val); | |
746 | -} | |
932 | + dma = lsbindex (val & 0xf); | |
933 | + hdma = lsbindex (val & 0xf0); | |
934 | + dolog ("attempt to set DMA register 8bit %d, 16bit %d (val=%#x)\n", | |
935 | + dma, hdma, val); | |
936 | +#if 0 | |
937 | + s->dma = dma; | |
938 | + s->hdma = hdma; | |
939 | +#endif | |
940 | + } | |
941 | + break; | |
747 | 942 | |
748 | -static IO_READ_PROTO(mpu_read) | |
749 | -{ | |
750 | - linfo ("mpu read: %#x\n", nport); | |
751 | - return 0x80; | |
752 | -} | |
943 | + case 0x82: | |
944 | + dolog ("attempt to write into IRQ status register (val=%#x)\n", | |
945 | + val); | |
946 | + return; | |
753 | 947 | |
754 | -static IO_READ_PROTO(adlib_read) | |
755 | -{ | |
756 | - linfo ("adlib read: %#x\n", nport); | |
757 | - return 0; | |
948 | + default: | |
949 | + if (s->mixer_nreg >= 0x80) | |
950 | + dolog ("attempt to write mixer[%#x] <- %#x\n", s->mixer_nreg, val); | |
951 | + break; | |
952 | + } | |
953 | + | |
954 | + s->mixer_regs[s->mixer_nreg] = val; | |
758 | 955 | } |
759 | 956 | |
760 | 957 | static IO_WRITE_PROTO(mixer_write_indexw) |
... | ... | @@ -765,194 +962,229 @@ static IO_WRITE_PROTO(mixer_write_indexw) |
765 | 962 | |
766 | 963 | static IO_READ_PROTO(mixer_read) |
767 | 964 | { |
768 | - SB16State *dsp = opaque; | |
769 | - linfo ("mixer [%#x] -> %#x\n", dsp->mixer_nreg, dsp->mixer_regs[dsp->mixer_nreg]); | |
770 | - return dsp->mixer_regs[dsp->mixer_nreg]; | |
771 | -} | |
772 | - | |
773 | -void SB16_run (void) | |
774 | -{ | |
775 | - if (0 == dsp.speaker) | |
776 | - return; | |
777 | - | |
778 | - AUD_run (); | |
965 | + SB16State *s = opaque; | |
966 | + ldebug ("mixer_read[%#x] -> %#x\n", | |
967 | + s->mixer_nreg, s->mixer_regs[s->mixer_nreg]); | |
968 | + return s->mixer_regs[s->mixer_nreg]; | |
779 | 969 | } |
780 | 970 | |
781 | -static int write_audio (uint32_t addr, int len, int size) | |
971 | +static int write_audio (SB16State *s, int nchan, int dma_pos, | |
972 | + int dma_len, int len) | |
782 | 973 | { |
783 | 974 | int temp, net; |
784 | 975 | uint8_t tmpbuf[4096]; |
785 | 976 | |
786 | - temp = size; | |
787 | - | |
977 | + temp = len; | |
788 | 978 | net = 0; |
789 | 979 | |
790 | 980 | while (temp) { |
791 | - int left_till_end; | |
792 | - int to_copy; | |
793 | - int copied; | |
981 | + int left = dma_len - dma_pos; | |
982 | + int to_copy, copied; | |
794 | 983 | |
795 | - left_till_end = len - dsp.dma_pos; | |
796 | - | |
797 | - to_copy = MIN (temp, left_till_end); | |
984 | + to_copy = audio_MIN (temp, left); | |
798 | 985 | if (to_copy > sizeof(tmpbuf)) |
799 | 986 | to_copy = sizeof(tmpbuf); |
800 | - cpu_physical_memory_read(addr + dsp.dma_pos, tmpbuf, to_copy); | |
801 | - copied = AUD_write (tmpbuf, to_copy); | |
802 | 987 | |
803 | - temp -= copied; | |
804 | - dsp.dma_pos += copied; | |
805 | - | |
806 | - if (dsp.dma_pos == len) { | |
807 | - dsp.dma_pos = 0; | |
808 | - } | |
988 | + copied = DMA_read_memory (nchan, tmpbuf, dma_pos, to_copy); | |
989 | + copied = AUD_write (s->voice, tmpbuf, copied); | |
809 | 990 | |
991 | + temp -= copied; | |
992 | + dma_pos = (dma_pos + copied) % dma_len; | |
810 | 993 | net += copied; |
811 | 994 | |
812 | - if (copied != to_copy) | |
813 | - return net; | |
995 | + if (!copied) | |
996 | + break; | |
814 | 997 | } |
815 | 998 | |
816 | 999 | return net; |
817 | 1000 | } |
818 | 1001 | |
819 | -static int SB_read_DMA (void *opaque, target_ulong addr, int size) | |
1002 | +static int SB_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len) | |
820 | 1003 | { |
821 | - SB16State *dsp = opaque; | |
822 | - int free, till, copy, written; | |
823 | - | |
824 | - if (0 == dsp->speaker) | |
825 | - return 0; | |
1004 | + SB16State *s = opaque; | |
1005 | + int free, rfree, till, copy, written, elapsed; | |
826 | 1006 | |
827 | - if (dsp->left_till_irq < 0) { | |
828 | - ldebug ("left_till_irq < 0, %d, pos %d \n", | |
829 | - dsp->left_till_irq, dsp->dma_buffer_size); | |
830 | - dsp->left_till_irq += dsp->dma_buffer_size; | |
831 | - return dsp->dma_pos; | |
1007 | + if (s->left_till_irq < 0) { | |
1008 | + s->left_till_irq = s->block_size; | |
832 | 1009 | } |
833 | 1010 | |
834 | - free = AUD_get_free (); | |
835 | - | |
836 | - if ((free <= 0) || (0 == size)) { | |
837 | - ldebug ("returning, since free = %d and size = %d\n", free, size); | |
838 | - return dsp->dma_pos; | |
839 | - } | |
1011 | + elapsed = AUD_calc_elapsed (s->voice); | |
1012 | + free = elapsed;/* AUD_get_free (s->voice); */ | |
1013 | + rfree = free; | |
1014 | + free = audio_MIN (free, elapsed) & ~s->align; | |
840 | 1015 | |
841 | - if (mix_block > 0) { | |
842 | - copy = MIN (free, mix_block); | |
843 | - } | |
844 | - else { | |
845 | - copy = free; | |
1016 | + if ((free <= 0) || !dma_len) { | |
1017 | + return dma_pos; | |
846 | 1018 | } |
847 | 1019 | |
848 | - till = dsp->left_till_irq; | |
1020 | + copy = free; | |
1021 | + till = s->left_till_irq; | |
849 | 1022 | |
850 | 1023 | #ifdef DEBUG_SB16_MOST |
851 | - ldebug ("addr:%#010x free:%d till:%d size:%d\n", | |
852 | - addr, free, till, size); | |
1024 | + dolog ("pos:%06d free:%d,%d till:%d len:%d\n", | |
1025 | + dma_pos, free, AUD_get_free (s->voice), till, dma_len); | |
853 | 1026 | #endif |
854 | 1027 | |
855 | 1028 | if (till <= copy) { |
856 | - if (0 == dsp->dma_auto) { | |
1029 | + if (0 == s->dma_auto) { | |
857 | 1030 | copy = till; |
858 | 1031 | } |
859 | 1032 | } |
860 | 1033 | |
861 | - written = write_audio (addr, size, copy); | |
862 | - dsp->left_till_irq -= written; | |
863 | - AUD_adjust_estimate (free - written); | |
1034 | + written = write_audio (s, nchan, dma_pos, dma_len, copy); | |
1035 | + dma_pos = (dma_pos + written) % dma_len; | |
1036 | + s->left_till_irq -= written; | |
864 | 1037 | |
865 | - if (dsp->left_till_irq <= 0) { | |
866 | - dsp->mixer_regs[0x82] |= dsp->mixer_regs[0x80]; | |
867 | - if (0 == noirq) { | |
868 | - ldebug ("request irq pos %d, left %d\n", | |
869 | - dsp->dma_pos, dsp->left_till_irq); | |
870 | - pic_set_irq(sb.irq, 1); | |
871 | - } | |
872 | - | |
873 | - if (0 == dsp->dma_auto) { | |
874 | - control (0); | |
1038 | + if (s->left_till_irq <= 0) { | |
1039 | + s->mixer_regs[0x82] |= (nchan & 4) ? 2 : 1; | |
1040 | + pic_set_irq (s->irq, 1); | |
1041 | + if (0 == s->dma_auto) { | |
1042 | + control (s, 0); | |
1043 | + speaker (s, 0); | |
875 | 1044 | } |
876 | 1045 | } |
877 | 1046 | |
878 | 1047 | #ifdef DEBUG_SB16_MOST |
879 | 1048 | ldebug ("pos %5d free %5d size %5d till % 5d copy %5d dma size %5d\n", |
880 | - dsp->dma_pos, free, size, dsp->left_till_irq, copy, | |
881 | - dsp->dma_buffer_size); | |
1049 | + dma_pos, free, dma_len, s->left_till_irq, copy, s->block_size); | |
882 | 1050 | #endif |
883 | 1051 | |
884 | - if (dsp->left_till_irq <= 0) { | |
885 | - dsp->left_till_irq += dsp->dma_buffer_size; | |
1052 | + while (s->left_till_irq <= 0) { | |
1053 | + s->left_till_irq = s->block_size + s->left_till_irq; | |
886 | 1054 | } |
887 | 1055 | |
888 | - return dsp->dma_pos; | |
889 | -} | |
890 | - | |
891 | -static int magic_of_irq (int irq) | |
892 | -{ | |
893 | - switch (irq) { | |
894 | - case 2: | |
895 | - return 1; | |
896 | - case 5: | |
897 | - return 2; | |
898 | - case 7: | |
899 | - return 4; | |
900 | - case 10: | |
901 | - return 8; | |
902 | - default: | |
903 | - dolog ("bad irq %d\n", irq); | |
904 | - return 2; | |
905 | - } | |
1056 | + AUD_adjust (s->voice, written); | |
1057 | + return dma_pos; | |
906 | 1058 | } |
907 | 1059 | |
908 | -#if 0 | |
909 | -static int irq_of_magic (int magic) | |
1060 | +void SB_timer (void *opaque) | |
910 | 1061 | { |
911 | - switch (magic) { | |
912 | - case 1: | |
913 | - return 2; | |
914 | - case 2: | |
915 | - return 5; | |
916 | - case 4: | |
917 | - return 7; | |
918 | - case 8: | |
919 | - return 10; | |
920 | - default: | |
921 | - dolog ("bad irq magic %d\n", magic); | |
922 | - return 2; | |
923 | - } | |
1062 | + SB16State *s = opaque; | |
1063 | + AUD_run (); | |
1064 | + qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + 1); | |
924 | 1065 | } |
925 | -#endif | |
926 | 1066 | |
927 | -#ifdef SB16_TRAP_ALL | |
928 | -static IO_READ_PROTO (trap_read) | |
1067 | +static void SB_save (QEMUFile *f, void *opaque) | |
929 | 1068 | { |
930 | - switch (nport) { | |
931 | - case 0x220: | |
932 | - return 0; | |
933 | - case 0x226: | |
934 | - case 0x22a: | |
935 | - case 0x22c: | |
936 | - case 0x22d: | |
937 | - case 0x22e: | |
938 | - case 0x22f: | |
939 | - return dsp_read (opaque, nport); | |
940 | - } | |
941 | - linfo ("trap_read: %#x\n", nport); | |
942 | - return 0xff; | |
1069 | + SB16State *s = opaque; | |
1070 | + | |
1071 | + qemu_put_be32s (f, &s->irq); | |
1072 | + qemu_put_be32s (f, &s->dma); | |
1073 | + qemu_put_be32s (f, &s->hdma); | |
1074 | + qemu_put_be32s (f, &s->port); | |
1075 | + qemu_put_be32s (f, &s->ver); | |
1076 | + qemu_put_be32s (f, &s->in_index); | |
1077 | + qemu_put_be32s (f, &s->out_data_len); | |
1078 | + qemu_put_be32s (f, &s->fmt_stereo); | |
1079 | + qemu_put_be32s (f, &s->fmt_signed); | |
1080 | + qemu_put_be32s (f, &s->fmt_bits); | |
1081 | + qemu_put_be32s (f, &s->fmt); | |
1082 | + qemu_put_be32s (f, &s->dma_auto); | |
1083 | + qemu_put_be32s (f, &s->block_size); | |
1084 | + qemu_put_be32s (f, &s->fifo); | |
1085 | + qemu_put_be32s (f, &s->freq); | |
1086 | + qemu_put_be32s (f, &s->time_const); | |
1087 | + qemu_put_be32s (f, &s->speaker); | |
1088 | + qemu_put_be32s (f, &s->needed_bytes); | |
1089 | + qemu_put_be32s (f, &s->cmd); | |
1090 | + qemu_put_be32s (f, &s->use_hdma); | |
1091 | + qemu_put_be32s (f, &s->highspeed); | |
1092 | + qemu_put_be32s (f, &s->can_write); | |
1093 | + qemu_put_be32s (f, &s->v2x6); | |
1094 | + | |
1095 | + qemu_put_8s (f, &s->csp_param); | |
1096 | + qemu_put_8s (f, &s->csp_value); | |
1097 | + qemu_put_8s (f, &s->csp_mode); | |
1098 | + qemu_put_8s (f, &s->csp_param); | |
1099 | + qemu_put_buffer (f, s->csp_regs, 256); | |
1100 | + qemu_put_8s (f, &s->csp_index); | |
1101 | + qemu_put_buffer (f, s->csp_reg83, 4); | |
1102 | + qemu_put_be32s (f, &s->csp_reg83r); | |
1103 | + qemu_put_be32s (f, &s->csp_reg83w); | |
1104 | + | |
1105 | + qemu_put_buffer (f, s->in2_data, sizeof (s->in2_data)); | |
1106 | + qemu_put_buffer (f, s->out_data, sizeof (s->out_data)); | |
1107 | + qemu_put_8s (f, &s->test_reg); | |
1108 | + qemu_put_8s (f, &s->last_read_byte); | |
1109 | + | |
1110 | + qemu_put_be32s (f, &s->nzero); | |
1111 | + qemu_put_be32s (f, &s->left_till_irq); | |
1112 | + qemu_put_be32s (f, &s->dma_running); | |
1113 | + qemu_put_be32s (f, &s->bytes_per_second); | |
1114 | + qemu_put_be32s (f, &s->align); | |
1115 | + | |
1116 | + qemu_put_be32s (f, &s->mixer_nreg); | |
1117 | + qemu_put_buffer (f, s->mixer_regs, 256); | |
943 | 1118 | } |
944 | 1119 | |
945 | -static IO_WRITE_PROTO (trap_write) | |
1120 | +static int SB_load (QEMUFile *f, void *opaque, int version_id) | |
946 | 1121 | { |
947 | - switch (nport) { | |
948 | - case 0x226: | |
949 | - case 0x22c: | |
950 | - dsp_write (opaque, nport, val); | |
951 | - return; | |
1122 | + SB16State *s = opaque; | |
1123 | + | |
1124 | + if (version_id != 1) | |
1125 | + return -EINVAL; | |
1126 | + | |
1127 | + qemu_get_be32s (f, &s->irq); | |
1128 | + qemu_get_be32s (f, &s->dma); | |
1129 | + qemu_get_be32s (f, &s->hdma); | |
1130 | + qemu_get_be32s (f, &s->port); | |
1131 | + qemu_get_be32s (f, &s->ver); | |
1132 | + qemu_get_be32s (f, &s->in_index); | |
1133 | + qemu_get_be32s (f, &s->out_data_len); | |
1134 | + qemu_get_be32s (f, &s->fmt_stereo); | |
1135 | + qemu_get_be32s (f, &s->fmt_signed); | |
1136 | + qemu_get_be32s (f, &s->fmt_bits); | |
1137 | + qemu_get_be32s (f, &s->fmt); | |
1138 | + qemu_get_be32s (f, &s->dma_auto); | |
1139 | + qemu_get_be32s (f, &s->block_size); | |
1140 | + qemu_get_be32s (f, &s->fifo); | |
1141 | + qemu_get_be32s (f, &s->freq); | |
1142 | + qemu_get_be32s (f, &s->time_const); | |
1143 | + qemu_get_be32s (f, &s->speaker); | |
1144 | + qemu_get_be32s (f, &s->needed_bytes); | |
1145 | + qemu_get_be32s (f, &s->cmd); | |
1146 | + qemu_get_be32s (f, &s->use_hdma); | |
1147 | + qemu_get_be32s (f, &s->highspeed); | |
1148 | + qemu_get_be32s (f, &s->can_write); | |
1149 | + qemu_get_be32s (f, &s->v2x6); | |
1150 | + | |
1151 | + qemu_get_8s (f, &s->csp_param); | |
1152 | + qemu_get_8s (f, &s->csp_value); | |
1153 | + qemu_get_8s (f, &s->csp_mode); | |
1154 | + qemu_get_8s (f, &s->csp_param); | |
1155 | + qemu_get_buffer (f, s->csp_regs, 256); | |
1156 | + qemu_get_8s (f, &s->csp_index); | |
1157 | + qemu_get_buffer (f, s->csp_reg83, 4); | |
1158 | + qemu_get_be32s (f, &s->csp_reg83r); | |
1159 | + qemu_get_be32s (f, &s->csp_reg83w); | |
1160 | + | |
1161 | + qemu_get_buffer (f, s->in2_data, sizeof (s->in2_data)); | |
1162 | + qemu_get_buffer (f, s->out_data, sizeof (s->out_data)); | |
1163 | + qemu_get_8s (f, &s->test_reg); | |
1164 | + qemu_get_8s (f, &s->last_read_byte); | |
1165 | + | |
1166 | + qemu_get_be32s (f, &s->nzero); | |
1167 | + qemu_get_be32s (f, &s->left_till_irq); | |
1168 | + qemu_get_be32s (f, &s->dma_running); | |
1169 | + qemu_get_be32s (f, &s->bytes_per_second); | |
1170 | + qemu_get_be32s (f, &s->align); | |
1171 | + | |
1172 | + qemu_get_be32s (f, &s->mixer_nreg); | |
1173 | + qemu_get_buffer (f, s->mixer_regs, 256); | |
1174 | + | |
1175 | + if (s->voice) | |
1176 | + AUD_reset (s->voice); | |
1177 | + | |
1178 | + if (s->dma_running) { | |
1179 | + if (s->freq) | |
1180 | + s->voice = AUD_open (s->voice, "sb16", s->freq, | |
1181 | + 1 << s->fmt_stereo, s->fmt); | |
1182 | + | |
1183 | + control (s, 1); | |
1184 | + speaker (s, s->speaker); | |
952 | 1185 | } |
953 | - linfo ("trap_write: %#x = %#x\n", nport, val); | |
1186 | + return 0; | |
954 | 1187 | } |
955 | -#endif | |
956 | 1188 | |
957 | 1189 | void SB16_init (void) |
958 | 1190 | { |
... | ... | @@ -961,47 +1193,45 @@ void SB16_init (void) |
961 | 1193 | static const uint8_t dsp_write_ports[] = {0x6, 0xc}; |
962 | 1194 | static const uint8_t dsp_read_ports[] = {0x6, 0xa, 0xc, 0xd, 0xe, 0xf}; |
963 | 1195 | |
964 | - memset(s->mixer_regs, 0xff, sizeof(s->mixer_regs)); | |
1196 | + s->ts = qemu_new_timer (vm_clock, SB_timer, s); | |
1197 | + if (!s->ts) | |
1198 | + return; | |
1199 | + | |
1200 | + s->irq = conf.irq; | |
1201 | + s->dma = conf.dma; | |
1202 | + s->hdma = conf.hdma; | |
1203 | + s->port = conf.port; | |
1204 | + s->ver = conf.ver_lo | (conf.ver_hi << 8); | |
965 | 1205 | |
966 | - s->mixer_regs[0x00] = 0; | |
967 | - s->mixer_regs[0x0e] = ~0; | |
968 | - s->mixer_regs[0x80] = magic_of_irq (sb.irq); | |
969 | - s->mixer_regs[0x81] = 0x80 | 0x10 | (sb.dma << 1); | |
970 | - s->mixer_regs[0x82] = 0; | |
971 | - s->mixer_regs[0xfd] = 16; /* bochs */ | |
972 | - s->mixer_regs[0xfe] = 6; /* bochs */ | |
973 | - mixer_write_indexw (s, 0x224, 0); | |
974 | - | |
975 | -#ifdef SB16_TRAP_ALL | |
976 | - for (i = 0; i < 0x100; i++) { | |
977 | - if (i != 4 && i != 5) { | |
978 | - register_ioport_write (sb.port + i, 1, 1, trap_write, s); | |
979 | - register_ioport_read (sb.port + i, 1, 1, trap_read, s); | |
980 | - } | |
981 | - } | |
982 | -#else | |
1206 | + s->mixer_regs[0x80] = magic_of_irq (s->irq); | |
1207 | + s->mixer_regs[0x81] = (1 << s->dma) | (1 << s->hdma); | |
1208 | + s->mixer_regs[0x82] = 2 << 5; | |
1209 | + | |
1210 | + s->csp_regs[5] = 1; | |
1211 | + s->csp_regs[9] = 0xf8; | |
1212 | + | |
1213 | + reset_mixer (s); | |
1214 | + s->aux_ts = qemu_new_timer (vm_clock, aux_timer, s); | |
1215 | + if (!s->aux_ts) | |
1216 | + return; | |
983 | 1217 | |
984 | 1218 | for (i = 0; i < LENOFA (dsp_write_ports); i++) { |
985 | - register_ioport_write (sb.port + dsp_write_ports[i], 1, 1, dsp_write, s); | |
1219 | + register_ioport_write (s->port + dsp_write_ports[i], 1, 1, dsp_write, s); | |
986 | 1220 | } |
987 | 1221 | |
988 | 1222 | for (i = 0; i < LENOFA (dsp_read_ports); i++) { |
989 | - register_ioport_read (sb.port + dsp_read_ports[i], 1, 1, dsp_read, s); | |
1223 | + register_ioport_read (s->port + dsp_read_ports[i], 1, 1, dsp_read, s); | |
990 | 1224 | } |
991 | -#endif | |
992 | 1225 | |
993 | - register_ioport_write (sb.port + 0x4, 1, 1, mixer_write_indexb, s); | |
994 | - register_ioport_write (sb.port + 0x4, 1, 2, mixer_write_indexw, s); | |
995 | - register_ioport_read (sb.port + 0x5, 1, 1, mixer_read, s); | |
996 | - register_ioport_write (sb.port + 0x5, 1, 1, mixer_write_datab, s); | |
1226 | + register_ioport_write (s->port + 0x4, 1, 1, mixer_write_indexb, s); | |
1227 | + register_ioport_write (s->port + 0x4, 1, 2, mixer_write_indexw, s); | |
1228 | + register_ioport_read (s->port + 0x5, 1, 1, mixer_read, s); | |
1229 | + register_ioport_write (s->port + 0x5, 1, 1, mixer_write_datab, s); | |
997 | 1230 | |
998 | - for (i = 0; 4 < 4; i++) { | |
999 | - register_ioport_read (0x330 + i, 1, 1, mpu_read, s); | |
1000 | - register_ioport_write (0x330 + i, 1, 1, mpu_write, s); | |
1001 | - register_ioport_read (0x388 + i, 1, 1, adlib_read, s); | |
1002 | - register_ioport_write (0x388 + i, 1, 1, adlib_write, s); | |
1003 | - } | |
1231 | + DMA_register_channel (s->hdma, SB_read_DMA, s); | |
1232 | + DMA_register_channel (s->dma, SB_read_DMA, s); | |
1233 | + s->can_write = 1; | |
1004 | 1234 | |
1005 | - DMA_register_channel (sb.hdma, SB_read_DMA, s); | |
1006 | - DMA_register_channel (sb.dma, SB_read_DMA, s); | |
1235 | + qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + 1); | |
1236 | + register_savevm ("sb16", 0, 1, SB_save, SB_load, s); | |
1007 | 1237 | } | ... | ... |
oss.c deleted
100644 → 0
1 | -/* | |
2 | - * QEMU OSS Audio output driver | |
3 | - * | |
4 | - * Copyright (c) 2003 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 | -#include <stdio.h> | |
27 | -#include <limits.h> | |
28 | -#include <stdlib.h> | |
29 | -#include <string.h> | |
30 | - | |
31 | -/* TODO: Graceful error handling */ | |
32 | - | |
33 | -#if defined(_WIN32) | |
34 | -#define USE_SDL_AUDIO | |
35 | -#endif | |
36 | - | |
37 | -#define MIN(a, b) ((a)>(b)?(b):(a)) | |
38 | -#define MAX(a, b) ((a)<(b)?(b):(a)) | |
39 | - | |
40 | -#define DEREF(x) (void)x | |
41 | -#define dolog(...) fprintf (stderr, "audio: " __VA_ARGS__) | |
42 | -#define ERRFail(...) do { \ | |
43 | - int _errno = errno; \ | |
44 | - fprintf (stderr, "audio: " __VA_ARGS__); \ | |
45 | - fprintf (stderr, "\nsystem error: %s\n", strerror (_errno)); \ | |
46 | - abort (); \ | |
47 | -} while (0) | |
48 | -#define Fail(...) do { \ | |
49 | - fprintf (stderr, "audio: " __VA_ARGS__); \ | |
50 | - fprintf (stderr, "\n"); \ | |
51 | - abort (); \ | |
52 | -} while (0) | |
53 | - | |
54 | -#ifdef DEBUG_AUDIO | |
55 | -#define lwarn(...) fprintf (stderr, "audio: " __VA_ARGS__) | |
56 | -#define linfo(...) fprintf (stderr, "audio: " __VA_ARGS__) | |
57 | -#define ldebug(...) fprintf (stderr, "audio: " __VA_ARGS__) | |
58 | -#else | |
59 | -#define lwarn(...) | |
60 | -#define linfo(...) | |
61 | -#define ldebug(...) | |
62 | -#endif | |
63 | - | |
64 | -static int get_conf_val (const char *key, int defval) | |
65 | -{ | |
66 | - int val = defval; | |
67 | - char *strval; | |
68 | - | |
69 | - strval = getenv (key); | |
70 | - if (strval) { | |
71 | - val = atoi (strval); | |
72 | - } | |
73 | - | |
74 | - return val; | |
75 | -} | |
76 | - | |
77 | -static void copy_no_conversion (void *dst, void *src, int size) | |
78 | -{ | |
79 | - memcpy (dst, src, size); | |
80 | -} | |
81 | - | |
82 | -static void copy_u16_to_s16 (void *dst, void *src, int size) | |
83 | -{ | |
84 | - int i; | |
85 | - uint16_t *out, *in; | |
86 | - | |
87 | - out = dst; | |
88 | - in = src; | |
89 | - | |
90 | - for (i = 0; i < size / 2; i++) { | |
91 | - out[i] = in[i] + 0x8000; | |
92 | - } | |
93 | -} | |
94 | - | |
95 | -#ifdef USE_SDL_AUDIO | |
96 | -#include <SDL/SDL.h> | |
97 | -#include <SDL/SDL_thread.h> | |
98 | - | |
99 | -static struct { | |
100 | - int samples; | |
101 | -} conf = { | |
102 | - .samples = 4096 | |
103 | -}; | |
104 | - | |
105 | -typedef struct AudioState { | |
106 | - int freq; | |
107 | - int bits16; | |
108 | - int nchannels; | |
109 | - int rpos; | |
110 | - int wpos; | |
111 | - volatile int live; | |
112 | - volatile int exit; | |
113 | - int bytes_per_second; | |
114 | - Uint8 *buf; | |
115 | - int bufsize; | |
116 | - int leftover; | |
117 | - uint64_t old_ticks; | |
118 | - SDL_AudioSpec spec; | |
119 | - SDL_mutex *mutex; | |
120 | - SDL_sem *sem; | |
121 | - void (*copy_fn)(void *, void *, int); | |
122 | -} AudioState; | |
123 | - | |
124 | -static AudioState sdl_audio; | |
125 | - | |
126 | -void AUD_run (void) | |
127 | -{ | |
128 | -} | |
129 | - | |
130 | -static void own (AudioState *s) | |
131 | -{ | |
132 | - /* SDL_LockAudio (); */ | |
133 | - if (SDL_mutexP (s->mutex)) | |
134 | - dolog ("SDL_mutexP: %s\n", SDL_GetError ()); | |
135 | -} | |
136 | - | |
137 | -static void disown (AudioState *s) | |
138 | -{ | |
139 | - /* SDL_UnlockAudio (); */ | |
140 | - if (SDL_mutexV (s->mutex)) | |
141 | - dolog ("SDL_mutexV: %s\n", SDL_GetError ()); | |
142 | -} | |
143 | - | |
144 | -static void sem_wait (AudioState *s) | |
145 | -{ | |
146 | - if (SDL_SemWait (s->sem)) | |
147 | - dolog ("SDL_SemWait: %s\n", SDL_GetError ()); | |
148 | -} | |
149 | - | |
150 | -static void sem_post (AudioState *s) | |
151 | -{ | |
152 | - if (SDL_SemPost (s->sem)) | |
153 | - dolog ("SDL_SemPost: %s\n", SDL_GetError ()); | |
154 | -} | |
155 | - | |
156 | -static void audio_callback (void *data, Uint8 *stream, int len) | |
157 | -{ | |
158 | - int to_mix; | |
159 | - AudioState *s = data; | |
160 | - | |
161 | - if (s->exit) return; | |
162 | - while (len) { | |
163 | - sem_wait (s); | |
164 | - if (s->exit) return; | |
165 | - own (s); | |
166 | - to_mix = MIN (len, s->live); | |
167 | - len -= to_mix; | |
168 | - /* printf ("to_mix=%d len=%d live=%d\n", to_mix, len, s->live); */ | |
169 | - while (to_mix) { | |
170 | - int chunk = MIN (to_mix, s->bufsize - s->rpos); | |
171 | - /* SDL_MixAudio (stream, buf, chunk, SDL_MIX_MAXVOLUME); */ | |
172 | - memcpy (stream, s->buf + s->rpos, chunk); | |
173 | - | |
174 | - s->rpos += chunk; | |
175 | - s->live -= chunk; | |
176 | - | |
177 | - stream += chunk; | |
178 | - to_mix -= chunk; | |
179 | - | |
180 | - if (s->rpos == s->bufsize) s->rpos = 0; | |
181 | - } | |
182 | - disown (s); | |
183 | - } | |
184 | -} | |
185 | - | |
186 | -static void sem_zero (AudioState *s) | |
187 | -{ | |
188 | - int res; | |
189 | - | |
190 | - do { | |
191 | - res = SDL_SemTryWait (s->sem); | |
192 | - if (res < 0) { | |
193 | - dolog ("SDL_SemTryWait: %s\n", SDL_GetError ()); | |
194 | - return; | |
195 | - } | |
196 | - } while (res != SDL_MUTEX_TIMEDOUT); | |
197 | -} | |
198 | - | |
199 | -static void do_open (AudioState *s) | |
200 | -{ | |
201 | - int status; | |
202 | - SDL_AudioSpec obtained; | |
203 | - | |
204 | - SDL_PauseAudio (1); | |
205 | - if (s->buf) { | |
206 | - s->exit = 1; | |
207 | - sem_post (s); | |
208 | - SDL_CloseAudio (); | |
209 | - s->exit = 0; | |
210 | - qemu_free (s->buf); | |
211 | - s->buf = NULL; | |
212 | - sem_zero (s); | |
213 | - } | |
214 | - | |
215 | - s->bytes_per_second = (s->spec.freq << (s->spec.channels >> 1)) << s->bits16; | |
216 | - s->spec.samples = conf.samples; | |
217 | - s->spec.userdata = s; | |
218 | - s->spec.callback = audio_callback; | |
219 | - | |
220 | - status = SDL_OpenAudio (&s->spec, &obtained); | |
221 | - if (status < 0) { | |
222 | - dolog ("SDL_OpenAudio: %s\n", SDL_GetError ()); | |
223 | - goto exit; | |
224 | - } | |
225 | - | |
226 | - if (obtained.freq != s->spec.freq || | |
227 | - obtained.channels != s->spec.channels || | |
228 | - obtained.format != s->spec.format) { | |
229 | - dolog ("Audio spec mismatch requested obtained\n" | |
230 | - "freq %5d %5d\n" | |
231 | - "channels %5d %5d\n" | |
232 | - "fmt %5d %5d\n", | |
233 | - s->spec.freq, obtained.freq, | |
234 | - s->spec.channels, obtained.channels, | |
235 | - s->spec.format, obtained.format | |
236 | - ); | |
237 | - } | |
238 | - | |
239 | - s->bufsize = obtained.size; | |
240 | - s->buf = qemu_mallocz (s->bufsize); | |
241 | - if (!s->buf) { | |
242 | - dolog ("qemu_mallocz(%d)\n", s->bufsize); | |
243 | - goto exit; | |
244 | - } | |
245 | - SDL_PauseAudio (0); | |
246 | - | |
247 | -exit: | |
248 | - s->rpos = 0; | |
249 | - s->wpos = 0; | |
250 | - s->live = 0; | |
251 | -} | |
252 | - | |
253 | -int AUD_write (void *in_buf, int size) | |
254 | -{ | |
255 | - AudioState *s = &sdl_audio; | |
256 | - int to_copy, temp; | |
257 | - uint8_t *in, *out; | |
258 | - | |
259 | - own (s); | |
260 | - to_copy = MIN (s->bufsize - s->live, size); | |
261 | - | |
262 | - temp = to_copy; | |
263 | - | |
264 | - in = in_buf; | |
265 | - out = s->buf; | |
266 | - | |
267 | - while (temp) { | |
268 | - int copy; | |
269 | - | |
270 | - copy = MIN (temp, s->bufsize - s->wpos); | |
271 | - s->copy_fn (out + s->wpos, in, copy); | |
272 | - | |
273 | - s->wpos += copy; | |
274 | - if (s->wpos == s->bufsize) { | |
275 | - s->wpos = 0; | |
276 | - } | |
277 | - | |
278 | - temp -= copy; | |
279 | - in += copy; | |
280 | - s->live += copy; | |
281 | - } | |
282 | - | |
283 | - disown (s); | |
284 | - sem_post (s); | |
285 | - return to_copy; | |
286 | -} | |
287 | - | |
288 | -static void maybe_open (AudioState *s, int req_freq, int req_nchannels, | |
289 | - audfmt_e req_fmt, int force_open) | |
290 | -{ | |
291 | - int sdl_fmt, bits16; | |
292 | - | |
293 | - switch (req_fmt) { | |
294 | - case AUD_FMT_U8: | |
295 | - bits16 = 0; | |
296 | - sdl_fmt = AUDIO_U8; | |
297 | - s->copy_fn = copy_no_conversion; | |
298 | - break; | |
299 | - | |
300 | - case AUD_FMT_S8: | |
301 | - fprintf (stderr, "audio: can not play 8bit signed\n"); | |
302 | - return; | |
303 | - | |
304 | - case AUD_FMT_S16: | |
305 | - bits16 = 1; | |
306 | - sdl_fmt = AUDIO_S16; | |
307 | - s->copy_fn = copy_no_conversion; | |
308 | - break; | |
309 | - | |
310 | - case AUD_FMT_U16: | |
311 | - bits16 = 1; | |
312 | - sdl_fmt = AUDIO_S16; | |
313 | - s->copy_fn = copy_u16_to_s16; | |
314 | - break; | |
315 | - | |
316 | - default: | |
317 | - abort (); | |
318 | - } | |
319 | - | |
320 | - if (force_open | |
321 | - || (NULL == s->buf) | |
322 | - || (sdl_fmt != s->spec.format) | |
323 | - || (req_nchannels != s->spec.channels) | |
324 | - || (req_freq != s->spec.freq) | |
325 | - || (bits16 != s->bits16)) { | |
326 | - | |
327 | - s->spec.format = sdl_fmt; | |
328 | - s->spec.channels = req_nchannels; | |
329 | - s->spec.freq = req_freq; | |
330 | - s->bits16 = bits16; | |
331 | - do_open (s); | |
332 | - } | |
333 | -} | |
334 | - | |
335 | -void AUD_reset (int req_freq, int req_nchannels, audfmt_e req_fmt) | |
336 | -{ | |
337 | - AudioState *s = &sdl_audio; | |
338 | - own (s); | |
339 | - maybe_open (s, req_freq, req_nchannels, req_fmt, 0); | |
340 | - disown (s); | |
341 | -} | |
342 | - | |
343 | -void AUD_open (int req_freq, int req_nchannels, audfmt_e req_fmt) | |
344 | -{ | |
345 | - AudioState *s = &sdl_audio; | |
346 | - own (s); | |
347 | - maybe_open (s, req_freq, req_nchannels, req_fmt, 1); | |
348 | - disown (s); | |
349 | -} | |
350 | - | |
351 | -void AUD_adjust_estimate (int leftover) | |
352 | -{ | |
353 | - AudioState *s = &sdl_audio; | |
354 | - own (s); | |
355 | - s->leftover = leftover; | |
356 | - disown (s); | |
357 | -} | |
358 | - | |
359 | -int AUD_get_free (void) | |
360 | -{ | |
361 | - int free, elapsed; | |
362 | - uint64_t ticks, delta; | |
363 | - uint64_t ua_elapsed; | |
364 | - uint64_t al_elapsed; | |
365 | - AudioState *s = &sdl_audio; | |
366 | - | |
367 | - own (s); | |
368 | - free = s->bufsize - s->live; | |
369 | - | |
370 | - if (0 == free) { | |
371 | - disown (s); | |
372 | - return 0; | |
373 | - } | |
374 | - | |
375 | - elapsed = free; | |
376 | - ticks = qemu_get_clock(rt_clock); | |
377 | - delta = ticks - s->old_ticks; | |
378 | - s->old_ticks = ticks; | |
379 | - | |
380 | - ua_elapsed = (delta * s->bytes_per_second) / 1000; | |
381 | - al_elapsed = ua_elapsed & ~3ULL; | |
382 | - | |
383 | - ldebug ("tid elapsed %llu bytes\n", ua_elapsed); | |
384 | - | |
385 | - if (al_elapsed > (uint64_t) INT_MAX) | |
386 | - elapsed = INT_MAX; | |
387 | - else | |
388 | - elapsed = al_elapsed; | |
389 | - | |
390 | - elapsed += s->leftover; | |
391 | - disown (s); | |
392 | - | |
393 | - if (elapsed > free) { | |
394 | - lwarn ("audio can not keep up elapsed %d free %d\n", elapsed, free); | |
395 | - return free; | |
396 | - } | |
397 | - else { | |
398 | - return elapsed; | |
399 | - } | |
400 | -} | |
401 | - | |
402 | -int AUD_get_live (void) | |
403 | -{ | |
404 | - int live; | |
405 | - AudioState *s = &sdl_audio; | |
406 | - | |
407 | - own (s); | |
408 | - live = s->live; | |
409 | - disown (s); | |
410 | - return live; | |
411 | -} | |
412 | - | |
413 | -int AUD_get_buffer_size (void) | |
414 | -{ | |
415 | - int bufsize; | |
416 | - AudioState *s = &sdl_audio; | |
417 | - | |
418 | - own (s); | |
419 | - bufsize = s->bufsize; | |
420 | - disown (s); | |
421 | - return bufsize; | |
422 | -} | |
423 | - | |
424 | -#define QC_SDL_NSAMPLES "QEMU_SDL_NSAMPLES" | |
425 | - | |
426 | -static void cleanup (void) | |
427 | -{ | |
428 | - AudioState *s = &sdl_audio; | |
429 | - own (s); | |
430 | - s->exit = 1; | |
431 | - sem_post (s); | |
432 | - disown (s); | |
433 | -} | |
434 | - | |
435 | -void AUD_init (void) | |
436 | -{ | |
437 | - AudioState *s = &sdl_audio; | |
438 | - | |
439 | - atexit (cleanup); | |
440 | - SDL_InitSubSystem (SDL_INIT_AUDIO); | |
441 | - s->mutex = SDL_CreateMutex (); | |
442 | - if (!s->mutex) { | |
443 | - dolog ("SDL_CreateMutex: %s\n", SDL_GetError ()); | |
444 | - return; | |
445 | - } | |
446 | - | |
447 | - s->sem = SDL_CreateSemaphore (0); | |
448 | - if (!s->sem) { | |
449 | - dolog ("SDL_CreateSemaphore: %s\n", SDL_GetError ()); | |
450 | - return; | |
451 | - } | |
452 | - | |
453 | - conf.samples = get_conf_val (QC_SDL_NSAMPLES, conf.samples); | |
454 | -} | |
455 | - | |
456 | -#elif !defined(_WIN32) && !defined(__APPLE__) | |
457 | - | |
458 | -#include <fcntl.h> | |
459 | -#include <errno.h> | |
460 | -#include <unistd.h> | |
461 | -#include <sys/mman.h> | |
462 | -#include <sys/types.h> | |
463 | -#include <sys/ioctl.h> | |
464 | -#include <sys/soundcard.h> | |
465 | - | |
466 | -/* http://www.df.lth.se/~john_e/gems/gem002d.html */ | |
467 | -/* http://www.multi-platforms.com/Tips/PopCount.htm */ | |
468 | -static inline uint32_t popcount (uint32_t u) | |
469 | -{ | |
470 | - u = ((u&0x55555555) + ((u>>1)&0x55555555)); | |
471 | - u = ((u&0x33333333) + ((u>>2)&0x33333333)); | |
472 | - u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f)); | |
473 | - u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff)); | |
474 | - u = ( u&0x0000ffff) + (u>>16); | |
475 | - return u; | |
476 | -} | |
477 | - | |
478 | -static inline uint32_t lsbindex (uint32_t u) | |
479 | -{ | |
480 | - return popcount ((u&-u)-1); | |
481 | -} | |
482 | - | |
483 | -#define IOCTL(args) do { \ | |
484 | - int ret = ioctl args; \ | |
485 | - if (-1 == ret) { \ | |
486 | - ERRFail (#args); \ | |
487 | - } \ | |
488 | - ldebug ("ioctl " #args " = %d\n", ret); \ | |
489 | -} while (0) | |
490 | - | |
491 | -typedef struct AudioState { | |
492 | - int fd; | |
493 | - int freq; | |
494 | - int bits16; | |
495 | - int nchannels; | |
496 | - int rpos; | |
497 | - int wpos; | |
498 | - int live; | |
499 | - int oss_fmt; | |
500 | - int bytes_per_second; | |
501 | - int is_mapped; | |
502 | - void *buf; | |
503 | - int bufsize; | |
504 | - int nfrags; | |
505 | - int fragsize; | |
506 | - int old_optr; | |
507 | - int leftover; | |
508 | - uint64_t old_ticks; | |
509 | - void (*copy_fn)(void *, void *, int); | |
510 | -} AudioState; | |
511 | - | |
512 | -static AudioState oss_audio = { .fd = -1 }; | |
513 | - | |
514 | -static struct { | |
515 | - int try_mmap; | |
516 | - int nfrags; | |
517 | - int fragsize; | |
518 | -} conf = { | |
519 | - .try_mmap = 0, | |
520 | - .nfrags = 4, | |
521 | - .fragsize = 4096 | |
522 | -}; | |
523 | - | |
524 | -static enum {DONT, DSP, TID} est = DONT; | |
525 | - | |
526 | -static void pab (AudioState *s, struct audio_buf_info *abinfo) | |
527 | -{ | |
528 | - DEREF (abinfo); | |
529 | - | |
530 | - ldebug ("fragments %d, fragstotal %d, fragsize %d, bytes %d\n" | |
531 | - "rpos %d, wpos %d, live %d\n", | |
532 | - abinfo->fragments, | |
533 | - abinfo->fragstotal, | |
534 | - abinfo->fragsize, | |
535 | - abinfo->bytes, | |
536 | - s->rpos, s->wpos, s->live); | |
537 | -} | |
538 | - | |
539 | -static void do_open (AudioState *s) | |
540 | -{ | |
541 | - int mmmmssss; | |
542 | - audio_buf_info abinfo; | |
543 | - int fmt, freq, nchannels; | |
544 | - | |
545 | - if (s->buf) { | |
546 | - if (s->is_mapped) { | |
547 | - if (-1 == munmap (s->buf, s->bufsize)) { | |
548 | - ERRFail ("failed to unmap audio buffer %p %d", | |
549 | - s->buf, s->bufsize); | |
550 | - } | |
551 | - } | |
552 | - else { | |
553 | - qemu_free (s->buf); | |
554 | - } | |
555 | - s->buf = NULL; | |
556 | - } | |
557 | - | |
558 | - if (-1 != s->fd) | |
559 | - close (s->fd); | |
560 | - | |
561 | - s->fd = open ("/dev/dsp", O_RDWR | O_NONBLOCK); | |
562 | - if (-1 == s->fd) { | |
563 | - ERRFail ("can not open /dev/dsp"); | |
564 | - } | |
565 | - | |
566 | - fmt = s->oss_fmt; | |
567 | - freq = s->freq; | |
568 | - nchannels = s->nchannels; | |
569 | - | |
570 | - IOCTL ((s->fd, SNDCTL_DSP_RESET, 1)); | |
571 | - IOCTL ((s->fd, SNDCTL_DSP_SAMPLESIZE, &fmt)); | |
572 | - IOCTL ((s->fd, SNDCTL_DSP_CHANNELS, &nchannels)); | |
573 | - IOCTL ((s->fd, SNDCTL_DSP_SPEED, &freq)); | |
574 | - IOCTL ((s->fd, SNDCTL_DSP_NONBLOCK)); | |
575 | - | |
576 | - mmmmssss = (conf.nfrags << 16) | conf.fragsize; | |
577 | - IOCTL ((s->fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss)); | |
578 | - | |
579 | - if ((s->oss_fmt != fmt) | |
580 | - || (s->nchannels != nchannels) | |
581 | - || (s->freq != freq)) { | |
582 | - Fail ("failed to set audio parameters\n" | |
583 | - "parameter | requested value | obtained value\n" | |
584 | - "format | %10d | %10d\n" | |
585 | - "channels | %10d | %10d\n" | |
586 | - "frequency | %10d | %10d\n", | |
587 | - s->oss_fmt, fmt, | |
588 | - s->nchannels, nchannels, | |
589 | - s->freq, freq); | |
590 | - } | |
591 | - | |
592 | - IOCTL ((s->fd, SNDCTL_DSP_GETOSPACE, &abinfo)); | |
593 | - | |
594 | - s->nfrags = abinfo.fragstotal; | |
595 | - s->fragsize = abinfo.fragsize; | |
596 | - s->bufsize = s->nfrags * s->fragsize; | |
597 | - s->old_optr = 0; | |
598 | - | |
599 | - s->bytes_per_second = (freq << (nchannels >> 1)) << s->bits16; | |
600 | - | |
601 | - linfo ("bytes per second %d\n", s->bytes_per_second); | |
602 | - | |
603 | - linfo ("fragments %d, fragstotal %d, fragsize %d, bytes %d, bufsize %d\n", | |
604 | - abinfo.fragments, | |
605 | - abinfo.fragstotal, | |
606 | - abinfo.fragsize, | |
607 | - abinfo.bytes, | |
608 | - s->bufsize); | |
609 | - | |
610 | - s->buf = MAP_FAILED; | |
611 | - s->is_mapped = 0; | |
612 | - | |
613 | - if (conf.try_mmap) { | |
614 | - s->buf = mmap (NULL, s->bufsize, PROT_WRITE, MAP_SHARED, s->fd, 0); | |
615 | - if (MAP_FAILED == s->buf) { | |
616 | - int err; | |
617 | - | |
618 | - err = errno; | |
619 | - dolog ("failed to mmap audio, size %d, fd %d\n" | |
620 | - "syserr: %s\n", | |
621 | - s->bufsize, s->fd, strerror (err)); | |
622 | - } | |
623 | - else { | |
624 | - est = TID; | |
625 | - s->is_mapped = 1; | |
626 | - } | |
627 | - } | |
628 | - | |
629 | - if (MAP_FAILED == s->buf) { | |
630 | - est = TID; | |
631 | - s->buf = qemu_mallocz (s->bufsize); | |
632 | - if (!s->buf) { | |
633 | - ERRFail ("audio buf malloc failed, size %d", s->bufsize); | |
634 | - } | |
635 | - } | |
636 | - | |
637 | - s->rpos = 0; | |
638 | - s->wpos = 0; | |
639 | - s->live = 0; | |
640 | - | |
641 | - if (s->is_mapped) { | |
642 | - int trig; | |
643 | - | |
644 | - trig = 0; | |
645 | - IOCTL ((s->fd, SNDCTL_DSP_SETTRIGGER, &trig)); | |
646 | - trig = PCM_ENABLE_OUTPUT; | |
647 | - IOCTL ((s->fd, SNDCTL_DSP_SETTRIGGER, &trig)); | |
648 | - } | |
649 | -} | |
650 | - | |
651 | -static void maybe_open (AudioState *s, int req_freq, int req_nchannels, | |
652 | - audfmt_e req_fmt, int force_open) | |
653 | -{ | |
654 | - int oss_fmt, bits16; | |
655 | - | |
656 | - switch (req_fmt) { | |
657 | - case AUD_FMT_U8: | |
658 | - bits16 = 0; | |
659 | - oss_fmt = AFMT_U8; | |
660 | - s->copy_fn = copy_no_conversion; | |
661 | - break; | |
662 | - | |
663 | - case AUD_FMT_S8: | |
664 | - Fail ("can not play 8bit signed"); | |
665 | - | |
666 | - case AUD_FMT_S16: | |
667 | - bits16 = 1; | |
668 | - oss_fmt = AFMT_S16_LE; | |
669 | - s->copy_fn = copy_no_conversion; | |
670 | - break; | |
671 | - | |
672 | - case AUD_FMT_U16: | |
673 | - bits16 = 1; | |
674 | - oss_fmt = AFMT_S16_LE; | |
675 | - s->copy_fn = copy_u16_to_s16; | |
676 | - break; | |
677 | - | |
678 | - default: | |
679 | - abort (); | |
680 | - } | |
681 | - | |
682 | - if (force_open | |
683 | - || (-1 == s->fd) | |
684 | - || (oss_fmt != s->oss_fmt) | |
685 | - || (req_nchannels != s->nchannels) | |
686 | - || (req_freq != s->freq) | |
687 | - || (bits16 != s->bits16)) { | |
688 | - s->oss_fmt = oss_fmt; | |
689 | - s->nchannels = req_nchannels; | |
690 | - s->freq = req_freq; | |
691 | - s->bits16 = bits16; | |
692 | - do_open (s); | |
693 | - } | |
694 | -} | |
695 | - | |
696 | -void AUD_reset (int req_freq, int req_nchannels, audfmt_e req_fmt) | |
697 | -{ | |
698 | - AudioState *s = &oss_audio; | |
699 | - maybe_open (s, req_freq, req_nchannels, req_fmt, 0); | |
700 | -} | |
701 | - | |
702 | -void AUD_open (int req_freq, int req_nchannels, audfmt_e req_fmt) | |
703 | -{ | |
704 | - AudioState *s = &oss_audio; | |
705 | - maybe_open (s, req_freq, req_nchannels, req_fmt, 1); | |
706 | -} | |
707 | - | |
708 | -int AUD_write (void *in_buf, int size) | |
709 | -{ | |
710 | - AudioState *s = &oss_audio; | |
711 | - int to_copy, temp; | |
712 | - uint8_t *in, *out; | |
713 | - | |
714 | - to_copy = MIN (s->bufsize - s->live, size); | |
715 | - | |
716 | - temp = to_copy; | |
717 | - | |
718 | - in = in_buf; | |
719 | - out = s->buf; | |
720 | - | |
721 | - while (temp) { | |
722 | - int copy; | |
723 | - | |
724 | - copy = MIN (temp, s->bufsize - s->wpos); | |
725 | - s->copy_fn (out + s->wpos, in, copy); | |
726 | - | |
727 | - s->wpos += copy; | |
728 | - if (s->wpos == s->bufsize) { | |
729 | - s->wpos = 0; | |
730 | - } | |
731 | - | |
732 | - temp -= copy; | |
733 | - in += copy; | |
734 | - s->live += copy; | |
735 | - } | |
736 | - | |
737 | - return to_copy; | |
738 | -} | |
739 | - | |
740 | -void AUD_run (void) | |
741 | -{ | |
742 | - int res; | |
743 | - int bytes; | |
744 | - struct audio_buf_info abinfo; | |
745 | - AudioState *s = &oss_audio; | |
746 | - | |
747 | - if (0 == s->live) | |
748 | - return; | |
749 | - | |
750 | - if (s->is_mapped) { | |
751 | - count_info info; | |
752 | - | |
753 | - res = ioctl (s->fd, SNDCTL_DSP_GETOPTR, &info); | |
754 | - if (res < 0) { | |
755 | - int err; | |
756 | - | |
757 | - err = errno; | |
758 | - lwarn ("SNDCTL_DSP_GETOPTR failed with %s\n", strerror (err)); | |
759 | - return; | |
760 | - } | |
761 | - | |
762 | - if (info.ptr > s->old_optr) { | |
763 | - bytes = info.ptr - s->old_optr; | |
764 | - } | |
765 | - else { | |
766 | - bytes = s->bufsize + info.ptr - s->old_optr; | |
767 | - } | |
768 | - | |
769 | - s->old_optr = info.ptr; | |
770 | - s->live -= bytes; | |
771 | - return; | |
772 | - } | |
773 | - | |
774 | - res = ioctl (s->fd, SNDCTL_DSP_GETOSPACE, &abinfo); | |
775 | - | |
776 | - if (res < 0) { | |
777 | - int err; | |
778 | - | |
779 | - err = errno; | |
780 | - lwarn ("SNDCTL_DSP_GETOSPACE failed with %s\n", strerror (err)); | |
781 | - return; | |
782 | - } | |
783 | - | |
784 | - bytes = abinfo.bytes; | |
785 | - bytes = MIN (s->live, bytes); | |
786 | -#if 0 | |
787 | - bytes = (bytes / fragsize) * fragsize; | |
788 | -#endif | |
789 | - | |
790 | - while (bytes) { | |
791 | - int left, play, written; | |
792 | - | |
793 | - left = s->bufsize - s->rpos; | |
794 | - play = MIN (left, bytes); | |
795 | - written = write (s->fd, (uint8_t *)s->buf + s->rpos, play); | |
796 | - | |
797 | - if (-1 == written) { | |
798 | - if (EAGAIN == errno || EINTR == errno) { | |
799 | - return; | |
800 | - } | |
801 | - else { | |
802 | - ERRFail ("write audio"); | |
803 | - } | |
804 | - } | |
805 | - | |
806 | - play = written; | |
807 | - s->live -= play; | |
808 | - s->rpos += play; | |
809 | - bytes -= play; | |
810 | - | |
811 | - if (s->rpos == s->bufsize) { | |
812 | - s->rpos = 0; | |
813 | - } | |
814 | - } | |
815 | -} | |
816 | - | |
817 | -static int get_dsp_bytes (void) | |
818 | -{ | |
819 | - int res; | |
820 | - struct count_info info; | |
821 | - AudioState *s = &oss_audio; | |
822 | - | |
823 | - res = ioctl (s->fd, SNDCTL_DSP_GETOPTR, &info); | |
824 | - if (-1 == res) { | |
825 | - int err; | |
826 | - | |
827 | - err = errno; | |
828 | - lwarn ("SNDCTL_DSP_GETOPTR failed with %s\n", strerror (err)); | |
829 | - return -1; | |
830 | - } | |
831 | - else { | |
832 | - ldebug ("bytes %d\n", info.bytes); | |
833 | - return info.bytes; | |
834 | - } | |
835 | -} | |
836 | - | |
837 | -void AUD_adjust_estimate (int leftover) | |
838 | -{ | |
839 | - AudioState *s = &oss_audio; | |
840 | - s->leftover = leftover; | |
841 | -} | |
842 | - | |
843 | -int AUD_get_free (void) | |
844 | -{ | |
845 | - int free, elapsed; | |
846 | - AudioState *s = &oss_audio; | |
847 | - | |
848 | - free = s->bufsize - s->live; | |
849 | - | |
850 | - if (free <= 0) | |
851 | - return 0; | |
852 | - | |
853 | - elapsed = free; | |
854 | - switch (est) { | |
855 | - case DONT: | |
856 | - break; | |
857 | - | |
858 | - case DSP: | |
859 | - { | |
860 | - static int old_bytes; | |
861 | - int bytes; | |
862 | - | |
863 | - bytes = get_dsp_bytes (); | |
864 | - if (bytes <= 0) | |
865 | - return free; | |
866 | - | |
867 | - elapsed = bytes - old_bytes; | |
868 | - old_bytes = bytes; | |
869 | - ldebug ("dsp elapsed %d bytes\n", elapsed); | |
870 | - break; | |
871 | - } | |
872 | - | |
873 | - case TID: | |
874 | - { | |
875 | - uint64_t ticks, delta; | |
876 | - uint64_t ua_elapsed; | |
877 | - uint64_t al_elapsed; | |
878 | - | |
879 | - ticks = qemu_get_clock(rt_clock); | |
880 | - delta = ticks - s->old_ticks; | |
881 | - s->old_ticks = ticks; | |
882 | - | |
883 | - ua_elapsed = (delta * s->bytes_per_second) / 1000; | |
884 | - al_elapsed = ua_elapsed & ~3ULL; | |
885 | - | |
886 | - ldebug ("tid elapsed %llu bytes\n", ua_elapsed); | |
887 | - | |
888 | - if (al_elapsed > (uint64_t) INT_MAX) | |
889 | - elapsed = INT_MAX; | |
890 | - else | |
891 | - elapsed = al_elapsed; | |
892 | - | |
893 | - elapsed += s->leftover; | |
894 | - } | |
895 | - } | |
896 | - | |
897 | - if (elapsed > free) { | |
898 | - lwarn ("audio can not keep up elapsed %d free %d\n", elapsed, free); | |
899 | - return free; | |
900 | - } | |
901 | - else { | |
902 | - return elapsed; | |
903 | - } | |
904 | -} | |
905 | - | |
906 | -int AUD_get_live (void) | |
907 | -{ | |
908 | - AudioState *s = &oss_audio; | |
909 | - return s->live; | |
910 | -} | |
911 | - | |
912 | -int AUD_get_buffer_size (void) | |
913 | -{ | |
914 | - AudioState *s = &oss_audio; | |
915 | - return s->bufsize; | |
916 | -} | |
917 | - | |
918 | -#define QC_OSS_FRAGSIZE "QEMU_OSS_FRAGSIZE" | |
919 | -#define QC_OSS_NFRAGS "QEMU_OSS_NFRAGS" | |
920 | -#define QC_OSS_MMAP "QEMU_OSS_MMAP" | |
921 | - | |
922 | -void AUD_init (void) | |
923 | -{ | |
924 | - int fsp; | |
925 | - | |
926 | - DEREF (pab); | |
927 | - | |
928 | - conf.fragsize = get_conf_val (QC_OSS_FRAGSIZE, conf.fragsize); | |
929 | - conf.nfrags = get_conf_val (QC_OSS_NFRAGS, conf.nfrags); | |
930 | - conf.try_mmap = get_conf_val (QC_OSS_MMAP, conf.try_mmap); | |
931 | - | |
932 | - fsp = conf.fragsize; | |
933 | - if (0 != (fsp & (fsp - 1))) { | |
934 | - Fail ("fragment size %d is not power of 2", fsp); | |
935 | - } | |
936 | - | |
937 | - conf.fragsize = lsbindex (fsp); | |
938 | -} | |
939 | - | |
940 | -#else | |
941 | - | |
942 | -void AUD_run (void) | |
943 | -{ | |
944 | -} | |
945 | - | |
946 | -int AUD_write (void *in_buf, int size) | |
947 | -{ | |
948 | - return 0; | |
949 | -} | |
950 | - | |
951 | -void AUD_reset (int rfreq, int rnchannels, audfmt_e rfmt) | |
952 | -{ | |
953 | -} | |
954 | - | |
955 | -void AUD_adjust_estimate (int _leftover) | |
956 | -{ | |
957 | -} | |
958 | - | |
959 | -int AUD_get_free (void) | |
960 | -{ | |
961 | - return 0; | |
962 | -} | |
963 | - | |
964 | -int AUD_get_live (void) | |
965 | -{ | |
966 | - return 0; | |
967 | -} | |
968 | - | |
969 | -int AUD_get_buffer_size (void) | |
970 | -{ | |
971 | - return 0; | |
972 | -} | |
973 | - | |
974 | -void AUD_init (void) | |
975 | -{ | |
976 | -} | |
977 | - | |
978 | -#endif |
vl.c
... | ... | @@ -2438,12 +2438,6 @@ void main_loop_wait(int timeout) |
2438 | 2438 | if (vm_running) { |
2439 | 2439 | qemu_run_timers(&active_timers[QEMU_TIMER_VIRTUAL], |
2440 | 2440 | qemu_get_clock(vm_clock)); |
2441 | - | |
2442 | - if (audio_enabled) { | |
2443 | - /* XXX: add explicit timer */ | |
2444 | - SB16_run(); | |
2445 | - } | |
2446 | - | |
2447 | 2441 | /* run dma transfers, if any */ |
2448 | 2442 | DMA_run(); |
2449 | 2443 | } | ... | ... |
vl.h
... | ... | @@ -30,6 +30,7 @@ |
30 | 30 | #include <stdarg.h> |
31 | 31 | #include <string.h> |
32 | 32 | #include <inttypes.h> |
33 | +#include <limits.h> | |
33 | 34 | #include <time.h> |
34 | 35 | #include <ctype.h> |
35 | 36 | #include <errno.h> |
... | ... | @@ -553,39 +554,22 @@ void pci_piix3_ide_init(PCIBus *bus, BlockDriverState **hd_table); |
553 | 554 | int pmac_ide_init (BlockDriverState **hd_table, |
554 | 555 | openpic_t *openpic, int irq); |
555 | 556 | |
556 | -/* oss.c */ | |
557 | -typedef enum { | |
558 | - AUD_FMT_U8, | |
559 | - AUD_FMT_S8, | |
560 | - AUD_FMT_U16, | |
561 | - AUD_FMT_S16 | |
562 | -} audfmt_e; | |
563 | - | |
564 | -void AUD_open (int rfreq, int rnchannels, audfmt_e rfmt); | |
565 | -void AUD_reset (int rfreq, int rnchannels, audfmt_e rfmt); | |
566 | -int AUD_write (void *in_buf, int size); | |
567 | -void AUD_run (void); | |
568 | -void AUD_adjust_estimate (int _leftover); | |
569 | -int AUD_get_free (void); | |
570 | -int AUD_get_live (void); | |
571 | -int AUD_get_buffer_size (void); | |
557 | +/* audio.c */ | |
572 | 558 | void AUD_init (void); |
573 | 559 | |
574 | 560 | /* dma.c */ |
575 | -typedef int (*DMA_transfer_handler) (void *opaque, target_ulong addr, int size); | |
561 | +typedef int (*DMA_transfer_handler) (void *opaque, int nchan, int pos, int size); | |
576 | 562 | int DMA_get_channel_mode (int nchan); |
563 | +int DMA_read_memory (int nchan, void *buf, int pos, int size); | |
564 | +int DMA_write_memory (int nchan, void *buf, int pos, int size); | |
577 | 565 | void DMA_hold_DREQ (int nchan); |
578 | 566 | void DMA_release_DREQ (int nchan); |
579 | 567 | void DMA_schedule(int nchan); |
580 | 568 | void DMA_run (void); |
581 | 569 | void DMA_init (int high_page_enable); |
582 | 570 | void DMA_register_channel (int nchan, |
583 | - DMA_transfer_handler transfer_handler, void *opaque); | |
584 | - | |
585 | -/* sb16.c */ | |
586 | -void SB16_run (void); | |
587 | -void SB16_init (void); | |
588 | - | |
571 | + DMA_transfer_handler transfer_handler, | |
572 | + void *opaque); | |
589 | 573 | /* fdc.c */ |
590 | 574 | #define MAX_FD 2 |
591 | 575 | extern BlockDriverState *fd_table[MAX_FD]; | ... | ... |