Commit 1d14ffa97eacd3cb722271eaf6f093038396eac4
1 parent
3b0d4f61
merged 15a_aqemu.patch audio patch (malc)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1584 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
9 changed files
with
3767 additions
and
677 deletions
Too many changes to show.
To preserve performance only 9 of 28 files are displayed.
Changelog
Makefile.target
| ... | ... | @@ -262,7 +262,7 @@ endif |
| 262 | 262 | VL_OBJS=vl.o osdep.o block.o readline.o monitor.o pci.o console.o |
| 263 | 263 | VL_OBJS+=block-cow.o block-qcow.o aes.o block-vmdk.o block-cloop.o block-dmg.o block-bochs.o block-vpc.o block-vvfat.o |
| 264 | 264 | |
| 265 | -SOUND_HW = sb16.o | |
| 265 | +SOUND_HW = sb16.o es1370.o | |
| 266 | 266 | AUDIODRV = audio.o noaudio.o wavaudio.o |
| 267 | 267 | ifdef CONFIG_SDL |
| 268 | 268 | AUDIODRV += sdlaudio.o |
| ... | ... | @@ -270,29 +270,38 @@ endif |
| 270 | 270 | ifdef CONFIG_OSS |
| 271 | 271 | AUDIODRV += ossaudio.o |
| 272 | 272 | endif |
| 273 | - | |
| 274 | -pc.o: DEFINES := -DUSE_SB16 $(DEFINES) | |
| 275 | - | |
| 276 | -ifdef CONFIG_ADLIB | |
| 277 | -SOUND_HW += fmopl.o adlib.o | |
| 273 | +ifdef CONFIG_COREAUDIO | |
| 274 | +AUDIODRV += coreaudio.o | |
| 275 | +endif | |
| 276 | +ifdef CONFIG_ALSA | |
| 277 | +AUDIODRV += alsaaudio.o | |
| 278 | +LIBS += -lasound | |
| 279 | +endif | |
| 280 | +ifdef CONFIG_DSOUND | |
| 281 | +AUDIODRV += dsoundaudio.o | |
| 282 | +LIBS += -lole32 -ldxguid | |
| 278 | 283 | endif |
| 279 | - | |
| 280 | 284 | ifdef CONFIG_FMOD |
| 281 | 285 | AUDIODRV += fmodaudio.o |
| 282 | 286 | audio.o fmodaudio.o: DEFINES := -I$(CONFIG_FMOD_INC) $(DEFINES) |
| 283 | 287 | LIBS += $(CONFIG_FMOD_LIB) |
| 284 | 288 | endif |
| 289 | +ifdef CONFIG_ADLIB | |
| 290 | +SOUND_HW += fmopl.o adlib.o | |
| 291 | +endif | |
| 285 | 292 | |
| 286 | 293 | ifeq ($(TARGET_BASE_ARCH), i386) |
| 287 | 294 | # Hardware support |
| 288 | 295 | VL_OBJS+= ide.o ne2000.o pckbd.o vga.o $(SOUND_HW) dma.o $(AUDIODRV) |
| 289 | 296 | VL_OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pc.o |
| 290 | 297 | VL_OBJS+= cirrus_vga.o mixeng.o apic.o parallel.o |
| 298 | +DEFINES += -DHAS_AUDIO | |
| 291 | 299 | endif |
| 292 | 300 | ifeq ($(TARGET_BASE_ARCH), ppc) |
| 293 | 301 | VL_OBJS+= ppc.o ide.o ne2000.o pckbd.o vga.o $(SOUND_HW) dma.o $(AUDIODRV) |
| 294 | 302 | VL_OBJS+= mc146818rtc.o serial.o i8259.o i8254.o fdc.o m48t59.o |
| 295 | 303 | VL_OBJS+= ppc_prep.o ppc_chrp.o cuda.o adb.o openpic.o heathrow_pic.o mixeng.o |
| 304 | +DEFINES += -DHAS_AUDIO | |
| 296 | 305 | endif |
| 297 | 306 | ifeq ($(TARGET_ARCH), mips) |
| 298 | 307 | VL_OBJS+= mips_r4k.o dma.o vga.o serial.o ne2000.o i8254.o i8259.o |
| ... | ... | @@ -317,7 +326,10 @@ VL_OBJS+=sdl.o |
| 317 | 326 | endif |
| 318 | 327 | ifdef CONFIG_COCOA |
| 319 | 328 | VL_OBJS+=cocoa.o |
| 320 | -COCOA_LIBS=-F/System/Library/Frameworks -framework Cocoa | |
| 329 | +COCOA_LIBS=-F/System/Library/Frameworks -framework Cocoa -framework IOKit | |
| 330 | +ifdef CONFIG_COREAUDIO | |
| 331 | +COCOA_LIBS+=-framework CoreAudio | |
| 332 | +endif | |
| 321 | 333 | endif |
| 322 | 334 | ifdef CONFIG_SLIRP |
| 323 | 335 | DEFINES+=-I$(SRC_PATH)/slirp |
| ... | ... | @@ -349,6 +361,10 @@ ifeq ($(ARCH),ia64) |
| 349 | 361 | VL_LDFLAGS+=-Wl,-G0 -Wl,-T,$(SRC_PATH)/ia64.ld |
| 350 | 362 | endif |
| 351 | 363 | |
| 364 | +ifdef CONFIG_WIN32 | |
| 365 | +SDL_LIBS := $(filter-out -mwindows, $(SDL_LIBS)) -mconsole | |
| 366 | +endif | |
| 367 | + | |
| 352 | 368 | $(QEMU_SYSTEM): $(VL_OBJS) libqemu.a |
| 353 | 369 | $(CC) $(VL_LDFLAGS) -o $@ $^ $(LIBS) $(SDL_LIBS) $(COCOA_LIBS) $(VL_LIBS) |
| 354 | 370 | |
| ... | ... | @@ -364,6 +380,9 @@ sdlaudio.o: sdlaudio.c |
| 364 | 380 | depend: $(SRCS) |
| 365 | 381 | $(CC) -MM $(CFLAGS) $(DEFINES) $^ 1>.depend |
| 366 | 382 | |
| 383 | +vldepend: $(VL_OBJS:.o=.c) | |
| 384 | + $(CC) -MM $(CFLAGS) $(DEFINES) $^ 1>.depend | |
| 385 | + | |
| 367 | 386 | # libqemu |
| 368 | 387 | |
| 369 | 388 | libqemu.a: $(LIBOBJS) |
| ... | ... | @@ -415,8 +434,6 @@ op.o: op.c op_template.c op_mem.c |
| 415 | 434 | op_helper.o: op_helper_mem.c |
| 416 | 435 | endif |
| 417 | 436 | |
| 418 | -mixeng.o: mixeng.c mixeng.h mixeng_template.h | |
| 419 | - | |
| 420 | 437 | %.o: %.c |
| 421 | 438 | $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< |
| 422 | 439 | |
| ... | ... | @@ -434,3 +451,9 @@ endif |
| 434 | 451 | ifneq ($(wildcard .depend),) |
| 435 | 452 | include .depend |
| 436 | 453 | endif |
| 454 | + | |
| 455 | +ifeq (0, 1) | |
| 456 | +audio.o sdlaudio.o dsoundaudio.o ossaudio.o wavaudio.o noaudio.o \ | |
| 457 | +fmodaudio.o alsaaudio.o mixeng.o: \ | |
| 458 | +CFLAGS := $(CFLAGS) -Wall -Werror -W -Wsign-compare | |
| 459 | +endif | ... | ... |
audio/alsaaudio.c
0 โ 100644
| 1 | +/* | |
| 2 | + * QEMU ALSA audio driver | |
| 3 | + * | |
| 4 | + * Copyright (c) 2005 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 <alsa/asoundlib.h> | |
| 25 | +#include "vl.h" | |
| 26 | + | |
| 27 | +#define AUDIO_CAP "alsa" | |
| 28 | +#include "audio_int.h" | |
| 29 | + | |
| 30 | +typedef struct ALSAVoiceOut { | |
| 31 | + HWVoiceOut hw; | |
| 32 | + void *pcm_buf; | |
| 33 | + snd_pcm_t *handle; | |
| 34 | + int can_pause; | |
| 35 | + int was_enabled; | |
| 36 | +} ALSAVoiceOut; | |
| 37 | + | |
| 38 | +typedef struct ALSAVoiceIn { | |
| 39 | + HWVoiceIn hw; | |
| 40 | + snd_pcm_t *handle; | |
| 41 | + void *pcm_buf; | |
| 42 | + int can_pause; | |
| 43 | +} ALSAVoiceIn; | |
| 44 | + | |
| 45 | +static struct { | |
| 46 | + int size_in_usec_in; | |
| 47 | + int size_in_usec_out; | |
| 48 | + const char *pcm_name_in; | |
| 49 | + const char *pcm_name_out; | |
| 50 | + unsigned int buffer_size_in; | |
| 51 | + unsigned int period_size_in; | |
| 52 | + unsigned int buffer_size_out; | |
| 53 | + unsigned int period_size_out; | |
| 54 | + unsigned int threshold; | |
| 55 | + | |
| 56 | + int buffer_size_in_overriden; | |
| 57 | + int period_size_in_overriden; | |
| 58 | + | |
| 59 | + int buffer_size_out_overriden; | |
| 60 | + int period_size_out_overriden; | |
| 61 | +} conf = { | |
| 62 | +#ifdef HIGH_LATENCY | |
| 63 | + .size_in_usec_in = 1, | |
| 64 | + .size_in_usec_out = 1, | |
| 65 | +#endif | |
| 66 | + .pcm_name_out = "hw:0,0", | |
| 67 | + .pcm_name_in = "hw:0,0", | |
| 68 | +#ifdef HIGH_LATENCY | |
| 69 | + .buffer_size_in = 400000, | |
| 70 | + .period_size_in = 400000 / 4, | |
| 71 | + .buffer_size_out = 400000, | |
| 72 | + .period_size_out = 400000 / 4, | |
| 73 | +#else | |
| 74 | +#define DEFAULT_BUFFER_SIZE 1024 | |
| 75 | +#define DEFAULT_PERIOD_SIZE 256 | |
| 76 | + .buffer_size_in = DEFAULT_BUFFER_SIZE, | |
| 77 | + .period_size_in = DEFAULT_PERIOD_SIZE, | |
| 78 | + .buffer_size_out = DEFAULT_BUFFER_SIZE, | |
| 79 | + .period_size_out = DEFAULT_PERIOD_SIZE, | |
| 80 | + .buffer_size_in_overriden = 0, | |
| 81 | + .buffer_size_out_overriden = 0, | |
| 82 | + .period_size_in_overriden = 0, | |
| 83 | + .period_size_out_overriden = 0, | |
| 84 | +#endif | |
| 85 | + .threshold = 0 | |
| 86 | +}; | |
| 87 | + | |
| 88 | +struct alsa_params_req { | |
| 89 | + int freq; | |
| 90 | + audfmt_e fmt; | |
| 91 | + int nchannels; | |
| 92 | + unsigned int buffer_size; | |
| 93 | + unsigned int period_size; | |
| 94 | +}; | |
| 95 | + | |
| 96 | +struct alsa_params_obt { | |
| 97 | + int freq; | |
| 98 | + audfmt_e fmt; | |
| 99 | + int nchannels; | |
| 100 | + int can_pause; | |
| 101 | + snd_pcm_uframes_t buffer_size; | |
| 102 | +}; | |
| 103 | + | |
| 104 | +static void GCC_FMT_ATTR (2, 3) alsa_logerr (int err, const char *fmt, ...) | |
| 105 | +{ | |
| 106 | + va_list ap; | |
| 107 | + | |
| 108 | + va_start (ap, fmt); | |
| 109 | + AUD_vlog (AUDIO_CAP, fmt, ap); | |
| 110 | + va_end (ap); | |
| 111 | + | |
| 112 | + AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err)); | |
| 113 | +} | |
| 114 | + | |
| 115 | +static void GCC_FMT_ATTR (3, 4) alsa_logerr2 ( | |
| 116 | + int err, | |
| 117 | + const char *typ, | |
| 118 | + const char *fmt, | |
| 119 | + ... | |
| 120 | + ) | |
| 121 | +{ | |
| 122 | + va_list ap; | |
| 123 | + | |
| 124 | + AUD_log (AUDIO_CAP, "Can not initialize %s\n", typ); | |
| 125 | + | |
| 126 | + va_start (ap, fmt); | |
| 127 | + AUD_vlog (AUDIO_CAP, fmt, ap); | |
| 128 | + va_end (ap); | |
| 129 | + | |
| 130 | + AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err)); | |
| 131 | +} | |
| 132 | + | |
| 133 | +static void alsa_anal_close (snd_pcm_t **handlep) | |
| 134 | +{ | |
| 135 | + int err = snd_pcm_close (*handlep); | |
| 136 | + if (err) { | |
| 137 | + alsa_logerr (err, "Failed to close PCM handle %p\n", *handlep); | |
| 138 | + } | |
| 139 | + *handlep = NULL; | |
| 140 | +} | |
| 141 | + | |
| 142 | +static int alsa_write (SWVoiceOut *sw, void *buf, int len) | |
| 143 | +{ | |
| 144 | + return audio_pcm_sw_write (sw, buf, len); | |
| 145 | +} | |
| 146 | + | |
| 147 | +static int aud_to_alsafmt (audfmt_e fmt) | |
| 148 | +{ | |
| 149 | + switch (fmt) { | |
| 150 | + case AUD_FMT_S8: | |
| 151 | + return SND_PCM_FORMAT_S8; | |
| 152 | + | |
| 153 | + case AUD_FMT_U8: | |
| 154 | + return SND_PCM_FORMAT_U8; | |
| 155 | + | |
| 156 | + case AUD_FMT_S16: | |
| 157 | + return SND_PCM_FORMAT_S16_LE; | |
| 158 | + | |
| 159 | + case AUD_FMT_U16: | |
| 160 | + return SND_PCM_FORMAT_U16_LE; | |
| 161 | + | |
| 162 | + default: | |
| 163 | + dolog ("Internal logic error: Bad audio format %d\n", fmt); | |
| 164 | +#ifdef DEBUG_AUDIO | |
| 165 | + abort (); | |
| 166 | +#endif | |
| 167 | + return SND_PCM_FORMAT_U8; | |
| 168 | + } | |
| 169 | +} | |
| 170 | + | |
| 171 | +static int alsa_to_audfmt (int alsafmt, audfmt_e *fmt, int *endianness) | |
| 172 | +{ | |
| 173 | + switch (alsafmt) { | |
| 174 | + case SND_PCM_FORMAT_S8: | |
| 175 | + *endianness = 0; | |
| 176 | + *fmt = AUD_FMT_S8; | |
| 177 | + break; | |
| 178 | + | |
| 179 | + case SND_PCM_FORMAT_U8: | |
| 180 | + *endianness = 0; | |
| 181 | + *fmt = AUD_FMT_U8; | |
| 182 | + break; | |
| 183 | + | |
| 184 | + case SND_PCM_FORMAT_S16_LE: | |
| 185 | + *endianness = 0; | |
| 186 | + *fmt = AUD_FMT_S16; | |
| 187 | + break; | |
| 188 | + | |
| 189 | + case SND_PCM_FORMAT_U16_LE: | |
| 190 | + *endianness = 0; | |
| 191 | + *fmt = AUD_FMT_U16; | |
| 192 | + break; | |
| 193 | + | |
| 194 | + case SND_PCM_FORMAT_S16_BE: | |
| 195 | + *endianness = 1; | |
| 196 | + *fmt = AUD_FMT_S16; | |
| 197 | + break; | |
| 198 | + | |
| 199 | + case SND_PCM_FORMAT_U16_BE: | |
| 200 | + *endianness = 1; | |
| 201 | + *fmt = AUD_FMT_U16; | |
| 202 | + break; | |
| 203 | + | |
| 204 | + default: | |
| 205 | + dolog ("Unrecognized audio format %d\n", alsafmt); | |
| 206 | + return -1; | |
| 207 | + } | |
| 208 | + | |
| 209 | + return 0; | |
| 210 | +} | |
| 211 | + | |
| 212 | +#ifdef DEBUG_MISMATCHES | |
| 213 | +static void alsa_dump_info (struct alsa_params_req *req, | |
| 214 | + struct alsa_params_obt *obt) | |
| 215 | +{ | |
| 216 | + dolog ("parameter | requested value | obtained value\n"); | |
| 217 | + dolog ("format | %10d | %10d\n", req->fmt, obt->fmt); | |
| 218 | + dolog ("channels | %10d | %10d\n", | |
| 219 | + req->nchannels, obt->nchannels); | |
| 220 | + dolog ("frequency | %10d | %10d\n", req->freq, obt->freq); | |
| 221 | + dolog ("============================================\n"); | |
| 222 | + dolog ("requested: buffer size %d period size %d\n", | |
| 223 | + req->buffer_size, req->period_size); | |
| 224 | + dolog ("obtained: buffer size %ld\n", obt->buffer_size); | |
| 225 | +} | |
| 226 | +#endif | |
| 227 | + | |
| 228 | +static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold) | |
| 229 | +{ | |
| 230 | + int err; | |
| 231 | + snd_pcm_sw_params_t *sw_params; | |
| 232 | + | |
| 233 | + snd_pcm_sw_params_alloca (&sw_params); | |
| 234 | + | |
| 235 | + err = snd_pcm_sw_params_current (handle, sw_params); | |
| 236 | + if (err < 0) { | |
| 237 | + dolog ("Can not fully initialize DAC\n"); | |
| 238 | + alsa_logerr (err, "Failed to get current software parameters\n"); | |
| 239 | + return; | |
| 240 | + } | |
| 241 | + | |
| 242 | + err = snd_pcm_sw_params_set_start_threshold (handle, sw_params, threshold); | |
| 243 | + if (err < 0) { | |
| 244 | + dolog ("Can not fully initialize DAC\n"); | |
| 245 | + alsa_logerr (err, "Failed to set software threshold to %ld\n", | |
| 246 | + threshold); | |
| 247 | + return; | |
| 248 | + } | |
| 249 | + | |
| 250 | + err = snd_pcm_sw_params (handle, sw_params); | |
| 251 | + if (err < 0) { | |
| 252 | + dolog ("Can not fully initialize DAC\n"); | |
| 253 | + alsa_logerr (err, "Failed to set software parameters\n"); | |
| 254 | + return; | |
| 255 | + } | |
| 256 | +} | |
| 257 | + | |
| 258 | +static int alsa_open (int in, struct alsa_params_req *req, | |
| 259 | + struct alsa_params_obt *obt, snd_pcm_t **handlep) | |
| 260 | +{ | |
| 261 | + snd_pcm_t *handle; | |
| 262 | + snd_pcm_hw_params_t *hw_params; | |
| 263 | + int err, freq, nchannels; | |
| 264 | + const char *pcm_name = in ? conf.pcm_name_in : conf.pcm_name_out; | |
| 265 | + unsigned int period_size, buffer_size; | |
| 266 | + snd_pcm_uframes_t obt_buffer_size; | |
| 267 | + const char *typ = in ? "ADC" : "DAC"; | |
| 268 | + | |
| 269 | + freq = req->freq; | |
| 270 | + period_size = req->period_size; | |
| 271 | + buffer_size = req->buffer_size; | |
| 272 | + nchannels = req->nchannels; | |
| 273 | + | |
| 274 | + snd_pcm_hw_params_alloca (&hw_params); | |
| 275 | + | |
| 276 | + err = snd_pcm_open ( | |
| 277 | + &handle, | |
| 278 | + pcm_name, | |
| 279 | + in ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, | |
| 280 | + SND_PCM_NONBLOCK | |
| 281 | + ); | |
| 282 | + if (err < 0) { | |
| 283 | + alsa_logerr2 (err, typ, "Failed to open `%s':\n", pcm_name); | |
| 284 | + return -1; | |
| 285 | + } | |
| 286 | + | |
| 287 | + err = snd_pcm_hw_params_any (handle, hw_params); | |
| 288 | + if (err < 0) { | |
| 289 | + alsa_logerr2 (err, typ, "Failed to initialize hardware parameters\n"); | |
| 290 | + goto err; | |
| 291 | + } | |
| 292 | + | |
| 293 | + err = snd_pcm_hw_params_set_access ( | |
| 294 | + handle, | |
| 295 | + hw_params, | |
| 296 | + SND_PCM_ACCESS_RW_INTERLEAVED | |
| 297 | + ); | |
| 298 | + if (err < 0) { | |
| 299 | + alsa_logerr2 (err, typ, "Failed to set access type\n"); | |
| 300 | + goto err; | |
| 301 | + } | |
| 302 | + | |
| 303 | + err = snd_pcm_hw_params_set_format (handle, hw_params, req->fmt); | |
| 304 | + if (err < 0) { | |
| 305 | + alsa_logerr2 (err, typ, "Failed to set format %d\n", req->fmt); | |
| 306 | + goto err; | |
| 307 | + } | |
| 308 | + | |
| 309 | + err = snd_pcm_hw_params_set_rate_near (handle, hw_params, &freq, 0); | |
| 310 | + if (err < 0) { | |
| 311 | + alsa_logerr2 (err, typ, "Failed to set frequency %d\n", req->freq); | |
| 312 | + goto err; | |
| 313 | + } | |
| 314 | + | |
| 315 | + err = snd_pcm_hw_params_set_channels_near ( | |
| 316 | + handle, | |
| 317 | + hw_params, | |
| 318 | + &nchannels | |
| 319 | + ); | |
| 320 | + if (err < 0) { | |
| 321 | + alsa_logerr2 (err, typ, "Failed to set number of channels %d\n", | |
| 322 | + req->nchannels); | |
| 323 | + goto err; | |
| 324 | + } | |
| 325 | + | |
| 326 | + if (nchannels != 1 && nchannels != 2) { | |
| 327 | + alsa_logerr2 (err, typ, | |
| 328 | + "Can not handle obtained number of channels %d\n", | |
| 329 | + nchannels); | |
| 330 | + goto err; | |
| 331 | + } | |
| 332 | + | |
| 333 | + if (!((in && conf.size_in_usec_in) || (!in && conf.size_in_usec_out))) { | |
| 334 | + if (!buffer_size) { | |
| 335 | + buffer_size = DEFAULT_BUFFER_SIZE; | |
| 336 | + period_size= DEFAULT_PERIOD_SIZE; | |
| 337 | + } | |
| 338 | + } | |
| 339 | + | |
| 340 | + if (buffer_size) { | |
| 341 | + if ((in && conf.size_in_usec_in) || (!in && conf.size_in_usec_out)) { | |
| 342 | + if (period_size) { | |
| 343 | + err = snd_pcm_hw_params_set_period_time_near ( | |
| 344 | + handle, | |
| 345 | + hw_params, | |
| 346 | + &period_size, | |
| 347 | + 0); | |
| 348 | + if (err < 0) { | |
| 349 | + alsa_logerr2 (err, typ, | |
| 350 | + "Failed to set period time %d\n", | |
| 351 | + req->period_size); | |
| 352 | + goto err; | |
| 353 | + } | |
| 354 | + } | |
| 355 | + | |
| 356 | + err = snd_pcm_hw_params_set_buffer_time_near ( | |
| 357 | + handle, | |
| 358 | + hw_params, | |
| 359 | + &buffer_size, | |
| 360 | + 0); | |
| 361 | + | |
| 362 | + if (err < 0) { | |
| 363 | + alsa_logerr2 (err, typ, | |
| 364 | + "Failed to set buffer time %d\n", | |
| 365 | + req->buffer_size); | |
| 366 | + goto err; | |
| 367 | + } | |
| 368 | + } | |
| 369 | + else { | |
| 370 | + int dir; | |
| 371 | + snd_pcm_uframes_t minval; | |
| 372 | + | |
| 373 | + if (period_size) { | |
| 374 | + minval = period_size; | |
| 375 | + dir = 0; | |
| 376 | + | |
| 377 | + err = snd_pcm_hw_params_get_period_size_min ( | |
| 378 | + hw_params, | |
| 379 | + &minval, | |
| 380 | + &dir | |
| 381 | + ); | |
| 382 | + if (err < 0) { | |
| 383 | + alsa_logerr ( | |
| 384 | + err, | |
| 385 | + "Can not get minmal period size for %s\n", | |
| 386 | + typ | |
| 387 | + ); | |
| 388 | + } | |
| 389 | + else { | |
| 390 | + if (period_size < minval) { | |
| 391 | + if ((in && conf.period_size_in_overriden) | |
| 392 | + || (!in && conf.period_size_out_overriden)) { | |
| 393 | + dolog ("%s period size(%d) is less " | |
| 394 | + "than minmal period size(%ld)\n", | |
| 395 | + typ, | |
| 396 | + period_size, | |
| 397 | + minval); | |
| 398 | + } | |
| 399 | + period_size = minval; | |
| 400 | + } | |
| 401 | + } | |
| 402 | + | |
| 403 | + err = snd_pcm_hw_params_set_period_size ( | |
| 404 | + handle, | |
| 405 | + hw_params, | |
| 406 | + period_size, | |
| 407 | + 0 | |
| 408 | + ); | |
| 409 | + if (err < 0) { | |
| 410 | + alsa_logerr2 (err, typ, "Failed to set period size %d\n", | |
| 411 | + req->period_size); | |
| 412 | + goto err; | |
| 413 | + } | |
| 414 | + } | |
| 415 | + | |
| 416 | + minval = buffer_size; | |
| 417 | + err = snd_pcm_hw_params_get_buffer_size_min ( | |
| 418 | + hw_params, | |
| 419 | + &minval | |
| 420 | + ); | |
| 421 | + if (err < 0) { | |
| 422 | + alsa_logerr (err, "Can not get minmal buffer size for %s\n", | |
| 423 | + typ); | |
| 424 | + } | |
| 425 | + else { | |
| 426 | + if (buffer_size < minval) { | |
| 427 | + if ((in && conf.buffer_size_in_overriden) | |
| 428 | + || (!in && conf.buffer_size_out_overriden)) { | |
| 429 | + dolog ( | |
| 430 | + "%s buffer size(%d) is less " | |
| 431 | + "than minimal buffer size(%ld)\n", | |
| 432 | + typ, | |
| 433 | + buffer_size, | |
| 434 | + minval | |
| 435 | + ); | |
| 436 | + } | |
| 437 | + buffer_size = minval; | |
| 438 | + } | |
| 439 | + } | |
| 440 | + | |
| 441 | + err = snd_pcm_hw_params_set_buffer_size ( | |
| 442 | + handle, | |
| 443 | + hw_params, | |
| 444 | + buffer_size | |
| 445 | + ); | |
| 446 | + if (err < 0) { | |
| 447 | + alsa_logerr2 (err, typ, "Failed to set buffer size %d\n", | |
| 448 | + req->buffer_size); | |
| 449 | + goto err; | |
| 450 | + } | |
| 451 | + } | |
| 452 | + } | |
| 453 | + else { | |
| 454 | + dolog ("warning: buffer size is not set\n"); | |
| 455 | + } | |
| 456 | + | |
| 457 | + err = snd_pcm_hw_params (handle, hw_params); | |
| 458 | + if (err < 0) { | |
| 459 | + alsa_logerr2 (err, typ, "Failed to apply audio parameters\n"); | |
| 460 | + goto err; | |
| 461 | + } | |
| 462 | + | |
| 463 | + err = snd_pcm_hw_params_get_buffer_size (hw_params, &obt_buffer_size); | |
| 464 | + if (err < 0) { | |
| 465 | + alsa_logerr2 (err, typ, "Failed to get buffer size\n"); | |
| 466 | + goto err; | |
| 467 | + } | |
| 468 | + | |
| 469 | + err = snd_pcm_prepare (handle); | |
| 470 | + if (err < 0) { | |
| 471 | + alsa_logerr2 (err, typ, "Can not prepare handle %p\n", handle); | |
| 472 | + goto err; | |
| 473 | + } | |
| 474 | + | |
| 475 | + obt->can_pause = snd_pcm_hw_params_can_pause (hw_params); | |
| 476 | + if (obt->can_pause < 0) { | |
| 477 | + alsa_logerr (err, "Can not get pause capability for %s\n", typ); | |
| 478 | + obt->can_pause = 0; | |
| 479 | + } | |
| 480 | + | |
| 481 | + if (!in && conf.threshold) { | |
| 482 | + snd_pcm_uframes_t threshold; | |
| 483 | + int bytes_per_sec; | |
| 484 | + | |
| 485 | + bytes_per_sec = freq | |
| 486 | + << (nchannels == 2) | |
| 487 | + << (req->fmt == AUD_FMT_S16 || req->fmt == AUD_FMT_U16); | |
| 488 | + | |
| 489 | + threshold = (conf.threshold * bytes_per_sec) / 1000; | |
| 490 | + alsa_set_threshold (handle, threshold); | |
| 491 | + } | |
| 492 | + | |
| 493 | + obt->fmt = req->fmt; | |
| 494 | + obt->nchannels = nchannels; | |
| 495 | + obt->freq = freq; | |
| 496 | + obt->buffer_size = snd_pcm_frames_to_bytes (handle, obt_buffer_size); | |
| 497 | + *handlep = handle; | |
| 498 | + | |
| 499 | + if (obt->fmt != req->fmt || | |
| 500 | + obt->nchannels != req->nchannels || | |
| 501 | + obt->freq != req->freq) { | |
| 502 | +#ifdef DEBUG_MISMATCHES | |
| 503 | + dolog ("Audio paramters mismatch for %s\n", typ); | |
| 504 | + alsa_dump_info (req, obt); | |
| 505 | +#endif | |
| 506 | + } | |
| 507 | + | |
| 508 | +#ifdef DEBUG | |
| 509 | + alsa_dump_info (req, obt); | |
| 510 | +#endif | |
| 511 | + return 0; | |
| 512 | + | |
| 513 | + err: | |
| 514 | + alsa_anal_close (&handle); | |
| 515 | + return -1; | |
| 516 | +} | |
| 517 | + | |
| 518 | +static int alsa_recover (snd_pcm_t *handle) | |
| 519 | +{ | |
| 520 | + int err = snd_pcm_prepare (handle); | |
| 521 | + if (err < 0) { | |
| 522 | + alsa_logerr (err, "Failed to prepare handle %p\n", handle); | |
| 523 | + return -1; | |
| 524 | + } | |
| 525 | + return 0; | |
| 526 | +} | |
| 527 | + | |
| 528 | +static int alsa_run_out (HWVoiceOut *hw) | |
| 529 | +{ | |
| 530 | + ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; | |
| 531 | + int rpos, live, decr; | |
| 532 | + int samples; | |
| 533 | + uint8_t *dst; | |
| 534 | + st_sample_t *src; | |
| 535 | + snd_pcm_sframes_t avail; | |
| 536 | + | |
| 537 | + live = audio_pcm_hw_get_live_out (hw); | |
| 538 | + if (!live) { | |
| 539 | + return 0; | |
| 540 | + } | |
| 541 | + | |
| 542 | + avail = snd_pcm_avail_update (alsa->handle); | |
| 543 | + if (avail < 0) { | |
| 544 | + if (avail == -EPIPE) { | |
| 545 | + if (!alsa_recover (alsa->handle)) { | |
| 546 | + avail = snd_pcm_avail_update (alsa->handle); | |
| 547 | + if (avail >= 0) { | |
| 548 | + goto ok; | |
| 549 | + } | |
| 550 | + } | |
| 551 | + } | |
| 552 | + | |
| 553 | + alsa_logerr (avail, "Can not get amount free space\n"); | |
| 554 | + return 0; | |
| 555 | + } | |
| 556 | + | |
| 557 | + ok: | |
| 558 | + decr = audio_MIN (live, avail); | |
| 559 | + samples = decr; | |
| 560 | + rpos = hw->rpos; | |
| 561 | + while (samples) { | |
| 562 | + int left_till_end_samples = hw->samples - rpos; | |
| 563 | + int convert_samples = audio_MIN (samples, left_till_end_samples); | |
| 564 | + snd_pcm_sframes_t written; | |
| 565 | + | |
| 566 | + src = hw->mix_buf + rpos; | |
| 567 | + dst = advance (alsa->pcm_buf, rpos << hw->info.shift); | |
| 568 | + | |
| 569 | + hw->clip (dst, src, convert_samples); | |
| 570 | + | |
| 571 | + again: | |
| 572 | + written = snd_pcm_writei (alsa->handle, dst, convert_samples); | |
| 573 | + | |
| 574 | + if (written < 0) { | |
| 575 | + switch (written) { | |
| 576 | + case -EPIPE: | |
| 577 | + if (!alsa_recover (alsa->handle)) { | |
| 578 | + goto again; | |
| 579 | + } | |
| 580 | + dolog ( | |
| 581 | + "Failed to write %d frames to %p, handle %p not prepared\n", | |
| 582 | + convert_samples, | |
| 583 | + dst, | |
| 584 | + alsa->handle | |
| 585 | + ); | |
| 586 | + goto exit; | |
| 587 | + | |
| 588 | + case -EAGAIN: | |
| 589 | + goto again; | |
| 590 | + | |
| 591 | + default: | |
| 592 | + alsa_logerr (written, "Failed to write %d frames to %p\n", | |
| 593 | + convert_samples, dst); | |
| 594 | + goto exit; | |
| 595 | + } | |
| 596 | + } | |
| 597 | + | |
| 598 | + mixeng_clear (src, written); | |
| 599 | + rpos = (rpos + written) % hw->samples; | |
| 600 | + samples -= written; | |
| 601 | + } | |
| 602 | + | |
| 603 | + exit: | |
| 604 | + hw->rpos = rpos; | |
| 605 | + return decr; | |
| 606 | +} | |
| 607 | + | |
| 608 | +static void alsa_fini_out (HWVoiceOut *hw) | |
| 609 | +{ | |
| 610 | + ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; | |
| 611 | + | |
| 612 | + ldebug ("alsa_fini\n"); | |
| 613 | + alsa_anal_close (&alsa->handle); | |
| 614 | + | |
| 615 | + if (alsa->pcm_buf) { | |
| 616 | + qemu_free (alsa->pcm_buf); | |
| 617 | + alsa->pcm_buf = NULL; | |
| 618 | + } | |
| 619 | +} | |
| 620 | + | |
| 621 | +static int alsa_init_out (HWVoiceOut *hw, int freq, int nchannels, audfmt_e fmt) | |
| 622 | +{ | |
| 623 | + ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; | |
| 624 | + struct alsa_params_req req; | |
| 625 | + struct alsa_params_obt obt; | |
| 626 | + audfmt_e effective_fmt; | |
| 627 | + int endianness; | |
| 628 | + int err; | |
| 629 | + snd_pcm_t *handle; | |
| 630 | + | |
| 631 | + req.fmt = aud_to_alsafmt (fmt); | |
| 632 | + req.freq = freq; | |
| 633 | + req.nchannels = nchannels; | |
| 634 | + req.period_size = conf.period_size_out; | |
| 635 | + req.buffer_size = conf.buffer_size_out; | |
| 636 | + | |
| 637 | + if (alsa_open (0, &req, &obt, &handle)) { | |
| 638 | + return -1; | |
| 639 | + } | |
| 640 | + | |
| 641 | + err = alsa_to_audfmt (obt.fmt, &effective_fmt, &endianness); | |
| 642 | + if (err) { | |
| 643 | + alsa_anal_close (&handle); | |
| 644 | + return -1; | |
| 645 | + } | |
| 646 | + | |
| 647 | + audio_pcm_init_info ( | |
| 648 | + &hw->info, | |
| 649 | + obt.freq, | |
| 650 | + obt.nchannels, | |
| 651 | + effective_fmt, | |
| 652 | + audio_need_to_swap_endian (endianness) | |
| 653 | + ); | |
| 654 | + alsa->can_pause = obt.can_pause; | |
| 655 | + hw->bufsize = obt.buffer_size; | |
| 656 | + | |
| 657 | + alsa->pcm_buf = qemu_mallocz (hw->bufsize); | |
| 658 | + if (!alsa->pcm_buf) { | |
| 659 | + alsa_anal_close (&handle); | |
| 660 | + return -1; | |
| 661 | + } | |
| 662 | + | |
| 663 | + alsa->handle = handle; | |
| 664 | + alsa->was_enabled = 0; | |
| 665 | + return 0; | |
| 666 | +} | |
| 667 | + | |
| 668 | +static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...) | |
| 669 | +{ | |
| 670 | + int err; | |
| 671 | + ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; | |
| 672 | + | |
| 673 | + switch (cmd) { | |
| 674 | + case VOICE_ENABLE: | |
| 675 | + ldebug ("enabling voice\n"); | |
| 676 | + audio_pcm_info_clear_buf (&hw->info, alsa->pcm_buf, hw->samples); | |
| 677 | + if (alsa->can_pause) { | |
| 678 | + /* Why this was_enabled madness is needed at all?? */ | |
| 679 | + if (alsa->was_enabled) { | |
| 680 | + err = snd_pcm_pause (alsa->handle, 0); | |
| 681 | + if (err < 0) { | |
| 682 | + alsa_logerr (err, "Failed to resume playing\n"); | |
| 683 | + /* not fatal really */ | |
| 684 | + } | |
| 685 | + } | |
| 686 | + else { | |
| 687 | + alsa->was_enabled = 1; | |
| 688 | + } | |
| 689 | + } | |
| 690 | + break; | |
| 691 | + | |
| 692 | + case VOICE_DISABLE: | |
| 693 | + ldebug ("disabling voice\n"); | |
| 694 | + if (alsa->can_pause) { | |
| 695 | + err = snd_pcm_pause (alsa->handle, 1); | |
| 696 | + if (err < 0) { | |
| 697 | + alsa_logerr (err, "Failed to stop playing\n"); | |
| 698 | + /* not fatal really */ | |
| 699 | + } | |
| 700 | + } | |
| 701 | + break; | |
| 702 | + } | |
| 703 | + return 0; | |
| 704 | +} | |
| 705 | + | |
| 706 | +static int alsa_init_in (HWVoiceIn *hw, | |
| 707 | + int freq, int nchannels, audfmt_e fmt) | |
| 708 | +{ | |
| 709 | + ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; | |
| 710 | + struct alsa_params_req req; | |
| 711 | + struct alsa_params_obt obt; | |
| 712 | + int endianness; | |
| 713 | + int err; | |
| 714 | + audfmt_e effective_fmt; | |
| 715 | + snd_pcm_t *handle; | |
| 716 | + | |
| 717 | + req.fmt = aud_to_alsafmt (fmt); | |
| 718 | + req.freq = freq; | |
| 719 | + req.nchannels = nchannels; | |
| 720 | + req.period_size = conf.period_size_in; | |
| 721 | + req.buffer_size = conf.buffer_size_in; | |
| 722 | + | |
| 723 | + if (alsa_open (1, &req, &obt, &handle)) { | |
| 724 | + return -1; | |
| 725 | + } | |
| 726 | + | |
| 727 | + err = alsa_to_audfmt (obt.fmt, &effective_fmt, &endianness); | |
| 728 | + if (err) { | |
| 729 | + alsa_anal_close (&handle); | |
| 730 | + return -1; | |
| 731 | + } | |
| 732 | + | |
| 733 | + audio_pcm_init_info ( | |
| 734 | + &hw->info, | |
| 735 | + obt.freq, | |
| 736 | + obt.nchannels, | |
| 737 | + effective_fmt, | |
| 738 | + audio_need_to_swap_endian (endianness) | |
| 739 | + ); | |
| 740 | + alsa->can_pause = obt.can_pause; | |
| 741 | + hw->bufsize = obt.buffer_size; | |
| 742 | + alsa->pcm_buf = qemu_mallocz (hw->bufsize); | |
| 743 | + if (!alsa->pcm_buf) { | |
| 744 | + alsa_anal_close (&handle); | |
| 745 | + return -1; | |
| 746 | + } | |
| 747 | + | |
| 748 | + alsa->handle = handle; | |
| 749 | + return 0; | |
| 750 | +} | |
| 751 | + | |
| 752 | +static void alsa_fini_in (HWVoiceIn *hw) | |
| 753 | +{ | |
| 754 | + ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; | |
| 755 | + | |
| 756 | + alsa_anal_close (&alsa->handle); | |
| 757 | + | |
| 758 | + if (alsa->pcm_buf) { | |
| 759 | + qemu_free (alsa->pcm_buf); | |
| 760 | + alsa->pcm_buf = NULL; | |
| 761 | + } | |
| 762 | +} | |
| 763 | + | |
| 764 | +static int alsa_run_in (HWVoiceIn *hw) | |
| 765 | +{ | |
| 766 | + ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; | |
| 767 | + int hwshift = hw->info.shift; | |
| 768 | + int i; | |
| 769 | + int live = audio_pcm_hw_get_live_in (hw); | |
| 770 | + int dead = hw->samples - live; | |
| 771 | + struct { | |
| 772 | + int add; | |
| 773 | + int len; | |
| 774 | + } bufs[2] = { | |
| 775 | + { hw->wpos, 0 }, | |
| 776 | + { 0, 0 } | |
| 777 | + }; | |
| 778 | + | |
| 779 | + snd_pcm_uframes_t read_samples = 0; | |
| 780 | + | |
| 781 | + if (!dead) { | |
| 782 | + return 0; | |
| 783 | + } | |
| 784 | + | |
| 785 | + if (hw->wpos + dead > hw->samples) { | |
| 786 | + bufs[0].len = (hw->samples - hw->wpos); | |
| 787 | + bufs[1].len = (dead - (hw->samples - hw->wpos)); | |
| 788 | + } | |
| 789 | + else { | |
| 790 | + bufs[0].len = dead; | |
| 791 | + } | |
| 792 | + | |
| 793 | + | |
| 794 | + for (i = 0; i < 2; ++i) { | |
| 795 | + void *src; | |
| 796 | + st_sample_t *dst; | |
| 797 | + snd_pcm_sframes_t nread; | |
| 798 | + snd_pcm_uframes_t len; | |
| 799 | + | |
| 800 | + len = bufs[i].len; | |
| 801 | + | |
| 802 | + src = advance (alsa->pcm_buf, bufs[i].add << hwshift); | |
| 803 | + dst = hw->conv_buf + bufs[i].add; | |
| 804 | + | |
| 805 | + while (len) { | |
| 806 | + nread = snd_pcm_readi (alsa->handle, src, len); | |
| 807 | + | |
| 808 | + if (nread < 0) { | |
| 809 | + switch (nread) { | |
| 810 | + case -EPIPE: | |
| 811 | + if (!alsa_recover (alsa->handle)) { | |
| 812 | + continue; | |
| 813 | + } | |
| 814 | + dolog ( | |
| 815 | + "Failed to read %ld frames from %p, " | |
| 816 | + "handle %p not prepared\n", | |
| 817 | + len, | |
| 818 | + src, | |
| 819 | + alsa->handle | |
| 820 | + ); | |
| 821 | + goto exit; | |
| 822 | + | |
| 823 | + case -EAGAIN: | |
| 824 | + continue; | |
| 825 | + | |
| 826 | + default: | |
| 827 | + alsa_logerr ( | |
| 828 | + nread, | |
| 829 | + "Failed to read %ld frames from %p\n", | |
| 830 | + len, | |
| 831 | + src | |
| 832 | + ); | |
| 833 | + goto exit; | |
| 834 | + } | |
| 835 | + } | |
| 836 | + | |
| 837 | + hw->conv (dst, src, nread, &nominal_volume); | |
| 838 | + | |
| 839 | + src = advance (src, nread << hwshift); | |
| 840 | + dst += nread; | |
| 841 | + | |
| 842 | + read_samples += nread; | |
| 843 | + len -= nread; | |
| 844 | + } | |
| 845 | + } | |
| 846 | + | |
| 847 | + exit: | |
| 848 | + hw->wpos = (hw->wpos + read_samples) % hw->samples; | |
| 849 | + return read_samples; | |
| 850 | +} | |
| 851 | + | |
| 852 | +static int alsa_read (SWVoiceIn *sw, void *buf, int size) | |
| 853 | +{ | |
| 854 | + return audio_pcm_sw_read (sw, buf, size); | |
| 855 | +} | |
| 856 | + | |
| 857 | +static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...) | |
| 858 | +{ | |
| 859 | + (void) hw; | |
| 860 | + (void) cmd; | |
| 861 | + return 0; | |
| 862 | +} | |
| 863 | + | |
| 864 | +static void *alsa_audio_init (void) | |
| 865 | +{ | |
| 866 | + return &conf; | |
| 867 | +} | |
| 868 | + | |
| 869 | +static void alsa_audio_fini (void *opaque) | |
| 870 | +{ | |
| 871 | + (void) opaque; | |
| 872 | +} | |
| 873 | + | |
| 874 | +static struct audio_option alsa_options[] = { | |
| 875 | + {"DAC_SIZE_IN_USEC", AUD_OPT_BOOL, &conf.size_in_usec_out, | |
| 876 | + "DAC period/buffer size in microseconds (otherwise in frames)", NULL, 0}, | |
| 877 | + {"DAC_PERIOD_SIZE", AUD_OPT_INT, &conf.period_size_out, | |
| 878 | + "DAC period size", &conf.period_size_out_overriden, 0}, | |
| 879 | + {"DAC_BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_size_out, | |
| 880 | + "DAC buffer size", &conf.buffer_size_out_overriden, 0}, | |
| 881 | + | |
| 882 | + {"ADC_SIZE_IN_USEC", AUD_OPT_BOOL, &conf.size_in_usec_in, | |
| 883 | + "ADC period/buffer size in microseconds (otherwise in frames)", NULL, 0}, | |
| 884 | + {"ADC_PERIOD_SIZE", AUD_OPT_INT, &conf.period_size_in, | |
| 885 | + "ADC period size", &conf.period_size_in_overriden, 0}, | |
| 886 | + {"ADC_BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_size_in, | |
| 887 | + "ADC buffer size", &conf.buffer_size_in_overriden, 0}, | |
| 888 | + | |
| 889 | + {"THRESHOLD", AUD_OPT_INT, &conf.threshold, | |
| 890 | + "(undocumented)", NULL, 0}, | |
| 891 | + | |
| 892 | + {"DAC_DEV", AUD_OPT_STR, &conf.pcm_name_out, | |
| 893 | + "DAC device name (for instance dmix)", NULL, 0}, | |
| 894 | + | |
| 895 | + {"ADC_DEV", AUD_OPT_STR, &conf.pcm_name_in, | |
| 896 | + "ADC device name", NULL, 0}, | |
| 897 | + {NULL, 0, NULL, NULL, NULL, 0} | |
| 898 | +}; | |
| 899 | + | |
| 900 | +static struct audio_pcm_ops alsa_pcm_ops = { | |
| 901 | + alsa_init_out, | |
| 902 | + alsa_fini_out, | |
| 903 | + alsa_run_out, | |
| 904 | + alsa_write, | |
| 905 | + alsa_ctl_out, | |
| 906 | + | |
| 907 | + alsa_init_in, | |
| 908 | + alsa_fini_in, | |
| 909 | + alsa_run_in, | |
| 910 | + alsa_read, | |
| 911 | + alsa_ctl_in | |
| 912 | +}; | |
| 913 | + | |
| 914 | +struct audio_driver alsa_audio_driver = { | |
| 915 | + INIT_FIELD (name = ) "alsa", | |
| 916 | + INIT_FIELD (descr = ) "ALSA http://www.alsa-project.org", | |
| 917 | + INIT_FIELD (options = ) alsa_options, | |
| 918 | + INIT_FIELD (init = ) alsa_audio_init, | |
| 919 | + INIT_FIELD (fini = ) alsa_audio_fini, | |
| 920 | + INIT_FIELD (pcm_ops = ) &alsa_pcm_ops, | |
| 921 | + INIT_FIELD (can_be_default = ) 1, | |
| 922 | + INIT_FIELD (max_voices_out = ) INT_MAX, | |
| 923 | + INIT_FIELD (max_voices_in = ) INT_MAX, | |
| 924 | + INIT_FIELD (voice_size_out = ) sizeof (ALSAVoiceOut), | |
| 925 | + INIT_FIELD (voice_size_in = ) sizeof (ALSAVoiceIn) | |
| 926 | +}; | ... | ... |
audio/audio.c
| 1 | 1 | /* |
| 2 | 2 | * QEMU Audio subsystem |
| 3 | - * | |
| 4 | - * Copyright (c) 2003-2004 Vassili Karpov (malc) | |
| 5 | - * | |
| 3 | + * | |
| 4 | + * Copyright (c) 2003-2005 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 |
| ... | ... | @@ -21,34 +21,78 @@ |
| 21 | 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| 22 | 22 | * THE SOFTWARE. |
| 23 | 23 | */ |
| 24 | -#include <assert.h> | |
| 25 | 24 | #include "vl.h" |
| 26 | 25 | |
| 27 | -#define USE_WAV_AUDIO | |
| 26 | +#define AUDIO_CAP "audio" | |
| 27 | +#include "audio_int.h" | |
| 28 | 28 | |
| 29 | -#include "audio/audio_int.h" | |
| 29 | +static void audio_pcm_hw_fini_in (HWVoiceIn *hw); | |
| 30 | +static void audio_pcm_hw_fini_out (HWVoiceOut *hw); | |
| 30 | 31 | |
| 31 | -#define dolog(...) AUD_log ("audio", __VA_ARGS__) | |
| 32 | -#ifdef DEBUG | |
| 33 | -#define ldebug(...) dolog (__VA_ARGS__) | |
| 34 | -#else | |
| 35 | -#define ldebug(...) | |
| 36 | -#endif | |
| 32 | +static LIST_HEAD (hw_in_listhead, HWVoiceIn) hw_head_in; | |
| 33 | +static LIST_HEAD (hw_out_listhead, HWVoiceOut) hw_head_out; | |
| 37 | 34 | |
| 38 | -#define QC_AUDIO_DRV "QEMU_AUDIO_DRV" | |
| 39 | -#define QC_VOICES "QEMU_VOICES" | |
| 40 | -#define QC_FIXED_FORMAT "QEMU_FIXED_FORMAT" | |
| 41 | -#define QC_FIXED_FREQ "QEMU_FIXED_FREQ" | |
| 35 | +/* #define DEBUG_PLIVE */ | |
| 36 | +/* #define DEBUG_LIVE */ | |
| 37 | +/* #define DEBUG_OUT */ | |
| 42 | 38 | |
| 43 | -static HWVoice *hw_voices; | |
| 39 | +static struct audio_driver *drvtab[] = { | |
| 40 | +#ifdef CONFIG_OSS | |
| 41 | + &oss_audio_driver, | |
| 42 | +#endif | |
| 43 | +#ifdef CONFIG_ALSA | |
| 44 | + &alsa_audio_driver, | |
| 45 | +#endif | |
| 46 | +#ifdef CONFIG_COREAUDIO | |
| 47 | + &coreaudio_audio_driver, | |
| 48 | +#endif | |
| 49 | +#ifdef CONFIG_DSOUND | |
| 50 | + &dsound_audio_driver, | |
| 51 | +#endif | |
| 52 | +#ifdef CONFIG_FMOD | |
| 53 | + &fmod_audio_driver, | |
| 54 | +#endif | |
| 55 | +#ifdef CONFIG_SDL | |
| 56 | + &sdl_audio_driver, | |
| 57 | +#endif | |
| 58 | + &no_audio_driver, | |
| 59 | + &wav_audio_driver | |
| 60 | +}; | |
| 44 | 61 | |
| 45 | 62 | AudioState audio_state = { |
| 63 | + /* Out */ | |
| 64 | + 1, /* use fixed settings */ | |
| 65 | + 44100, /* fixed frequency */ | |
| 66 | + 2, /* fixed channels */ | |
| 67 | + AUD_FMT_S16, /* fixed format */ | |
| 68 | + 1, /* number of hw voices */ | |
| 69 | + 1, /* greedy */ | |
| 70 | + | |
| 71 | + /* In */ | |
| 46 | 72 | 1, /* use fixed settings */ |
| 47 | 73 | 44100, /* fixed frequency */ |
| 48 | 74 | 2, /* fixed channels */ |
| 49 | 75 | AUD_FMT_S16, /* fixed format */ |
| 50 | 76 | 1, /* number of hw voices */ |
| 51 | - -1 /* voice size */ | |
| 77 | + 1, /* greedy */ | |
| 78 | + | |
| 79 | + NULL, /* driver opaque */ | |
| 80 | + NULL, /* driver */ | |
| 81 | + | |
| 82 | + NULL, /* timer handle */ | |
| 83 | + { 0 }, /* period */ | |
| 84 | + 0 /* plive */ | |
| 85 | +}; | |
| 86 | + | |
| 87 | +volume_t nominal_volume = { | |
| 88 | + 0, | |
| 89 | +#ifdef FLOAT_MIXENG | |
| 90 | + 1.0, | |
| 91 | + 1.0 | |
| 92 | +#else | |
| 93 | + UINT_MAX, | |
| 94 | + UINT_MAX | |
| 95 | +#endif | |
| 52 | 96 | }; |
| 53 | 97 | |
| 54 | 98 | /* http://www.df.lth.se/~john_e/gems/gem002d.html */ |
| ... | ... | @@ -68,275 +112,361 @@ inline uint32_t lsbindex (uint32_t u) |
| 68 | 112 | return popcount ((u&-u)-1); |
| 69 | 113 | } |
| 70 | 114 | |
| 71 | -int audio_get_conf_int (const char *key, int defval) | |
| 115 | +#ifdef AUDIO_IS_FLAWLESS_AND_NO_CHECKS_ARE_REQURIED | |
| 116 | +#error No its not | |
| 117 | +#else | |
| 118 | +int audio_bug (const char *funcname, int cond) | |
| 72 | 119 | { |
| 73 | - int val = defval; | |
| 74 | - char *strval; | |
| 75 | - | |
| 76 | - strval = getenv (key); | |
| 77 | - if (strval) { | |
| 78 | - val = atoi (strval); | |
| 120 | + if (cond) { | |
| 121 | + static int shown; | |
| 122 | + | |
| 123 | + AUD_log (NULL, "Error a bug that was just triggered in %s\n", funcname); | |
| 124 | + if (!shown) { | |
| 125 | + shown = 1; | |
| 126 | + AUD_log (NULL, "Save all your work and restart without audio\n"); | |
| 127 | + AUD_log (NULL, "Please send bug report to malc@pulsesoft.com\n"); | |
| 128 | + AUD_log (NULL, "I am sorry\n"); | |
| 129 | + } | |
| 130 | + AUD_log (NULL, "Context:\n"); | |
| 131 | + | |
| 132 | +#if defined AUDIO_BREAKPOINT_ON_BUG | |
| 133 | +# if defined HOST_I386 | |
| 134 | +# if defined __GNUC__ | |
| 135 | + __asm__ ("int3"); | |
| 136 | +# elif defined _MSC_VER | |
| 137 | + _asm _emit 0xcc; | |
| 138 | +# else | |
| 139 | + abort (); | |
| 140 | +# endif | |
| 141 | +# else | |
| 142 | + abort (); | |
| 143 | +# endif | |
| 144 | +#endif | |
| 79 | 145 | } |
| 80 | 146 | |
| 81 | - return val; | |
| 147 | + return cond; | |
| 82 | 148 | } |
| 149 | +#endif | |
| 83 | 150 | |
| 84 | -const char *audio_get_conf_str (const char *key, const char *defval) | |
| 151 | +static char *audio_alloc_prefix (const char *s) | |
| 85 | 152 | { |
| 86 | - const char *val = getenv (key); | |
| 87 | - if (!val) | |
| 88 | - return defval; | |
| 89 | - else | |
| 90 | - return val; | |
| 91 | -} | |
| 153 | + const char qemu_prefix[] = "QEMU_"; | |
| 154 | + size_t len; | |
| 155 | + char *r; | |
| 92 | 156 | |
| 93 | -void AUD_log (const char *cap, const char *fmt, ...) | |
| 94 | -{ | |
| 95 | - va_list ap; | |
| 96 | - fprintf (stderr, "%s: ", cap); | |
| 97 | - va_start (ap, fmt); | |
| 98 | - vfprintf (stderr, fmt, ap); | |
| 99 | - va_end (ap); | |
| 100 | -} | |
| 157 | + if (!s) { | |
| 158 | + return NULL; | |
| 159 | + } | |
| 101 | 160 | |
| 102 | -/* | |
| 103 | - * Soft Voice | |
| 104 | - */ | |
| 105 | -void pcm_sw_free_resources (SWVoice *sw) | |
| 106 | -{ | |
| 107 | - if (sw->buf) qemu_free (sw->buf); | |
| 108 | - if (sw->rate) st_rate_stop (sw->rate); | |
| 109 | - sw->buf = NULL; | |
| 110 | - sw->rate = NULL; | |
| 111 | -} | |
| 161 | + len = strlen (s); | |
| 162 | + r = qemu_malloc (len + sizeof (qemu_prefix)); | |
| 112 | 163 | |
| 113 | -int pcm_sw_alloc_resources (SWVoice *sw) | |
| 114 | -{ | |
| 115 | - sw->buf = qemu_mallocz (sw->hw->samples * sizeof (st_sample_t)); | |
| 116 | - if (!sw->buf) | |
| 117 | - return -1; | |
| 164 | + if (r) { | |
| 165 | + size_t i; | |
| 166 | + char *u = r + sizeof (qemu_prefix) - 1; | |
| 118 | 167 | |
| 119 | - sw->rate = st_rate_start (sw->freq, sw->hw->freq); | |
| 120 | - if (!sw->rate) { | |
| 121 | - qemu_free (sw->buf); | |
| 122 | - sw->buf = NULL; | |
| 123 | - return -1; | |
| 168 | + strcpy (r, qemu_prefix); | |
| 169 | + strcat (r, s); | |
| 170 | + | |
| 171 | + for (i = 0; i < len; ++i) { | |
| 172 | + u[i] = toupper (u[i]); | |
| 173 | + } | |
| 124 | 174 | } |
| 125 | - return 0; | |
| 175 | + return r; | |
| 126 | 176 | } |
| 127 | 177 | |
| 128 | -void pcm_sw_fini (SWVoice *sw) | |
| 178 | +const char *audio_audfmt_to_string (audfmt_e fmt) | |
| 129 | 179 | { |
| 130 | - pcm_sw_free_resources (sw); | |
| 131 | -} | |
| 180 | + switch (fmt) { | |
| 181 | + case AUD_FMT_U8: | |
| 182 | + return "U8"; | |
| 132 | 183 | |
| 133 | -int pcm_sw_init (SWVoice *sw, HWVoice *hw, int freq, | |
| 134 | - int nchannels, audfmt_e fmt) | |
| 135 | -{ | |
| 136 | - int bits = 8, sign = 0; | |
| 184 | + case AUD_FMT_U16: | |
| 185 | + return "U16"; | |
| 137 | 186 | |
| 138 | - switch (fmt) { | |
| 139 | 187 | case AUD_FMT_S8: |
| 140 | - sign = 1; | |
| 141 | - case AUD_FMT_U8: | |
| 142 | - break; | |
| 188 | + return "S8"; | |
| 143 | 189 | |
| 144 | 190 | case AUD_FMT_S16: |
| 145 | - sign = 1; | |
| 146 | - case AUD_FMT_U16: | |
| 147 | - bits = 16; | |
| 148 | - break; | |
| 191 | + return "S16"; | |
| 149 | 192 | } |
| 150 | 193 | |
| 151 | - sw->hw = hw; | |
| 152 | - sw->freq = freq; | |
| 153 | - sw->fmt = fmt; | |
| 154 | - sw->nchannels = nchannels; | |
| 155 | - sw->shift = (nchannels == 2) + (bits == 16); | |
| 156 | - sw->align = (1 << sw->shift) - 1; | |
| 157 | - sw->left = 0; | |
| 158 | - sw->pos = 0; | |
| 159 | - sw->wpos = 0; | |
| 160 | - sw->live = 0; | |
| 161 | - sw->ratio = (sw->hw->freq * ((int64_t) INT_MAX)) / sw->freq; | |
| 162 | - sw->bytes_per_second = sw->freq << sw->shift; | |
| 163 | - sw->conv = mixeng_conv[nchannels == 2][sign][bits == 16]; | |
| 164 | - | |
| 165 | - pcm_sw_free_resources (sw); | |
| 166 | - return pcm_sw_alloc_resources (sw); | |
| 167 | -} | |
| 168 | - | |
| 169 | -/* Hard voice */ | |
| 170 | -void pcm_hw_free_resources (HWVoice *hw) | |
| 171 | -{ | |
| 172 | - if (hw->mix_buf) | |
| 173 | - qemu_free (hw->mix_buf); | |
| 174 | - hw->mix_buf = NULL; | |
| 194 | + dolog ("Bogus audfmt %d returning S16\n", fmt); | |
| 195 | + return "S16"; | |
| 175 | 196 | } |
| 176 | 197 | |
| 177 | -int pcm_hw_alloc_resources (HWVoice *hw) | |
| 198 | +audfmt_e audio_string_to_audfmt (const char *s, audfmt_e defval, int *defaultp) | |
| 178 | 199 | { |
| 179 | - hw->mix_buf = qemu_mallocz (hw->samples * sizeof (st_sample_t)); | |
| 180 | - if (!hw->mix_buf) | |
| 181 | - return -1; | |
| 182 | - return 0; | |
| 200 | + if (!strcasecmp (s, "u8")) { | |
| 201 | + *defaultp = 0; | |
| 202 | + return AUD_FMT_U8; | |
| 203 | + } | |
| 204 | + else if (!strcasecmp (s, "u16")) { | |
| 205 | + *defaultp = 0; | |
| 206 | + return AUD_FMT_U16; | |
| 207 | + } | |
| 208 | + else if (!strcasecmp (s, "s8")) { | |
| 209 | + *defaultp = 0; | |
| 210 | + return AUD_FMT_S8; | |
| 211 | + } | |
| 212 | + else if (!strcasecmp (s, "s16")) { | |
| 213 | + *defaultp = 0; | |
| 214 | + return AUD_FMT_S16; | |
| 215 | + } | |
| 216 | + else { | |
| 217 | + dolog ("Bogus audio format `%s' using %s\n", | |
| 218 | + s, audio_audfmt_to_string (defval)); | |
| 219 | + *defaultp = 1; | |
| 220 | + return defval; | |
| 221 | + } | |
| 183 | 222 | } |
| 184 | 223 | |
| 185 | -void pcm_hw_fini (HWVoice *hw) | |
| 224 | +static audfmt_e audio_get_conf_fmt (const char *envname, | |
| 225 | + audfmt_e defval, | |
| 226 | + int *defaultp) | |
| 186 | 227 | { |
| 187 | - if (hw->active) { | |
| 188 | - ldebug ("pcm_hw_fini: %d %d %d\n", hw->freq, hw->nchannels, hw->fmt); | |
| 189 | - pcm_hw_free_resources (hw); | |
| 190 | - hw->pcm_ops->fini (hw); | |
| 191 | - memset (hw, 0, audio_state.drv->voice_size); | |
| 228 | + const char *var = getenv (envname); | |
| 229 | + if (!var) { | |
| 230 | + *defaultp = 1; | |
| 231 | + return defval; | |
| 192 | 232 | } |
| 233 | + return audio_string_to_audfmt (var, defval, defaultp); | |
| 193 | 234 | } |
| 194 | 235 | |
| 195 | -void pcm_hw_gc (HWVoice *hw) | |
| 236 | +static int audio_get_conf_int (const char *key, int defval, int *defaultp) | |
| 196 | 237 | { |
| 197 | - if (hw->nb_voices) | |
| 198 | - return; | |
| 238 | + int val; | |
| 239 | + char *strval; | |
| 199 | 240 | |
| 200 | - pcm_hw_fini (hw); | |
| 241 | + strval = getenv (key); | |
| 242 | + if (strval) { | |
| 243 | + *defaultp = 0; | |
| 244 | + val = atoi (strval); | |
| 245 | + return val; | |
| 246 | + } | |
| 247 | + else { | |
| 248 | + *defaultp = 1; | |
| 249 | + return defval; | |
| 250 | + } | |
| 201 | 251 | } |
| 202 | 252 | |
| 203 | -int pcm_hw_get_live (HWVoice *hw) | |
| 253 | +static const char *audio_get_conf_str (const char *key, | |
| 254 | + const char *defval, | |
| 255 | + int *defaultp) | |
| 204 | 256 | { |
| 205 | - int i, alive = 0, live = hw->samples; | |
| 206 | - | |
| 207 | - for (i = 0; i < hw->nb_voices; i++) { | |
| 208 | - if (hw->pvoice[i]->live) { | |
| 209 | - live = audio_MIN (hw->pvoice[i]->live, live); | |
| 210 | - alive += 1; | |
| 211 | - } | |
| 257 | + const char *val = getenv (key); | |
| 258 | + if (!val) { | |
| 259 | + *defaultp = 1; | |
| 260 | + return defval; | |
| 261 | + } | |
| 262 | + else { | |
| 263 | + *defaultp = 0; | |
| 264 | + return val; | |
| 212 | 265 | } |
| 213 | - | |
| 214 | - if (alive) | |
| 215 | - return live; | |
| 216 | - else | |
| 217 | - return -1; | |
| 218 | 266 | } |
| 219 | 267 | |
| 220 | -int pcm_hw_get_live2 (HWVoice *hw, int *nb_active) | |
| 268 | +void AUD_log (const char *cap, const char *fmt, ...) | |
| 221 | 269 | { |
| 222 | - int i, alive = 0, live = hw->samples; | |
| 223 | - | |
| 224 | - *nb_active = 0; | |
| 225 | - for (i = 0; i < hw->nb_voices; i++) { | |
| 226 | - if (hw->pvoice[i]->live) { | |
| 227 | - if (hw->pvoice[i]->live < live) { | |
| 228 | - *nb_active = hw->pvoice[i]->active != 0; | |
| 229 | - live = hw->pvoice[i]->live; | |
| 230 | - } | |
| 231 | - alive += 1; | |
| 232 | - } | |
| 270 | + va_list ap; | |
| 271 | + if (cap) { | |
| 272 | + fprintf (stderr, "%s: ", cap); | |
| 233 | 273 | } |
| 234 | - | |
| 235 | - if (alive) | |
| 236 | - return live; | |
| 237 | - else | |
| 238 | - return -1; | |
| 274 | + va_start (ap, fmt); | |
| 275 | + vfprintf (stderr, fmt, ap); | |
| 276 | + va_end (ap); | |
| 239 | 277 | } |
| 240 | 278 | |
| 241 | -void pcm_hw_dec_live (HWVoice *hw, int decr) | |
| 279 | +void AUD_vlog (const char *cap, const char *fmt, va_list ap) | |
| 242 | 280 | { |
| 243 | - int i; | |
| 244 | - | |
| 245 | - for (i = 0; i < hw->nb_voices; i++) { | |
| 246 | - if (hw->pvoice[i]->live) { | |
| 247 | - hw->pvoice[i]->live -= decr; | |
| 248 | - } | |
| 281 | + if (cap) { | |
| 282 | + fprintf (stderr, "%s: ", cap); | |
| 249 | 283 | } |
| 284 | + vfprintf (stderr, fmt, ap); | |
| 250 | 285 | } |
| 251 | 286 | |
| 252 | -void pcm_hw_clear (HWVoice *hw, void *buf, int len) | |
| 287 | +static void audio_print_options (const char *prefix, | |
| 288 | + struct audio_option *opt) | |
| 253 | 289 | { |
| 254 | - if (!len) | |
| 290 | + char *uprefix; | |
| 291 | + | |
| 292 | + if (!prefix) { | |
| 293 | + dolog ("No prefix specified\n"); | |
| 294 | + return; | |
| 295 | + } | |
| 296 | + | |
| 297 | + if (!opt) { | |
| 298 | + dolog ("No options\n"); | |
| 255 | 299 | return; |
| 300 | + } | |
| 256 | 301 | |
| 257 | - switch (hw->fmt) { | |
| 258 | - case AUD_FMT_S16: | |
| 259 | - case AUD_FMT_S8: | |
| 260 | - memset (buf, len << hw->shift, 0x00); | |
| 261 | - break; | |
| 302 | + uprefix = audio_alloc_prefix (prefix); | |
| 262 | 303 | |
| 263 | - case AUD_FMT_U8: | |
| 264 | - memset (buf, len << hw->shift, 0x80); | |
| 265 | - break; | |
| 304 | + for (; opt->name; opt++) { | |
| 305 | + const char *state = "default"; | |
| 306 | + printf (" %s_%s: ", uprefix, opt->name); | |
| 266 | 307 | |
| 267 | - case AUD_FMT_U16: | |
| 268 | - { | |
| 269 | - unsigned int i; | |
| 270 | - uint16_t *p = buf; | |
| 271 | - int shift = hw->nchannels - 1; | |
| 308 | + if (opt->overridenp && *opt->overridenp) { | |
| 309 | + state = "current"; | |
| 310 | + } | |
| 272 | 311 | |
| 273 | - for (i = 0; i < len << shift; i++) { | |
| 274 | - p[i] = INT16_MAX; | |
| 312 | + switch (opt->tag) { | |
| 313 | + case AUD_OPT_BOOL: | |
| 314 | + { | |
| 315 | + int *intp = opt->valp; | |
| 316 | + printf ("boolean, %s = %d\n", state, *intp ? 1 : 0); | |
| 317 | + } | |
| 318 | + break; | |
| 319 | + | |
| 320 | + case AUD_OPT_INT: | |
| 321 | + { | |
| 322 | + int *intp = opt->valp; | |
| 323 | + printf ("integer, %s = %d\n", state, *intp); | |
| 324 | + } | |
| 325 | + break; | |
| 326 | + | |
| 327 | + case AUD_OPT_FMT: | |
| 328 | + { | |
| 329 | + audfmt_e *fmtp = opt->valp; | |
| 330 | + printf ( | |
| 331 | + "format, %s = %s, (one of: U8 S8 U16 S16)\n", | |
| 332 | + state, | |
| 333 | + audio_audfmt_to_string (*fmtp) | |
| 334 | + ); | |
| 335 | + } | |
| 336 | + break; | |
| 337 | + | |
| 338 | + case AUD_OPT_STR: | |
| 339 | + { | |
| 340 | + const char **strp = opt->valp; | |
| 341 | + printf ("string, %s = %s\n", | |
| 342 | + state, | |
| 343 | + *strp ? *strp : "(not set)"); | |
| 275 | 344 | } |
| 345 | + break; | |
| 346 | + | |
| 347 | + default: | |
| 348 | + printf ("???\n"); | |
| 349 | + dolog ("Bad value tag for option %s_%s %d\n", | |
| 350 | + uprefix, opt->name, opt->tag); | |
| 351 | + break; | |
| 276 | 352 | } |
| 277 | - break; | |
| 353 | + printf (" %s\n", opt->descr); | |
| 278 | 354 | } |
| 355 | + | |
| 356 | + qemu_free (uprefix); | |
| 279 | 357 | } |
| 280 | 358 | |
| 281 | -int pcm_hw_write (SWVoice *sw, void *buf, int size) | |
| 359 | +static void audio_process_options (const char *prefix, | |
| 360 | + struct audio_option *opt) | |
| 282 | 361 | { |
| 283 | - int hwsamples, samples, isamp, osamp, wpos, live, dead, left, swlim, blck; | |
| 284 | - int ret = 0, pos = 0; | |
| 285 | - if (!sw) | |
| 286 | - return size; | |
| 362 | + char *optname; | |
| 363 | + const char qemu_prefix[] = "QEMU_"; | |
| 364 | + size_t preflen; | |
| 287 | 365 | |
| 288 | - hwsamples = sw->hw->samples; | |
| 289 | - samples = size >> sw->shift; | |
| 366 | + if (audio_bug (AUDIO_FUNC, !prefix)) { | |
| 367 | + dolog ("prefix = NULL\n"); | |
| 368 | + return; | |
| 369 | + } | |
| 290 | 370 | |
| 291 | - if (!sw->live) { | |
| 292 | - sw->wpos = sw->hw->rpos; | |
| 371 | + if (audio_bug (AUDIO_FUNC, !opt)) { | |
| 372 | + dolog ("opt = NULL\n"); | |
| 373 | + return; | |
| 293 | 374 | } |
| 294 | - wpos = sw->wpos; | |
| 295 | - live = sw->live; | |
| 296 | - dead = hwsamples - live; | |
| 297 | - swlim = (dead * ((int64_t) INT_MAX)) / sw->ratio; | |
| 298 | - swlim = audio_MIN (swlim, samples); | |
| 299 | 375 | |
| 300 | - ldebug ("size=%d live=%d dead=%d swlim=%d wpos=%d\n", | |
| 301 | - size, live, dead, swlim, wpos); | |
| 302 | - if (swlim) | |
| 303 | - sw->conv (sw->buf, buf, swlim); | |
| 376 | + preflen = strlen (prefix); | |
| 304 | 377 | |
| 305 | - while (swlim) { | |
| 306 | - dead = hwsamples - live; | |
| 307 | - left = hwsamples - wpos; | |
| 308 | - blck = audio_MIN (dead, left); | |
| 309 | - if (!blck) { | |
| 310 | - /* dolog ("swlim=%d\n", swlim); */ | |
| 378 | + for (; opt->name; opt++) { | |
| 379 | + size_t len, i; | |
| 380 | + int def; | |
| 381 | + | |
| 382 | + if (!opt->valp) { | |
| 383 | + dolog ("Option value pointer for `%s' is not set\n", | |
| 384 | + opt->name); | |
| 385 | + continue; | |
| 386 | + } | |
| 387 | + | |
| 388 | + len = strlen (opt->name); | |
| 389 | + optname = qemu_malloc (len + preflen + sizeof (qemu_prefix) + 1); | |
| 390 | + if (!optname) { | |
| 391 | + dolog ("Can not allocate memory for option name `%s'\n", | |
| 392 | + opt->name); | |
| 393 | + continue; | |
| 394 | + } | |
| 395 | + | |
| 396 | + strcpy (optname, qemu_prefix); | |
| 397 | + for (i = 0; i <= preflen; ++i) { | |
| 398 | + optname[i + sizeof (qemu_prefix) - 1] = toupper (prefix[i]); | |
| 399 | + } | |
| 400 | + strcat (optname, "_"); | |
| 401 | + strcat (optname, opt->name); | |
| 402 | + | |
| 403 | + def = 1; | |
| 404 | + switch (opt->tag) { | |
| 405 | + case AUD_OPT_BOOL: | |
| 406 | + case AUD_OPT_INT: | |
| 407 | + { | |
| 408 | + int *intp = opt->valp; | |
| 409 | + *intp = audio_get_conf_int (optname, *intp, &def); | |
| 410 | + } | |
| 411 | + break; | |
| 412 | + | |
| 413 | + case AUD_OPT_FMT: | |
| 414 | + { | |
| 415 | + audfmt_e *fmtp = opt->valp; | |
| 416 | + *fmtp = audio_get_conf_fmt (optname, *fmtp, &def); | |
| 417 | + } | |
| 418 | + break; | |
| 419 | + | |
| 420 | + case AUD_OPT_STR: | |
| 421 | + { | |
| 422 | + const char **strp = opt->valp; | |
| 423 | + *strp = audio_get_conf_str (optname, *strp, &def); | |
| 424 | + } | |
| 425 | + break; | |
| 426 | + | |
| 427 | + default: | |
| 428 | + dolog ("Bad value tag for option `%s' - %d\n", | |
| 429 | + optname, opt->tag); | |
| 311 | 430 | break; |
| 312 | 431 | } |
| 313 | - isamp = swlim; | |
| 314 | - osamp = blck; | |
| 315 | - st_rate_flow (sw->rate, sw->buf + pos, sw->hw->mix_buf + wpos, &isamp, &osamp); | |
| 316 | - ret += isamp; | |
| 317 | - swlim -= isamp; | |
| 318 | - pos += isamp; | |
| 319 | - live += osamp; | |
| 320 | - wpos = (wpos + osamp) % hwsamples; | |
| 321 | - } | |
| 322 | 432 | |
| 323 | - sw->wpos = wpos; | |
| 324 | - sw->live = live; | |
| 325 | - return ret << sw->shift; | |
| 433 | + if (!opt->overridenp) { | |
| 434 | + opt->overridenp = &opt->overriden; | |
| 435 | + } | |
| 436 | + *opt->overridenp = !def; | |
| 437 | + qemu_free (optname); | |
| 438 | + } | |
| 326 | 439 | } |
| 327 | 440 | |
| 328 | -int pcm_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt) | |
| 441 | +static int audio_pcm_info_eq (struct audio_pcm_info *info, int freq, | |
| 442 | + int nchannels, audfmt_e fmt) | |
| 329 | 443 | { |
| 330 | - int sign = 0, bits = 8; | |
| 444 | + int bits = 8, sign = 0; | |
| 331 | 445 | |
| 332 | - pcm_hw_fini (hw); | |
| 333 | - ldebug ("pcm_hw_init: %d %d %d\n", freq, nchannels, fmt); | |
| 334 | - if (hw->pcm_ops->init (hw, freq, nchannels, fmt)) { | |
| 335 | - memset (hw, 0, audio_state.drv->voice_size); | |
| 336 | - return -1; | |
| 446 | + switch (fmt) { | |
| 447 | + case AUD_FMT_S8: | |
| 448 | + sign = 1; | |
| 449 | + case AUD_FMT_U8: | |
| 450 | + break; | |
| 451 | + | |
| 452 | + case AUD_FMT_S16: | |
| 453 | + sign = 1; | |
| 454 | + case AUD_FMT_U16: | |
| 455 | + bits = 16; | |
| 456 | + break; | |
| 337 | 457 | } |
| 458 | + return info->freq == freq | |
| 459 | + && info->nchannels == nchannels | |
| 460 | + && info->sign == sign | |
| 461 | + && info->bits == bits; | |
| 462 | +} | |
| 338 | 463 | |
| 339 | - switch (hw->fmt) { | |
| 464 | +void audio_pcm_init_info (struct audio_pcm_info *info, int freq, | |
| 465 | + int nchannels, audfmt_e fmt, int swap_endian) | |
| 466 | +{ | |
| 467 | + int bits = 8, sign = 0; | |
| 468 | + | |
| 469 | + switch (fmt) { | |
| 340 | 470 | case AUD_FMT_S8: |
| 341 | 471 | sign = 1; |
| 342 | 472 | case AUD_FMT_U8: |
| ... | ... | @@ -349,425 +479,595 @@ int pcm_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt) |
| 349 | 479 | break; |
| 350 | 480 | } |
| 351 | 481 | |
| 352 | - hw->nb_voices = 0; | |
| 353 | - hw->active = 1; | |
| 354 | - hw->shift = (hw->nchannels == 2) + (bits == 16); | |
| 355 | - hw->bytes_per_second = hw->freq << hw->shift; | |
| 356 | - hw->align = (1 << hw->shift) - 1; | |
| 357 | - hw->samples = hw->bufsize >> hw->shift; | |
| 358 | - hw->clip = mixeng_clip[hw->nchannels == 2][sign][bits == 16]; | |
| 359 | - if (pcm_hw_alloc_resources (hw)) { | |
| 360 | - pcm_hw_fini (hw); | |
| 361 | - return -1; | |
| 362 | - } | |
| 363 | - return 0; | |
| 482 | + info->freq = freq; | |
| 483 | + info->bits = bits; | |
| 484 | + info->sign = sign; | |
| 485 | + info->nchannels = nchannels; | |
| 486 | + info->shift = (nchannels == 2) + (bits == 16); | |
| 487 | + info->align = (1 << info->shift) - 1; | |
| 488 | + info->bytes_per_second = info->freq << info->shift; | |
| 489 | + info->swap_endian = swap_endian; | |
| 364 | 490 | } |
| 365 | 491 | |
| 366 | -static int dist (void *hw) | |
| 492 | +void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len) | |
| 367 | 493 | { |
| 368 | - if (hw) { | |
| 369 | - return (((uint8_t *) hw - (uint8_t *) hw_voices) | |
| 370 | - / audio_state.drv->voice_size) + 1; | |
| 494 | + if (!len) { | |
| 495 | + return; | |
| 496 | + } | |
| 497 | + | |
| 498 | + if (info->sign) { | |
| 499 | + memset (buf, len << info->shift, 0x00); | |
| 371 | 500 | } |
| 372 | 501 | else { |
| 373 | - return 0; | |
| 502 | + if (info->bits == 8) { | |
| 503 | + memset (buf, len << info->shift, 0x80); | |
| 504 | + } | |
| 505 | + else { | |
| 506 | + int i; | |
| 507 | + uint16_t *p = buf; | |
| 508 | + int shift = info->nchannels - 1; | |
| 509 | + short s = INT16_MAX; | |
| 510 | + | |
| 511 | + if (info->swap_endian) { | |
| 512 | + s = bswap16 (s); | |
| 513 | + } | |
| 514 | + | |
| 515 | + for (i = 0; i < len << shift; i++) { | |
| 516 | + p[i] = s; | |
| 517 | + } | |
| 518 | + } | |
| 374 | 519 | } |
| 375 | 520 | } |
| 376 | 521 | |
| 377 | -#define ADVANCE(hw) \ | |
| 378 | - ((hw) ? advance (hw, audio_state.drv->voice_size) : hw_voices) | |
| 379 | - | |
| 380 | -HWVoice *pcm_hw_find_any (HWVoice *hw) | |
| 522 | +/* | |
| 523 | + * Hard voice (capture) | |
| 524 | + */ | |
| 525 | +static void audio_pcm_hw_free_resources_in (HWVoiceIn *hw) | |
| 381 | 526 | { |
| 382 | - int i = dist (hw); | |
| 383 | - for (; i < audio_state.nb_hw_voices; i++) { | |
| 384 | - hw = ADVANCE (hw); | |
| 385 | - return hw; | |
| 527 | + if (hw->conv_buf) { | |
| 528 | + qemu_free (hw->conv_buf); | |
| 386 | 529 | } |
| 387 | - return NULL; | |
| 530 | + hw->conv_buf = NULL; | |
| 388 | 531 | } |
| 389 | 532 | |
| 390 | -HWVoice *pcm_hw_find_any_active (HWVoice *hw) | |
| 533 | +static int audio_pcm_hw_alloc_resources_in (HWVoiceIn *hw) | |
| 391 | 534 | { |
| 392 | - int i = dist (hw); | |
| 393 | - for (; i < audio_state.nb_hw_voices; i++) { | |
| 394 | - hw = ADVANCE (hw); | |
| 395 | - if (hw->active) | |
| 396 | - return hw; | |
| 535 | + hw->conv_buf = qemu_mallocz (hw->samples * sizeof (st_sample_t)); | |
| 536 | + if (!hw->conv_buf) { | |
| 537 | + return -1; | |
| 397 | 538 | } |
| 398 | - return NULL; | |
| 539 | + return 0; | |
| 399 | 540 | } |
| 400 | 541 | |
| 401 | -HWVoice *pcm_hw_find_any_active_enabled (HWVoice *hw) | |
| 542 | +static int audio_pcm_hw_init_in (HWVoiceIn *hw, int freq, int nchannels, audfmt_e fmt) | |
| 402 | 543 | { |
| 403 | - int i = dist (hw); | |
| 404 | - for (; i < audio_state.nb_hw_voices; i++) { | |
| 405 | - hw = ADVANCE (hw); | |
| 406 | - if (hw->active && hw->enabled) | |
| 407 | - return hw; | |
| 544 | + audio_pcm_hw_fini_in (hw); | |
| 545 | + | |
| 546 | + if (hw->pcm_ops->init_in (hw, freq, nchannels, fmt)) { | |
| 547 | + memset (hw, 0, audio_state.drv->voice_size_in); | |
| 548 | + return -1; | |
| 408 | 549 | } |
| 409 | - return NULL; | |
| 550 | + LIST_INIT (&hw->sw_head); | |
| 551 | + hw->active = 1; | |
| 552 | + hw->samples = hw->bufsize >> hw->info.shift; | |
| 553 | + hw->conv = | |
| 554 | + mixeng_conv | |
| 555 | + [nchannels == 2] | |
| 556 | + [hw->info.sign] | |
| 557 | + [hw->info.swap_endian] | |
| 558 | + [hw->info.bits == 16]; | |
| 559 | + if (audio_pcm_hw_alloc_resources_in (hw)) { | |
| 560 | + audio_pcm_hw_free_resources_in (hw); | |
| 561 | + return -1; | |
| 562 | + } | |
| 563 | + return 0; | |
| 410 | 564 | } |
| 411 | 565 | |
| 412 | -HWVoice *pcm_hw_find_any_passive (HWVoice *hw) | |
| 566 | +static uint64_t audio_pcm_hw_find_min_in (HWVoiceIn *hw) | |
| 413 | 567 | { |
| 414 | - int i = dist (hw); | |
| 415 | - for (; i < audio_state.nb_hw_voices; i++) { | |
| 416 | - hw = ADVANCE (hw); | |
| 417 | - if (!hw->active) | |
| 418 | - return hw; | |
| 568 | + SWVoiceIn *sw; | |
| 569 | + int m = hw->total_samples_captured; | |
| 570 | + | |
| 571 | + for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { | |
| 572 | + if (sw->active) { | |
| 573 | + m = audio_MIN (m, sw->total_hw_samples_acquired); | |
| 574 | + } | |
| 419 | 575 | } |
| 420 | - return NULL; | |
| 576 | + return m; | |
| 421 | 577 | } |
| 422 | 578 | |
| 423 | -HWVoice *pcm_hw_find_specific (HWVoice *hw, int freq, | |
| 424 | - int nchannels, audfmt_e fmt) | |
| 579 | +int audio_pcm_hw_get_live_in (HWVoiceIn *hw) | |
| 425 | 580 | { |
| 426 | - while ((hw = pcm_hw_find_any_active (hw))) { | |
| 427 | - if (hw->freq == freq && | |
| 428 | - hw->nchannels == nchannels && | |
| 429 | - hw->fmt == fmt) | |
| 430 | - return hw; | |
| 581 | + int live = hw->total_samples_captured - audio_pcm_hw_find_min_in (hw); | |
| 582 | + if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) { | |
| 583 | + dolog ("live=%d hw->samples=%d\n", live, hw->samples); | |
| 584 | + return 0; | |
| 431 | 585 | } |
| 432 | - return NULL; | |
| 586 | + return live; | |
| 433 | 587 | } |
| 434 | 588 | |
| 435 | -HWVoice *pcm_hw_add (int freq, int nchannels, audfmt_e fmt) | |
| 589 | +/* | |
| 590 | + * Soft voice (capture) | |
| 591 | + */ | |
| 592 | +static void audio_pcm_sw_free_resources_in (SWVoiceIn *sw) | |
| 436 | 593 | { |
| 437 | - HWVoice *hw; | |
| 438 | - | |
| 439 | - if (audio_state.fixed_format) { | |
| 440 | - freq = audio_state.fixed_freq; | |
| 441 | - nchannels = audio_state.fixed_channels; | |
| 442 | - fmt = audio_state.fixed_fmt; | |
| 594 | + if (sw->conv_buf) { | |
| 595 | + qemu_free (sw->conv_buf); | |
| 443 | 596 | } |
| 444 | 597 | |
| 445 | - hw = pcm_hw_find_specific (NULL, freq, nchannels, fmt); | |
| 446 | - | |
| 447 | - if (hw) | |
| 448 | - return hw; | |
| 449 | - | |
| 450 | - hw = pcm_hw_find_any_passive (NULL); | |
| 451 | - if (hw) { | |
| 452 | - hw->pcm_ops = audio_state.drv->pcm_ops; | |
| 453 | - if (!hw->pcm_ops) | |
| 454 | - return NULL; | |
| 455 | - | |
| 456 | - if (pcm_hw_init (hw, freq, nchannels, fmt)) { | |
| 457 | - pcm_hw_gc (hw); | |
| 458 | - return NULL; | |
| 459 | - } | |
| 460 | - else | |
| 461 | - return hw; | |
| 598 | + if (sw->rate) { | |
| 599 | + st_rate_stop (sw->rate); | |
| 462 | 600 | } |
| 463 | 601 | |
| 464 | - return pcm_hw_find_any (NULL); | |
| 602 | + sw->conv_buf = NULL; | |
| 603 | + sw->rate = NULL; | |
| 465 | 604 | } |
| 466 | 605 | |
| 467 | -int pcm_hw_add_sw (HWVoice *hw, SWVoice *sw) | |
| 606 | +static int audio_pcm_sw_alloc_resources_in (SWVoiceIn *sw) | |
| 468 | 607 | { |
| 469 | - SWVoice **pvoice = qemu_mallocz ((hw->nb_voices + 1) * sizeof (sw)); | |
| 470 | - if (!pvoice) | |
| 608 | + int samples = ((int64_t) sw->hw->samples << 32) / sw->ratio; | |
| 609 | + sw->conv_buf = qemu_mallocz (samples * sizeof (st_sample_t)); | |
| 610 | + if (!sw->conv_buf) { | |
| 471 | 611 | return -1; |
| 612 | + } | |
| 472 | 613 | |
| 473 | - memcpy (pvoice, hw->pvoice, hw->nb_voices * sizeof (sw)); | |
| 474 | - qemu_free (hw->pvoice); | |
| 475 | - hw->pvoice = pvoice; | |
| 476 | - hw->pvoice[hw->nb_voices++] = sw; | |
| 614 | + sw->rate = st_rate_start (sw->hw->info.freq, sw->info.freq); | |
| 615 | + if (!sw->rate) { | |
| 616 | + qemu_free (sw->conv_buf); | |
| 617 | + sw->conv_buf = NULL; | |
| 618 | + return -1; | |
| 619 | + } | |
| 477 | 620 | return 0; |
| 478 | 621 | } |
| 479 | 622 | |
| 480 | -int pcm_hw_del_sw (HWVoice *hw, SWVoice *sw) | |
| 623 | +static int audio_pcm_sw_init_in (SWVoiceIn *sw, HWVoiceIn *hw, const char *name, | |
| 624 | + int freq, int nchannels, audfmt_e fmt) | |
| 481 | 625 | { |
| 482 | - int i, j; | |
| 483 | - if (hw->nb_voices > 1) { | |
| 484 | - SWVoice **pvoice = qemu_mallocz ((hw->nb_voices - 1) * sizeof (sw)); | |
| 626 | + audio_pcm_init_info (&sw->info, freq, nchannels, fmt, | |
| 627 | + /* None of the cards emulated by QEMU are big-endian | |
| 628 | + hence following shortcut */ | |
| 629 | + audio_need_to_swap_endian (0)); | |
| 630 | + sw->hw = hw; | |
| 631 | + sw->ratio = ((int64_t) sw->info.freq << 32) / sw->hw->info.freq; | |
| 485 | 632 | |
| 486 | - if (!pvoice) { | |
| 487 | - dolog ("Can not maintain consistent state (not enough memory)\n"); | |
| 488 | - return -1; | |
| 489 | - } | |
| 633 | + sw->clip = | |
| 634 | + mixeng_clip | |
| 635 | + [nchannels == 2] | |
| 636 | + [sw->info.sign] | |
| 637 | + [sw->info.swap_endian] | |
| 638 | + [sw->info.bits == 16]; | |
| 490 | 639 | |
| 491 | - for (i = 0, j = 0; i < hw->nb_voices; i++) { | |
| 492 | - if (j >= hw->nb_voices - 1) { | |
| 493 | - dolog ("Can not maintain consistent state " | |
| 494 | - "(invariant violated)\n"); | |
| 495 | - return -1; | |
| 496 | - } | |
| 497 | - if (hw->pvoice[i] != sw) | |
| 498 | - pvoice[j++] = hw->pvoice[i]; | |
| 499 | - } | |
| 500 | - qemu_free (hw->pvoice); | |
| 501 | - hw->pvoice = pvoice; | |
| 502 | - hw->nb_voices -= 1; | |
| 640 | + sw->name = qemu_strdup (name); | |
| 641 | + audio_pcm_sw_free_resources_in (sw); | |
| 642 | + return audio_pcm_sw_alloc_resources_in (sw); | |
| 643 | +} | |
| 644 | + | |
| 645 | +static int audio_pcm_sw_get_rpos_in (SWVoiceIn *sw) | |
| 646 | +{ | |
| 647 | + HWVoiceIn *hw = sw->hw; | |
| 648 | + int live = hw->total_samples_captured - sw->total_hw_samples_acquired; | |
| 649 | + int rpos; | |
| 650 | + | |
| 651 | + if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) { | |
| 652 | + dolog ("live=%d hw->samples=%d\n", live, hw->samples); | |
| 653 | + return 0; | |
| 654 | + } | |
| 655 | + | |
| 656 | + rpos = hw->wpos - live; | |
| 657 | + if (rpos >= 0) { | |
| 658 | + return rpos; | |
| 503 | 659 | } |
| 504 | 660 | else { |
| 505 | - qemu_free (hw->pvoice); | |
| 506 | - hw->pvoice = NULL; | |
| 507 | - hw->nb_voices = 0; | |
| 661 | + return hw->samples + rpos; | |
| 508 | 662 | } |
| 509 | - return 0; | |
| 510 | 663 | } |
| 511 | 664 | |
| 512 | -SWVoice *pcm_create_voice_pair (int freq, int nchannels, audfmt_e fmt) | |
| 665 | +int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int size) | |
| 513 | 666 | { |
| 514 | - SWVoice *sw; | |
| 515 | - HWVoice *hw; | |
| 667 | + HWVoiceIn *hw = sw->hw; | |
| 668 | + int samples, live, ret = 0, swlim, isamp, osamp, rpos, total = 0; | |
| 669 | + st_sample_t *src, *dst = sw->conv_buf; | |
| 670 | + | |
| 671 | + rpos = audio_pcm_sw_get_rpos_in (sw) % hw->samples; | |
| 672 | + | |
| 673 | + live = hw->total_samples_captured - sw->total_hw_samples_acquired; | |
| 674 | + if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) { | |
| 675 | + dolog ("live_in=%d hw->samples=%d\n", live, hw->samples); | |
| 676 | + return 0; | |
| 677 | + } | |
| 678 | + | |
| 679 | + samples = size >> sw->info.shift; | |
| 680 | + if (!live) { | |
| 681 | + return 0; | |
| 682 | + } | |
| 516 | 683 | |
| 517 | - sw = qemu_mallocz (sizeof (*sw)); | |
| 518 | - if (!sw) | |
| 519 | - goto err1; | |
| 684 | + swlim = (live * sw->ratio) >> 32; | |
| 685 | + swlim = audio_MIN (swlim, samples); | |
| 520 | 686 | |
| 521 | - hw = pcm_hw_add (freq, nchannels, fmt); | |
| 522 | - if (!hw) | |
| 523 | - goto err2; | |
| 687 | + while (swlim) { | |
| 688 | + src = hw->conv_buf + rpos; | |
| 689 | + isamp = hw->wpos - rpos; | |
| 690 | + /* XXX: <= ? */ | |
| 691 | + if (isamp <= 0) { | |
| 692 | + isamp = hw->samples - rpos; | |
| 693 | + } | |
| 524 | 694 | |
| 525 | - if (pcm_hw_add_sw (hw, sw)) | |
| 526 | - goto err3; | |
| 695 | + if (!isamp) { | |
| 696 | + break; | |
| 697 | + } | |
| 698 | + osamp = swlim; | |
| 527 | 699 | |
| 528 | - if (pcm_sw_init (sw, hw, freq, nchannels, fmt)) | |
| 529 | - goto err4; | |
| 700 | + if (audio_bug (AUDIO_FUNC, osamp < 0)) { | |
| 701 | + dolog ("osamp=%d\n", osamp); | |
| 702 | + } | |
| 530 | 703 | |
| 531 | - return sw; | |
| 704 | + st_rate_flow (sw->rate, src, dst, &isamp, &osamp); | |
| 705 | + swlim -= osamp; | |
| 706 | + rpos = (rpos + isamp) % hw->samples; | |
| 707 | + dst += osamp; | |
| 708 | + ret += osamp; | |
| 709 | + total += isamp; | |
| 710 | + } | |
| 532 | 711 | |
| 533 | -err4: | |
| 534 | - pcm_hw_del_sw (hw, sw); | |
| 535 | -err3: | |
| 536 | - pcm_hw_gc (hw); | |
| 537 | -err2: | |
| 538 | - qemu_free (sw); | |
| 539 | -err1: | |
| 540 | - return NULL; | |
| 712 | + sw->clip (buf, sw->conv_buf, ret); | |
| 713 | + sw->total_hw_samples_acquired += total; | |
| 714 | + return ret << sw->info.shift; | |
| 541 | 715 | } |
| 542 | 716 | |
| 543 | -SWVoice *AUD_open (SWVoice *sw, const char *name, | |
| 544 | - int freq, int nchannels, audfmt_e fmt) | |
| 717 | +/* | |
| 718 | + * Hard voice (playback) | |
| 719 | + */ | |
| 720 | +static int audio_pcm_hw_find_min_out (HWVoiceOut *hw, int *nb_livep) | |
| 545 | 721 | { |
| 546 | - if (!audio_state.drv) { | |
| 547 | - return NULL; | |
| 722 | + SWVoiceOut *sw; | |
| 723 | + int m = INT_MAX; | |
| 724 | + int nb_live = 0; | |
| 725 | + | |
| 726 | + for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { | |
| 727 | + if (sw->active || !sw->empty) { | |
| 728 | + m = audio_MIN (m, sw->total_hw_samples_mixed); | |
| 729 | + nb_live += 1; | |
| 730 | + } | |
| 548 | 731 | } |
| 549 | 732 | |
| 550 | - if (sw && freq == sw->freq && sw->nchannels == nchannels && sw->fmt == fmt) { | |
| 551 | - return sw; | |
| 733 | + *nb_livep = nb_live; | |
| 734 | + return m; | |
| 735 | +} | |
| 736 | + | |
| 737 | +static void audio_pcm_hw_free_resources_out (HWVoiceOut *hw) | |
| 738 | +{ | |
| 739 | + if (hw->mix_buf) { | |
| 740 | + qemu_free (hw->mix_buf); | |
| 552 | 741 | } |
| 553 | 742 | |
| 554 | - if (sw) { | |
| 555 | - ldebug ("Different format %s %d %d %d\n", | |
| 556 | - name, | |
| 557 | - sw->freq == freq, | |
| 558 | - sw->nchannels == nchannels, | |
| 559 | - sw->fmt == fmt); | |
| 743 | + hw->mix_buf = NULL; | |
| 744 | +} | |
| 745 | + | |
| 746 | +static int audio_pcm_hw_alloc_resources_out (HWVoiceOut *hw) | |
| 747 | +{ | |
| 748 | + hw->mix_buf = qemu_mallocz (hw->samples * sizeof (st_sample_t)); | |
| 749 | + if (!hw->mix_buf) { | |
| 750 | + return -1; | |
| 560 | 751 | } |
| 561 | 752 | |
| 562 | - if (nchannels != 1 && nchannels != 2) { | |
| 563 | - dolog ("Bogus channel count %d for voice %s\n", nchannels, name); | |
| 564 | - return NULL; | |
| 753 | + return 0; | |
| 754 | +} | |
| 755 | + | |
| 756 | +static int audio_pcm_hw_init_out (HWVoiceOut *hw, int freq, | |
| 757 | + int nchannels, audfmt_e fmt) | |
| 758 | +{ | |
| 759 | + audio_pcm_hw_fini_out (hw); | |
| 760 | + if (hw->pcm_ops->init_out (hw, freq, nchannels, fmt)) { | |
| 761 | + memset (hw, 0, audio_state.drv->voice_size_out); | |
| 762 | + return -1; | |
| 565 | 763 | } |
| 566 | 764 | |
| 567 | - if (!audio_state.fixed_format && sw) { | |
| 568 | - pcm_sw_fini (sw); | |
| 569 | - pcm_hw_del_sw (sw->hw, sw); | |
| 570 | - pcm_hw_gc (sw->hw); | |
| 571 | - if (sw->name) { | |
| 572 | - qemu_free (sw->name); | |
| 573 | - sw->name = NULL; | |
| 574 | - } | |
| 575 | - qemu_free (sw); | |
| 576 | - sw = NULL; | |
| 765 | + LIST_INIT (&hw->sw_head); | |
| 766 | + hw->active = 1; | |
| 767 | + hw->samples = hw->bufsize >> hw->info.shift; | |
| 768 | + hw->clip = | |
| 769 | + mixeng_clip | |
| 770 | + [nchannels == 2] | |
| 771 | + [hw->info.sign] | |
| 772 | + [hw->info.swap_endian] | |
| 773 | + [hw->info.bits == 16]; | |
| 774 | + if (audio_pcm_hw_alloc_resources_out (hw)) { | |
| 775 | + audio_pcm_hw_fini_out (hw); | |
| 776 | + return -1; | |
| 577 | 777 | } |
| 778 | + return 0; | |
| 779 | +} | |
| 578 | 780 | |
| 579 | - if (sw) { | |
| 580 | - HWVoice *hw = sw->hw; | |
| 581 | - if (!hw) { | |
| 582 | - dolog ("Internal logic error voice %s has no hardware store\n", | |
| 583 | - name); | |
| 584 | - return sw; | |
| 585 | - } | |
| 781 | +int audio_pcm_hw_get_live_out2 (HWVoiceOut *hw, int *nb_live) | |
| 782 | +{ | |
| 783 | + int smin; | |
| 586 | 784 | |
| 587 | - if (pcm_sw_init (sw, hw, freq, nchannels, fmt)) { | |
| 588 | - pcm_sw_fini (sw); | |
| 589 | - pcm_hw_del_sw (hw, sw); | |
| 590 | - pcm_hw_gc (hw); | |
| 591 | - if (sw->name) { | |
| 592 | - qemu_free (sw->name); | |
| 593 | - sw->name = NULL; | |
| 594 | - } | |
| 595 | - qemu_free (sw); | |
| 596 | - return NULL; | |
| 597 | - } | |
| 785 | + smin = audio_pcm_hw_find_min_out (hw, nb_live); | |
| 786 | + | |
| 787 | + if (!*nb_live) { | |
| 788 | + return 0; | |
| 598 | 789 | } |
| 599 | 790 | else { |
| 600 | - sw = pcm_create_voice_pair (freq, nchannels, fmt); | |
| 601 | - if (!sw) { | |
| 602 | - dolog ("Failed to create voice %s\n", name); | |
| 603 | - return NULL; | |
| 791 | + int live = smin; | |
| 792 | + | |
| 793 | + if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) { | |
| 794 | + dolog ("live=%d hw->samples=%d\n", live, hw->samples); | |
| 795 | + return 0; | |
| 604 | 796 | } |
| 797 | + return live; | |
| 605 | 798 | } |
| 799 | +} | |
| 800 | + | |
| 801 | +int audio_pcm_hw_get_live_out (HWVoiceOut *hw) | |
| 802 | +{ | |
| 803 | + int nb_live; | |
| 804 | + int live; | |
| 606 | 805 | |
| 607 | - if (sw->name) { | |
| 608 | - qemu_free (sw->name); | |
| 609 | - sw->name = NULL; | |
| 806 | + live = audio_pcm_hw_get_live_out2 (hw, &nb_live); | |
| 807 | + if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) { | |
| 808 | + dolog ("live=%d hw->samples=%d\n", live, hw->samples); | |
| 809 | + return 0; | |
| 610 | 810 | } |
| 611 | - sw->name = qemu_strdup (name); | |
| 612 | - return sw; | |
| 811 | + return live; | |
| 613 | 812 | } |
| 614 | 813 | |
| 615 | -void AUD_close (SWVoice *sw) | |
| 814 | +/* | |
| 815 | + * Soft voice (playback) | |
| 816 | + */ | |
| 817 | +static void audio_pcm_sw_free_resources_out (SWVoiceOut *sw) | |
| 616 | 818 | { |
| 617 | - if (!sw) | |
| 618 | - return; | |
| 819 | + if (sw->buf) { | |
| 820 | + qemu_free (sw->buf); | |
| 821 | + } | |
| 619 | 822 | |
| 620 | - pcm_sw_fini (sw); | |
| 621 | - pcm_hw_del_sw (sw->hw, sw); | |
| 622 | - pcm_hw_gc (sw->hw); | |
| 623 | - if (sw->name) { | |
| 624 | - qemu_free (sw->name); | |
| 625 | - sw->name = NULL; | |
| 823 | + if (sw->rate) { | |
| 824 | + st_rate_stop (sw->rate); | |
| 626 | 825 | } |
| 627 | - qemu_free (sw); | |
| 826 | + | |
| 827 | + sw->buf = NULL; | |
| 828 | + sw->rate = NULL; | |
| 628 | 829 | } |
| 629 | 830 | |
| 630 | -int AUD_write (SWVoice *sw, void *buf, int size) | |
| 831 | +static int audio_pcm_sw_alloc_resources_out (SWVoiceOut *sw) | |
| 631 | 832 | { |
| 632 | - int bytes; | |
| 833 | + sw->buf = qemu_mallocz (sw->hw->samples * sizeof (st_sample_t)); | |
| 834 | + if (!sw->buf) { | |
| 835 | + return -1; | |
| 836 | + } | |
| 633 | 837 | |
| 634 | - if (!sw->hw->enabled) | |
| 635 | - dolog ("Writing to disabled voice %s\n", sw->name); | |
| 636 | - bytes = sw->hw->pcm_ops->write (sw, buf, size); | |
| 637 | - return bytes; | |
| 838 | + sw->rate = st_rate_start (sw->info.freq, sw->hw->info.freq); | |
| 839 | + if (!sw->rate) { | |
| 840 | + qemu_free (sw->buf); | |
| 841 | + sw->buf = NULL; | |
| 842 | + return -1; | |
| 843 | + } | |
| 844 | + return 0; | |
| 638 | 845 | } |
| 639 | 846 | |
| 640 | -void AUD_run (void) | |
| 847 | +static int audio_pcm_sw_init_out (SWVoiceOut *sw, HWVoiceOut *hw, | |
| 848 | + const char *name, int freq, | |
| 849 | + int nchannels, audfmt_e fmt) | |
| 641 | 850 | { |
| 642 | - HWVoice *hw = NULL; | |
| 643 | - | |
| 644 | - while ((hw = pcm_hw_find_any_active_enabled (hw))) { | |
| 645 | - int i; | |
| 646 | - if (hw->pending_disable && pcm_hw_get_live (hw) <= 0) { | |
| 647 | - hw->enabled = 0; | |
| 648 | - hw->pcm_ops->ctl (hw, VOICE_DISABLE); | |
| 649 | - for (i = 0; i < hw->nb_voices; i++) { | |
| 650 | - hw->pvoice[i]->live = 0; | |
| 651 | - /* hw->pvoice[i]->old_ticks = 0; */ | |
| 652 | - } | |
| 653 | - continue; | |
| 654 | - } | |
| 851 | + audio_pcm_init_info (&sw->info, freq, nchannels, fmt, | |
| 852 | + /* None of the cards emulated by QEMU are big-endian | |
| 853 | + hence following shortcut */ | |
| 854 | + audio_need_to_swap_endian (0)); | |
| 855 | + sw->hw = hw; | |
| 856 | + sw->empty = 1; | |
| 857 | + sw->active = 0; | |
| 858 | + sw->ratio = ((int64_t) sw->hw->info.freq << 32) / sw->info.freq; | |
| 859 | + sw->total_hw_samples_mixed = 0; | |
| 860 | + | |
| 861 | + sw->conv = | |
| 862 | + mixeng_conv | |
| 863 | + [nchannels == 2] | |
| 864 | + [sw->info.sign] | |
| 865 | + [sw->info.swap_endian] | |
| 866 | + [sw->info.bits == 16]; | |
| 867 | + sw->name = qemu_strdup (name); | |
| 655 | 868 | |
| 656 | - hw->pcm_ops->run (hw); | |
| 657 | - assert (hw->rpos < hw->samples); | |
| 658 | - for (i = 0; i < hw->nb_voices; i++) { | |
| 659 | - SWVoice *sw = hw->pvoice[i]; | |
| 660 | - if (!sw->active && !sw->live && sw->old_ticks) { | |
| 661 | - int64_t delta = qemu_get_clock (vm_clock) - sw->old_ticks; | |
| 662 | - if (delta > audio_state.ticks_threshold) { | |
| 663 | - ldebug ("resetting old_ticks for %s\n", sw->name); | |
| 664 | - sw->old_ticks = 0; | |
| 665 | - } | |
| 666 | - } | |
| 667 | - } | |
| 668 | - } | |
| 869 | + audio_pcm_sw_free_resources_out (sw); | |
| 870 | + return audio_pcm_sw_alloc_resources_out (sw); | |
| 669 | 871 | } |
| 670 | 872 | |
| 671 | -int AUD_get_free (SWVoice *sw) | |
| 873 | +int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int size) | |
| 672 | 874 | { |
| 673 | - int free; | |
| 875 | + int hwsamples, samples, isamp, osamp, wpos, live, dead, left, swlim, blck; | |
| 876 | + int ret = 0, pos = 0, total = 0; | |
| 674 | 877 | |
| 675 | - if (!sw) | |
| 676 | - return 4096; | |
| 878 | + if (!sw) { | |
| 879 | + return size; | |
| 880 | + } | |
| 677 | 881 | |
| 678 | - free = ((sw->hw->samples - sw->live) << sw->hw->shift) * sw->ratio | |
| 679 | - / INT_MAX; | |
| 882 | + hwsamples = sw->hw->samples; | |
| 680 | 883 | |
| 681 | - free &= ~sw->hw->align; | |
| 682 | - if (!free) return 0; | |
| 884 | + live = sw->total_hw_samples_mixed; | |
| 885 | + if (audio_bug (AUDIO_FUNC, live < 0 || live > hwsamples)){ | |
| 886 | + dolog ("live=%d hw->samples=%d\n", live, hwsamples); | |
| 887 | + return 0; | |
| 888 | + } | |
| 683 | 889 | |
| 684 | - return free; | |
| 685 | -} | |
| 890 | + if (live == hwsamples) { | |
| 891 | + return 0; | |
| 892 | + } | |
| 686 | 893 | |
| 687 | -int AUD_get_buffer_size (SWVoice *sw) | |
| 688 | -{ | |
| 689 | - return sw->hw->bufsize; | |
| 690 | -} | |
| 894 | + wpos = (sw->hw->rpos + live) % hwsamples; | |
| 895 | + samples = size >> sw->info.shift; | |
| 691 | 896 | |
| 692 | -void AUD_adjust (SWVoice *sw, int bytes) | |
| 693 | -{ | |
| 694 | - if (!sw) | |
| 695 | - return; | |
| 696 | - sw->old_ticks += (ticks_per_sec * (int64_t) bytes) / sw->bytes_per_second; | |
| 897 | + dead = hwsamples - live; | |
| 898 | + swlim = ((int64_t) dead << 32) / sw->ratio; | |
| 899 | + swlim = audio_MIN (swlim, samples); | |
| 900 | + if (swlim) { | |
| 901 | + sw->conv (sw->buf, buf, swlim, &sw->vol); | |
| 902 | + } | |
| 903 | + | |
| 904 | + while (swlim) { | |
| 905 | + dead = hwsamples - live; | |
| 906 | + left = hwsamples - wpos; | |
| 907 | + blck = audio_MIN (dead, left); | |
| 908 | + if (!blck) { | |
| 909 | + break; | |
| 910 | + } | |
| 911 | + isamp = swlim; | |
| 912 | + osamp = blck; | |
| 913 | + st_rate_flow_mix ( | |
| 914 | + sw->rate, | |
| 915 | + sw->buf + pos, | |
| 916 | + sw->hw->mix_buf + wpos, | |
| 917 | + &isamp, | |
| 918 | + &osamp | |
| 919 | + ); | |
| 920 | + ret += isamp; | |
| 921 | + swlim -= isamp; | |
| 922 | + pos += isamp; | |
| 923 | + live += osamp; | |
| 924 | + wpos = (wpos + osamp) % hwsamples; | |
| 925 | + total += osamp; | |
| 926 | + } | |
| 927 | + | |
| 928 | + sw->total_hw_samples_mixed += total; | |
| 929 | + sw->empty = sw->total_hw_samples_mixed == 0; | |
| 930 | + | |
| 931 | +#ifdef DEBUG_OUT | |
| 932 | + dolog ( | |
| 933 | + "%s: write size %d ret %d total sw %d, hw %d\n", | |
| 934 | + sw->name, | |
| 935 | + size >> sw->info.shift, | |
| 936 | + ret, | |
| 937 | + sw->total_hw_samples_mixed, | |
| 938 | + sw->hw->total_samples_played | |
| 939 | + ); | |
| 940 | +#endif | |
| 941 | + | |
| 942 | + return ret << sw->info.shift; | |
| 697 | 943 | } |
| 698 | 944 | |
| 699 | -void AUD_reset (SWVoice *sw) | |
| 945 | +#ifdef DEBUG_AUDIO | |
| 946 | +static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info) | |
| 700 | 947 | { |
| 701 | - sw->active = 0; | |
| 702 | - sw->old_ticks = 0; | |
| 948 | + dolog ("%s: bits %d, sign %d, freq %d, nchan %d\n", | |
| 949 | + cap, info->bits, info->sign, info->freq, info->nchannels); | |
| 703 | 950 | } |
| 951 | +#endif | |
| 704 | 952 | |
| 705 | -int AUD_calc_elapsed (SWVoice *sw) | |
| 953 | +#define DAC | |
| 954 | +#include "audio_template.h" | |
| 955 | +#undef DAC | |
| 956 | +#include "audio_template.h" | |
| 957 | + | |
| 958 | +int AUD_write (SWVoiceOut *sw, void *buf, int size) | |
| 706 | 959 | { |
| 707 | - int64_t now, delta, bytes; | |
| 708 | - int dead, swlim; | |
| 960 | + int bytes; | |
| 709 | 961 | |
| 710 | - if (!sw) | |
| 711 | - return 0; | |
| 962 | + if (!sw) { | |
| 963 | + /* XXX: Consider options */ | |
| 964 | + return size; | |
| 965 | + } | |
| 712 | 966 | |
| 713 | - now = qemu_get_clock (vm_clock); | |
| 714 | - delta = now - sw->old_ticks; | |
| 715 | - bytes = (delta * sw->bytes_per_second) / ticks_per_sec; | |
| 716 | - if (delta < 0) { | |
| 717 | - dolog ("whoops delta(<0)=%lld\n", delta); | |
| 967 | + if (!sw->hw->enabled) { | |
| 968 | + dolog ("Writing to disabled voice %s\n", sw->name); | |
| 718 | 969 | return 0; |
| 719 | 970 | } |
| 720 | 971 | |
| 721 | - dead = sw->hw->samples - sw->live; | |
| 722 | - swlim = ((dead * (int64_t) INT_MAX) / sw->ratio); | |
| 972 | + bytes = sw->hw->pcm_ops->write (sw, buf, size); | |
| 973 | + return bytes; | |
| 974 | +} | |
| 975 | + | |
| 976 | +int AUD_read (SWVoiceIn *sw, void *buf, int size) | |
| 977 | +{ | |
| 978 | + int bytes; | |
| 723 | 979 | |
| 724 | - if (bytes > swlim) { | |
| 725 | - return swlim; | |
| 980 | + if (!sw) { | |
| 981 | + /* XXX: Consider options */ | |
| 982 | + return size; | |
| 726 | 983 | } |
| 727 | - else { | |
| 728 | - return bytes; | |
| 984 | + | |
| 985 | + if (!sw->hw->enabled) { | |
| 986 | + dolog ("Reading from disabled voice %s\n", sw->name); | |
| 987 | + return 0; | |
| 729 | 988 | } |
| 989 | + | |
| 990 | + bytes = sw->hw->pcm_ops->read (sw, buf, size); | |
| 991 | + return bytes; | |
| 730 | 992 | } |
| 731 | 993 | |
| 732 | -void AUD_enable (SWVoice *sw, int on) | |
| 994 | +int AUD_get_buffer_size_out (SWVoiceOut *sw) | |
| 733 | 995 | { |
| 734 | - int i; | |
| 735 | - HWVoice *hw; | |
| 996 | + return sw->hw->bufsize; | |
| 997 | +} | |
| 998 | + | |
| 999 | +void AUD_set_active_out (SWVoiceOut *sw, int on) | |
| 1000 | +{ | |
| 1001 | + HWVoiceOut *hw; | |
| 736 | 1002 | |
| 737 | - if (!sw) | |
| 1003 | + if (!sw) { | |
| 738 | 1004 | return; |
| 1005 | + } | |
| 739 | 1006 | |
| 740 | 1007 | hw = sw->hw; |
| 741 | - if (on) { | |
| 742 | - if (!sw->live) | |
| 743 | - sw->wpos = sw->hw->rpos; | |
| 744 | - if (!sw->old_ticks) { | |
| 745 | - sw->old_ticks = qemu_get_clock (vm_clock); | |
| 1008 | + if (sw->active != on) { | |
| 1009 | + SWVoiceOut *temp_sw; | |
| 1010 | + | |
| 1011 | + if (on) { | |
| 1012 | + int total; | |
| 1013 | + | |
| 1014 | + hw->pending_disable = 0; | |
| 1015 | + if (!hw->enabled) { | |
| 1016 | + hw->enabled = 1; | |
| 1017 | + hw->pcm_ops->ctl_out (hw, VOICE_ENABLE); | |
| 1018 | + } | |
| 1019 | + | |
| 1020 | + if (sw->empty) { | |
| 1021 | + total = 0; | |
| 1022 | + } | |
| 1023 | + } | |
| 1024 | + else { | |
| 1025 | + if (hw->enabled) { | |
| 1026 | + int nb_active = 0; | |
| 1027 | + | |
| 1028 | + for (temp_sw = hw->sw_head.lh_first; temp_sw; | |
| 1029 | + temp_sw = temp_sw->entries.le_next) { | |
| 1030 | + nb_active += temp_sw->active != 0; | |
| 1031 | + } | |
| 1032 | + | |
| 1033 | + hw->pending_disable = nb_active == 1; | |
| 1034 | + } | |
| 746 | 1035 | } |
| 1036 | + sw->active = on; | |
| 1037 | + } | |
| 1038 | +} | |
| 1039 | + | |
| 1040 | +void AUD_set_active_in (SWVoiceIn *sw, int on) | |
| 1041 | +{ | |
| 1042 | + HWVoiceIn *hw; | |
| 1043 | + | |
| 1044 | + if (!sw) { | |
| 1045 | + return; | |
| 747 | 1046 | } |
| 748 | 1047 | |
| 1048 | + hw = sw->hw; | |
| 749 | 1049 | if (sw->active != on) { |
| 1050 | + SWVoiceIn *temp_sw; | |
| 1051 | + | |
| 750 | 1052 | if (on) { |
| 751 | - hw->pending_disable = 0; | |
| 752 | 1053 | if (!hw->enabled) { |
| 753 | 1054 | hw->enabled = 1; |
| 754 | - for (i = 0; i < hw->nb_voices; i++) { | |
| 755 | - ldebug ("resetting voice\n"); | |
| 756 | - sw = hw->pvoice[i]; | |
| 757 | - sw->old_ticks = qemu_get_clock (vm_clock); | |
| 758 | - } | |
| 759 | - hw->pcm_ops->ctl (hw, VOICE_ENABLE); | |
| 1055 | + hw->pcm_ops->ctl_in (hw, VOICE_ENABLE); | |
| 760 | 1056 | } |
| 1057 | + sw->total_hw_samples_acquired = hw->total_samples_captured; | |
| 761 | 1058 | } |
| 762 | 1059 | else { |
| 763 | - if (hw->enabled && !hw->pending_disable) { | |
| 1060 | + if (hw->enabled) { | |
| 764 | 1061 | int nb_active = 0; |
| 765 | - for (i = 0; i < hw->nb_voices; i++) { | |
| 766 | - nb_active += hw->pvoice[i]->active != 0; | |
| 1062 | + | |
| 1063 | + for (temp_sw = hw->sw_head.lh_first; temp_sw; | |
| 1064 | + temp_sw = temp_sw->entries.le_next) { | |
| 1065 | + nb_active += temp_sw->active != 0; | |
| 767 | 1066 | } |
| 768 | 1067 | |
| 769 | 1068 | if (nb_active == 1) { |
| 770 | - hw->pending_disable = 1; | |
| 1069 | + hw->enabled = 0; | |
| 1070 | + hw->pcm_ops->ctl_in (hw, VOICE_DISABLE); | |
| 771 | 1071 | } |
| 772 | 1072 | } |
| 773 | 1073 | } |
| ... | ... | @@ -775,118 +1075,547 @@ void AUD_enable (SWVoice *sw, int on) |
| 775 | 1075 | } |
| 776 | 1076 | } |
| 777 | 1077 | |
| 778 | -static struct audio_output_driver *drvtab[] = { | |
| 779 | -#ifdef CONFIG_OSS | |
| 780 | - &oss_output_driver, | |
| 1078 | +static int audio_get_avail (SWVoiceIn *sw) | |
| 1079 | +{ | |
| 1080 | + int live; | |
| 1081 | + | |
| 1082 | + if (!sw) { | |
| 1083 | + return 0; | |
| 1084 | + } | |
| 1085 | + | |
| 1086 | + live = sw->hw->total_samples_captured - sw->total_hw_samples_acquired; | |
| 1087 | + if (audio_bug (AUDIO_FUNC, live < 0 || live > sw->hw->samples)) { | |
| 1088 | + dolog ("live=%d sw->hw->samples=%d\n", live, sw->hw->samples); | |
| 1089 | + return 0; | |
| 1090 | + } | |
| 1091 | + | |
| 1092 | + ldebug ( | |
| 1093 | + "%s: get_avail live %d ret %lld\n", | |
| 1094 | + sw->name, | |
| 1095 | + live, (((int64_t) live << 32) / sw->ratio) << sw->info.shift | |
| 1096 | + ); | |
| 1097 | + | |
| 1098 | + return (((int64_t) live << 32) / sw->ratio) << sw->info.shift; | |
| 1099 | +} | |
| 1100 | + | |
| 1101 | +static int audio_get_free (SWVoiceOut *sw) | |
| 1102 | +{ | |
| 1103 | + int live, dead; | |
| 1104 | + | |
| 1105 | + if (!sw) { | |
| 1106 | + return 0; | |
| 1107 | + } | |
| 1108 | + | |
| 1109 | + live = sw->total_hw_samples_mixed; | |
| 1110 | + | |
| 1111 | + if (audio_bug (AUDIO_FUNC, live < 0 || live > sw->hw->samples)) { | |
| 1112 | + dolog ("live=%d sw->hw->samples=%d\n", live, sw->hw->samples); | |
| 1113 | + } | |
| 1114 | + | |
| 1115 | + dead = sw->hw->samples - live; | |
| 1116 | + | |
| 1117 | +#ifdef DEBUG_OUT | |
| 1118 | + dolog ("%s: get_free live %d dead %d ret %lld\n", | |
| 1119 | + sw->name, | |
| 1120 | + live, dead, (((int64_t) dead << 32) / sw->ratio) << sw->info.shift); | |
| 781 | 1121 | #endif |
| 782 | -#ifdef CONFIG_FMOD | |
| 783 | - &fmod_output_driver, | |
| 1122 | + | |
| 1123 | + return (((int64_t) dead << 32) / sw->ratio) << sw->info.shift; | |
| 1124 | +} | |
| 1125 | + | |
| 1126 | +static void audio_run_out (void) | |
| 1127 | +{ | |
| 1128 | + HWVoiceOut *hw = NULL; | |
| 1129 | + SWVoiceOut *sw; | |
| 1130 | + | |
| 1131 | + while ((hw = audio_pcm_hw_find_any_active_enabled_out (hw))) { | |
| 1132 | + int played; | |
| 1133 | + int live, free, nb_live; | |
| 1134 | + | |
| 1135 | + live = audio_pcm_hw_get_live_out2 (hw, &nb_live); | |
| 1136 | + if (!nb_live) { | |
| 1137 | + live = 0; | |
| 1138 | + } | |
| 1139 | + if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) { | |
| 1140 | + dolog ("live=%d hw->samples=%d\n", live, hw->samples); | |
| 1141 | + } | |
| 1142 | + | |
| 1143 | + if (hw->pending_disable && !nb_live) { | |
| 1144 | +#ifdef DEBUG_OUT | |
| 1145 | + dolog ("Disabling voice\n"); | |
| 784 | 1146 | #endif |
| 785 | -#ifdef CONFIG_SDL | |
| 786 | - &sdl_output_driver, | |
| 1147 | + hw->enabled = 0; | |
| 1148 | + hw->pending_disable = 0; | |
| 1149 | + hw->pcm_ops->ctl_out (hw, VOICE_DISABLE); | |
| 1150 | + continue; | |
| 1151 | + } | |
| 1152 | + | |
| 1153 | + if (!live) { | |
| 1154 | + for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { | |
| 1155 | + if (sw->active) { | |
| 1156 | + free = audio_get_free (sw); | |
| 1157 | + if (free > 0) { | |
| 1158 | + sw->callback.fn (sw->callback.opaque, free); | |
| 1159 | + } | |
| 1160 | + } | |
| 1161 | + } | |
| 1162 | + continue; | |
| 1163 | + } | |
| 1164 | + | |
| 1165 | + played = hw->pcm_ops->run_out (hw); | |
| 1166 | + if (audio_bug (AUDIO_FUNC, hw->rpos >= hw->samples)) { | |
| 1167 | + dolog ("hw->rpos=%d hw->samples=%d played=%d\n", | |
| 1168 | + hw->rpos, hw->samples, played); | |
| 1169 | + hw->rpos = 0; | |
| 1170 | + } | |
| 1171 | + | |
| 1172 | +#ifdef DEBUG_OUT | |
| 1173 | + dolog ("played = %d total %d\n", played, hw->total_samples_played); | |
| 787 | 1174 | #endif |
| 788 | - &no_output_driver, | |
| 789 | -#ifdef USE_WAV_AUDIO | |
| 790 | - &wav_output_driver, | |
| 1175 | + | |
| 1176 | + if (played) { | |
| 1177 | + hw->ts_helper += played; | |
| 1178 | + } | |
| 1179 | + | |
| 1180 | + for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { | |
| 1181 | + again: | |
| 1182 | + if (!sw->active && sw->empty) { | |
| 1183 | + continue; | |
| 1184 | + } | |
| 1185 | + | |
| 1186 | + if (audio_bug (AUDIO_FUNC, played > sw->total_hw_samples_mixed)) { | |
| 1187 | + dolog ("played=%d sw->total_hw_samples_mixed=%d\n", | |
| 1188 | + played, sw->total_hw_samples_mixed); | |
| 1189 | + played = sw->total_hw_samples_mixed; | |
| 1190 | + } | |
| 1191 | + | |
| 1192 | + sw->total_hw_samples_mixed -= played; | |
| 1193 | + | |
| 1194 | + if (!sw->total_hw_samples_mixed) { | |
| 1195 | + sw->empty = 1; | |
| 1196 | + | |
| 1197 | + if (!sw->active && !sw->callback.fn) { | |
| 1198 | + SWVoiceOut *temp = sw->entries.le_next; | |
| 1199 | + | |
| 1200 | +#ifdef DEBUG_PLIVE | |
| 1201 | + dolog ("Finishing with old voice\n"); | |
| 791 | 1202 | #endif |
| 1203 | + AUD_close_out (sw); | |
| 1204 | + sw = temp; | |
| 1205 | + if (sw) { | |
| 1206 | + goto again; | |
| 1207 | + } | |
| 1208 | + else { | |
| 1209 | + break; | |
| 1210 | + } | |
| 1211 | + } | |
| 1212 | + } | |
| 1213 | + | |
| 1214 | + if (sw->active) { | |
| 1215 | + free = audio_get_free (sw); | |
| 1216 | + if (free > 0) { | |
| 1217 | + sw->callback.fn (sw->callback.opaque, free); | |
| 1218 | + } | |
| 1219 | + } | |
| 1220 | + } | |
| 1221 | + } | |
| 1222 | +} | |
| 1223 | + | |
| 1224 | +static void audio_run_in (void) | |
| 1225 | +{ | |
| 1226 | + HWVoiceIn *hw = NULL; | |
| 1227 | + | |
| 1228 | + while ((hw = audio_pcm_hw_find_any_active_enabled_in (hw))) { | |
| 1229 | + SWVoiceIn *sw; | |
| 1230 | + int captured, min; | |
| 1231 | + | |
| 1232 | + captured = hw->pcm_ops->run_in (hw); | |
| 1233 | + | |
| 1234 | + min = audio_pcm_hw_find_min_in (hw); | |
| 1235 | + hw->total_samples_captured += captured - min; | |
| 1236 | + hw->ts_helper += captured; | |
| 1237 | + | |
| 1238 | + for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { | |
| 1239 | + sw->total_hw_samples_acquired -= min; | |
| 1240 | + | |
| 1241 | + if (sw->active) { | |
| 1242 | + int avail; | |
| 1243 | + | |
| 1244 | + avail = audio_get_avail (sw); | |
| 1245 | + if (avail > 0) { | |
| 1246 | + sw->callback.fn (sw->callback.opaque, avail); | |
| 1247 | + } | |
| 1248 | + } | |
| 1249 | + } | |
| 1250 | + } | |
| 1251 | +} | |
| 1252 | + | |
| 1253 | +static struct audio_option audio_options[] = { | |
| 1254 | + /* DAC */ | |
| 1255 | + {"DAC_FIXED_SETTINGS", AUD_OPT_BOOL, &audio_state.fixed_settings_out, | |
| 1256 | + "Use fixed settings for host DAC", NULL, 0}, | |
| 1257 | + | |
| 1258 | + {"DAC_FIXED_FREQ", AUD_OPT_INT, &audio_state.fixed_freq_out, | |
| 1259 | + "Frequency for fixed host DAC", NULL, 0}, | |
| 1260 | + | |
| 1261 | + {"DAC_FIXED_FMT", AUD_OPT_FMT, &audio_state.fixed_fmt_out, | |
| 1262 | + "Format for fixed host DAC", NULL, 0}, | |
| 1263 | + | |
| 1264 | + {"DAC_FIXED_CHANNELS", AUD_OPT_INT, &audio_state.fixed_channels_out, | |
| 1265 | + "Number of channels for fixed DAC (1 - mono, 2 - stereo)", NULL, 0}, | |
| 1266 | + | |
| 1267 | + {"DAC_VOICES", AUD_OPT_INT, &audio_state.nb_hw_voices_out, | |
| 1268 | + "Number of voices for DAC", NULL, 0}, | |
| 1269 | + | |
| 1270 | + /* ADC */ | |
| 1271 | + {"ADC_FIXED_SETTINGS", AUD_OPT_BOOL, &audio_state.fixed_settings_out, | |
| 1272 | + "Use fixed settings for host ADC", NULL, 0}, | |
| 1273 | + | |
| 1274 | + {"ADC_FIXED_FREQ", AUD_OPT_INT, &audio_state.fixed_freq_out, | |
| 1275 | + "Frequency for fixed ADC", NULL, 0}, | |
| 1276 | + | |
| 1277 | + {"ADC_FIXED_FMT", AUD_OPT_FMT, &audio_state.fixed_fmt_out, | |
| 1278 | + "Format for fixed ADC", NULL, 0}, | |
| 1279 | + | |
| 1280 | + {"ADC_FIXED_CHANNELS", AUD_OPT_INT, &audio_state.fixed_channels_in, | |
| 1281 | + "Number of channels for fixed ADC (1 - mono, 2 - stereo)", NULL, 0}, | |
| 1282 | + | |
| 1283 | + {"ADC_VOICES", AUD_OPT_INT, &audio_state.nb_hw_voices_out, | |
| 1284 | + "Number of voices for ADC", NULL, 0}, | |
| 1285 | + | |
| 1286 | + /* Misc */ | |
| 1287 | + {"TIMER_PERIOD", AUD_OPT_INT, &audio_state.period.usec, | |
| 1288 | + "Timer period in microseconds (0 - try lowest possible)", NULL, 0}, | |
| 1289 | + | |
| 1290 | + {"PLIVE", AUD_OPT_BOOL, &audio_state.plive, | |
| 1291 | + "(undocumented)", NULL, 0}, | |
| 1292 | + | |
| 1293 | + {NULL, 0, NULL, NULL, NULL, 0} | |
| 792 | 1294 | }; |
| 793 | 1295 | |
| 794 | -static int voice_init (struct audio_output_driver *drv) | |
| 1296 | +void AUD_help (void) | |
| 1297 | +{ | |
| 1298 | + size_t i; | |
| 1299 | + | |
| 1300 | + audio_process_options ("AUDIO", audio_options); | |
| 1301 | + for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) { | |
| 1302 | + struct audio_driver *d = drvtab[i]; | |
| 1303 | + if (d->options) { | |
| 1304 | + audio_process_options (d->name, d->options); | |
| 1305 | + } | |
| 1306 | + } | |
| 1307 | + | |
| 1308 | + printf ("Audio options:\n"); | |
| 1309 | + audio_print_options ("AUDIO", audio_options); | |
| 1310 | + printf ("\n"); | |
| 1311 | + | |
| 1312 | + printf ("Available drivers:\n"); | |
| 1313 | + | |
| 1314 | + for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) { | |
| 1315 | + struct audio_driver *d = drvtab[i]; | |
| 1316 | + | |
| 1317 | + printf ("Name: %s\n", d->name); | |
| 1318 | + printf ("Description: %s\n", d->descr); | |
| 1319 | + | |
| 1320 | + switch (d->max_voices_out) { | |
| 1321 | + case 0: | |
| 1322 | + printf ("Does not support DAC\n"); | |
| 1323 | + break; | |
| 1324 | + case 1: | |
| 1325 | + printf ("One DAC voice\n"); | |
| 1326 | + break; | |
| 1327 | + case INT_MAX: | |
| 1328 | + printf ("Theoretically supports many DAC voices\n"); | |
| 1329 | + break; | |
| 1330 | + default: | |
| 1331 | + printf ("Theoretically supports upto %d DAC voices\n", | |
| 1332 | + d->max_voices_out); | |
| 1333 | + break; | |
| 1334 | + } | |
| 1335 | + | |
| 1336 | + switch (d->max_voices_in) { | |
| 1337 | + case 0: | |
| 1338 | + printf ("Does not support ADC\n"); | |
| 1339 | + break; | |
| 1340 | + case 1: | |
| 1341 | + printf ("One ADC voice\n"); | |
| 1342 | + break; | |
| 1343 | + case INT_MAX: | |
| 1344 | + printf ("Theoretically supports many ADC voices\n"); | |
| 1345 | + break; | |
| 1346 | + default: | |
| 1347 | + printf ("Theoretically supports upto %d ADC voices\n", | |
| 1348 | + d->max_voices_in); | |
| 1349 | + break; | |
| 1350 | + } | |
| 1351 | + | |
| 1352 | + if (d->options) { | |
| 1353 | + printf ("Options:\n"); | |
| 1354 | + audio_print_options (d->name, d->options); | |
| 1355 | + } | |
| 1356 | + else { | |
| 1357 | + printf ("No options\n"); | |
| 1358 | + } | |
| 1359 | + printf ("\n"); | |
| 1360 | + } | |
| 1361 | + | |
| 1362 | + printf ( | |
| 1363 | + "Options are settable through environment variables.\n" | |
| 1364 | + "Example:\n" | |
| 1365 | +#ifdef _WIN32 | |
| 1366 | + " set QEMU_AUDIO_DRV=wav\n" | |
| 1367 | + " set QEMU_WAV_PATH=c:/tune.wav\n" | |
| 1368 | +#else | |
| 1369 | + " export QEMU_AUDIO_DRV=wav\n" | |
| 1370 | + " export QEMU_WAV_PATH=$HOME/tune.wav\n" | |
| 1371 | + "(for csh replace export with setenv in the above)\n" | |
| 1372 | +#endif | |
| 1373 | + " qemu ...\n\n" | |
| 1374 | + ); | |
| 1375 | +} | |
| 1376 | + | |
| 1377 | +void audio_timer (void *opaque) | |
| 1378 | +{ | |
| 1379 | + AudioState *s = opaque; | |
| 1380 | + | |
| 1381 | + audio_run_out (); | |
| 1382 | + audio_run_in (); | |
| 1383 | + | |
| 1384 | + qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + s->period.ticks); | |
| 1385 | +} | |
| 1386 | + | |
| 1387 | +static int audio_driver_init (struct audio_driver *drv) | |
| 795 | 1388 | { |
| 1389 | + if (drv->options) { | |
| 1390 | + audio_process_options (drv->name, drv->options); | |
| 1391 | + } | |
| 796 | 1392 | audio_state.opaque = drv->init (); |
| 1393 | + | |
| 797 | 1394 | if (audio_state.opaque) { |
| 798 | - if (audio_state.nb_hw_voices > drv->max_voices) { | |
| 799 | - dolog ("`%s' does not support %d multiple hardware channels\n" | |
| 800 | - "Resetting to %d\n", | |
| 801 | - drv->name, audio_state.nb_hw_voices, drv->max_voices); | |
| 802 | - audio_state.nb_hw_voices = drv->max_voices; | |
| 1395 | + int i; | |
| 1396 | + HWVoiceOut *hwo; | |
| 1397 | + HWVoiceIn *hwi; | |
| 1398 | + | |
| 1399 | + if (audio_state.nb_hw_voices_out > drv->max_voices_out) { | |
| 1400 | + if (!drv->max_voices_out) { | |
| 1401 | + dolog ("`%s' does not support DAC\n", drv->name); | |
| 1402 | + } | |
| 1403 | + else { | |
| 1404 | + dolog ( | |
| 1405 | + "`%s' does not support %d multiple DAC voicess\n" | |
| 1406 | + "Resetting to %d\n", | |
| 1407 | + drv->name, | |
| 1408 | + audio_state.nb_hw_voices_out, | |
| 1409 | + drv->max_voices_out | |
| 1410 | + ); | |
| 1411 | + } | |
| 1412 | + audio_state.nb_hw_voices_out = drv->max_voices_out; | |
| 803 | 1413 | } |
| 804 | - hw_voices = qemu_mallocz (audio_state.nb_hw_voices * drv->voice_size); | |
| 805 | - if (hw_voices) { | |
| 806 | - audio_state.drv = drv; | |
| 807 | - return 1; | |
| 1414 | + | |
| 1415 | + LIST_INIT (&hw_head_out); | |
| 1416 | + hwo = qemu_mallocz (audio_state.nb_hw_voices_out * drv->voice_size_out); | |
| 1417 | + if (!hwo) { | |
| 1418 | + dolog ( | |
| 1419 | + "Not enough memory for %d `%s' DAC voices (each %d bytes)\n", | |
| 1420 | + audio_state.nb_hw_voices_out, | |
| 1421 | + drv->name, | |
| 1422 | + drv->voice_size_out | |
| 1423 | + ); | |
| 1424 | + drv->fini (audio_state.opaque); | |
| 1425 | + return -1; | |
| 808 | 1426 | } |
| 809 | - else { | |
| 810 | - dolog ("Not enough memory for %d `%s' voices (each %d bytes)\n", | |
| 811 | - audio_state.nb_hw_voices, drv->name, drv->voice_size); | |
| 1427 | + | |
| 1428 | + for (i = 0; i < audio_state.nb_hw_voices_out; ++i) { | |
| 1429 | + LIST_INSERT_HEAD (&hw_head_out, hwo, entries); | |
| 1430 | + hwo = advance (hwo, drv->voice_size_out); | |
| 1431 | + } | |
| 1432 | + | |
| 1433 | + if (!drv->voice_size_in && drv->max_voices_in) { | |
| 1434 | + ldebug ("warning: No ADC voice size defined for `%s'\n", | |
| 1435 | + drv->name); | |
| 1436 | + drv->max_voices_in = 0; | |
| 1437 | + } | |
| 1438 | + | |
| 1439 | + if (!drv->voice_size_out && drv->max_voices_out) { | |
| 1440 | + ldebug ("warning: No DAC voice size defined for `%s'\n", | |
| 1441 | + drv->name); | |
| 1442 | + } | |
| 1443 | + | |
| 1444 | + if (drv->voice_size_in && !drv->max_voices_in) { | |
| 1445 | + ldebug ("warning: ADC voice size is %d for ADC less driver `%s'\n", | |
| 1446 | + drv->voice_size_out, drv->name); | |
| 1447 | + } | |
| 1448 | + | |
| 1449 | + if (drv->voice_size_out && !drv->max_voices_out) { | |
| 1450 | + ldebug ("warning: DAC voice size is %d for DAC less driver `%s'\n", | |
| 1451 | + drv->voice_size_in, drv->name); | |
| 1452 | + } | |
| 1453 | + | |
| 1454 | + if (audio_state.nb_hw_voices_in > drv->max_voices_in) { | |
| 1455 | + if (!drv->max_voices_in) { | |
| 1456 | + ldebug ("`%s' does not support ADC\n", drv->name); | |
| 1457 | + } | |
| 1458 | + else { | |
| 1459 | + dolog ( | |
| 1460 | + "`%s' does not support %d multiple ADC voices\n" | |
| 1461 | + "Resetting to %d\n", | |
| 1462 | + drv->name, | |
| 1463 | + audio_state.nb_hw_voices_in, | |
| 1464 | + drv->max_voices_in | |
| 1465 | + ); | |
| 1466 | + } | |
| 1467 | + audio_state.nb_hw_voices_in = drv->max_voices_in; | |
| 1468 | + } | |
| 1469 | + | |
| 1470 | + LIST_INIT (&hw_head_in); | |
| 1471 | + hwi = qemu_mallocz (audio_state.nb_hw_voices_in * drv->voice_size_in); | |
| 1472 | + if (!hwi) { | |
| 1473 | + dolog ( | |
| 1474 | + "Not enough memory for %d `%s' ADC voices (each %d bytes)\n", | |
| 1475 | + audio_state.nb_hw_voices_in, | |
| 1476 | + drv->name, | |
| 1477 | + drv->voice_size_in | |
| 1478 | + ); | |
| 1479 | + qemu_free (hwo); | |
| 812 | 1480 | drv->fini (audio_state.opaque); |
| 813 | - return 0; | |
| 1481 | + return -1; | |
| 1482 | + } | |
| 1483 | + | |
| 1484 | + for (i = 0; i < audio_state.nb_hw_voices_in; ++i) { | |
| 1485 | + LIST_INSERT_HEAD (&hw_head_in, hwi, entries); | |
| 1486 | + hwi = advance (hwi, drv->voice_size_in); | |
| 814 | 1487 | } |
| 1488 | + | |
| 1489 | + audio_state.drv = drv; | |
| 1490 | + return 0; | |
| 815 | 1491 | } |
| 816 | 1492 | else { |
| 817 | - dolog ("Could not init `%s' audio\n", drv->name); | |
| 818 | - return 0; | |
| 1493 | + dolog ("Could not init `%s' audio driver\n", drv->name); | |
| 1494 | + return -1; | |
| 819 | 1495 | } |
| 820 | 1496 | } |
| 821 | 1497 | |
| 822 | 1498 | static void audio_vm_stop_handler (void *opaque, int reason) |
| 823 | 1499 | { |
| 824 | - HWVoice *hw = NULL; | |
| 1500 | + HWVoiceOut *hwo = NULL; | |
| 1501 | + HWVoiceIn *hwi = NULL; | |
| 1502 | + int op = reason ? VOICE_ENABLE : VOICE_DISABLE; | |
| 1503 | + | |
| 1504 | + (void) opaque; | |
| 1505 | + while ((hwo = audio_pcm_hw_find_any_out (hwo))) { | |
| 1506 | + if (!hwo->pcm_ops) { | |
| 1507 | + continue; | |
| 1508 | + } | |
| 1509 | + | |
| 1510 | + if (hwo->enabled != reason) { | |
| 1511 | + hwo->pcm_ops->ctl_out (hwo, op); | |
| 1512 | + } | |
| 1513 | + } | |
| 825 | 1514 | |
| 826 | - while ((hw = pcm_hw_find_any (hw))) { | |
| 827 | - if (!hw->pcm_ops) | |
| 1515 | + while ((hwi = audio_pcm_hw_find_any_in (hwi))) { | |
| 1516 | + if (!hwi->pcm_ops) { | |
| 828 | 1517 | continue; |
| 1518 | + } | |
| 829 | 1519 | |
| 830 | - hw->pcm_ops->ctl (hw, reason ? VOICE_ENABLE : VOICE_DISABLE); | |
| 1520 | + if (hwi->enabled != reason) { | |
| 1521 | + hwi->pcm_ops->ctl_in (hwi, op); | |
| 1522 | + } | |
| 831 | 1523 | } |
| 832 | 1524 | } |
| 833 | 1525 | |
| 834 | 1526 | static void audio_atexit (void) |
| 835 | 1527 | { |
| 836 | - HWVoice *hw = NULL; | |
| 1528 | + HWVoiceOut *hwo = NULL; | |
| 1529 | + HWVoiceIn *hwi = NULL; | |
| 1530 | + | |
| 1531 | + while ((hwo = audio_pcm_hw_find_any_out (hwo))) { | |
| 1532 | + if (!hwo->pcm_ops) { | |
| 1533 | + continue; | |
| 1534 | + } | |
| 1535 | + | |
| 1536 | + if (hwo->enabled) { | |
| 1537 | + hwo->pcm_ops->ctl_out (hwo, VOICE_DISABLE); | |
| 1538 | + } | |
| 1539 | + hwo->pcm_ops->fini_out (hwo); | |
| 1540 | + } | |
| 837 | 1541 | |
| 838 | - while ((hw = pcm_hw_find_any (hw))) { | |
| 839 | - if (!hw->pcm_ops) | |
| 1542 | + while ((hwi = audio_pcm_hw_find_any_in (hwi))) { | |
| 1543 | + if (!hwi->pcm_ops) { | |
| 840 | 1544 | continue; |
| 1545 | + } | |
| 841 | 1546 | |
| 842 | - hw->pcm_ops->ctl (hw, VOICE_DISABLE); | |
| 843 | - hw->pcm_ops->fini (hw); | |
| 1547 | + if (hwi->enabled) { | |
| 1548 | + hwi->pcm_ops->ctl_in (hwi, VOICE_DISABLE); | |
| 1549 | + } | |
| 1550 | + hwi->pcm_ops->fini_in (hwi); | |
| 844 | 1551 | } |
| 845 | 1552 | audio_state.drv->fini (audio_state.opaque); |
| 846 | 1553 | } |
| 847 | 1554 | |
| 848 | 1555 | static void audio_save (QEMUFile *f, void *opaque) |
| 849 | 1556 | { |
| 1557 | + (void) f; | |
| 1558 | + (void) opaque; | |
| 850 | 1559 | } |
| 851 | 1560 | |
| 852 | 1561 | static int audio_load (QEMUFile *f, void *opaque, int version_id) |
| 853 | 1562 | { |
| 854 | - if (version_id != 1) | |
| 1563 | + (void) f; | |
| 1564 | + (void) opaque; | |
| 1565 | + | |
| 1566 | + if (version_id != 1) { | |
| 855 | 1567 | return -EINVAL; |
| 1568 | + } | |
| 856 | 1569 | |
| 857 | 1570 | return 0; |
| 858 | 1571 | } |
| 859 | 1572 | |
| 860 | 1573 | void AUD_init (void) |
| 861 | 1574 | { |
| 862 | - int i; | |
| 1575 | + size_t i; | |
| 863 | 1576 | int done = 0; |
| 864 | 1577 | const char *drvname; |
| 1578 | + AudioState *s = &audio_state; | |
| 1579 | + | |
| 1580 | + audio_process_options ("AUDIO", audio_options); | |
| 1581 | + | |
| 1582 | + if (s->nb_hw_voices_out <= 0) { | |
| 1583 | + dolog ("Bogus number of DAC voices %d\n", | |
| 1584 | + s->nb_hw_voices_out); | |
| 1585 | + s->nb_hw_voices_out = 1; | |
| 1586 | + } | |
| 1587 | + | |
| 1588 | + if (s->nb_hw_voices_in <= 0) { | |
| 1589 | + dolog ("Bogus number of ADC voices %d\n", | |
| 1590 | + s->nb_hw_voices_in); | |
| 1591 | + s->nb_hw_voices_in = 1; | |
| 1592 | + } | |
| 865 | 1593 | |
| 866 | - audio_state.fixed_format = | |
| 867 | - !!audio_get_conf_int (QC_FIXED_FORMAT, audio_state.fixed_format); | |
| 868 | - audio_state.fixed_freq = | |
| 869 | - audio_get_conf_int (QC_FIXED_FREQ, audio_state.fixed_freq); | |
| 870 | - audio_state.nb_hw_voices = | |
| 871 | - audio_get_conf_int (QC_VOICES, audio_state.nb_hw_voices); | |
| 1594 | + { | |
| 1595 | + int def; | |
| 1596 | + drvname = audio_get_conf_str ("QEMU_AUDIO_DRV", NULL, &def); | |
| 1597 | + } | |
| 872 | 1598 | |
| 873 | - if (audio_state.nb_hw_voices <= 0) { | |
| 874 | - dolog ("Bogus number of voices %d, resetting to 1\n", | |
| 875 | - audio_state.nb_hw_voices); | |
| 1599 | + s->ts = qemu_new_timer (vm_clock, audio_timer, s); | |
| 1600 | + if (!s->ts) { | |
| 1601 | + dolog ("Can not create audio timer\n"); | |
| 1602 | + return; | |
| 876 | 1603 | } |
| 877 | 1604 | |
| 878 | - drvname = audio_get_conf_str (QC_AUDIO_DRV, NULL); | |
| 879 | 1605 | if (drvname) { |
| 880 | 1606 | int found = 0; |
| 1607 | + | |
| 881 | 1608 | for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) { |
| 882 | 1609 | if (!strcmp (drvname, drvtab[i]->name)) { |
| 883 | - done = voice_init (drvtab[i]); | |
| 1610 | + done = !audio_driver_init (drvtab[i]); | |
| 884 | 1611 | found = 1; |
| 885 | 1612 | break; |
| 886 | 1613 | } |
| 887 | 1614 | } |
| 1615 | + | |
| 888 | 1616 | if (!found) { |
| 889 | 1617 | dolog ("Unknown audio driver `%s'\n", drvname); |
| 1618 | + dolog ("Run with -audio-help to list available drivers\n"); | |
| 890 | 1619 | } |
| 891 | 1620 | } |
| 892 | 1621 | |
| ... | ... | @@ -895,17 +1624,32 @@ void AUD_init (void) |
| 895 | 1624 | |
| 896 | 1625 | if (!done) { |
| 897 | 1626 | for (i = 0; !done && i < sizeof (drvtab) / sizeof (drvtab[0]); i++) { |
| 898 | - if (drvtab[i]->can_be_default) | |
| 899 | - done = voice_init (drvtab[i]); | |
| 1627 | + if (drvtab[i]->can_be_default) { | |
| 1628 | + done = !audio_driver_init (drvtab[i]); | |
| 1629 | + } | |
| 900 | 1630 | } |
| 901 | 1631 | } |
| 902 | 1632 | |
| 903 | - audio_state.ticks_threshold = ticks_per_sec / 50; | |
| 904 | - audio_state.freq_threshold = 100; | |
| 905 | - | |
| 906 | 1633 | register_savevm ("audio", 0, 1, audio_save, audio_load, NULL); |
| 907 | 1634 | if (!done) { |
| 908 | - dolog ("Can not initialize audio subsystem\n"); | |
| 909 | - voice_init (&no_output_driver); | |
| 1635 | + if (audio_driver_init (&no_audio_driver)) { | |
| 1636 | + dolog ("Can not initialize audio subsystem\n"); | |
| 1637 | + } | |
| 1638 | + else { | |
| 1639 | + dolog ("warning: using timer based audio emulation\n"); | |
| 1640 | + } | |
| 910 | 1641 | } |
| 1642 | + | |
| 1643 | + if (s->period.usec <= 0) { | |
| 1644 | + if (s->period.usec < 0) { | |
| 1645 | + dolog ("warning: timer period is negative - %d treating as zero\n", | |
| 1646 | + s->period.usec); | |
| 1647 | + } | |
| 1648 | + s->period.ticks = 1; | |
| 1649 | + } | |
| 1650 | + else { | |
| 1651 | + s->period.ticks = (ticks_per_sec * s->period.usec) / 1000000; | |
| 1652 | + } | |
| 1653 | + | |
| 1654 | + qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + s->period.ticks); | |
| 911 | 1655 | } | ... | ... |
audio/audio.h
| 1 | 1 | /* |
| 2 | 2 | * QEMU Audio subsystem header |
| 3 | - * | |
| 4 | - * Copyright (c) 2003-2004 Vassili Karpov (malc) | |
| 5 | - * | |
| 3 | + * | |
| 4 | + * Copyright (c) 2003-2005 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 |
| ... | ... | @@ -24,7 +24,7 @@ |
| 24 | 24 | #ifndef QEMU_AUDIO_H |
| 25 | 25 | #define QEMU_AUDIO_H |
| 26 | 26 | |
| 27 | -#include "mixeng.h" | |
| 27 | +typedef void (*audio_callback_fn_t) (void *opaque, int avail); | |
| 28 | 28 | |
| 29 | 29 | typedef enum { |
| 30 | 30 | AUD_FMT_U8, |
| ... | ... | @@ -33,22 +33,60 @@ typedef enum { |
| 33 | 33 | AUD_FMT_S16 |
| 34 | 34 | } audfmt_e; |
| 35 | 35 | |
| 36 | -typedef struct SWVoice SWVoice; | |
| 36 | +typedef struct SWVoiceOut SWVoiceOut; | |
| 37 | +typedef struct SWVoiceIn SWVoiceIn; | |
| 38 | + | |
| 39 | +typedef struct QEMUAudioTimeStamp { | |
| 40 | + uint64_t old_ts; | |
| 41 | +} QEMUAudioTimeStamp; | |
| 42 | + | |
| 43 | +void AUD_vlog (const char *cap, const char *fmt, va_list ap); | |
| 44 | +void AUD_log (const char *cap, const char *fmt, ...) | |
| 45 | +#ifdef __GNUC__ | |
| 46 | + __attribute__ ((__format__ (__printf__, 2, 3))) | |
| 47 | +#endif | |
| 48 | + ; | |
| 37 | 49 | |
| 38 | -SWVoice * AUD_open (SWVoice *sw, const char *name, int freq, | |
| 39 | - int nchannels, audfmt_e fmt); | |
| 40 | -void AUD_init (void); | |
| 41 | -void AUD_log (const char *cap, const char *fmt, ...) | |
| 42 | - __attribute__ ((__format__ (__printf__, 2, 3)));; | |
| 43 | -void AUD_close (SWVoice *sw); | |
| 44 | -int AUD_write (SWVoice *sw, void *pcm_buf, int size); | |
| 45 | -void AUD_adjust (SWVoice *sw, int leftover); | |
| 46 | -void AUD_reset (SWVoice *sw); | |
| 47 | -int AUD_get_free (SWVoice *sw); | |
| 48 | -int AUD_get_buffer_size (SWVoice *sw); | |
| 49 | -void AUD_run (void); | |
| 50 | -void AUD_enable (SWVoice *sw, int on); | |
| 51 | -int AUD_calc_elapsed (SWVoice *sw); | |
| 50 | +void AUD_init (void); | |
| 51 | +void AUD_help (void); | |
| 52 | + | |
| 53 | +SWVoiceOut *AUD_open_out ( | |
| 54 | + SWVoiceOut *sw, | |
| 55 | + const char *name, | |
| 56 | + void *callback_opaque, | |
| 57 | + audio_callback_fn_t callback_fn, | |
| 58 | + int freq, | |
| 59 | + int nchannels, | |
| 60 | + audfmt_e fmt | |
| 61 | + ); | |
| 62 | +void AUD_close_out (SWVoiceOut *sw); | |
| 63 | +int AUD_write (SWVoiceOut *sw, void *pcm_buf, int size); | |
| 64 | +int AUD_get_buffer_size_out (SWVoiceOut *sw); | |
| 65 | +void AUD_set_active_out (SWVoiceOut *sw, int on); | |
| 66 | +int AUD_is_active_out (SWVoiceOut *sw); | |
| 67 | +void AUD_init_time_stamp_out (SWVoiceOut *sw, | |
| 68 | + QEMUAudioTimeStamp *ts); | |
| 69 | +uint64_t AUD_time_stamp_get_elapsed_usec_out (SWVoiceOut *sw, | |
| 70 | + QEMUAudioTimeStamp *ts); | |
| 71 | + | |
| 72 | +SWVoiceIn *AUD_open_in ( | |
| 73 | + SWVoiceIn *sw, | |
| 74 | + const char *name, | |
| 75 | + void *callback_opaque, | |
| 76 | + audio_callback_fn_t callback_fn, | |
| 77 | + int freq, | |
| 78 | + int nchannels, | |
| 79 | + audfmt_e fmt | |
| 80 | + ); | |
| 81 | +void AUD_close_in (SWVoiceIn *sw); | |
| 82 | +int AUD_read (SWVoiceIn *sw, void *pcm_buf, int size); | |
| 83 | +void AUD_adjust_in (SWVoiceIn *sw, int leftover); | |
| 84 | +void AUD_set_active_in (SWVoiceIn *sw, int on); | |
| 85 | +int AUD_is_active_in (SWVoiceIn *sw); | |
| 86 | +void AUD_init_time_stamp_in (SWVoiceIn *sw, | |
| 87 | + QEMUAudioTimeStamp *ts); | |
| 88 | +uint64_t AUD_time_stamp_get_elapsed_usec_in (SWVoiceIn *sw, | |
| 89 | + QEMUAudioTimeStamp *ts); | |
| 52 | 90 | |
| 53 | 91 | static inline void *advance (void *p, int incr) |
| 54 | 92 | { |
| ... | ... | @@ -59,7 +97,21 @@ static inline void *advance (void *p, int incr) |
| 59 | 97 | uint32_t popcount (uint32_t u); |
| 60 | 98 | inline uint32_t lsbindex (uint32_t u); |
| 61 | 99 | |
| 100 | +#ifdef __GNUC__ | |
| 101 | +#define audio_MIN(a, b) ( __extension__ ({ \ | |
| 102 | + __typeof (a) ta = a; \ | |
| 103 | + __typeof (b) tb = b; \ | |
| 104 | + ((ta)>(tb)?(tb):(ta)); \ | |
| 105 | +})) | |
| 106 | + | |
| 107 | +#define audio_MAX(a, b) ( __extension__ ({ \ | |
| 108 | + __typeof (a) ta = a; \ | |
| 109 | + __typeof (b) tb = b; \ | |
| 110 | + ((ta)<(tb)?(tb):(ta)); \ | |
| 111 | +})) | |
| 112 | +#else | |
| 62 | 113 | #define audio_MIN(a, b) ((a)>(b)?(b):(a)) |
| 63 | 114 | #define audio_MAX(a, b) ((a)<(b)?(b):(a)) |
| 115 | +#endif | |
| 64 | 116 | |
| 65 | 117 | #endif /* audio.h */ | ... | ... |
audio/audio_int.h
| 1 | 1 | /* |
| 2 | 2 | * QEMU Audio subsystem header |
| 3 | - * | |
| 4 | - * Copyright (c) 2003-2004 Vassili Karpov (malc) | |
| 5 | - * | |
| 3 | + * | |
| 4 | + * Copyright (c) 2003-2005 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 |
| ... | ... | @@ -24,140 +24,266 @@ |
| 24 | 24 | #ifndef QEMU_AUDIO_INT_H |
| 25 | 25 | #define QEMU_AUDIO_INT_H |
| 26 | 26 | |
| 27 | -#include "vl.h" | |
| 27 | +#include "sys-queue.h" | |
| 28 | + | |
| 29 | +#ifdef CONFIG_COREAUDIO | |
| 30 | +#define FLOAT_MIXENG | |
| 31 | +/* #define RECIPROCAL */ | |
| 32 | +#endif | |
| 33 | +#include "mixeng.h" | |
| 34 | + | |
| 35 | +int audio_bug (const char *funcname, int cond); | |
| 36 | + | |
| 37 | +struct audio_pcm_ops; | |
| 38 | + | |
| 39 | +typedef enum { | |
| 40 | + AUD_OPT_INT, | |
| 41 | + AUD_OPT_FMT, | |
| 42 | + AUD_OPT_STR, | |
| 43 | + AUD_OPT_BOOL | |
| 44 | +} audio_option_tag_e; | |
| 45 | + | |
| 46 | +struct audio_option { | |
| 47 | + const char *name; | |
| 48 | + audio_option_tag_e tag; | |
| 49 | + void *valp; | |
| 50 | + const char *descr; | |
| 51 | + int *overridenp; | |
| 52 | + int overriden; | |
| 53 | +}; | |
| 54 | + | |
| 55 | +struct audio_callback { | |
| 56 | + void *opaque; | |
| 57 | + audio_callback_fn_t fn; | |
| 58 | +}; | |
| 28 | 59 | |
| 29 | -struct pcm_ops; | |
| 60 | +struct audio_pcm_info { | |
| 61 | + int bits; | |
| 62 | + int sign; | |
| 63 | + int freq; | |
| 64 | + int nchannels; | |
| 65 | + int align; | |
| 66 | + int shift; | |
| 67 | + int bytes_per_second; | |
| 68 | + int swap_endian; | |
| 69 | +}; | |
| 30 | 70 | |
| 31 | -typedef struct HWVoice { | |
| 71 | +typedef struct HWVoiceOut { | |
| 32 | 72 | int active; |
| 33 | 73 | int enabled; |
| 34 | 74 | int pending_disable; |
| 35 | 75 | int valid; |
| 36 | - int freq; | |
| 76 | + struct audio_pcm_info info; | |
| 37 | 77 | |
| 38 | 78 | f_sample *clip; |
| 39 | - audfmt_e fmt; | |
| 40 | - int nchannels; | |
| 41 | - | |
| 42 | - int align; | |
| 43 | - int shift; | |
| 44 | 79 | |
| 45 | 80 | int rpos; |
| 46 | 81 | int bufsize; |
| 82 | + uint64_t ts_helper; | |
| 47 | 83 | |
| 48 | - int bytes_per_second; | |
| 49 | 84 | st_sample_t *mix_buf; |
| 50 | 85 | |
| 51 | 86 | int samples; |
| 52 | - int64_t old_ticks; | |
| 53 | - int nb_voices; | |
| 54 | - struct SWVoice **pvoice; | |
| 55 | - struct pcm_ops *pcm_ops; | |
| 56 | -} HWVoice; | |
| 87 | + LIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head; | |
| 88 | + struct audio_pcm_ops *pcm_ops; | |
| 89 | + LIST_ENTRY (HWVoiceOut) entries; | |
| 90 | +} HWVoiceOut; | |
| 57 | 91 | |
| 58 | -extern struct pcm_ops no_pcm_ops; | |
| 59 | -extern struct audio_output_driver no_output_driver; | |
| 92 | +typedef struct HWVoiceIn { | |
| 93 | + int enabled; | |
| 94 | + int active; | |
| 95 | + struct audio_pcm_info info; | |
| 96 | + | |
| 97 | + t_sample *conv; | |
| 60 | 98 | |
| 61 | -extern struct pcm_ops oss_pcm_ops; | |
| 62 | -extern struct audio_output_driver oss_output_driver; | |
| 99 | + int wpos; | |
| 100 | + int bufsize; | |
| 101 | + int total_samples_captured; | |
| 102 | + uint64_t ts_helper; | |
| 63 | 103 | |
| 64 | -extern struct pcm_ops sdl_pcm_ops; | |
| 65 | -extern struct audio_output_driver sdl_output_driver; | |
| 104 | + st_sample_t *conv_buf; | |
| 66 | 105 | |
| 67 | -extern struct pcm_ops wav_pcm_ops; | |
| 68 | -extern struct audio_output_driver wav_output_driver; | |
| 106 | + int samples; | |
| 107 | + LIST_HEAD (sw_in_listhead, SWVoiceIn) sw_head; | |
| 108 | + struct audio_pcm_ops *pcm_ops; | |
| 109 | + LIST_ENTRY (HWVoiceIn) entries; | |
| 110 | +} HWVoiceIn; | |
| 69 | 111 | |
| 70 | -extern struct pcm_ops fmod_pcm_ops; | |
| 71 | -extern struct audio_output_driver fmod_output_driver; | |
| 112 | +extern struct audio_driver no_audio_driver; | |
| 113 | +extern struct audio_driver oss_audio_driver; | |
| 114 | +extern struct audio_driver sdl_audio_driver; | |
| 115 | +extern struct audio_driver wav_audio_driver; | |
| 116 | +extern struct audio_driver fmod_audio_driver; | |
| 117 | +extern struct audio_driver alsa_audio_driver; | |
| 118 | +extern struct audio_driver coreaudio_audio_driver; | |
| 119 | +extern struct audio_driver dsound_audio_driver; | |
| 120 | +extern volume_t nominal_volume; | |
| 72 | 121 | |
| 73 | -struct audio_output_driver { | |
| 122 | +struct audio_driver { | |
| 74 | 123 | const char *name; |
| 124 | + const char *descr; | |
| 125 | + struct audio_option *options; | |
| 75 | 126 | void *(*init) (void); |
| 76 | 127 | void (*fini) (void *); |
| 77 | - struct pcm_ops *pcm_ops; | |
| 128 | + struct audio_pcm_ops *pcm_ops; | |
| 78 | 129 | int can_be_default; |
| 79 | - int max_voices; | |
| 80 | - int voice_size; | |
| 130 | + int max_voices_out; | |
| 131 | + int max_voices_in; | |
| 132 | + int voice_size_out; | |
| 133 | + int voice_size_in; | |
| 81 | 134 | }; |
| 82 | 135 | |
| 83 | 136 | typedef struct AudioState { |
| 84 | - int fixed_format; | |
| 85 | - int fixed_freq; | |
| 86 | - int fixed_channels; | |
| 87 | - int fixed_fmt; | |
| 88 | - int nb_hw_voices; | |
| 89 | - int64_t ticks_threshold; | |
| 90 | - int freq_threshold; | |
| 137 | + int fixed_settings_out; | |
| 138 | + int fixed_freq_out; | |
| 139 | + int fixed_channels_out; | |
| 140 | + int fixed_fmt_out; | |
| 141 | + int nb_hw_voices_out; | |
| 142 | + int greedy_out; | |
| 143 | + | |
| 144 | + int fixed_settings_in; | |
| 145 | + int fixed_freq_in; | |
| 146 | + int fixed_channels_in; | |
| 147 | + int fixed_fmt_in; | |
| 148 | + int nb_hw_voices_in; | |
| 149 | + int greedy_in; | |
| 150 | + | |
| 91 | 151 | void *opaque; |
| 92 | - struct audio_output_driver *drv; | |
| 93 | -} AudioState; | |
| 94 | -extern AudioState audio_state; | |
| 152 | + struct audio_driver *drv; | |
| 95 | 153 | |
| 96 | -struct SWVoice { | |
| 97 | - int freq; | |
| 98 | - audfmt_e fmt; | |
| 99 | - int nchannels; | |
| 154 | + QEMUTimer *ts; | |
| 155 | + union { | |
| 156 | + int usec; | |
| 157 | + int64_t ticks; | |
| 158 | + } period; | |
| 100 | 159 | |
| 101 | - int shift; | |
| 102 | - int align; | |
| 160 | + int plive; | |
| 161 | +} AudioState; | |
| 162 | +extern AudioState audio_state; | |
| 103 | 163 | |
| 164 | +struct SWVoiceOut { | |
| 165 | + struct audio_pcm_info info; | |
| 104 | 166 | t_sample *conv; |
| 105 | - | |
| 106 | - int left; | |
| 107 | - int pos; | |
| 108 | - int bytes_per_second; | |
| 109 | 167 | int64_t ratio; |
| 110 | 168 | st_sample_t *buf; |
| 111 | 169 | void *rate; |
| 170 | + int total_hw_samples_mixed; | |
| 171 | + int active; | |
| 172 | + int empty; | |
| 173 | + HWVoiceOut *hw; | |
| 174 | + char *name; | |
| 175 | + volume_t vol; | |
| 176 | + struct audio_callback callback; | |
| 177 | + LIST_ENTRY (SWVoiceOut) entries; | |
| 178 | +}; | |
| 112 | 179 | |
| 113 | - int wpos; | |
| 114 | - int live; | |
| 180 | +struct SWVoiceIn { | |
| 115 | 181 | int active; |
| 116 | - int64_t old_ticks; | |
| 117 | - HWVoice *hw; | |
| 182 | + struct audio_pcm_info info; | |
| 183 | + int64_t ratio; | |
| 184 | + void *rate; | |
| 185 | + int total_hw_samples_acquired; | |
| 186 | + st_sample_t *conv_buf; | |
| 187 | + f_sample *clip; | |
| 188 | + HWVoiceIn *hw; | |
| 118 | 189 | char *name; |
| 190 | + volume_t vol; | |
| 191 | + struct audio_callback callback; | |
| 192 | + LIST_ENTRY (SWVoiceIn) entries; | |
| 119 | 193 | }; |
| 120 | 194 | |
| 121 | -struct pcm_ops { | |
| 122 | - int (*init) (HWVoice *hw, int freq, int nchannels, audfmt_e fmt); | |
| 123 | - void (*fini) (HWVoice *hw); | |
| 124 | - void (*run) (HWVoice *hw); | |
| 125 | - int (*write) (SWVoice *sw, void *buf, int size); | |
| 126 | - int (*ctl) (HWVoice *hw, int cmd, ...); | |
| 195 | +struct audio_pcm_ops { | |
| 196 | + int (*init_out)(HWVoiceOut *hw, int freq, int nchannels, audfmt_e fmt); | |
| 197 | + void (*fini_out)(HWVoiceOut *hw); | |
| 198 | + int (*run_out) (HWVoiceOut *hw); | |
| 199 | + int (*write) (SWVoiceOut *sw, void *buf, int size); | |
| 200 | + int (*ctl_out) (HWVoiceOut *hw, int cmd, ...); | |
| 201 | + | |
| 202 | + int (*init_in) (HWVoiceIn *hw, int freq, int nchannels, audfmt_e fmt); | |
| 203 | + void (*fini_in) (HWVoiceIn *hw); | |
| 204 | + int (*run_in) (HWVoiceIn *hw); | |
| 205 | + int (*read) (SWVoiceIn *sw, void *buf, int size); | |
| 206 | + int (*ctl_in) (HWVoiceIn *hw, int cmd, ...); | |
| 127 | 207 | }; |
| 128 | 208 | |
| 129 | -void pcm_sw_free_resources (SWVoice *sw); | |
| 130 | -int pcm_sw_alloc_resources (SWVoice *sw); | |
| 131 | -void pcm_sw_fini (SWVoice *sw); | |
| 132 | -int pcm_sw_init (SWVoice *sw, HWVoice *hw, int freq, | |
| 133 | - int nchannels, audfmt_e fmt); | |
| 134 | - | |
| 135 | -void pcm_hw_clear (HWVoice *hw, void *buf, int len); | |
| 136 | -HWVoice * pcm_hw_find_any (HWVoice *hw); | |
| 137 | -HWVoice * pcm_hw_find_any_active (HWVoice *hw); | |
| 138 | -HWVoice * pcm_hw_find_any_passive (HWVoice *hw); | |
| 139 | -HWVoice * pcm_hw_find_specific (HWVoice *hw, int freq, | |
| 140 | - int nchannels, audfmt_e fmt); | |
| 141 | -HWVoice * pcm_hw_add (int freq, int nchannels, audfmt_e fmt); | |
| 142 | -int pcm_hw_add_sw (HWVoice *hw, SWVoice *sw); | |
| 143 | -int pcm_hw_del_sw (HWVoice *hw, SWVoice *sw); | |
| 144 | -SWVoice * pcm_create_voice_pair (int freq, int nchannels, audfmt_e fmt); | |
| 145 | - | |
| 146 | -void pcm_hw_free_resources (HWVoice *hw); | |
| 147 | -int pcm_hw_alloc_resources (HWVoice *hw); | |
| 148 | -void pcm_hw_fini (HWVoice *hw); | |
| 149 | -void pcm_hw_gc (HWVoice *hw); | |
| 150 | -int pcm_hw_get_live (HWVoice *hw); | |
| 151 | -int pcm_hw_get_live2 (HWVoice *hw, int *nb_active); | |
| 152 | -void pcm_hw_dec_live (HWVoice *hw, int decr); | |
| 153 | -int pcm_hw_write (SWVoice *sw, void *buf, int len); | |
| 154 | - | |
| 155 | -int audio_get_conf_int (const char *key, int defval); | |
| 156 | -const char *audio_get_conf_str (const char *key, const char *defval); | |
| 157 | - | |
| 158 | -struct audio_output_driver; | |
| 209 | +void audio_pcm_init_info (struct audio_pcm_info *info, int freq, | |
| 210 | + int nchannels, audfmt_e fmt, int swap_endian); | |
| 211 | +void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len); | |
| 212 | + | |
| 213 | +int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int len); | |
| 214 | +int audio_pcm_hw_get_live_in (HWVoiceIn *hw); | |
| 215 | + | |
| 216 | +int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int len); | |
| 217 | +int audio_pcm_hw_get_live_out (HWVoiceOut *hw); | |
| 218 | +int audio_pcm_hw_get_live_out2 (HWVoiceOut *hw, int *nb_live); | |
| 159 | 219 | |
| 160 | 220 | #define VOICE_ENABLE 1 |
| 161 | 221 | #define VOICE_DISABLE 2 |
| 162 | 222 | |
| 223 | +static inline int audio_ring_dist (int dst, int src, int len) | |
| 224 | +{ | |
| 225 | + return (dst >= src) ? (dst - src) : (len - src + dst); | |
| 226 | +} | |
| 227 | + | |
| 228 | +static inline int audio_need_to_swap_endian (int endianness) | |
| 229 | +{ | |
| 230 | +#ifdef WORDS_BIGENDIAN | |
| 231 | + return endianness != 1; | |
| 232 | +#else | |
| 233 | + return endianness != 0; | |
| 234 | +#endif | |
| 235 | +} | |
| 236 | + | |
| 237 | +#if defined __GNUC__ | |
| 238 | +#define GCC_ATTR __attribute__ ((__unused__, __format__ (__printf__, 1, 2))) | |
| 239 | +#define INIT_FIELD(f) . f | |
| 240 | +#define GCC_FMT_ATTR(n, m) __attribute__ ((__format__ (printf, n, m))) | |
| 241 | +#else | |
| 242 | +#define GCC_ATTR /**/ | |
| 243 | +#define INIT_FIELD(f) /**/ | |
| 244 | +#define GCC_FMT_ATTR(n, m) | |
| 245 | +#endif | |
| 246 | + | |
| 247 | +static void GCC_ATTR dolog (const char *fmt, ...) | |
| 248 | +{ | |
| 249 | + va_list ap; | |
| 250 | + | |
| 251 | + va_start (ap, fmt); | |
| 252 | + AUD_vlog (AUDIO_CAP, fmt, ap); | |
| 253 | + va_end (ap); | |
| 254 | +} | |
| 255 | + | |
| 256 | +#ifdef DEBUG | |
| 257 | +static void GCC_ATTR ldebug (const char *fmt, ...) | |
| 258 | +{ | |
| 259 | + va_list ap; | |
| 260 | + | |
| 261 | + va_start (ap, fmt); | |
| 262 | + AUD_vlog (AUDIO_CAP, fmt, ap); | |
| 263 | + va_end (ap); | |
| 264 | +} | |
| 265 | +#else | |
| 266 | +#if defined NDEBUG && defined __GNUC__ | |
| 267 | +#define ldebug(...) | |
| 268 | +#elif defined NDEBUG && defined _MSC_VER | |
| 269 | +#define ldebug __noop | |
| 270 | +#else | |
| 271 | +static void GCC_ATTR ldebug (const char *fmt, ...) | |
| 272 | +{ | |
| 273 | + (void) fmt; | |
| 274 | +} | |
| 275 | +#endif | |
| 276 | +#endif | |
| 277 | + | |
| 278 | +#undef GCC_ATTR | |
| 279 | + | |
| 280 | +#define AUDIO_STRINGIFY_(n) #n | |
| 281 | +#define AUDIO_STRINGIFY(n) AUDIO_STRINGIFY_(n) | |
| 282 | + | |
| 283 | +#if defined _MSC_VER || defined __GNUC__ | |
| 284 | +#define AUDIO_FUNC __FUNCTION__ | |
| 285 | +#else | |
| 286 | +#define AUDIO_FUNC __FILE__ ":" AUDIO_STRINGIFY (__LINE__) | |
| 287 | +#endif | |
| 288 | + | |
| 163 | 289 | #endif /* audio_int.h */ | ... | ... |
audio/audio_template.h
0 โ 100644
| 1 | +/* | |
| 2 | + * QEMU Audio subsystem header | |
| 3 | + * | |
| 4 | + * Copyright (c) 2005 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 | +#ifdef DAC | |
| 26 | +#define TYPE out | |
| 27 | +#define HW glue (HWVoice, Out) | |
| 28 | +#define SW glue (SWVoice, Out) | |
| 29 | +#else | |
| 30 | +#define TYPE in | |
| 31 | +#define HW glue (HWVoice, In) | |
| 32 | +#define SW glue (SWVoice, In) | |
| 33 | +#endif | |
| 34 | + | |
| 35 | +static void glue (audio_pcm_sw_fini_, TYPE) (SW *sw) | |
| 36 | +{ | |
| 37 | + glue (audio_pcm_sw_free_resources_, TYPE) (sw); | |
| 38 | + if (sw->name) { | |
| 39 | + qemu_free (sw->name); | |
| 40 | + sw->name = NULL; | |
| 41 | + } | |
| 42 | +} | |
| 43 | + | |
| 44 | +static void glue (audio_pcm_hw_add_sw_, TYPE) (HW *hw, SW *sw) | |
| 45 | +{ | |
| 46 | + LIST_INSERT_HEAD (&hw->sw_head, sw, entries); | |
| 47 | +} | |
| 48 | + | |
| 49 | +static void glue (audio_pcm_hw_del_sw_, TYPE) (SW *sw) | |
| 50 | +{ | |
| 51 | + LIST_REMOVE (sw, entries); | |
| 52 | +} | |
| 53 | + | |
| 54 | +static void glue (audio_pcm_hw_fini_, TYPE) (HW *hw) | |
| 55 | +{ | |
| 56 | + if (hw->active) { | |
| 57 | + glue (audio_pcm_hw_free_resources_ ,TYPE) (hw); | |
| 58 | + glue (hw->pcm_ops->fini_, TYPE) (hw); | |
| 59 | + memset (hw, 0, glue (audio_state.drv->voice_size_, TYPE)); | |
| 60 | + } | |
| 61 | +} | |
| 62 | + | |
| 63 | +static void glue (audio_pcm_hw_gc_, TYPE) (HW *hw) | |
| 64 | +{ | |
| 65 | + if (!hw->sw_head.lh_first) { | |
| 66 | + glue (audio_pcm_hw_fini_, TYPE) (hw); | |
| 67 | + } | |
| 68 | +} | |
| 69 | + | |
| 70 | +static HW *glue (audio_pcm_hw_find_any_, TYPE) (HW *hw) | |
| 71 | +{ | |
| 72 | + return hw ? hw->entries.le_next : glue (hw_head_, TYPE).lh_first; | |
| 73 | +} | |
| 74 | + | |
| 75 | +static HW *glue (audio_pcm_hw_find_any_active_, TYPE) (HW *hw) | |
| 76 | +{ | |
| 77 | + while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (hw))) { | |
| 78 | + if (hw->active) { | |
| 79 | + return hw; | |
| 80 | + } | |
| 81 | + } | |
| 82 | + return NULL; | |
| 83 | +} | |
| 84 | + | |
| 85 | +static HW *glue (audio_pcm_hw_find_any_active_enabled_, TYPE) (HW *hw) | |
| 86 | +{ | |
| 87 | + while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (hw))) { | |
| 88 | + if (hw->active && hw->enabled) { | |
| 89 | + return hw; | |
| 90 | + } | |
| 91 | + } | |
| 92 | + return NULL; | |
| 93 | +} | |
| 94 | + | |
| 95 | +static HW *glue (audio_pcm_hw_find_any_passive_, TYPE) (HW *hw) | |
| 96 | +{ | |
| 97 | + while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (hw))) { | |
| 98 | + if (!hw->active) { | |
| 99 | + return hw; | |
| 100 | + } | |
| 101 | + } | |
| 102 | + return NULL; | |
| 103 | +} | |
| 104 | + | |
| 105 | +static HW *glue (audio_pcm_hw_find_specific_, TYPE) ( | |
| 106 | + HW *hw, | |
| 107 | + int freq, | |
| 108 | + int nchannels, | |
| 109 | + audfmt_e fmt | |
| 110 | + ) | |
| 111 | +{ | |
| 112 | + while ((hw = glue (audio_pcm_hw_find_any_active_, TYPE) (hw))) { | |
| 113 | + if (audio_pcm_info_eq (&hw->info, freq, nchannels, fmt)) { | |
| 114 | + return hw; | |
| 115 | + } | |
| 116 | + } | |
| 117 | + return NULL; | |
| 118 | +} | |
| 119 | + | |
| 120 | +static HW *glue (audio_pcm_hw_add_new_, TYPE) ( | |
| 121 | + int freq, | |
| 122 | + int nchannels, | |
| 123 | + audfmt_e fmt | |
| 124 | + ) | |
| 125 | +{ | |
| 126 | + HW *hw; | |
| 127 | + | |
| 128 | + hw = glue (audio_pcm_hw_find_any_passive_, TYPE) (NULL); | |
| 129 | + if (hw) { | |
| 130 | + hw->pcm_ops = audio_state.drv->pcm_ops; | |
| 131 | + if (!hw->pcm_ops) { | |
| 132 | + return NULL; | |
| 133 | + } | |
| 134 | + | |
| 135 | + if (glue (audio_pcm_hw_init_, TYPE) (hw, freq, nchannels, fmt)) { | |
| 136 | + glue (audio_pcm_hw_gc_, TYPE) (hw); | |
| 137 | + return NULL; | |
| 138 | + } | |
| 139 | + else { | |
| 140 | + return hw; | |
| 141 | + } | |
| 142 | + } | |
| 143 | + | |
| 144 | + return NULL; | |
| 145 | +} | |
| 146 | + | |
| 147 | +static HW *glue (audio_pcm_hw_add_, TYPE) ( | |
| 148 | + int freq, | |
| 149 | + int nchannels, | |
| 150 | + audfmt_e fmt | |
| 151 | + ) | |
| 152 | +{ | |
| 153 | + HW *hw; | |
| 154 | + | |
| 155 | + if (glue (audio_state.greedy_, TYPE)) { | |
| 156 | + hw = glue (audio_pcm_hw_add_new_, TYPE) (freq, nchannels, fmt); | |
| 157 | + if (hw) { | |
| 158 | + return hw; | |
| 159 | + } | |
| 160 | + } | |
| 161 | + | |
| 162 | + hw = glue (audio_pcm_hw_find_specific_, TYPE) (NULL, freq, nchannels, fmt); | |
| 163 | + if (hw) { | |
| 164 | + return hw; | |
| 165 | + } | |
| 166 | + | |
| 167 | + hw = glue (audio_pcm_hw_add_new_, TYPE) (freq, nchannels, fmt); | |
| 168 | + if (hw) { | |
| 169 | + return hw; | |
| 170 | + } | |
| 171 | + | |
| 172 | + return glue (audio_pcm_hw_find_any_active_, TYPE) (NULL); | |
| 173 | +} | |
| 174 | + | |
| 175 | +static SW *glue (audio_pcm_create_voice_pair_, TYPE) ( | |
| 176 | + const char *name, | |
| 177 | + int freq, | |
| 178 | + int nchannels, | |
| 179 | + audfmt_e fmt | |
| 180 | + ) | |
| 181 | +{ | |
| 182 | + SW *sw; | |
| 183 | + HW *hw; | |
| 184 | + int hw_freq = freq; | |
| 185 | + int hw_nchannels = nchannels; | |
| 186 | + int hw_fmt = fmt; | |
| 187 | + | |
| 188 | + if (glue (audio_state.fixed_settings_, TYPE)) { | |
| 189 | + hw_freq = glue (audio_state.fixed_freq_, TYPE); | |
| 190 | + hw_nchannels = glue (audio_state.fixed_channels_, TYPE); | |
| 191 | + hw_fmt = glue (audio_state.fixed_fmt_, TYPE); | |
| 192 | + } | |
| 193 | + | |
| 194 | + sw = qemu_mallocz (sizeof (*sw)); | |
| 195 | + if (!sw) { | |
| 196 | + goto err1; | |
| 197 | + } | |
| 198 | + | |
| 199 | + hw = glue (audio_pcm_hw_add_, TYPE) (hw_freq, hw_nchannels, hw_fmt); | |
| 200 | + if (!hw) { | |
| 201 | + goto err2; | |
| 202 | + } | |
| 203 | + | |
| 204 | + glue (audio_pcm_hw_add_sw_, TYPE) (hw, sw); | |
| 205 | + | |
| 206 | + if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, name, freq, nchannels, fmt)) { | |
| 207 | + goto err3; | |
| 208 | + } | |
| 209 | + | |
| 210 | + return sw; | |
| 211 | + | |
| 212 | +err3: | |
| 213 | + glue (audio_pcm_hw_del_sw_, TYPE) (sw); | |
| 214 | + glue (audio_pcm_hw_gc_, TYPE) (hw); | |
| 215 | +err2: | |
| 216 | + qemu_free (sw); | |
| 217 | +err1: | |
| 218 | + return NULL; | |
| 219 | +} | |
| 220 | + | |
| 221 | +void glue (AUD_close_, TYPE) (SW *sw) | |
| 222 | +{ | |
| 223 | + if (sw) { | |
| 224 | + glue (audio_pcm_sw_fini_, TYPE) (sw); | |
| 225 | + glue (audio_pcm_hw_del_sw_, TYPE) (sw); | |
| 226 | + glue (audio_pcm_hw_gc_, TYPE) (sw->hw); | |
| 227 | + qemu_free (sw); | |
| 228 | + } | |
| 229 | +} | |
| 230 | + | |
| 231 | +SW *glue (AUD_open_, TYPE) ( | |
| 232 | + SW *sw, | |
| 233 | + const char *name, | |
| 234 | + void *callback_opaque , | |
| 235 | + audio_callback_fn_t callback_fn, | |
| 236 | + int freq, | |
| 237 | + int nchannels, | |
| 238 | + audfmt_e fmt | |
| 239 | + ) | |
| 240 | +{ | |
| 241 | +#ifdef DAC | |
| 242 | + int live = 0; | |
| 243 | + SW *old_sw = NULL; | |
| 244 | +#endif | |
| 245 | + | |
| 246 | + if (!callback_fn) { | |
| 247 | + dolog ("No callback specifed for voice `%s'\n", name); | |
| 248 | + goto fail; | |
| 249 | + } | |
| 250 | + | |
| 251 | + if (nchannels != 1 && nchannels != 2) { | |
| 252 | + dolog ("Bogus channel count %d for voice `%s'\n", nchannels, name); | |
| 253 | + goto fail; | |
| 254 | + } | |
| 255 | + | |
| 256 | + if (!audio_state.drv) { | |
| 257 | + dolog ("No audio driver defined\n"); | |
| 258 | + goto fail; | |
| 259 | + } | |
| 260 | + | |
| 261 | + if (sw && audio_pcm_info_eq (&sw->info, freq, nchannels, fmt)) { | |
| 262 | + return sw; | |
| 263 | + } | |
| 264 | + | |
| 265 | +#ifdef DAC | |
| 266 | + if (audio_state.plive && sw && (!sw->active && !sw->empty)) { | |
| 267 | + live = sw->total_hw_samples_mixed; | |
| 268 | + | |
| 269 | +#ifdef DEBUG_PLIVE | |
| 270 | + dolog ("Replacing voice %s with %d live samples\n", sw->name, live); | |
| 271 | + dolog ("Old %s freq %d, bits %d, channels %d\n", | |
| 272 | + sw->name, sw->info.freq, sw->info.bits, sw->info.nchannels); | |
| 273 | + dolog ("New %s freq %d, bits %d, channels %d\n", | |
| 274 | + name, freq, (fmt == AUD_FMT_S16 || fmt == AUD_FMT_U16) ? 16 : 8, | |
| 275 | + nchannels); | |
| 276 | +#endif | |
| 277 | + | |
| 278 | + if (live) { | |
| 279 | + old_sw = sw; | |
| 280 | + old_sw->callback.fn = NULL; | |
| 281 | + sw = NULL; | |
| 282 | + } | |
| 283 | + } | |
| 284 | +#endif | |
| 285 | + | |
| 286 | + if (!glue (audio_state.fixed_settings_, TYPE) && sw) { | |
| 287 | + glue (AUD_close_, TYPE) (sw); | |
| 288 | + sw = NULL; | |
| 289 | + } | |
| 290 | + | |
| 291 | + if (sw) { | |
| 292 | + HW *hw = sw->hw; | |
| 293 | + | |
| 294 | + if (!hw) { | |
| 295 | + dolog ("Internal logic error voice %s has no hardware store\n", | |
| 296 | + name); | |
| 297 | + goto fail; | |
| 298 | + } | |
| 299 | + | |
| 300 | + if (glue (audio_pcm_sw_init_, TYPE) ( | |
| 301 | + sw, | |
| 302 | + hw, | |
| 303 | + name, | |
| 304 | + freq, | |
| 305 | + nchannels, | |
| 306 | + fmt | |
| 307 | + )) { | |
| 308 | + goto fail; | |
| 309 | + } | |
| 310 | + } | |
| 311 | + else { | |
| 312 | + sw = glue (audio_pcm_create_voice_pair_, TYPE) ( | |
| 313 | + name, | |
| 314 | + freq, | |
| 315 | + nchannels, | |
| 316 | + fmt); | |
| 317 | + if (!sw) { | |
| 318 | + dolog ("Failed to create voice %s\n", name); | |
| 319 | + goto fail; | |
| 320 | + } | |
| 321 | + } | |
| 322 | + | |
| 323 | + if (sw) { | |
| 324 | + sw->vol = nominal_volume; | |
| 325 | + sw->callback.fn = callback_fn; | |
| 326 | + sw->callback.opaque = callback_opaque; | |
| 327 | + | |
| 328 | +#ifdef DAC | |
| 329 | + if (live) { | |
| 330 | + int mixed = | |
| 331 | + (live << old_sw->info.shift) | |
| 332 | + * old_sw->info.bytes_per_second | |
| 333 | + / sw->info.bytes_per_second; | |
| 334 | + | |
| 335 | +#ifdef DEBUG_PLIVE | |
| 336 | + dolog ("Silence will be mixed %d\n", mixed); | |
| 337 | +#endif | |
| 338 | + sw->total_hw_samples_mixed += mixed; | |
| 339 | + } | |
| 340 | +#endif | |
| 341 | + | |
| 342 | +#ifdef DEBUG_AUDIO | |
| 343 | + dolog ("%s\n", name); | |
| 344 | + audio_pcm_print_info ("hw", &sw->hw->info); | |
| 345 | + audio_pcm_print_info ("sw", &sw->info); | |
| 346 | +#endif | |
| 347 | + } | |
| 348 | + | |
| 349 | + return sw; | |
| 350 | + | |
| 351 | + fail: | |
| 352 | + glue (AUD_close_, TYPE) (sw); | |
| 353 | + return NULL; | |
| 354 | +} | |
| 355 | + | |
| 356 | +int glue (AUD_is_active_, TYPE) (SW *sw) | |
| 357 | +{ | |
| 358 | + return sw ? sw->active : 0; | |
| 359 | +} | |
| 360 | + | |
| 361 | +void glue (AUD_init_time_stamp_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts) | |
| 362 | +{ | |
| 363 | + if (!sw) { | |
| 364 | + return; | |
| 365 | + } | |
| 366 | + | |
| 367 | + ts->old_ts = sw->hw->ts_helper; | |
| 368 | +} | |
| 369 | + | |
| 370 | +uint64_t glue (AUD_time_stamp_get_elapsed_usec_, TYPE) ( | |
| 371 | + SW *sw, | |
| 372 | + QEMUAudioTimeStamp *ts | |
| 373 | + ) | |
| 374 | +{ | |
| 375 | + uint64_t delta, cur_ts, old_ts; | |
| 376 | + | |
| 377 | + if (!sw) { | |
| 378 | + return 0; | |
| 379 | + } | |
| 380 | + | |
| 381 | + cur_ts = sw->hw->ts_helper; | |
| 382 | + old_ts = ts->old_ts; | |
| 383 | + /* dolog ("cur %lld old %lld\n", cur_ts, old_ts); */ | |
| 384 | + | |
| 385 | + if (cur_ts >= old_ts) { | |
| 386 | + delta = cur_ts - old_ts; | |
| 387 | + } | |
| 388 | + else { | |
| 389 | + delta = UINT64_MAX - old_ts + cur_ts; | |
| 390 | + } | |
| 391 | + | |
| 392 | + if (!delta) { | |
| 393 | + return 0; | |
| 394 | + } | |
| 395 | + | |
| 396 | + return (delta * sw->hw->info.freq) / 1000000; | |
| 397 | +} | |
| 398 | + | |
| 399 | +#undef TYPE | |
| 400 | +#undef HW | |
| 401 | +#undef SW | ... | ... |
audio/coreaudio.c
0 โ 100644
| 1 | +/* | |
| 2 | + * QEMU OS X CoreAudio audio driver | |
| 3 | + * | |
| 4 | + * Copyright (c) 2005 Mike Kronenberg | |
| 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 | +#include <CoreAudio/CoreAudio.h> | |
| 26 | +#include <string.h> /* strerror */ | |
| 27 | +#include <pthread.h> /* pthread_X */ | |
| 28 | + | |
| 29 | +#include "vl.h" | |
| 30 | + | |
| 31 | +#define AUDIO_CAP "coreaudio" | |
| 32 | +#include "audio_int.h" | |
| 33 | + | |
| 34 | +#define DEVICE_BUFFER_FRAMES (512) | |
| 35 | + | |
| 36 | +struct { | |
| 37 | + int buffer_frames; | |
| 38 | +} conf = { | |
| 39 | + .buffer_frames = 512 | |
| 40 | +}; | |
| 41 | + | |
| 42 | +typedef struct coreaudioVoiceOut { | |
| 43 | + HWVoiceOut hw; | |
| 44 | + pthread_mutex_t mutex; | |
| 45 | + AudioDeviceID outputDeviceID; | |
| 46 | + UInt32 audioDevicePropertyBufferSize; | |
| 47 | + AudioStreamBasicDescription outputStreamBasicDescription; | |
| 48 | + int isPlaying; | |
| 49 | + int live; | |
| 50 | + int decr; | |
| 51 | + int rpos; | |
| 52 | +} coreaudioVoiceOut; | |
| 53 | + | |
| 54 | +static void coreaudio_logstatus (OSStatus status) | |
| 55 | +{ | |
| 56 | + char *str = "BUG"; | |
| 57 | + | |
| 58 | + switch(status) { | |
| 59 | + case kAudioHardwareNoError: | |
| 60 | + str = "kAudioHardwareNoError"; | |
| 61 | + break; | |
| 62 | + | |
| 63 | + case kAudioHardwareNotRunningError: | |
| 64 | + str = "kAudioHardwareNotRunningError"; | |
| 65 | + break; | |
| 66 | + | |
| 67 | + case kAudioHardwareUnspecifiedError: | |
| 68 | + str = "kAudioHardwareUnspecifiedError"; | |
| 69 | + break; | |
| 70 | + | |
| 71 | + case kAudioHardwareUnknownPropertyError: | |
| 72 | + str = "kAudioHardwareUnknownPropertyError"; | |
| 73 | + break; | |
| 74 | + | |
| 75 | + case kAudioHardwareBadPropertySizeError: | |
| 76 | + str = "kAudioHardwareBadPropertySizeError"; | |
| 77 | + break; | |
| 78 | + | |
| 79 | + case kAudioHardwareIllegalOperationError: | |
| 80 | + str = "kAudioHardwareIllegalOperationError"; | |
| 81 | + break; | |
| 82 | + | |
| 83 | + case kAudioHardwareBadDeviceError: | |
| 84 | + str = "kAudioHardwareBadDeviceError"; | |
| 85 | + break; | |
| 86 | + | |
| 87 | + case kAudioHardwareBadStreamError: | |
| 88 | + str = "kAudioHardwareBadStreamError"; | |
| 89 | + break; | |
| 90 | + | |
| 91 | + case kAudioHardwareUnsupportedOperationError: | |
| 92 | + str = "kAudioHardwareUnsupportedOperationError"; | |
| 93 | + break; | |
| 94 | + | |
| 95 | + case kAudioDeviceUnsupportedFormatError: | |
| 96 | + str = "kAudioDeviceUnsupportedFormatError"; | |
| 97 | + break; | |
| 98 | + | |
| 99 | + case kAudioDevicePermissionsError: | |
| 100 | + str = "kAudioDevicePermissionsError"; | |
| 101 | + break; | |
| 102 | + | |
| 103 | + default: | |
| 104 | + AUD_log (AUDIO_CAP, "Reason: status code %ld\n", status); | |
| 105 | + return; | |
| 106 | + } | |
| 107 | + | |
| 108 | + AUD_log (AUDIO_CAP, "Reason: %s\n", str); | |
| 109 | +} | |
| 110 | + | |
| 111 | +static void GCC_FMT_ATTR (2, 3) coreaudio_logerr ( | |
| 112 | + OSStatus status, | |
| 113 | + const char *fmt, | |
| 114 | + ... | |
| 115 | + ) | |
| 116 | +{ | |
| 117 | + va_list ap; | |
| 118 | + | |
| 119 | + va_start (ap, fmt); | |
| 120 | + AUD_log (AUDIO_CAP, fmt, ap); | |
| 121 | + va_end (ap); | |
| 122 | + | |
| 123 | + coreaudio_logstatus (status); | |
| 124 | +} | |
| 125 | + | |
| 126 | +static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 ( | |
| 127 | + OSStatus status, | |
| 128 | + const char *typ, | |
| 129 | + const char *fmt, | |
| 130 | + ... | |
| 131 | + ) | |
| 132 | +{ | |
| 133 | + va_list ap; | |
| 134 | + | |
| 135 | + AUD_log (AUDIO_CAP, "Can not initialize %s\n", typ); | |
| 136 | + | |
| 137 | + va_start (ap, fmt); | |
| 138 | + AUD_vlog (AUDIO_CAP, fmt, ap); | |
| 139 | + va_end (ap); | |
| 140 | + | |
| 141 | + coreaudio_logstatus (status); | |
| 142 | +} | |
| 143 | + | |
| 144 | +static int coreaudio_lock (coreaudioVoiceOut *core, const char *fn_name) | |
| 145 | +{ | |
| 146 | + int err; | |
| 147 | + | |
| 148 | + err = pthread_mutex_lock (&core->mutex); | |
| 149 | + if (err) { | |
| 150 | + dolog ("Can not lock voice for %s\nReason: %s\n", | |
| 151 | + fn_name, strerror (err)); | |
| 152 | + return -1; | |
| 153 | + } | |
| 154 | + return 0; | |
| 155 | +} | |
| 156 | + | |
| 157 | +static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name) | |
| 158 | +{ | |
| 159 | + int err; | |
| 160 | + | |
| 161 | + err = pthread_mutex_unlock (&core->mutex); | |
| 162 | + if (err) { | |
| 163 | + dolog ("Can not unlock voice for %s\nReason: %s\n", | |
| 164 | + fn_name, strerror (err)); | |
| 165 | + return -1; | |
| 166 | + } | |
| 167 | + return 0; | |
| 168 | +} | |
| 169 | + | |
| 170 | +static int coreaudio_run_out (HWVoiceOut *hw) | |
| 171 | +{ | |
| 172 | + int live, decr; | |
| 173 | + coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; | |
| 174 | + | |
| 175 | + if (coreaudio_lock (core, "coreaudio_run_out")) { | |
| 176 | + return 0; | |
| 177 | + } | |
| 178 | + | |
| 179 | + live = audio_pcm_hw_get_live_out (hw); | |
| 180 | + | |
| 181 | + if (core->decr > live) { | |
| 182 | + ldebug ("core->decr %d live %d core->live %d\n", | |
| 183 | + core->decr, | |
| 184 | + live, | |
| 185 | + core->live); | |
| 186 | + } | |
| 187 | + | |
| 188 | + decr = audio_MIN (core->decr, live); | |
| 189 | + core->decr -= decr; | |
| 190 | + | |
| 191 | + core->live = live - decr; | |
| 192 | + hw->rpos = core->rpos; | |
| 193 | + | |
| 194 | + coreaudio_unlock (core, "coreaudio_run_out"); | |
| 195 | + return decr; | |
| 196 | +} | |
| 197 | + | |
| 198 | +/* callback to feed audiooutput buffer */ | |
| 199 | +static OSStatus audioDeviceIOProc( | |
| 200 | + AudioDeviceID inDevice, | |
| 201 | + const AudioTimeStamp* inNow, | |
| 202 | + const AudioBufferList* inInputData, | |
| 203 | + const AudioTimeStamp* inInputTime, | |
| 204 | + AudioBufferList* outOutputData, | |
| 205 | + const AudioTimeStamp* inOutputTime, | |
| 206 | + void* hwptr) | |
| 207 | +{ | |
| 208 | + unsigned int frame, frameCount; | |
| 209 | + float *out = outOutputData->mBuffers[0].mData; | |
| 210 | + HWVoiceOut *hw = hwptr; | |
| 211 | + coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr; | |
| 212 | + int rpos, live; | |
| 213 | + st_sample_t *src; | |
| 214 | +#ifndef FLOAT_MIXENG | |
| 215 | +#ifdef RECIPROCAL | |
| 216 | + const float scale = 1.f / UINT_MAX; | |
| 217 | +#else | |
| 218 | + const float scale = UINT_MAX; | |
| 219 | +#endif | |
| 220 | +#endif | |
| 221 | + | |
| 222 | + if (coreaudio_lock (core, "audioDeviceIOProc")) { | |
| 223 | + inInputTime = 0; | |
| 224 | + return 0; | |
| 225 | + } | |
| 226 | + | |
| 227 | + frameCount = conf.buffer_frames; | |
| 228 | + live = core->live; | |
| 229 | + | |
| 230 | + /* if there are not enough samples, set signal and return */ | |
| 231 | + if (live < frameCount) { | |
| 232 | + inInputTime = 0; | |
| 233 | + coreaudio_unlock (core, "audioDeviceIOProc(empty)"); | |
| 234 | + return 0; | |
| 235 | + } | |
| 236 | + | |
| 237 | + rpos = core->rpos; | |
| 238 | + src = hw->mix_buf + rpos; | |
| 239 | + | |
| 240 | + /* fill buffer */ | |
| 241 | + for (frame = 0; frame < frameCount; frame++) { | |
| 242 | +#ifdef FLOAT_MIXENG | |
| 243 | + *out++ = src[frame].l; /* left channel */ | |
| 244 | + *out++ = src[frame].r; /* right channel */ | |
| 245 | +#else | |
| 246 | +#ifdef RECIPROCAL | |
| 247 | + *out++ = src[frame].l * scale; /* left channel */ | |
| 248 | + *out++ = src[frame].r * scale; /* right channel */ | |
| 249 | +#else | |
| 250 | + *out++ = src[frame].l / scale; /* left channel */ | |
| 251 | + *out++ = src[frame].r / scale; /* right channel */ | |
| 252 | +#endif | |
| 253 | +#endif | |
| 254 | + } | |
| 255 | + | |
| 256 | + /* cleanup */ | |
| 257 | + mixeng_clear (src, frameCount); | |
| 258 | + rpos = (rpos + frameCount) % hw->samples; | |
| 259 | + core->decr = frameCount; | |
| 260 | + core->rpos = rpos; | |
| 261 | + | |
| 262 | + coreaudio_unlock (core, "audioDeviceIOProc"); | |
| 263 | + return 0; | |
| 264 | +} | |
| 265 | + | |
| 266 | +static int coreaudio_write (SWVoiceOut *sw, void *buf, int len) | |
| 267 | +{ | |
| 268 | + return audio_pcm_sw_write (sw, buf, len); | |
| 269 | +} | |
| 270 | + | |
| 271 | +static int coreaudio_init_out (HWVoiceOut *hw, int freq, | |
| 272 | + int nchannels, audfmt_e fmt) | |
| 273 | +{ | |
| 274 | + OSStatus status; | |
| 275 | + coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; | |
| 276 | + UInt32 propertySize; | |
| 277 | + int err; | |
| 278 | + int bits = 8; | |
| 279 | + int endianess = 0; | |
| 280 | + const char *typ = "DAC"; | |
| 281 | + | |
| 282 | + /* create mutex */ | |
| 283 | + err = pthread_mutex_init(&core->mutex, NULL); | |
| 284 | + if (err) { | |
| 285 | + dolog("Can not create mutex\nReason: %s\n", strerror (err)); | |
| 286 | + return -1; | |
| 287 | + } | |
| 288 | + | |
| 289 | + if (fmt == AUD_FMT_S16 || fmt == AUD_FMT_U16) { | |
| 290 | + bits = 16; | |
| 291 | + endianess = 1; | |
| 292 | + } | |
| 293 | + | |
| 294 | + audio_pcm_init_info ( | |
| 295 | + &hw->info, | |
| 296 | + freq, | |
| 297 | + nchannels, | |
| 298 | + fmt, | |
| 299 | + /* Following is irrelevant actually since we do not use | |
| 300 | + mixengs clipping routines */ | |
| 301 | + audio_need_to_swap_endian (endianess) | |
| 302 | + ); | |
| 303 | + hw->bufsize = 4 * conf.buffer_frames * nchannels * bits; | |
| 304 | + | |
| 305 | + /* open default output device */ | |
| 306 | + propertySize = sizeof(core->outputDeviceID); | |
| 307 | + status = AudioHardwareGetProperty( | |
| 308 | + kAudioHardwarePropertyDefaultOutputDevice, | |
| 309 | + &propertySize, | |
| 310 | + &core->outputDeviceID); | |
| 311 | + if (status != kAudioHardwareNoError) { | |
| 312 | + coreaudio_logerr2 (status, typ, | |
| 313 | + "Can not get default output Device\n"); | |
| 314 | + return -1; | |
| 315 | + } | |
| 316 | + if (core->outputDeviceID == kAudioDeviceUnknown) { | |
| 317 | + dolog ("Can not initialize %s - Unknown Audiodevice\n", typ); | |
| 318 | + return -1; | |
| 319 | + } | |
| 320 | + | |
| 321 | + /* set Buffersize to conf.buffer_frames frames */ | |
| 322 | + propertySize = sizeof(core->audioDevicePropertyBufferSize); | |
| 323 | + core->audioDevicePropertyBufferSize = | |
| 324 | + conf.buffer_frames * sizeof(float) * 2; | |
| 325 | + status = AudioDeviceSetProperty( | |
| 326 | + core->outputDeviceID, | |
| 327 | + NULL, | |
| 328 | + 0, | |
| 329 | + false, | |
| 330 | + kAudioDevicePropertyBufferSize, | |
| 331 | + propertySize, | |
| 332 | + &core->audioDevicePropertyBufferSize); | |
| 333 | + if (status != kAudioHardwareNoError) { | |
| 334 | + coreaudio_logerr2 (status, typ, | |
| 335 | + "Can not set device buffer size %d\n", | |
| 336 | + kAudioDevicePropertyBufferSize); | |
| 337 | + return -1; | |
| 338 | + } | |
| 339 | + | |
| 340 | + /* get Buffersize */ | |
| 341 | + propertySize = sizeof(core->audioDevicePropertyBufferSize); | |
| 342 | + status = AudioDeviceGetProperty( | |
| 343 | + core->outputDeviceID, | |
| 344 | + 0, | |
| 345 | + false, | |
| 346 | + kAudioDevicePropertyBufferSize, | |
| 347 | + &propertySize, | |
| 348 | + &core->audioDevicePropertyBufferSize); | |
| 349 | + if (status != kAudioHardwareNoError) { | |
| 350 | + coreaudio_logerr2 (status, typ, "Can not get device buffer size\n"); | |
| 351 | + return -1; | |
| 352 | + } | |
| 353 | + | |
| 354 | + /* get StreamFormat */ | |
| 355 | + propertySize = sizeof(core->outputStreamBasicDescription); | |
| 356 | + status = AudioDeviceGetProperty( | |
| 357 | + core->outputDeviceID, | |
| 358 | + 0, | |
| 359 | + false, | |
| 360 | + kAudioDevicePropertyStreamFormat, | |
| 361 | + &propertySize, | |
| 362 | + &core->outputStreamBasicDescription); | |
| 363 | + if (status != kAudioHardwareNoError) { | |
| 364 | + coreaudio_logerr2 (status, typ, | |
| 365 | + "Can not get Device Stream properties\n"); | |
| 366 | + core->outputDeviceID = kAudioDeviceUnknown; | |
| 367 | + return -1; | |
| 368 | + } | |
| 369 | + | |
| 370 | + /* set Samplerate */ | |
| 371 | + core->outputStreamBasicDescription.mSampleRate = (Float64)freq; | |
| 372 | + propertySize = sizeof(core->outputStreamBasicDescription); | |
| 373 | + status = AudioDeviceSetProperty( | |
| 374 | + core->outputDeviceID, | |
| 375 | + 0, | |
| 376 | + 0, | |
| 377 | + 0, | |
| 378 | + kAudioDevicePropertyStreamFormat, | |
| 379 | + propertySize, | |
| 380 | + &core->outputStreamBasicDescription); | |
| 381 | + if (status != kAudioHardwareNoError) { | |
| 382 | + coreaudio_logerr2 (status, typ, "Can not set samplerate %d\n", freq); | |
| 383 | + core->outputDeviceID = kAudioDeviceUnknown; | |
| 384 | + return -1; | |
| 385 | + } | |
| 386 | + | |
| 387 | + /* set Callback */ | |
| 388 | + status = AudioDeviceAddIOProc(core->outputDeviceID, audioDeviceIOProc, hw); | |
| 389 | + if (status != kAudioHardwareNoError) { | |
| 390 | + coreaudio_logerr2 (status, typ, "Can not set IOProc\n"); | |
| 391 | + core->outputDeviceID = kAudioDeviceUnknown; | |
| 392 | + return -1; | |
| 393 | + } | |
| 394 | + | |
| 395 | + /* start Playback */ | |
| 396 | + if (!core->isPlaying) { | |
| 397 | + status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc); | |
| 398 | + if (status != kAudioHardwareNoError) { | |
| 399 | + coreaudio_logerr2 (status, typ, "Can not start playback\n"); | |
| 400 | + AudioDeviceRemoveIOProc(core->outputDeviceID, audioDeviceIOProc); | |
| 401 | + core->outputDeviceID = kAudioDeviceUnknown; | |
| 402 | + return -1; | |
| 403 | + } | |
| 404 | + core->isPlaying = 1; | |
| 405 | + } | |
| 406 | + | |
| 407 | + return 0; | |
| 408 | +} | |
| 409 | + | |
| 410 | +static void coreaudio_fini_out (HWVoiceOut *hw) | |
| 411 | +{ | |
| 412 | + OSStatus status; | |
| 413 | + int err; | |
| 414 | + coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; | |
| 415 | + | |
| 416 | + /* stop playback */ | |
| 417 | + if (core->isPlaying) { | |
| 418 | + status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc); | |
| 419 | + if (status != kAudioHardwareNoError) { | |
| 420 | + coreaudio_logerr (status, "Can not stop playback\n"); | |
| 421 | + } | |
| 422 | + core->isPlaying = 0; | |
| 423 | + } | |
| 424 | + | |
| 425 | + /* remove callback */ | |
| 426 | + status = AudioDeviceRemoveIOProc(core->outputDeviceID, audioDeviceIOProc); | |
| 427 | + if (status != kAudioHardwareNoError) { | |
| 428 | + coreaudio_logerr (status, "Can not remove IOProc\n"); | |
| 429 | + } | |
| 430 | + core->outputDeviceID = kAudioDeviceUnknown; | |
| 431 | + | |
| 432 | + /* destroy mutex */ | |
| 433 | + err = pthread_mutex_destroy(&core->mutex); | |
| 434 | + if (err) { | |
| 435 | + dolog("Can not destroy mutex\nReason: %s\n", strerror (err)); | |
| 436 | + } | |
| 437 | +} | |
| 438 | + | |
| 439 | +static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...) | |
| 440 | +{ | |
| 441 | + OSStatus status; | |
| 442 | + coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; | |
| 443 | + | |
| 444 | + switch (cmd) { | |
| 445 | + case VOICE_ENABLE: | |
| 446 | + /* start playback */ | |
| 447 | + if (!core->isPlaying) { | |
| 448 | + status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc); | |
| 449 | + if (status != kAudioHardwareNoError) { | |
| 450 | + coreaudio_logerr (status, "Can not unpause playback\n"); | |
| 451 | + } | |
| 452 | + core->isPlaying = 1; | |
| 453 | + } | |
| 454 | + break; | |
| 455 | + | |
| 456 | + case VOICE_DISABLE: | |
| 457 | + /* stop playback */ | |
| 458 | + if (core->isPlaying) { | |
| 459 | + status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc); | |
| 460 | + if (status != kAudioHardwareNoError) { | |
| 461 | + coreaudio_logerr (status, "Can not pause playback\n"); | |
| 462 | + } | |
| 463 | + core->isPlaying = 0; | |
| 464 | + } | |
| 465 | + break; | |
| 466 | + } | |
| 467 | + return 0; | |
| 468 | +} | |
| 469 | + | |
| 470 | +static void *coreaudio_audio_init (void) | |
| 471 | +{ | |
| 472 | + return &coreaudio_audio_init; | |
| 473 | +} | |
| 474 | + | |
| 475 | +static void coreaudio_audio_fini (void *opaque) | |
| 476 | +{ | |
| 477 | + (void) opaque; | |
| 478 | +} | |
| 479 | + | |
| 480 | +static struct audio_option coreaudio_options[] = { | |
| 481 | + {"BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_frames, | |
| 482 | + "Size of the buffer in frames", NULL, 0}, | |
| 483 | + {NULL, 0, NULL, NULL, NULL, 0} | |
| 484 | +}; | |
| 485 | + | |
| 486 | +static struct audio_pcm_ops coreaudio_pcm_ops = { | |
| 487 | + coreaudio_init_out, | |
| 488 | + coreaudio_fini_out, | |
| 489 | + coreaudio_run_out, | |
| 490 | + coreaudio_write, | |
| 491 | + coreaudio_ctl_out, | |
| 492 | + | |
| 493 | + NULL, | |
| 494 | + NULL, | |
| 495 | + NULL, | |
| 496 | + NULL, | |
| 497 | + NULL | |
| 498 | +}; | |
| 499 | + | |
| 500 | +struct audio_driver coreaudio_audio_driver = { | |
| 501 | + INIT_FIELD (name = ) "coreaudio", | |
| 502 | + INIT_FIELD (descr = ) | |
| 503 | + "CoreAudio http://developer.apple.com/audio/coreaudio.html", | |
| 504 | + INIT_FIELD (options = ) coreaudio_options, | |
| 505 | + INIT_FIELD (init = ) coreaudio_audio_init, | |
| 506 | + INIT_FIELD (fini = ) coreaudio_audio_fini, | |
| 507 | + INIT_FIELD (pcm_ops = ) &coreaudio_pcm_ops, | |
| 508 | + INIT_FIELD (can_be_default = ) 1, | |
| 509 | + INIT_FIELD (max_voices_out = ) 1, | |
| 510 | + INIT_FIELD (max_voices_in = ) 0, | |
| 511 | + INIT_FIELD (voice_size_out = ) sizeof (coreaudioVoiceOut), | |
| 512 | + INIT_FIELD (voice_size_in = ) 0 | |
| 513 | +}; | ... | ... |
audio/dsound_template.h
0 โ 100644
| 1 | +/* | |
| 2 | + * QEMU DirectSound audio driver header | |
| 3 | + * | |
| 4 | + * Copyright (c) 2005 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 | +#ifdef DSBTYPE_IN | |
| 25 | +#define NAME "capture buffer" | |
| 26 | +#define TYPE in | |
| 27 | +#define IFACE IDirectSoundCaptureBuffer | |
| 28 | +#define BUFPTR LPDIRECTSOUNDCAPTUREBUFFER | |
| 29 | +#define FIELD dsound_capture_buffer | |
| 30 | +#else | |
| 31 | +#define NAME "playback buffer" | |
| 32 | +#define TYPE out | |
| 33 | +#define IFACE IDirectSoundBuffer | |
| 34 | +#define BUFPTR LPDIRECTSOUNDBUFFER | |
| 35 | +#define FIELD dsound_buffer | |
| 36 | +#endif | |
| 37 | + | |
| 38 | +static int glue (dsound_unlock_, TYPE) ( | |
| 39 | + BUFPTR buf, | |
| 40 | + LPVOID p1, | |
| 41 | + LPVOID p2, | |
| 42 | + DWORD blen1, | |
| 43 | + DWORD blen2 | |
| 44 | + ) | |
| 45 | +{ | |
| 46 | + HRESULT hr; | |
| 47 | + | |
| 48 | + hr = glue (IFACE, _Unlock) (buf, p1, blen1, p2, blen2); | |
| 49 | + if (FAILED (hr)) { | |
| 50 | + dsound_logerr (hr, "Can not unlock " NAME "\n"); | |
| 51 | + return -1; | |
| 52 | + } | |
| 53 | + | |
| 54 | + return 0; | |
| 55 | +} | |
| 56 | + | |
| 57 | +static int glue (dsound_lock_, TYPE) ( | |
| 58 | + BUFPTR buf, | |
| 59 | + struct audio_pcm_info *info, | |
| 60 | + DWORD pos, | |
| 61 | + DWORD len, | |
| 62 | + LPVOID *p1p, | |
| 63 | + LPVOID *p2p, | |
| 64 | + DWORD *blen1p, | |
| 65 | + DWORD *blen2p, | |
| 66 | + int entire | |
| 67 | + ) | |
| 68 | +{ | |
| 69 | + HRESULT hr; | |
| 70 | + int i; | |
| 71 | + LPVOID p1 = NULL, p2 = NULL; | |
| 72 | + DWORD blen1 = 0, blen2 = 0; | |
| 73 | + | |
| 74 | + for (i = 0; i < conf.lock_retries; ++i) { | |
| 75 | + hr = glue (IFACE, _Lock) ( | |
| 76 | + buf, | |
| 77 | + pos, | |
| 78 | + len, | |
| 79 | + &p1, | |
| 80 | + &blen1, | |
| 81 | + &p2, | |
| 82 | + &blen2, | |
| 83 | + (entire | |
| 84 | +#ifdef DSBTYPE_IN | |
| 85 | + ? DSCBLOCK_ENTIREBUFFER | |
| 86 | +#else | |
| 87 | + ? DSBLOCK_ENTIREBUFFER | |
| 88 | +#endif | |
| 89 | + : 0) | |
| 90 | + ); | |
| 91 | + | |
| 92 | + if (FAILED (hr)) { | |
| 93 | +#ifndef DSBTYPE_IN | |
| 94 | + if (hr == DSERR_BUFFERLOST) { | |
| 95 | + if (glue (dsound_restore_, TYPE) (buf)) { | |
| 96 | + dsound_logerr (hr, "Can not lock " NAME "\n"); | |
| 97 | + goto fail; | |
| 98 | + } | |
| 99 | + continue; | |
| 100 | + } | |
| 101 | +#endif | |
| 102 | + dsound_logerr (hr, "Can not lock " NAME "\n"); | |
| 103 | + goto fail; | |
| 104 | + } | |
| 105 | + | |
| 106 | + break; | |
| 107 | + } | |
| 108 | + | |
| 109 | + if (i == conf.lock_retries) { | |
| 110 | + dolog ("%d attempts to lock " NAME " failed\n", i); | |
| 111 | + goto fail; | |
| 112 | + } | |
| 113 | + | |
| 114 | + if ((p1 && (blen1 & info->align)) || (p2 && (blen2 & info->align))) { | |
| 115 | + dolog ("DirectSound returned misaligned buffer %ld %ld\n", | |
| 116 | + blen1, blen2); | |
| 117 | + glue (dsound_unlock_, TYPE) (buf, p1, p2, blen1, blen2); | |
| 118 | + goto fail; | |
| 119 | + } | |
| 120 | + | |
| 121 | + if (!p1 && blen1) { | |
| 122 | + dolog ("warning: !p1 && blen1=%ld\n", blen1); | |
| 123 | + blen1 = 0; | |
| 124 | + } | |
| 125 | + | |
| 126 | + if (!p2 && blen2) { | |
| 127 | + dolog ("warning: !p2 && blen2=%ld\n", blen2); | |
| 128 | + blen2 = 0; | |
| 129 | + } | |
| 130 | + | |
| 131 | + *p1p = p1; | |
| 132 | + *p2p = p2; | |
| 133 | + *blen1p = blen1; | |
| 134 | + *blen2p = blen2; | |
| 135 | + return 0; | |
| 136 | + | |
| 137 | + fail: | |
| 138 | + *p1p = NULL - 1; | |
| 139 | + *p2p = NULL - 1; | |
| 140 | + *blen1p = -1; | |
| 141 | + *blen2p = -1; | |
| 142 | + return -1; | |
| 143 | +} | |
| 144 | + | |
| 145 | +#ifdef DSBTYPE_IN | |
| 146 | +static void dsound_fini_in (HWVoiceIn *hw) | |
| 147 | +#else | |
| 148 | +static void dsound_fini_out (HWVoiceOut *hw) | |
| 149 | +#endif | |
| 150 | +{ | |
| 151 | + HRESULT hr; | |
| 152 | +#ifdef DSBTYPE_IN | |
| 153 | + DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; | |
| 154 | +#else | |
| 155 | + DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; | |
| 156 | +#endif | |
| 157 | + | |
| 158 | + if (ds->FIELD) { | |
| 159 | + hr = glue (IFACE, _Stop) (ds->FIELD); | |
| 160 | + if (FAILED (hr)) { | |
| 161 | + dsound_logerr (hr, "Can not stop " NAME "\n"); | |
| 162 | + } | |
| 163 | + | |
| 164 | + hr = glue (IFACE, _Release) (ds->FIELD); | |
| 165 | + if (FAILED (hr)) { | |
| 166 | + dsound_logerr (hr, "Can not release " NAME "\n"); | |
| 167 | + } | |
| 168 | + ds->FIELD = NULL; | |
| 169 | + } | |
| 170 | +} | |
| 171 | + | |
| 172 | +#ifdef DSBTYPE_IN | |
| 173 | +static int dsound_init_in ( | |
| 174 | + HWVoiceIn *hw, | |
| 175 | + int freq, | |
| 176 | + int nchannels, | |
| 177 | + audfmt_e fmt | |
| 178 | + ) | |
| 179 | +#else | |
| 180 | +static int dsound_init_out ( | |
| 181 | + HWVoiceOut *hw, | |
| 182 | + int freq, | |
| 183 | + int nchannels, | |
| 184 | + audfmt_e fmt | |
| 185 | + ) | |
| 186 | +#endif | |
| 187 | +{ | |
| 188 | + int err; | |
| 189 | + HRESULT hr; | |
| 190 | + dsound *s = &glob_dsound; | |
| 191 | + WAVEFORMATEX wfx; | |
| 192 | + struct full_fmt full_fmt; | |
| 193 | +#ifdef DSBTYPE_IN | |
| 194 | + const char *typ = "ADC"; | |
| 195 | + DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; | |
| 196 | + DSCBUFFERDESC bd; | |
| 197 | + DSCBCAPS bc; | |
| 198 | +#else | |
| 199 | + const char *typ = "DAC"; | |
| 200 | + DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; | |
| 201 | + DSBUFFERDESC bd; | |
| 202 | + DSBCAPS bc; | |
| 203 | +#endif | |
| 204 | + | |
| 205 | + full_fmt.freq = freq; | |
| 206 | + full_fmt.nchannels = nchannels; | |
| 207 | + full_fmt.fmt = fmt; | |
| 208 | + err = waveformat_from_full_fmt (&wfx, &full_fmt); | |
| 209 | + if (err) { | |
| 210 | + return -1; | |
| 211 | + } | |
| 212 | + | |
| 213 | + memset (&bd, 0, sizeof (bd)); | |
| 214 | + bd.dwSize = sizeof (bd); | |
| 215 | + bd.lpwfxFormat = &wfx; | |
| 216 | +#ifdef DSBTYPE_IN | |
| 217 | + bd.dwBufferBytes = conf.bufsize_in; | |
| 218 | + hr = IDirectSoundCapture_CreateCaptureBuffer ( | |
| 219 | + s->dsound_capture, | |
| 220 | + &bd, | |
| 221 | + &ds->dsound_capture_buffer, | |
| 222 | + NULL | |
| 223 | + ); | |
| 224 | +#else | |
| 225 | + bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2; | |
| 226 | + bd.dwBufferBytes = conf.bufsize_out; | |
| 227 | + hr = IDirectSound_CreateSoundBuffer ( | |
| 228 | + s->dsound, | |
| 229 | + &bd, | |
| 230 | + &ds->dsound_buffer, | |
| 231 | + NULL | |
| 232 | + ); | |
| 233 | +#endif | |
| 234 | + | |
| 235 | + if (FAILED (hr)) { | |
| 236 | + dsound_logerr2 (hr, typ, "Can not create " NAME "\n"); | |
| 237 | + return -1; | |
| 238 | + } | |
| 239 | + | |
| 240 | + hr = glue (IFACE, _GetFormat) ( | |
| 241 | + ds->FIELD, | |
| 242 | + &wfx, | |
| 243 | + sizeof (wfx), | |
| 244 | + NULL | |
| 245 | + ); | |
| 246 | + if (FAILED (hr)) { | |
| 247 | + dsound_logerr2 (hr, typ, "Can not get " NAME " format\n"); | |
| 248 | + goto fail0; | |
| 249 | + } | |
| 250 | + | |
| 251 | +#ifdef DEBUG_DSOUND | |
| 252 | + dolog (NAME "\n"); | |
| 253 | + print_wave_format (&wfx); | |
| 254 | +#endif | |
| 255 | + | |
| 256 | + memset (&bc, 0, sizeof (bc)); | |
| 257 | + bc.dwSize = sizeof (bc); | |
| 258 | + | |
| 259 | + hr = glue (IFACE, _GetCaps) (ds->FIELD, &bc); | |
| 260 | + if (FAILED (hr)) { | |
| 261 | + dsound_logerr2 (hr, typ, "Can not get " NAME " format\n"); | |
| 262 | + goto fail0; | |
| 263 | + } | |
| 264 | + | |
| 265 | + err = waveformat_to_full_fmt (&wfx, &full_fmt); | |
| 266 | + if (err) { | |
| 267 | + goto fail0; | |
| 268 | + } | |
| 269 | + | |
| 270 | + ds->first_time = 1; | |
| 271 | + hw->bufsize = bc.dwBufferBytes; | |
| 272 | + audio_pcm_init_info ( | |
| 273 | + &hw->info, | |
| 274 | + full_fmt.freq, | |
| 275 | + full_fmt.nchannels, | |
| 276 | + full_fmt.fmt, | |
| 277 | + audio_need_to_swap_endian (0) | |
| 278 | + ); | |
| 279 | + | |
| 280 | +#ifdef DEBUG_DSOUND | |
| 281 | + dolog ("caps %ld, desc %ld\n", | |
| 282 | + bc.dwBufferBytes, bd.dwBufferBytes); | |
| 283 | + | |
| 284 | + dolog ("bufsize %d, freq %d, chan %d, fmt %d\n", | |
| 285 | + hw->bufsize, full_fmt.freq, full_fmt.nchannels, full_fmt.fmt); | |
| 286 | +#endif | |
| 287 | + return 0; | |
| 288 | + | |
| 289 | + fail0: | |
| 290 | + glue (dsound_fini_, TYPE) (hw); | |
| 291 | + return -1; | |
| 292 | +} | |
| 293 | + | |
| 294 | +#undef NAME | |
| 295 | +#undef TYPE | |
| 296 | +#undef IFACE | |
| 297 | +#undef BUFPTR | |
| 298 | +#undef FIELD | ... | ... |