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