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]; | ... | ... |