merged 15a_aqemu.patch audio patch (malc)

git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1584 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
bellard 2005-10-30 18:58:22 +00:00
parent 3b0d4f61c9
commit 1d14ffa97e
28 changed files with 8285 additions and 1733 deletions

View File

@ -1,3 +1,10 @@
version 0.7.3:
- Mac OS X cocoa improvements (Mike Kronenberg)
- DirectSound driver (malc)
- new audio options: '-soundhw' and 'audio-help' (malc)
- ES1370 PCI audio device (malc)
version 0.7.2:
- x86_64 fixes (Win2000 and Linux 2.6 boot in 32 bit)

View File

@ -262,7 +262,7 @@ endif
VL_OBJS=vl.o osdep.o block.o readline.o monitor.o pci.o console.o
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
SOUND_HW = sb16.o
SOUND_HW = sb16.o es1370.o
AUDIODRV = audio.o noaudio.o wavaudio.o
ifdef CONFIG_SDL
AUDIODRV += sdlaudio.o
@ -270,29 +270,38 @@ endif
ifdef CONFIG_OSS
AUDIODRV += ossaudio.o
endif
pc.o: DEFINES := -DUSE_SB16 $(DEFINES)
ifdef CONFIG_ADLIB
SOUND_HW += fmopl.o adlib.o
ifdef CONFIG_COREAUDIO
AUDIODRV += coreaudio.o
endif
ifdef CONFIG_ALSA
AUDIODRV += alsaaudio.o
LIBS += -lasound
endif
ifdef CONFIG_DSOUND
AUDIODRV += dsoundaudio.o
LIBS += -lole32 -ldxguid
endif
ifdef CONFIG_FMOD
AUDIODRV += fmodaudio.o
audio.o fmodaudio.o: DEFINES := -I$(CONFIG_FMOD_INC) $(DEFINES)
LIBS += $(CONFIG_FMOD_LIB)
endif
ifdef CONFIG_ADLIB
SOUND_HW += fmopl.o adlib.o
endif
ifeq ($(TARGET_BASE_ARCH), i386)
# Hardware support
VL_OBJS+= ide.o ne2000.o pckbd.o vga.o $(SOUND_HW) dma.o $(AUDIODRV)
VL_OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pc.o
VL_OBJS+= cirrus_vga.o mixeng.o apic.o parallel.o
DEFINES += -DHAS_AUDIO
endif
ifeq ($(TARGET_BASE_ARCH), ppc)
VL_OBJS+= ppc.o ide.o ne2000.o pckbd.o vga.o $(SOUND_HW) dma.o $(AUDIODRV)
VL_OBJS+= mc146818rtc.o serial.o i8259.o i8254.o fdc.o m48t59.o
VL_OBJS+= ppc_prep.o ppc_chrp.o cuda.o adb.o openpic.o heathrow_pic.o mixeng.o
DEFINES += -DHAS_AUDIO
endif
ifeq ($(TARGET_ARCH), mips)
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
endif
ifdef CONFIG_COCOA
VL_OBJS+=cocoa.o
COCOA_LIBS=-F/System/Library/Frameworks -framework Cocoa
COCOA_LIBS=-F/System/Library/Frameworks -framework Cocoa -framework IOKit
ifdef CONFIG_COREAUDIO
COCOA_LIBS+=-framework CoreAudio
endif
endif
ifdef CONFIG_SLIRP
DEFINES+=-I$(SRC_PATH)/slirp
@ -349,6 +361,10 @@ ifeq ($(ARCH),ia64)
VL_LDFLAGS+=-Wl,-G0 -Wl,-T,$(SRC_PATH)/ia64.ld
endif
ifdef CONFIG_WIN32
SDL_LIBS := $(filter-out -mwindows, $(SDL_LIBS)) -mconsole
endif
$(QEMU_SYSTEM): $(VL_OBJS) libqemu.a
$(CC) $(VL_LDFLAGS) -o $@ $^ $(LIBS) $(SDL_LIBS) $(COCOA_LIBS) $(VL_LIBS)
@ -364,6 +380,9 @@ sdlaudio.o: sdlaudio.c
depend: $(SRCS)
$(CC) -MM $(CFLAGS) $(DEFINES) $^ 1>.depend
vldepend: $(VL_OBJS:.o=.c)
$(CC) -MM $(CFLAGS) $(DEFINES) $^ 1>.depend
# libqemu
libqemu.a: $(LIBOBJS)
@ -415,8 +434,6 @@ op.o: op.c op_template.c op_mem.c
op_helper.o: op_helper_mem.c
endif
mixeng.o: mixeng.c mixeng.h mixeng_template.h
%.o: %.c
$(CC) $(CFLAGS) $(DEFINES) -c -o $@ $<
@ -434,3 +451,9 @@ endif
ifneq ($(wildcard .depend),)
include .depend
endif
ifeq (0, 1)
audio.o sdlaudio.o dsoundaudio.o ossaudio.o wavaudio.o noaudio.o \
fmodaudio.o alsaaudio.o mixeng.o: \
CFLAGS := $(CFLAGS) -Wall -Werror -W -Wsign-compare
endif

926
audio/alsaaudio.c Normal file
View File

@ -0,0 +1,926 @@
/*
* QEMU ALSA audio driver
*
* Copyright (c) 2005 Vassili Karpov (malc)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <alsa/asoundlib.h>
#include "vl.h"
#define AUDIO_CAP "alsa"
#include "audio_int.h"
typedef struct ALSAVoiceOut {
HWVoiceOut hw;
void *pcm_buf;
snd_pcm_t *handle;
int can_pause;
int was_enabled;
} ALSAVoiceOut;
typedef struct ALSAVoiceIn {
HWVoiceIn hw;
snd_pcm_t *handle;
void *pcm_buf;
int can_pause;
} ALSAVoiceIn;
static struct {
int size_in_usec_in;
int size_in_usec_out;
const char *pcm_name_in;
const char *pcm_name_out;
unsigned int buffer_size_in;
unsigned int period_size_in;
unsigned int buffer_size_out;
unsigned int period_size_out;
unsigned int threshold;
int buffer_size_in_overriden;
int period_size_in_overriden;
int buffer_size_out_overriden;
int period_size_out_overriden;
} conf = {
#ifdef HIGH_LATENCY
.size_in_usec_in = 1,
.size_in_usec_out = 1,
#endif
.pcm_name_out = "hw:0,0",
.pcm_name_in = "hw:0,0",
#ifdef HIGH_LATENCY
.buffer_size_in = 400000,
.period_size_in = 400000 / 4,
.buffer_size_out = 400000,
.period_size_out = 400000 / 4,
#else
#define DEFAULT_BUFFER_SIZE 1024
#define DEFAULT_PERIOD_SIZE 256
.buffer_size_in = DEFAULT_BUFFER_SIZE,
.period_size_in = DEFAULT_PERIOD_SIZE,
.buffer_size_out = DEFAULT_BUFFER_SIZE,
.period_size_out = DEFAULT_PERIOD_SIZE,
.buffer_size_in_overriden = 0,
.buffer_size_out_overriden = 0,
.period_size_in_overriden = 0,
.period_size_out_overriden = 0,
#endif
.threshold = 0
};
struct alsa_params_req {
int freq;
audfmt_e fmt;
int nchannels;
unsigned int buffer_size;
unsigned int period_size;
};
struct alsa_params_obt {
int freq;
audfmt_e fmt;
int nchannels;
int can_pause;
snd_pcm_uframes_t buffer_size;
};
static void GCC_FMT_ATTR (2, 3) alsa_logerr (int err, const char *fmt, ...)
{
va_list ap;
va_start (ap, fmt);
AUD_vlog (AUDIO_CAP, fmt, ap);
va_end (ap);
AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err));
}
static void GCC_FMT_ATTR (3, 4) alsa_logerr2 (
int err,
const char *typ,
const char *fmt,
...
)
{
va_list ap;
AUD_log (AUDIO_CAP, "Can not initialize %s\n", typ);
va_start (ap, fmt);
AUD_vlog (AUDIO_CAP, fmt, ap);
va_end (ap);
AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err));
}
static void alsa_anal_close (snd_pcm_t **handlep)
{
int err = snd_pcm_close (*handlep);
if (err) {
alsa_logerr (err, "Failed to close PCM handle %p\n", *handlep);
}
*handlep = NULL;
}
static int alsa_write (SWVoiceOut *sw, void *buf, int len)
{
return audio_pcm_sw_write (sw, buf, len);
}
static int aud_to_alsafmt (audfmt_e fmt)
{
switch (fmt) {
case AUD_FMT_S8:
return SND_PCM_FORMAT_S8;
case AUD_FMT_U8:
return SND_PCM_FORMAT_U8;
case AUD_FMT_S16:
return SND_PCM_FORMAT_S16_LE;
case AUD_FMT_U16:
return SND_PCM_FORMAT_U16_LE;
default:
dolog ("Internal logic error: Bad audio format %d\n", fmt);
#ifdef DEBUG_AUDIO
abort ();
#endif
return SND_PCM_FORMAT_U8;
}
}
static int alsa_to_audfmt (int alsafmt, audfmt_e *fmt, int *endianness)
{
switch (alsafmt) {
case SND_PCM_FORMAT_S8:
*endianness = 0;
*fmt = AUD_FMT_S8;
break;
case SND_PCM_FORMAT_U8:
*endianness = 0;
*fmt = AUD_FMT_U8;
break;
case SND_PCM_FORMAT_S16_LE:
*endianness = 0;
*fmt = AUD_FMT_S16;
break;
case SND_PCM_FORMAT_U16_LE:
*endianness = 0;
*fmt = AUD_FMT_U16;
break;
case SND_PCM_FORMAT_S16_BE:
*endianness = 1;
*fmt = AUD_FMT_S16;
break;
case SND_PCM_FORMAT_U16_BE:
*endianness = 1;
*fmt = AUD_FMT_U16;
break;
default:
dolog ("Unrecognized audio format %d\n", alsafmt);
return -1;
}
return 0;
}
#ifdef DEBUG_MISMATCHES
static void alsa_dump_info (struct alsa_params_req *req,
struct alsa_params_obt *obt)
{
dolog ("parameter | requested value | obtained value\n");
dolog ("format | %10d | %10d\n", req->fmt, obt->fmt);
dolog ("channels | %10d | %10d\n",
req->nchannels, obt->nchannels);
dolog ("frequency | %10d | %10d\n", req->freq, obt->freq);
dolog ("============================================\n");
dolog ("requested: buffer size %d period size %d\n",
req->buffer_size, req->period_size);
dolog ("obtained: buffer size %ld\n", obt->buffer_size);
}
#endif
static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold)
{
int err;
snd_pcm_sw_params_t *sw_params;
snd_pcm_sw_params_alloca (&sw_params);
err = snd_pcm_sw_params_current (handle, sw_params);
if (err < 0) {
dolog ("Can not fully initialize DAC\n");
alsa_logerr (err, "Failed to get current software parameters\n");
return;
}
err = snd_pcm_sw_params_set_start_threshold (handle, sw_params, threshold);
if (err < 0) {
dolog ("Can not fully initialize DAC\n");
alsa_logerr (err, "Failed to set software threshold to %ld\n",
threshold);
return;
}
err = snd_pcm_sw_params (handle, sw_params);
if (err < 0) {
dolog ("Can not fully initialize DAC\n");
alsa_logerr (err, "Failed to set software parameters\n");
return;
}
}
static int alsa_open (int in, struct alsa_params_req *req,
struct alsa_params_obt *obt, snd_pcm_t **handlep)
{
snd_pcm_t *handle;
snd_pcm_hw_params_t *hw_params;
int err, freq, nchannels;
const char *pcm_name = in ? conf.pcm_name_in : conf.pcm_name_out;
unsigned int period_size, buffer_size;
snd_pcm_uframes_t obt_buffer_size;
const char *typ = in ? "ADC" : "DAC";
freq = req->freq;
period_size = req->period_size;
buffer_size = req->buffer_size;
nchannels = req->nchannels;
snd_pcm_hw_params_alloca (&hw_params);
err = snd_pcm_open (
&handle,
pcm_name,
in ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
SND_PCM_NONBLOCK
);
if (err < 0) {
alsa_logerr2 (err, typ, "Failed to open `%s':\n", pcm_name);
return -1;
}
err = snd_pcm_hw_params_any (handle, hw_params);
if (err < 0) {
alsa_logerr2 (err, typ, "Failed to initialize hardware parameters\n");
goto err;
}
err = snd_pcm_hw_params_set_access (
handle,
hw_params,
SND_PCM_ACCESS_RW_INTERLEAVED
);
if (err < 0) {
alsa_logerr2 (err, typ, "Failed to set access type\n");
goto err;
}
err = snd_pcm_hw_params_set_format (handle, hw_params, req->fmt);
if (err < 0) {
alsa_logerr2 (err, typ, "Failed to set format %d\n", req->fmt);
goto err;
}
err = snd_pcm_hw_params_set_rate_near (handle, hw_params, &freq, 0);
if (err < 0) {
alsa_logerr2 (err, typ, "Failed to set frequency %d\n", req->freq);
goto err;
}
err = snd_pcm_hw_params_set_channels_near (
handle,
hw_params,
&nchannels
);
if (err < 0) {
alsa_logerr2 (err, typ, "Failed to set number of channels %d\n",
req->nchannels);
goto err;
}
if (nchannels != 1 && nchannels != 2) {
alsa_logerr2 (err, typ,
"Can not handle obtained number of channels %d\n",
nchannels);
goto err;
}
if (!((in && conf.size_in_usec_in) || (!in && conf.size_in_usec_out))) {
if (!buffer_size) {
buffer_size = DEFAULT_BUFFER_SIZE;
period_size= DEFAULT_PERIOD_SIZE;
}
}
if (buffer_size) {
if ((in && conf.size_in_usec_in) || (!in && conf.size_in_usec_out)) {
if (period_size) {
err = snd_pcm_hw_params_set_period_time_near (
handle,
hw_params,
&period_size,
0);
if (err < 0) {
alsa_logerr2 (err, typ,
"Failed to set period time %d\n",
req->period_size);
goto err;
}
}
err = snd_pcm_hw_params_set_buffer_time_near (
handle,
hw_params,
&buffer_size,
0);
if (err < 0) {
alsa_logerr2 (err, typ,
"Failed to set buffer time %d\n",
req->buffer_size);
goto err;
}
}
else {
int dir;
snd_pcm_uframes_t minval;
if (period_size) {
minval = period_size;
dir = 0;
err = snd_pcm_hw_params_get_period_size_min (
hw_params,
&minval,
&dir
);
if (err < 0) {
alsa_logerr (
err,
"Can not get minmal period size for %s\n",
typ
);
}
else {
if (period_size < minval) {
if ((in && conf.period_size_in_overriden)
|| (!in && conf.period_size_out_overriden)) {
dolog ("%s period size(%d) is less "
"than minmal period size(%ld)\n",
typ,
period_size,
minval);
}
period_size = minval;
}
}
err = snd_pcm_hw_params_set_period_size (
handle,
hw_params,
period_size,
0
);
if (err < 0) {
alsa_logerr2 (err, typ, "Failed to set period size %d\n",
req->period_size);
goto err;
}
}
minval = buffer_size;
err = snd_pcm_hw_params_get_buffer_size_min (
hw_params,
&minval
);
if (err < 0) {
alsa_logerr (err, "Can not get minmal buffer size for %s\n",
typ);
}
else {
if (buffer_size < minval) {
if ((in && conf.buffer_size_in_overriden)
|| (!in && conf.buffer_size_out_overriden)) {
dolog (
"%s buffer size(%d) is less "
"than minimal buffer size(%ld)\n",
typ,
buffer_size,
minval
);
}
buffer_size = minval;
}
}
err = snd_pcm_hw_params_set_buffer_size (
handle,
hw_params,
buffer_size
);
if (err < 0) {
alsa_logerr2 (err, typ, "Failed to set buffer size %d\n",
req->buffer_size);
goto err;
}
}
}
else {
dolog ("warning: buffer size is not set\n");
}
err = snd_pcm_hw_params (handle, hw_params);
if (err < 0) {
alsa_logerr2 (err, typ, "Failed to apply audio parameters\n");
goto err;
}
err = snd_pcm_hw_params_get_buffer_size (hw_params, &obt_buffer_size);
if (err < 0) {
alsa_logerr2 (err, typ, "Failed to get buffer size\n");
goto err;
}
err = snd_pcm_prepare (handle);
if (err < 0) {
alsa_logerr2 (err, typ, "Can not prepare handle %p\n", handle);
goto err;
}
obt->can_pause = snd_pcm_hw_params_can_pause (hw_params);
if (obt->can_pause < 0) {
alsa_logerr (err, "Can not get pause capability for %s\n", typ);
obt->can_pause = 0;
}
if (!in && conf.threshold) {
snd_pcm_uframes_t threshold;
int bytes_per_sec;
bytes_per_sec = freq
<< (nchannels == 2)
<< (req->fmt == AUD_FMT_S16 || req->fmt == AUD_FMT_U16);
threshold = (conf.threshold * bytes_per_sec) / 1000;
alsa_set_threshold (handle, threshold);
}
obt->fmt = req->fmt;
obt->nchannels = nchannels;
obt->freq = freq;
obt->buffer_size = snd_pcm_frames_to_bytes (handle, obt_buffer_size);
*handlep = handle;
if (obt->fmt != req->fmt ||
obt->nchannels != req->nchannels ||
obt->freq != req->freq) {
#ifdef DEBUG_MISMATCHES
dolog ("Audio paramters mismatch for %s\n", typ);
alsa_dump_info (req, obt);
#endif
}
#ifdef DEBUG
alsa_dump_info (req, obt);
#endif
return 0;
err:
alsa_anal_close (&handle);
return -1;
}
static int alsa_recover (snd_pcm_t *handle)
{
int err = snd_pcm_prepare (handle);
if (err < 0) {
alsa_logerr (err, "Failed to prepare handle %p\n", handle);
return -1;
}
return 0;
}
static int alsa_run_out (HWVoiceOut *hw)
{
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
int rpos, live, decr;
int samples;
uint8_t *dst;
st_sample_t *src;
snd_pcm_sframes_t avail;
live = audio_pcm_hw_get_live_out (hw);
if (!live) {
return 0;
}
avail = snd_pcm_avail_update (alsa->handle);
if (avail < 0) {
if (avail == -EPIPE) {
if (!alsa_recover (alsa->handle)) {
avail = snd_pcm_avail_update (alsa->handle);
if (avail >= 0) {
goto ok;
}
}
}
alsa_logerr (avail, "Can not get amount free space\n");
return 0;
}
ok:
decr = audio_MIN (live, avail);
samples = decr;
rpos = hw->rpos;
while (samples) {
int left_till_end_samples = hw->samples - rpos;
int convert_samples = audio_MIN (samples, left_till_end_samples);
snd_pcm_sframes_t written;
src = hw->mix_buf + rpos;
dst = advance (alsa->pcm_buf, rpos << hw->info.shift);
hw->clip (dst, src, convert_samples);
again:
written = snd_pcm_writei (alsa->handle, dst, convert_samples);
if (written < 0) {
switch (written) {
case -EPIPE:
if (!alsa_recover (alsa->handle)) {
goto again;
}
dolog (
"Failed to write %d frames to %p, handle %p not prepared\n",
convert_samples,
dst,
alsa->handle
);
goto exit;
case -EAGAIN:
goto again;
default:
alsa_logerr (written, "Failed to write %d frames to %p\n",
convert_samples, dst);
goto exit;
}
}
mixeng_clear (src, written);
rpos = (rpos + written) % hw->samples;
samples -= written;
}
exit:
hw->rpos = rpos;
return decr;
}
static void alsa_fini_out (HWVoiceOut *hw)
{
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
ldebug ("alsa_fini\n");
alsa_anal_close (&alsa->handle);
if (alsa->pcm_buf) {
qemu_free (alsa->pcm_buf);
alsa->pcm_buf = NULL;
}
}
static int alsa_init_out (HWVoiceOut *hw, int freq, int nchannels, audfmt_e fmt)
{
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
struct alsa_params_req req;
struct alsa_params_obt obt;
audfmt_e effective_fmt;
int endianness;
int err;
snd_pcm_t *handle;
req.fmt = aud_to_alsafmt (fmt);
req.freq = freq;
req.nchannels = nchannels;
req.period_size = conf.period_size_out;
req.buffer_size = conf.buffer_size_out;
if (alsa_open (0, &req, &obt, &handle)) {
return -1;
}
err = alsa_to_audfmt (obt.fmt, &effective_fmt, &endianness);
if (err) {
alsa_anal_close (&handle);
return -1;
}
audio_pcm_init_info (
&hw->info,
obt.freq,
obt.nchannels,
effective_fmt,
audio_need_to_swap_endian (endianness)
);
alsa->can_pause = obt.can_pause;
hw->bufsize = obt.buffer_size;
alsa->pcm_buf = qemu_mallocz (hw->bufsize);
if (!alsa->pcm_buf) {
alsa_anal_close (&handle);
return -1;
}
alsa->handle = handle;
alsa->was_enabled = 0;
return 0;
}
static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...)
{
int err;
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
switch (cmd) {
case VOICE_ENABLE:
ldebug ("enabling voice\n");
audio_pcm_info_clear_buf (&hw->info, alsa->pcm_buf, hw->samples);
if (alsa->can_pause) {
/* Why this was_enabled madness is needed at all?? */
if (alsa->was_enabled) {
err = snd_pcm_pause (alsa->handle, 0);
if (err < 0) {
alsa_logerr (err, "Failed to resume playing\n");
/* not fatal really */
}
}
else {
alsa->was_enabled = 1;
}
}
break;
case VOICE_DISABLE:
ldebug ("disabling voice\n");
if (alsa->can_pause) {
err = snd_pcm_pause (alsa->handle, 1);
if (err < 0) {
alsa_logerr (err, "Failed to stop playing\n");
/* not fatal really */
}
}
break;
}
return 0;
}
static int alsa_init_in (HWVoiceIn *hw,
int freq, int nchannels, audfmt_e fmt)
{
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
struct alsa_params_req req;
struct alsa_params_obt obt;
int endianness;
int err;
audfmt_e effective_fmt;
snd_pcm_t *handle;
req.fmt = aud_to_alsafmt (fmt);
req.freq = freq;
req.nchannels = nchannels;
req.period_size = conf.period_size_in;
req.buffer_size = conf.buffer_size_in;
if (alsa_open (1, &req, &obt, &handle)) {
return -1;
}
err = alsa_to_audfmt (obt.fmt, &effective_fmt, &endianness);
if (err) {
alsa_anal_close (&handle);
return -1;
}
audio_pcm_init_info (
&hw->info,
obt.freq,
obt.nchannels,
effective_fmt,
audio_need_to_swap_endian (endianness)
);
alsa->can_pause = obt.can_pause;
hw->bufsize = obt.buffer_size;
alsa->pcm_buf = qemu_mallocz (hw->bufsize);
if (!alsa->pcm_buf) {
alsa_anal_close (&handle);
return -1;
}
alsa->handle = handle;
return 0;
}
static void alsa_fini_in (HWVoiceIn *hw)
{
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
alsa_anal_close (&alsa->handle);
if (alsa->pcm_buf) {
qemu_free (alsa->pcm_buf);
alsa->pcm_buf = NULL;
}
}
static int alsa_run_in (HWVoiceIn *hw)
{
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
int hwshift = hw->info.shift;
int i;
int live = audio_pcm_hw_get_live_in (hw);
int dead = hw->samples - live;
struct {
int add;
int len;
} bufs[2] = {
{ hw->wpos, 0 },
{ 0, 0 }
};
snd_pcm_uframes_t read_samples = 0;
if (!dead) {
return 0;
}
if (hw->wpos + dead > hw->samples) {
bufs[0].len = (hw->samples - hw->wpos);
bufs[1].len = (dead - (hw->samples - hw->wpos));
}
else {
bufs[0].len = dead;
}
for (i = 0; i < 2; ++i) {
void *src;
st_sample_t *dst;
snd_pcm_sframes_t nread;
snd_pcm_uframes_t len;
len = bufs[i].len;
src = advance (alsa->pcm_buf, bufs[i].add << hwshift);
dst = hw->conv_buf + bufs[i].add;
while (len) {
nread = snd_pcm_readi (alsa->handle, src, len);
if (nread < 0) {
switch (nread) {
case -EPIPE:
if (!alsa_recover (alsa->handle)) {
continue;
}
dolog (
"Failed to read %ld frames from %p, "
"handle %p not prepared\n",
len,
src,
alsa->handle
);
goto exit;
case -EAGAIN:
continue;
default:
alsa_logerr (
nread,
"Failed to read %ld frames from %p\n",
len,
src
);
goto exit;
}
}
hw->conv (dst, src, nread, &nominal_volume);
src = advance (src, nread << hwshift);
dst += nread;
read_samples += nread;
len -= nread;
}
}
exit:
hw->wpos = (hw->wpos + read_samples) % hw->samples;
return read_samples;
}
static int alsa_read (SWVoiceIn *sw, void *buf, int size)
{
return audio_pcm_sw_read (sw, buf, size);
}
static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
{
(void) hw;
(void) cmd;
return 0;
}
static void *alsa_audio_init (void)
{
return &conf;
}
static void alsa_audio_fini (void *opaque)
{
(void) opaque;
}
static struct audio_option alsa_options[] = {
{"DAC_SIZE_IN_USEC", AUD_OPT_BOOL, &conf.size_in_usec_out,
"DAC period/buffer size in microseconds (otherwise in frames)", NULL, 0},
{"DAC_PERIOD_SIZE", AUD_OPT_INT, &conf.period_size_out,
"DAC period size", &conf.period_size_out_overriden, 0},
{"DAC_BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_size_out,
"DAC buffer size", &conf.buffer_size_out_overriden, 0},
{"ADC_SIZE_IN_USEC", AUD_OPT_BOOL, &conf.size_in_usec_in,
"ADC period/buffer size in microseconds (otherwise in frames)", NULL, 0},
{"ADC_PERIOD_SIZE", AUD_OPT_INT, &conf.period_size_in,
"ADC period size", &conf.period_size_in_overriden, 0},
{"ADC_BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_size_in,
"ADC buffer size", &conf.buffer_size_in_overriden, 0},
{"THRESHOLD", AUD_OPT_INT, &conf.threshold,
"(undocumented)", NULL, 0},
{"DAC_DEV", AUD_OPT_STR, &conf.pcm_name_out,
"DAC device name (for instance dmix)", NULL, 0},
{"ADC_DEV", AUD_OPT_STR, &conf.pcm_name_in,
"ADC device name", NULL, 0},
{NULL, 0, NULL, NULL, NULL, 0}
};
static struct audio_pcm_ops alsa_pcm_ops = {
alsa_init_out,
alsa_fini_out,
alsa_run_out,
alsa_write,
alsa_ctl_out,
alsa_init_in,
alsa_fini_in,
alsa_run_in,
alsa_read,
alsa_ctl_in
};
struct audio_driver alsa_audio_driver = {
INIT_FIELD (name = ) "alsa",
INIT_FIELD (descr = ) "ALSA http://www.alsa-project.org",
INIT_FIELD (options = ) alsa_options,
INIT_FIELD (init = ) alsa_audio_init,
INIT_FIELD (fini = ) alsa_audio_fini,
INIT_FIELD (pcm_ops = ) &alsa_pcm_ops,
INIT_FIELD (can_be_default = ) 1,
INIT_FIELD (max_voices_out = ) INT_MAX,
INIT_FIELD (max_voices_in = ) INT_MAX,
INIT_FIELD (voice_size_out = ) sizeof (ALSAVoiceOut),
INIT_FIELD (voice_size_in = ) sizeof (ALSAVoiceIn)
};

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
/*
* QEMU Audio subsystem header
*
* Copyright (c) 2003-2004 Vassili Karpov (malc)
* Copyright (c) 2003-2005 Vassili Karpov (malc)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -24,7 +24,7 @@
#ifndef QEMU_AUDIO_H
#define QEMU_AUDIO_H
#include "mixeng.h"
typedef void (*audio_callback_fn_t) (void *opaque, int avail);
typedef enum {
AUD_FMT_U8,
@ -33,22 +33,60 @@ typedef enum {
AUD_FMT_S16
} audfmt_e;
typedef struct SWVoice SWVoice;
typedef struct SWVoiceOut SWVoiceOut;
typedef struct SWVoiceIn SWVoiceIn;
SWVoice * AUD_open (SWVoice *sw, const char *name, int freq,
int nchannels, audfmt_e fmt);
void AUD_init (void);
typedef struct QEMUAudioTimeStamp {
uint64_t old_ts;
} QEMUAudioTimeStamp;
void AUD_vlog (const char *cap, const char *fmt, va_list ap);
void AUD_log (const char *cap, const char *fmt, ...)
__attribute__ ((__format__ (__printf__, 2, 3)));;
void AUD_close (SWVoice *sw);
int AUD_write (SWVoice *sw, void *pcm_buf, int size);
void AUD_adjust (SWVoice *sw, int leftover);
void AUD_reset (SWVoice *sw);
int AUD_get_free (SWVoice *sw);
int AUD_get_buffer_size (SWVoice *sw);
void AUD_run (void);
void AUD_enable (SWVoice *sw, int on);
int AUD_calc_elapsed (SWVoice *sw);
#ifdef __GNUC__
__attribute__ ((__format__ (__printf__, 2, 3)))
#endif
;
void AUD_init (void);
void AUD_help (void);
SWVoiceOut *AUD_open_out (
SWVoiceOut *sw,
const char *name,
void *callback_opaque,
audio_callback_fn_t callback_fn,
int freq,
int nchannels,
audfmt_e fmt
);
void AUD_close_out (SWVoiceOut *sw);
int AUD_write (SWVoiceOut *sw, void *pcm_buf, int size);
int AUD_get_buffer_size_out (SWVoiceOut *sw);
void AUD_set_active_out (SWVoiceOut *sw, int on);
int AUD_is_active_out (SWVoiceOut *sw);
void AUD_init_time_stamp_out (SWVoiceOut *sw,
QEMUAudioTimeStamp *ts);
uint64_t AUD_time_stamp_get_elapsed_usec_out (SWVoiceOut *sw,
QEMUAudioTimeStamp *ts);
SWVoiceIn *AUD_open_in (
SWVoiceIn *sw,
const char *name,
void *callback_opaque,
audio_callback_fn_t callback_fn,
int freq,
int nchannels,
audfmt_e fmt
);
void AUD_close_in (SWVoiceIn *sw);
int AUD_read (SWVoiceIn *sw, void *pcm_buf, int size);
void AUD_adjust_in (SWVoiceIn *sw, int leftover);
void AUD_set_active_in (SWVoiceIn *sw, int on);
int AUD_is_active_in (SWVoiceIn *sw);
void AUD_init_time_stamp_in (SWVoiceIn *sw,
QEMUAudioTimeStamp *ts);
uint64_t AUD_time_stamp_get_elapsed_usec_in (SWVoiceIn *sw,
QEMUAudioTimeStamp *ts);
static inline void *advance (void *p, int incr)
{
@ -59,7 +97,21 @@ static inline void *advance (void *p, int incr)
uint32_t popcount (uint32_t u);
inline uint32_t lsbindex (uint32_t u);
#ifdef __GNUC__
#define audio_MIN(a, b) ( __extension__ ({ \
__typeof (a) ta = a; \
__typeof (b) tb = b; \
((ta)>(tb)?(tb):(ta)); \
}))
#define audio_MAX(a, b) ( __extension__ ({ \
__typeof (a) ta = a; \
__typeof (b) tb = b; \
((ta)<(tb)?(tb):(ta)); \
}))
#else
#define audio_MIN(a, b) ((a)>(b)?(b):(a))
#define audio_MAX(a, b) ((a)<(b)?(b):(a))
#endif
#endif /* audio.h */

View File

@ -1,7 +1,7 @@
/*
* QEMU Audio subsystem header
*
* Copyright (c) 2003-2004 Vassili Karpov (malc)
* Copyright (c) 2003-2005 Vassili Karpov (malc)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -24,140 +24,266 @@
#ifndef QEMU_AUDIO_INT_H
#define QEMU_AUDIO_INT_H
#include "vl.h"
#include "sys-queue.h"
struct pcm_ops;
#ifdef CONFIG_COREAUDIO
#define FLOAT_MIXENG
/* #define RECIPROCAL */
#endif
#include "mixeng.h"
typedef struct HWVoice {
int audio_bug (const char *funcname, int cond);
struct audio_pcm_ops;
typedef enum {
AUD_OPT_INT,
AUD_OPT_FMT,
AUD_OPT_STR,
AUD_OPT_BOOL
} audio_option_tag_e;
struct audio_option {
const char *name;
audio_option_tag_e tag;
void *valp;
const char *descr;
int *overridenp;
int overriden;
};
struct audio_callback {
void *opaque;
audio_callback_fn_t fn;
};
struct audio_pcm_info {
int bits;
int sign;
int freq;
int nchannels;
int align;
int shift;
int bytes_per_second;
int swap_endian;
};
typedef struct HWVoiceOut {
int active;
int enabled;
int pending_disable;
int valid;
int freq;
struct audio_pcm_info info;
f_sample *clip;
audfmt_e fmt;
int nchannels;
int align;
int shift;
int rpos;
int bufsize;
uint64_t ts_helper;
int bytes_per_second;
st_sample_t *mix_buf;
int samples;
int64_t old_ticks;
int nb_voices;
struct SWVoice **pvoice;
struct pcm_ops *pcm_ops;
} HWVoice;
LIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head;
struct audio_pcm_ops *pcm_ops;
LIST_ENTRY (HWVoiceOut) entries;
} HWVoiceOut;
extern struct pcm_ops no_pcm_ops;
extern struct audio_output_driver no_output_driver;
extern struct pcm_ops oss_pcm_ops;
extern struct audio_output_driver oss_output_driver;
extern struct pcm_ops sdl_pcm_ops;
extern struct audio_output_driver sdl_output_driver;
extern struct pcm_ops wav_pcm_ops;
extern struct audio_output_driver wav_output_driver;
extern struct pcm_ops fmod_pcm_ops;
extern struct audio_output_driver fmod_output_driver;
struct audio_output_driver {
const char *name;
void *(*init) (void);
void (*fini) (void *);
struct pcm_ops *pcm_ops;
int can_be_default;
int max_voices;
int voice_size;
};
typedef struct AudioState {
int fixed_format;
int fixed_freq;
int fixed_channels;
int fixed_fmt;
int nb_hw_voices;
int64_t ticks_threshold;
int freq_threshold;
void *opaque;
struct audio_output_driver *drv;
} AudioState;
extern AudioState audio_state;
struct SWVoice {
int freq;
audfmt_e fmt;
int nchannels;
int shift;
int align;
typedef struct HWVoiceIn {
int enabled;
int active;
struct audio_pcm_info info;
t_sample *conv;
int left;
int pos;
int bytes_per_second;
int wpos;
int bufsize;
int total_samples_captured;
uint64_t ts_helper;
st_sample_t *conv_buf;
int samples;
LIST_HEAD (sw_in_listhead, SWVoiceIn) sw_head;
struct audio_pcm_ops *pcm_ops;
LIST_ENTRY (HWVoiceIn) entries;
} HWVoiceIn;
extern struct audio_driver no_audio_driver;
extern struct audio_driver oss_audio_driver;
extern struct audio_driver sdl_audio_driver;
extern struct audio_driver wav_audio_driver;
extern struct audio_driver fmod_audio_driver;
extern struct audio_driver alsa_audio_driver;
extern struct audio_driver coreaudio_audio_driver;
extern struct audio_driver dsound_audio_driver;
extern volume_t nominal_volume;
struct audio_driver {
const char *name;
const char *descr;
struct audio_option *options;
void *(*init) (void);
void (*fini) (void *);
struct audio_pcm_ops *pcm_ops;
int can_be_default;
int max_voices_out;
int max_voices_in;
int voice_size_out;
int voice_size_in;
};
typedef struct AudioState {
int fixed_settings_out;
int fixed_freq_out;
int fixed_channels_out;
int fixed_fmt_out;
int nb_hw_voices_out;
int greedy_out;
int fixed_settings_in;
int fixed_freq_in;
int fixed_channels_in;
int fixed_fmt_in;
int nb_hw_voices_in;
int greedy_in;
void *opaque;
struct audio_driver *drv;
QEMUTimer *ts;
union {
int usec;
int64_t ticks;
} period;
int plive;
} AudioState;
extern AudioState audio_state;
struct SWVoiceOut {
struct audio_pcm_info info;
t_sample *conv;
int64_t ratio;
st_sample_t *buf;
void *rate;
int wpos;
int live;
int total_hw_samples_mixed;
int active;
int64_t old_ticks;
HWVoice *hw;
int empty;
HWVoiceOut *hw;
char *name;
volume_t vol;
struct audio_callback callback;
LIST_ENTRY (SWVoiceOut) entries;
};
struct pcm_ops {
int (*init) (HWVoice *hw, int freq, int nchannels, audfmt_e fmt);
void (*fini) (HWVoice *hw);
void (*run) (HWVoice *hw);
int (*write) (SWVoice *sw, void *buf, int size);
int (*ctl) (HWVoice *hw, int cmd, ...);
struct SWVoiceIn {
int active;
struct audio_pcm_info info;
int64_t ratio;
void *rate;
int total_hw_samples_acquired;
st_sample_t *conv_buf;
f_sample *clip;
HWVoiceIn *hw;
char *name;
volume_t vol;
struct audio_callback callback;
LIST_ENTRY (SWVoiceIn) entries;
};
void pcm_sw_free_resources (SWVoice *sw);
int pcm_sw_alloc_resources (SWVoice *sw);
void pcm_sw_fini (SWVoice *sw);
int pcm_sw_init (SWVoice *sw, HWVoice *hw, int freq,
int nchannels, audfmt_e fmt);
struct audio_pcm_ops {
int (*init_out)(HWVoiceOut *hw, int freq, int nchannels, audfmt_e fmt);
void (*fini_out)(HWVoiceOut *hw);
int (*run_out) (HWVoiceOut *hw);
int (*write) (SWVoiceOut *sw, void *buf, int size);
int (*ctl_out) (HWVoiceOut *hw, int cmd, ...);
void pcm_hw_clear (HWVoice *hw, void *buf, int len);
HWVoice * pcm_hw_find_any (HWVoice *hw);
HWVoice * pcm_hw_find_any_active (HWVoice *hw);
HWVoice * pcm_hw_find_any_passive (HWVoice *hw);
HWVoice * pcm_hw_find_specific (HWVoice *hw, int freq,
int nchannels, audfmt_e fmt);
HWVoice * pcm_hw_add (int freq, int nchannels, audfmt_e fmt);
int pcm_hw_add_sw (HWVoice *hw, SWVoice *sw);
int pcm_hw_del_sw (HWVoice *hw, SWVoice *sw);
SWVoice * pcm_create_voice_pair (int freq, int nchannels, audfmt_e fmt);
int (*init_in) (HWVoiceIn *hw, int freq, int nchannels, audfmt_e fmt);
void (*fini_in) (HWVoiceIn *hw);
int (*run_in) (HWVoiceIn *hw);
int (*read) (SWVoiceIn *sw, void *buf, int size);
int (*ctl_in) (HWVoiceIn *hw, int cmd, ...);
};
void pcm_hw_free_resources (HWVoice *hw);
int pcm_hw_alloc_resources (HWVoice *hw);
void pcm_hw_fini (HWVoice *hw);
void pcm_hw_gc (HWVoice *hw);
int pcm_hw_get_live (HWVoice *hw);
int pcm_hw_get_live2 (HWVoice *hw, int *nb_active);
void pcm_hw_dec_live (HWVoice *hw, int decr);
int pcm_hw_write (SWVoice *sw, void *buf, int len);
void audio_pcm_init_info (struct audio_pcm_info *info, int freq,
int nchannels, audfmt_e fmt, int swap_endian);
void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len);
int audio_get_conf_int (const char *key, int defval);
const char *audio_get_conf_str (const char *key, const char *defval);
int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int len);
int audio_pcm_hw_get_live_in (HWVoiceIn *hw);
struct audio_output_driver;
int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int len);
int audio_pcm_hw_get_live_out (HWVoiceOut *hw);
int audio_pcm_hw_get_live_out2 (HWVoiceOut *hw, int *nb_live);
#define VOICE_ENABLE 1
#define VOICE_DISABLE 2
static inline int audio_ring_dist (int dst, int src, int len)
{
return (dst >= src) ? (dst - src) : (len - src + dst);
}
static inline int audio_need_to_swap_endian (int endianness)
{
#ifdef WORDS_BIGENDIAN
return endianness != 1;
#else
return endianness != 0;
#endif
}
#if defined __GNUC__
#define GCC_ATTR __attribute__ ((__unused__, __format__ (__printf__, 1, 2)))
#define INIT_FIELD(f) . f
#define GCC_FMT_ATTR(n, m) __attribute__ ((__format__ (printf, n, m)))
#else
#define GCC_ATTR /**/
#define INIT_FIELD(f) /**/
#define GCC_FMT_ATTR(n, m)
#endif
static void GCC_ATTR dolog (const char *fmt, ...)
{
va_list ap;
va_start (ap, fmt);
AUD_vlog (AUDIO_CAP, fmt, ap);
va_end (ap);
}
#ifdef DEBUG
static void GCC_ATTR ldebug (const char *fmt, ...)
{
va_list ap;
va_start (ap, fmt);
AUD_vlog (AUDIO_CAP, fmt, ap);
va_end (ap);
}
#else
#if defined NDEBUG && defined __GNUC__
#define ldebug(...)
#elif defined NDEBUG && defined _MSC_VER
#define ldebug __noop
#else
static void GCC_ATTR ldebug (const char *fmt, ...)
{
(void) fmt;
}
#endif
#endif
#undef GCC_ATTR
#define AUDIO_STRINGIFY_(n) #n
#define AUDIO_STRINGIFY(n) AUDIO_STRINGIFY_(n)
#if defined _MSC_VER || defined __GNUC__
#define AUDIO_FUNC __FUNCTION__
#else
#define AUDIO_FUNC __FILE__ ":" AUDIO_STRINGIFY (__LINE__)
#endif
#endif /* audio_int.h */

401
audio/audio_template.h Normal file
View File

@ -0,0 +1,401 @@
/*
* QEMU Audio subsystem header
*
* Copyright (c) 2005 Vassili Karpov (malc)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifdef DAC
#define TYPE out
#define HW glue (HWVoice, Out)
#define SW glue (SWVoice, Out)
#else
#define TYPE in
#define HW glue (HWVoice, In)
#define SW glue (SWVoice, In)
#endif
static void glue (audio_pcm_sw_fini_, TYPE) (SW *sw)
{
glue (audio_pcm_sw_free_resources_, TYPE) (sw);
if (sw->name) {
qemu_free (sw->name);
sw->name = NULL;
}
}
static void glue (audio_pcm_hw_add_sw_, TYPE) (HW *hw, SW *sw)
{
LIST_INSERT_HEAD (&hw->sw_head, sw, entries);
}
static void glue (audio_pcm_hw_del_sw_, TYPE) (SW *sw)
{
LIST_REMOVE (sw, entries);
}
static void glue (audio_pcm_hw_fini_, TYPE) (HW *hw)
{
if (hw->active) {
glue (audio_pcm_hw_free_resources_ ,TYPE) (hw);
glue (hw->pcm_ops->fini_, TYPE) (hw);
memset (hw, 0, glue (audio_state.drv->voice_size_, TYPE));
}
}
static void glue (audio_pcm_hw_gc_, TYPE) (HW *hw)
{
if (!hw->sw_head.lh_first) {
glue (audio_pcm_hw_fini_, TYPE) (hw);
}
}
static HW *glue (audio_pcm_hw_find_any_, TYPE) (HW *hw)
{
return hw ? hw->entries.le_next : glue (hw_head_, TYPE).lh_first;
}
static HW *glue (audio_pcm_hw_find_any_active_, TYPE) (HW *hw)
{
while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (hw))) {
if (hw->active) {
return hw;
}
}
return NULL;
}
static HW *glue (audio_pcm_hw_find_any_active_enabled_, TYPE) (HW *hw)
{
while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (hw))) {
if (hw->active && hw->enabled) {
return hw;
}
}
return NULL;
}
static HW *glue (audio_pcm_hw_find_any_passive_, TYPE) (HW *hw)
{
while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (hw))) {
if (!hw->active) {
return hw;
}
}
return NULL;
}
static HW *glue (audio_pcm_hw_find_specific_, TYPE) (
HW *hw,
int freq,
int nchannels,
audfmt_e fmt
)
{
while ((hw = glue (audio_pcm_hw_find_any_active_, TYPE) (hw))) {
if (audio_pcm_info_eq (&hw->info, freq, nchannels, fmt)) {
return hw;
}
}
return NULL;
}
static HW *glue (audio_pcm_hw_add_new_, TYPE) (
int freq,
int nchannels,
audfmt_e fmt
)
{
HW *hw;
hw = glue (audio_pcm_hw_find_any_passive_, TYPE) (NULL);
if (hw) {
hw->pcm_ops = audio_state.drv->pcm_ops;
if (!hw->pcm_ops) {
return NULL;
}
if (glue (audio_pcm_hw_init_, TYPE) (hw, freq, nchannels, fmt)) {
glue (audio_pcm_hw_gc_, TYPE) (hw);
return NULL;
}
else {
return hw;
}
}
return NULL;
}
static HW *glue (audio_pcm_hw_add_, TYPE) (
int freq,
int nchannels,
audfmt_e fmt
)
{
HW *hw;
if (glue (audio_state.greedy_, TYPE)) {
hw = glue (audio_pcm_hw_add_new_, TYPE) (freq, nchannels, fmt);
if (hw) {
return hw;
}
}
hw = glue (audio_pcm_hw_find_specific_, TYPE) (NULL, freq, nchannels, fmt);
if (hw) {
return hw;
}
hw = glue (audio_pcm_hw_add_new_, TYPE) (freq, nchannels, fmt);
if (hw) {
return hw;
}
return glue (audio_pcm_hw_find_any_active_, TYPE) (NULL);
}
static SW *glue (audio_pcm_create_voice_pair_, TYPE) (
const char *name,
int freq,
int nchannels,
audfmt_e fmt
)
{
SW *sw;
HW *hw;
int hw_freq = freq;
int hw_nchannels = nchannels;
int hw_fmt = fmt;
if (glue (audio_state.fixed_settings_, TYPE)) {
hw_freq = glue (audio_state.fixed_freq_, TYPE);
hw_nchannels = glue (audio_state.fixed_channels_, TYPE);
hw_fmt = glue (audio_state.fixed_fmt_, TYPE);
}
sw = qemu_mallocz (sizeof (*sw));
if (!sw) {
goto err1;
}
hw = glue (audio_pcm_hw_add_, TYPE) (hw_freq, hw_nchannels, hw_fmt);
if (!hw) {
goto err2;
}
glue (audio_pcm_hw_add_sw_, TYPE) (hw, sw);
if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, name, freq, nchannels, fmt)) {
goto err3;
}
return sw;
err3:
glue (audio_pcm_hw_del_sw_, TYPE) (sw);
glue (audio_pcm_hw_gc_, TYPE) (hw);
err2:
qemu_free (sw);
err1:
return NULL;
}
void glue (AUD_close_, TYPE) (SW *sw)
{
if (sw) {
glue (audio_pcm_sw_fini_, TYPE) (sw);
glue (audio_pcm_hw_del_sw_, TYPE) (sw);
glue (audio_pcm_hw_gc_, TYPE) (sw->hw);
qemu_free (sw);
}
}
SW *glue (AUD_open_, TYPE) (
SW *sw,
const char *name,
void *callback_opaque ,
audio_callback_fn_t callback_fn,
int freq,
int nchannels,
audfmt_e fmt
)
{
#ifdef DAC
int live = 0;
SW *old_sw = NULL;
#endif
if (!callback_fn) {
dolog ("No callback specifed for voice `%s'\n", name);
goto fail;
}
if (nchannels != 1 && nchannels != 2) {
dolog ("Bogus channel count %d for voice `%s'\n", nchannels, name);
goto fail;
}
if (!audio_state.drv) {
dolog ("No audio driver defined\n");
goto fail;
}
if (sw && audio_pcm_info_eq (&sw->info, freq, nchannels, fmt)) {
return sw;
}
#ifdef DAC
if (audio_state.plive && sw && (!sw->active && !sw->empty)) {
live = sw->total_hw_samples_mixed;
#ifdef DEBUG_PLIVE
dolog ("Replacing voice %s with %d live samples\n", sw->name, live);
dolog ("Old %s freq %d, bits %d, channels %d\n",
sw->name, sw->info.freq, sw->info.bits, sw->info.nchannels);
dolog ("New %s freq %d, bits %d, channels %d\n",
name, freq, (fmt == AUD_FMT_S16 || fmt == AUD_FMT_U16) ? 16 : 8,
nchannels);
#endif
if (live) {
old_sw = sw;
old_sw->callback.fn = NULL;
sw = NULL;
}
}
#endif
if (!glue (audio_state.fixed_settings_, TYPE) && sw) {
glue (AUD_close_, TYPE) (sw);
sw = NULL;
}
if (sw) {
HW *hw = sw->hw;
if (!hw) {
dolog ("Internal logic error voice %s has no hardware store\n",
name);
goto fail;
}
if (glue (audio_pcm_sw_init_, TYPE) (
sw,
hw,
name,
freq,
nchannels,
fmt
)) {
goto fail;
}
}
else {
sw = glue (audio_pcm_create_voice_pair_, TYPE) (
name,
freq,
nchannels,
fmt);
if (!sw) {
dolog ("Failed to create voice %s\n", name);
goto fail;
}
}
if (sw) {
sw->vol = nominal_volume;
sw->callback.fn = callback_fn;
sw->callback.opaque = callback_opaque;
#ifdef DAC
if (live) {
int mixed =
(live << old_sw->info.shift)
* old_sw->info.bytes_per_second
/ sw->info.bytes_per_second;
#ifdef DEBUG_PLIVE
dolog ("Silence will be mixed %d\n", mixed);
#endif
sw->total_hw_samples_mixed += mixed;
}
#endif
#ifdef DEBUG_AUDIO
dolog ("%s\n", name);
audio_pcm_print_info ("hw", &sw->hw->info);
audio_pcm_print_info ("sw", &sw->info);
#endif
}
return sw;
fail:
glue (AUD_close_, TYPE) (sw);
return NULL;
}
int glue (AUD_is_active_, TYPE) (SW *sw)
{
return sw ? sw->active : 0;
}
void glue (AUD_init_time_stamp_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts)
{
if (!sw) {
return;
}
ts->old_ts = sw->hw->ts_helper;
}
uint64_t glue (AUD_time_stamp_get_elapsed_usec_, TYPE) (
SW *sw,
QEMUAudioTimeStamp *ts
)
{
uint64_t delta, cur_ts, old_ts;
if (!sw) {
return 0;
}
cur_ts = sw->hw->ts_helper;
old_ts = ts->old_ts;
/* dolog ("cur %lld old %lld\n", cur_ts, old_ts); */
if (cur_ts >= old_ts) {
delta = cur_ts - old_ts;
}
else {
delta = UINT64_MAX - old_ts + cur_ts;
}
if (!delta) {
return 0;
}
return (delta * sw->hw->info.freq) / 1000000;
}
#undef TYPE
#undef HW
#undef SW

513
audio/coreaudio.c Normal file
View File

@ -0,0 +1,513 @@
/*
* QEMU OS X CoreAudio audio driver
*
* Copyright (c) 2005 Mike Kronenberg
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <CoreAudio/CoreAudio.h>
#include <string.h> /* strerror */
#include <pthread.h> /* pthread_X */
#include "vl.h"
#define AUDIO_CAP "coreaudio"
#include "audio_int.h"
#define DEVICE_BUFFER_FRAMES (512)
struct {
int buffer_frames;
} conf = {
.buffer_frames = 512
};
typedef struct coreaudioVoiceOut {
HWVoiceOut hw;
pthread_mutex_t mutex;
AudioDeviceID outputDeviceID;
UInt32 audioDevicePropertyBufferSize;
AudioStreamBasicDescription outputStreamBasicDescription;
int isPlaying;
int live;
int decr;
int rpos;
} coreaudioVoiceOut;
static void coreaudio_logstatus (OSStatus status)
{
char *str = "BUG";
switch(status) {
case kAudioHardwareNoError:
str = "kAudioHardwareNoError";
break;
case kAudioHardwareNotRunningError:
str = "kAudioHardwareNotRunningError";
break;
case kAudioHardwareUnspecifiedError:
str = "kAudioHardwareUnspecifiedError";
break;
case kAudioHardwareUnknownPropertyError:
str = "kAudioHardwareUnknownPropertyError";
break;
case kAudioHardwareBadPropertySizeError:
str = "kAudioHardwareBadPropertySizeError";
break;
case kAudioHardwareIllegalOperationError:
str = "kAudioHardwareIllegalOperationError";
break;
case kAudioHardwareBadDeviceError:
str = "kAudioHardwareBadDeviceError";
break;
case kAudioHardwareBadStreamError:
str = "kAudioHardwareBadStreamError";
break;
case kAudioHardwareUnsupportedOperationError:
str = "kAudioHardwareUnsupportedOperationError";
break;
case kAudioDeviceUnsupportedFormatError:
str = "kAudioDeviceUnsupportedFormatError";
break;
case kAudioDevicePermissionsError:
str = "kAudioDevicePermissionsError";
break;
default:
AUD_log (AUDIO_CAP, "Reason: status code %ld\n", status);
return;
}
AUD_log (AUDIO_CAP, "Reason: %s\n", str);
}
static void GCC_FMT_ATTR (2, 3) coreaudio_logerr (
OSStatus status,
const char *fmt,
...
)
{
va_list ap;
va_start (ap, fmt);
AUD_log (AUDIO_CAP, fmt, ap);
va_end (ap);
coreaudio_logstatus (status);
}
static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 (
OSStatus status,
const char *typ,
const char *fmt,
...
)
{
va_list ap;
AUD_log (AUDIO_CAP, "Can not initialize %s\n", typ);
va_start (ap, fmt);
AUD_vlog (AUDIO_CAP, fmt, ap);
va_end (ap);
coreaudio_logstatus (status);
}
static int coreaudio_lock (coreaudioVoiceOut *core, const char *fn_name)
{
int err;
err = pthread_mutex_lock (&core->mutex);
if (err) {
dolog ("Can not lock voice for %s\nReason: %s\n",
fn_name, strerror (err));
return -1;
}
return 0;
}
static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name)
{
int err;
err = pthread_mutex_unlock (&core->mutex);
if (err) {
dolog ("Can not unlock voice for %s\nReason: %s\n",
fn_name, strerror (err));
return -1;
}
return 0;
}
static int coreaudio_run_out (HWVoiceOut *hw)
{
int live, decr;
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
if (coreaudio_lock (core, "coreaudio_run_out")) {
return 0;
}
live = audio_pcm_hw_get_live_out (hw);
if (core->decr > live) {
ldebug ("core->decr %d live %d core->live %d\n",
core->decr,
live,
core->live);
}
decr = audio_MIN (core->decr, live);
core->decr -= decr;
core->live = live - decr;
hw->rpos = core->rpos;
coreaudio_unlock (core, "coreaudio_run_out");
return decr;
}
/* callback to feed audiooutput buffer */
static OSStatus audioDeviceIOProc(
AudioDeviceID inDevice,
const AudioTimeStamp* inNow,
const AudioBufferList* inInputData,
const AudioTimeStamp* inInputTime,
AudioBufferList* outOutputData,
const AudioTimeStamp* inOutputTime,
void* hwptr)
{
unsigned int frame, frameCount;
float *out = outOutputData->mBuffers[0].mData;
HWVoiceOut *hw = hwptr;
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr;
int rpos, live;
st_sample_t *src;
#ifndef FLOAT_MIXENG
#ifdef RECIPROCAL
const float scale = 1.f / UINT_MAX;
#else
const float scale = UINT_MAX;
#endif
#endif
if (coreaudio_lock (core, "audioDeviceIOProc")) {
inInputTime = 0;
return 0;
}
frameCount = conf.buffer_frames;
live = core->live;
/* if there are not enough samples, set signal and return */
if (live < frameCount) {
inInputTime = 0;
coreaudio_unlock (core, "audioDeviceIOProc(empty)");
return 0;
}
rpos = core->rpos;
src = hw->mix_buf + rpos;
/* fill buffer */
for (frame = 0; frame < frameCount; frame++) {
#ifdef FLOAT_MIXENG
*out++ = src[frame].l; /* left channel */
*out++ = src[frame].r; /* right channel */
#else
#ifdef RECIPROCAL
*out++ = src[frame].l * scale; /* left channel */
*out++ = src[frame].r * scale; /* right channel */
#else
*out++ = src[frame].l / scale; /* left channel */
*out++ = src[frame].r / scale; /* right channel */
#endif
#endif
}
/* cleanup */
mixeng_clear (src, frameCount);
rpos = (rpos + frameCount) % hw->samples;
core->decr = frameCount;
core->rpos = rpos;
coreaudio_unlock (core, "audioDeviceIOProc");
return 0;
}
static int coreaudio_write (SWVoiceOut *sw, void *buf, int len)
{
return audio_pcm_sw_write (sw, buf, len);
}
static int coreaudio_init_out (HWVoiceOut *hw, int freq,
int nchannels, audfmt_e fmt)
{
OSStatus status;
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
UInt32 propertySize;
int err;
int bits = 8;
int endianess = 0;
const char *typ = "DAC";
/* create mutex */
err = pthread_mutex_init(&core->mutex, NULL);
if (err) {
dolog("Can not create mutex\nReason: %s\n", strerror (err));
return -1;
}
if (fmt == AUD_FMT_S16 || fmt == AUD_FMT_U16) {
bits = 16;
endianess = 1;
}
audio_pcm_init_info (
&hw->info,
freq,
nchannels,
fmt,
/* Following is irrelevant actually since we do not use
mixengs clipping routines */
audio_need_to_swap_endian (endianess)
);
hw->bufsize = 4 * conf.buffer_frames * nchannels * bits;
/* open default output device */
propertySize = sizeof(core->outputDeviceID);
status = AudioHardwareGetProperty(
kAudioHardwarePropertyDefaultOutputDevice,
&propertySize,
&core->outputDeviceID);
if (status != kAudioHardwareNoError) {
coreaudio_logerr2 (status, typ,
"Can not get default output Device\n");
return -1;
}
if (core->outputDeviceID == kAudioDeviceUnknown) {
dolog ("Can not initialize %s - Unknown Audiodevice\n", typ);
return -1;
}
/* set Buffersize to conf.buffer_frames frames */
propertySize = sizeof(core->audioDevicePropertyBufferSize);
core->audioDevicePropertyBufferSize =
conf.buffer_frames * sizeof(float) * 2;
status = AudioDeviceSetProperty(
core->outputDeviceID,
NULL,
0,
false,
kAudioDevicePropertyBufferSize,
propertySize,
&core->audioDevicePropertyBufferSize);
if (status != kAudioHardwareNoError) {
coreaudio_logerr2 (status, typ,
"Can not set device buffer size %d\n",
kAudioDevicePropertyBufferSize);
return -1;
}
/* get Buffersize */
propertySize = sizeof(core->audioDevicePropertyBufferSize);
status = AudioDeviceGetProperty(
core->outputDeviceID,
0,
false,
kAudioDevicePropertyBufferSize,
&propertySize,
&core->audioDevicePropertyBufferSize);
if (status != kAudioHardwareNoError) {
coreaudio_logerr2 (status, typ, "Can not get device buffer size\n");
return -1;
}
/* get StreamFormat */
propertySize = sizeof(core->outputStreamBasicDescription);
status = AudioDeviceGetProperty(
core->outputDeviceID,
0,
false,
kAudioDevicePropertyStreamFormat,
&propertySize,
&core->outputStreamBasicDescription);
if (status != kAudioHardwareNoError) {
coreaudio_logerr2 (status, typ,
"Can not get Device Stream properties\n");
core->outputDeviceID = kAudioDeviceUnknown;
return -1;
}
/* set Samplerate */
core->outputStreamBasicDescription.mSampleRate = (Float64)freq;
propertySize = sizeof(core->outputStreamBasicDescription);
status = AudioDeviceSetProperty(
core->outputDeviceID,
0,
0,
0,
kAudioDevicePropertyStreamFormat,
propertySize,
&core->outputStreamBasicDescription);
if (status != kAudioHardwareNoError) {
coreaudio_logerr2 (status, typ, "Can not set samplerate %d\n", freq);
core->outputDeviceID = kAudioDeviceUnknown;
return -1;
}
/* set Callback */
status = AudioDeviceAddIOProc(core->outputDeviceID, audioDeviceIOProc, hw);
if (status != kAudioHardwareNoError) {
coreaudio_logerr2 (status, typ, "Can not set IOProc\n");
core->outputDeviceID = kAudioDeviceUnknown;
return -1;
}
/* start Playback */
if (!core->isPlaying) {
status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc);
if (status != kAudioHardwareNoError) {
coreaudio_logerr2 (status, typ, "Can not start playback\n");
AudioDeviceRemoveIOProc(core->outputDeviceID, audioDeviceIOProc);
core->outputDeviceID = kAudioDeviceUnknown;
return -1;
}
core->isPlaying = 1;
}
return 0;
}
static void coreaudio_fini_out (HWVoiceOut *hw)
{
OSStatus status;
int err;
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
/* stop playback */
if (core->isPlaying) {
status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc);
if (status != kAudioHardwareNoError) {
coreaudio_logerr (status, "Can not stop playback\n");
}
core->isPlaying = 0;
}
/* remove callback */
status = AudioDeviceRemoveIOProc(core->outputDeviceID, audioDeviceIOProc);
if (status != kAudioHardwareNoError) {
coreaudio_logerr (status, "Can not remove IOProc\n");
}
core->outputDeviceID = kAudioDeviceUnknown;
/* destroy mutex */
err = pthread_mutex_destroy(&core->mutex);
if (err) {
dolog("Can not destroy mutex\nReason: %s\n", strerror (err));
}
}
static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
{
OSStatus status;
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
switch (cmd) {
case VOICE_ENABLE:
/* start playback */
if (!core->isPlaying) {
status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc);
if (status != kAudioHardwareNoError) {
coreaudio_logerr (status, "Can not unpause playback\n");
}
core->isPlaying = 1;
}
break;
case VOICE_DISABLE:
/* stop playback */
if (core->isPlaying) {
status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc);
if (status != kAudioHardwareNoError) {
coreaudio_logerr (status, "Can not pause playback\n");
}
core->isPlaying = 0;
}
break;
}
return 0;
}
static void *coreaudio_audio_init (void)
{
return &coreaudio_audio_init;
}
static void coreaudio_audio_fini (void *opaque)
{
(void) opaque;
}
static struct audio_option coreaudio_options[] = {
{"BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_frames,
"Size of the buffer in frames", NULL, 0},
{NULL, 0, NULL, NULL, NULL, 0}
};
static struct audio_pcm_ops coreaudio_pcm_ops = {
coreaudio_init_out,
coreaudio_fini_out,
coreaudio_run_out,
coreaudio_write,
coreaudio_ctl_out,
NULL,
NULL,
NULL,
NULL,
NULL
};
struct audio_driver coreaudio_audio_driver = {
INIT_FIELD (name = ) "coreaudio",
INIT_FIELD (descr = )
"CoreAudio http://developer.apple.com/audio/coreaudio.html",
INIT_FIELD (options = ) coreaudio_options,
INIT_FIELD (init = ) coreaudio_audio_init,
INIT_FIELD (fini = ) coreaudio_audio_fini,
INIT_FIELD (pcm_ops = ) &coreaudio_pcm_ops,
INIT_FIELD (can_be_default = ) 1,
INIT_FIELD (max_voices_out = ) 1,
INIT_FIELD (max_voices_in = ) 0,
INIT_FIELD (voice_size_out = ) sizeof (coreaudioVoiceOut),
INIT_FIELD (voice_size_in = ) 0
};

298
audio/dsound_template.h Normal file
View File

@ -0,0 +1,298 @@
/*
* QEMU DirectSound audio driver header
*
* Copyright (c) 2005 Vassili Karpov (malc)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifdef DSBTYPE_IN
#define NAME "capture buffer"
#define TYPE in
#define IFACE IDirectSoundCaptureBuffer
#define BUFPTR LPDIRECTSOUNDCAPTUREBUFFER
#define FIELD dsound_capture_buffer
#else
#define NAME "playback buffer"
#define TYPE out
#define IFACE IDirectSoundBuffer
#define BUFPTR LPDIRECTSOUNDBUFFER
#define FIELD dsound_buffer
#endif
static int glue (dsound_unlock_, TYPE) (
BUFPTR buf,
LPVOID p1,
LPVOID p2,
DWORD blen1,
DWORD blen2
)
{
HRESULT hr;
hr = glue (IFACE, _Unlock) (buf, p1, blen1, p2, blen2);
if (FAILED (hr)) {
dsound_logerr (hr, "Can not unlock " NAME "\n");
return -1;
}
return 0;
}
static int glue (dsound_lock_, TYPE) (
BUFPTR buf,
struct audio_pcm_info *info,
DWORD pos,
DWORD len,
LPVOID *p1p,
LPVOID *p2p,
DWORD *blen1p,
DWORD *blen2p,
int entire
)
{
HRESULT hr;
int i;
LPVOID p1 = NULL, p2 = NULL;
DWORD blen1 = 0, blen2 = 0;
for (i = 0; i < conf.lock_retries; ++i) {
hr = glue (IFACE, _Lock) (
buf,
pos,
len,
&p1,
&blen1,
&p2,
&blen2,
(entire
#ifdef DSBTYPE_IN
? DSCBLOCK_ENTIREBUFFER
#else
? DSBLOCK_ENTIREBUFFER
#endif
: 0)
);
if (FAILED (hr)) {
#ifndef DSBTYPE_IN
if (hr == DSERR_BUFFERLOST) {
if (glue (dsound_restore_, TYPE) (buf)) {
dsound_logerr (hr, "Can not lock " NAME "\n");
goto fail;
}
continue;
}
#endif
dsound_logerr (hr, "Can not lock " NAME "\n");
goto fail;
}
break;
}
if (i == conf.lock_retries) {
dolog ("%d attempts to lock " NAME " failed\n", i);
goto fail;
}
if ((p1 && (blen1 & info->align)) || (p2 && (blen2 & info->align))) {
dolog ("DirectSound returned misaligned buffer %ld %ld\n",
blen1, blen2);
glue (dsound_unlock_, TYPE) (buf, p1, p2, blen1, blen2);
goto fail;
}
if (!p1 && blen1) {
dolog ("warning: !p1 && blen1=%ld\n", blen1);
blen1 = 0;
}
if (!p2 && blen2) {
dolog ("warning: !p2 && blen2=%ld\n", blen2);
blen2 = 0;
}
*p1p = p1;
*p2p = p2;
*blen1p = blen1;
*blen2p = blen2;
return 0;
fail:
*p1p = NULL - 1;
*p2p = NULL - 1;
*blen1p = -1;
*blen2p = -1;
return -1;
}
#ifdef DSBTYPE_IN
static void dsound_fini_in (HWVoiceIn *hw)
#else
static void dsound_fini_out (HWVoiceOut *hw)
#endif
{
HRESULT hr;
#ifdef DSBTYPE_IN
DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
#else
DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
#endif
if (ds->FIELD) {
hr = glue (IFACE, _Stop) (ds->FIELD);
if (FAILED (hr)) {
dsound_logerr (hr, "Can not stop " NAME "\n");
}
hr = glue (IFACE, _Release) (ds->FIELD);
if (FAILED (hr)) {
dsound_logerr (hr, "Can not release " NAME "\n");
}
ds->FIELD = NULL;
}
}
#ifdef DSBTYPE_IN
static int dsound_init_in (
HWVoiceIn *hw,
int freq,
int nchannels,
audfmt_e fmt
)
#else
static int dsound_init_out (
HWVoiceOut *hw,
int freq,
int nchannels,
audfmt_e fmt
)
#endif
{
int err;
HRESULT hr;
dsound *s = &glob_dsound;
WAVEFORMATEX wfx;
struct full_fmt full_fmt;
#ifdef DSBTYPE_IN
const char *typ = "ADC";
DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
DSCBUFFERDESC bd;
DSCBCAPS bc;
#else
const char *typ = "DAC";
DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
DSBUFFERDESC bd;
DSBCAPS bc;
#endif
full_fmt.freq = freq;
full_fmt.nchannels = nchannels;
full_fmt.fmt = fmt;
err = waveformat_from_full_fmt (&wfx, &full_fmt);
if (err) {
return -1;
}
memset (&bd, 0, sizeof (bd));
bd.dwSize = sizeof (bd);
bd.lpwfxFormat = &wfx;
#ifdef DSBTYPE_IN
bd.dwBufferBytes = conf.bufsize_in;
hr = IDirectSoundCapture_CreateCaptureBuffer (
s->dsound_capture,
&bd,
&ds->dsound_capture_buffer,
NULL
);
#else
bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2;
bd.dwBufferBytes = conf.bufsize_out;
hr = IDirectSound_CreateSoundBuffer (
s->dsound,
&bd,
&ds->dsound_buffer,
NULL
);
#endif
if (FAILED (hr)) {
dsound_logerr2 (hr, typ, "Can not create " NAME "\n");
return -1;
}
hr = glue (IFACE, _GetFormat) (
ds->FIELD,
&wfx,
sizeof (wfx),
NULL
);
if (FAILED (hr)) {
dsound_logerr2 (hr, typ, "Can not get " NAME " format\n");
goto fail0;
}
#ifdef DEBUG_DSOUND
dolog (NAME "\n");
print_wave_format (&wfx);
#endif
memset (&bc, 0, sizeof (bc));
bc.dwSize = sizeof (bc);
hr = glue (IFACE, _GetCaps) (ds->FIELD, &bc);
if (FAILED (hr)) {
dsound_logerr2 (hr, typ, "Can not get " NAME " format\n");
goto fail0;
}
err = waveformat_to_full_fmt (&wfx, &full_fmt);
if (err) {
goto fail0;
}
ds->first_time = 1;
hw->bufsize = bc.dwBufferBytes;
audio_pcm_init_info (
&hw->info,
full_fmt.freq,
full_fmt.nchannels,
full_fmt.fmt,
audio_need_to_swap_endian (0)
);
#ifdef DEBUG_DSOUND
dolog ("caps %ld, desc %ld\n",
bc.dwBufferBytes, bd.dwBufferBytes);
dolog ("bufsize %d, freq %d, chan %d, fmt %d\n",
hw->bufsize, full_fmt.freq, full_fmt.nchannels, full_fmt.fmt);
#endif
return 0;
fail0:
glue (dsound_fini_, TYPE) (hw);
return -1;
}
#undef NAME
#undef TYPE
#undef IFACE
#undef BUFPTR
#undef FIELD

1082
audio/dsoundaudio.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
/*
* QEMU FMOD audio output driver
* QEMU FMOD audio driver
*
* Copyright (c) 2004 Vassili Karpov (malc)
* Copyright (c) 2004-2005 Vassili Karpov (malc)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -25,53 +25,77 @@
#include <fmod_errors.h>
#include "vl.h"
#include "audio/audio_int.h"
#define AUDIO_CAP "fmod"
#include "audio_int.h"
typedef struct FMODVoice {
HWVoice hw;
typedef struct FMODVoiceOut {
HWVoiceOut hw;
unsigned int old_pos;
FSOUND_SAMPLE *fmod_sample;
int channel;
} FMODVoice;
} FMODVoiceOut;
#define dolog(...) AUD_log ("fmod", __VA_ARGS__)
#ifdef DEBUG
#define ldebug(...) dolog (__VA_ARGS__)
#else
#define ldebug(...)
#endif
#define QC_FMOD_DRV "QEMU_FMOD_DRV"
#define QC_FMOD_FREQ "QEMU_FMOD_FREQ"
#define QC_FMOD_SAMPLES "QEMU_FMOD_SAMPLES"
#define QC_FMOD_CHANNELS "QEMU_FMOD_CHANNELS"
#define QC_FMOD_BUFSIZE "QEMU_FMOD_BUFSIZE"
#define QC_FMOD_THRESHOLD "QEMU_FMOD_THRESHOLD"
typedef struct FMODVoiceIn {
HWVoiceIn hw;
FSOUND_SAMPLE *fmod_sample;
} FMODVoiceIn;
static struct {
const char *drvname;
int nb_samples;
int freq;
int nb_channels;
int bufsize;
int threshold;
int broken_adc;
} conf = {
2048,
NULL,
2048 * 2,
44100,
1,
2,
0,
128
0,
0
};
#define errstr() FMOD_ErrorString (FSOUND_GetError ())
static int fmod_hw_write (SWVoice *sw, void *buf, int len)
static void GCC_FMT_ATTR (1, 2) fmod_logerr (const char *fmt, ...)
{
return pcm_hw_write (sw, buf, len);
va_list ap;
va_start (ap, fmt);
AUD_vlog (AUDIO_CAP, fmt, ap);
va_end (ap);
AUD_log (AUDIO_CAP, "Reason: %s\n",
FMOD_ErrorString (FSOUND_GetError ()));
}
static void fmod_clear_sample (FMODVoice *fmd)
static void GCC_FMT_ATTR (2, 3) fmod_logerr2 (
const char *typ,
const char *fmt,
...
)
{
HWVoice *hw = &fmd->hw;
va_list ap;
AUD_log (AUDIO_CAP, "Can not initialize %s\n", typ);
va_start (ap, fmt);
AUD_vlog (AUDIO_CAP, fmt, ap);
va_end (ap);
AUD_log (AUDIO_CAP, "Reason: %s\n",
FMOD_ErrorString (FSOUND_GetError ()));
}
static int fmod_write (SWVoiceOut *sw, void *buf, int len)
{
return audio_pcm_sw_write (sw, buf, len);
}
static void fmod_clear_sample (FMODVoiceOut *fmd)
{
HWVoiceOut *hw = &fmd->hw;
int status;
void *p1 = 0, *p2 = 0;
unsigned int len1 = 0, len2 = 0;
@ -79,7 +103,7 @@ static void fmod_clear_sample (FMODVoice *fmd)
status = FSOUND_Sample_Lock (
fmd->fmod_sample,
0,
hw->samples << hw->shift,
hw->samples << hw->info.shift,
&p1,
&p2,
&len1,
@ -87,78 +111,88 @@ static void fmod_clear_sample (FMODVoice *fmd)
);
if (!status) {
dolog ("Failed to lock sample\nReason: %s\n", errstr ());
fmod_logerr ("Failed to lock sample\n");
return;
}
if ((len1 & hw->align) || (len2 & hw->align)) {
dolog ("Locking sample returned unaligned length %d, %d\n",
len1, len2);
if ((len1 & hw->info.align) || (len2 & hw->info.align)) {
dolog ("Lock returned misaligned length %d, %d, alignment %d\n",
len1, len2, hw->info.align + 1);
goto fail;
}
if (len1 + len2 != hw->samples << hw->shift) {
dolog ("Locking sample returned incomplete length %d, %d\n",
len1 + len2, hw->samples << hw->shift);
if ((len1 + len2) - (hw->samples << hw->info.shift)) {
dolog ("Lock returned incomplete length %d, %d\n",
len1 + len2, hw->samples << hw->info.shift);
goto fail;
}
pcm_hw_clear (hw, p1, hw->samples);
audio_pcm_info_clear_buf (&hw->info, p1, hw->samples);
fail:
status = FSOUND_Sample_Unlock (fmd->fmod_sample, p1, p2, len1, len2);
if (!status) {
dolog ("Failed to unlock sample\nReason: %s\n", errstr ());
fmod_logerr ("Failed to unlock sample\n");
}
}
static int fmod_write_sample (HWVoice *hw, uint8_t *dst, st_sample_t *src,
int src_size, int src_pos, int dst_len)
static void fmod_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
{
int src_len1 = dst_len, src_len2 = 0, pos = src_pos + dst_len;
st_sample_t *src1 = src + src_pos, *src2 = 0;
int src_len1 = dst_len;
int src_len2 = 0;
int pos = hw->rpos + dst_len;
st_sample_t *src1 = hw->mix_buf + hw->rpos;
st_sample_t *src2 = NULL;
if (src_pos + dst_len > src_size) {
src_len1 = src_size - src_pos;
src2 = src;
if (pos > hw->samples) {
src_len1 = hw->samples - hw->rpos;
src2 = hw->mix_buf;
src_len2 = dst_len - src_len1;
pos = src_len2;
}
if (src_len1) {
hw->clip (dst, src1, src_len1);
memset (src1, 0, src_len1 * sizeof (st_sample_t));
advance (dst, src_len1);
mixeng_clear (src1, src_len1);
}
if (src_len2) {
dst = advance (dst, src_len1 << hw->info.shift);
hw->clip (dst, src2, src_len2);
memset (src2, 0, src_len2 * sizeof (st_sample_t));
}
return pos;
mixeng_clear (src2, src_len2);
}
static int fmod_unlock_sample (FMODVoice *fmd, void *p1, void *p2,
hw->rpos = pos % hw->samples;
}
static int fmod_unlock_sample (FSOUND_SAMPLE *sample, void *p1, void *p2,
unsigned int blen1, unsigned int blen2)
{
int status = FSOUND_Sample_Unlock (fmd->fmod_sample, p1, p2, blen1, blen2);
int status = FSOUND_Sample_Unlock (sample, p1, p2, blen1, blen2);
if (!status) {
dolog ("Failed to unlock sample\nReason: %s\n", errstr ());
fmod_logerr ("Failed to unlock sample\n");
return -1;
}
return 0;
}
static int fmod_lock_sample (FMODVoice *fmd, int pos, int len,
void **p1, void **p2,
unsigned int *blen1, unsigned int *blen2)
static int fmod_lock_sample (
FSOUND_SAMPLE *sample,
struct audio_pcm_info *info,
int pos,
int len,
void **p1,
void **p2,
unsigned int *blen1,
unsigned int *blen2
)
{
HWVoice *hw = &fmd->hw;
int status;
status = FSOUND_Sample_Lock (
fmd->fmod_sample,
pos << hw->shift,
len << hw->shift,
sample,
pos << info->shift,
len << info->shift,
p1,
p2,
blen1,
@ -166,89 +200,117 @@ static int fmod_lock_sample (FMODVoice *fmd, int pos, int len,
);
if (!status) {
dolog ("Failed to lock sample\nReason: %s\n", errstr ());
fmod_logerr ("Failed to lock sample\n");
return -1;
}
if ((*blen1 & hw->align) || (*blen2 & hw->align)) {
dolog ("Locking sample returned unaligned length %d, %d\n",
*blen1, *blen2);
fmod_unlock_sample (fmd, *p1, *p2, *blen1, *blen2);
if ((*blen1 & info->align) || (*blen2 & info->align)) {
dolog ("Lock returned misaligned length %d, %d, alignment %d\n",
*blen1, *blen2, info->align + 1);
fmod_unlock_sample (sample, *p1, *p2, *blen1, *blen2);
*p1 = NULL - 1;
*p2 = NULL - 1;
*blen1 = ~0U;
*blen2 = ~0U;
return -1;
}
if (!*p1 && *blen1) {
dolog ("warning: !p1 && blen1=%d\n", *blen1);
*blen1 = 0;
}
if (!p2 && *blen2) {
dolog ("warning: !p2 && blen2=%d\n", *blen2);
*blen2 = 0;
}
return 0;
}
static void fmod_hw_run (HWVoice *hw)
static int fmod_run_out (HWVoiceOut *hw)
{
FMODVoice *fmd = (FMODVoice *) hw;
int rpos, live, decr;
FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
int live, decr;
void *p1 = 0, *p2 = 0;
unsigned int blen1 = 0, blen2 = 0;
unsigned int len1 = 0, len2 = 0;
int nb_active;
int nb_live;
live = pcm_hw_get_live2 (hw, &nb_active);
if (live <= 0) {
return;
live = audio_pcm_hw_get_live_out2 (hw, &nb_live);
if (!live) {
return 0;
}
if (!hw->pending_disable
&& nb_active
&& conf.threshold
&& live <= conf.threshold) {
ldebug ("live=%d nb_active=%d\n", live, nb_active);
return;
&& nb_live
&& (conf.threshold && live <= conf.threshold)) {
ldebug ("live=%d nb_live=%d\n", live, nb_live);
return 0;
}
decr = live;
#if 1
if (fmd->channel >= 0) {
int pos2 = (fmd->old_pos + decr) % hw->samples;
int pos = FSOUND_GetCurrentPosition (fmd->channel);
int len = decr;
int old_pos = fmd->old_pos;
int ppos = FSOUND_GetCurrentPosition (fmd->channel);
if (fmd->old_pos < pos && pos2 >= pos) {
decr = pos - fmd->old_pos - (pos2 == pos) - 1;
}
else if (fmd->old_pos > pos && pos2 >= pos && pos2 < fmd->old_pos) {
decr = (hw->samples - fmd->old_pos) + pos - (pos2 == pos) - 1;
}
/* ldebug ("pos=%d pos2=%d old=%d live=%d decr=%d\n", */
/* pos, pos2, fmd->old_pos, live, decr); */
}
#endif
if (decr <= 0) {
return;
if (ppos == old_pos || !ppos) {
return 0;
}
if (fmod_lock_sample (fmd, fmd->old_pos, decr, &p1, &p2, &blen1, &blen2)) {
return;
if ((old_pos < ppos) && ((old_pos + len) > ppos)) {
len = ppos - old_pos;
}
else {
if ((old_pos > ppos) && ((old_pos + len) > (ppos + hw->samples))) {
len = hw->samples - old_pos + ppos;
}
}
decr = len;
if (audio_bug (AUDIO_FUNC, decr < 0)) {
dolog ("decr=%d live=%d ppos=%d old_pos=%d len=%d\n",
decr, live, ppos, old_pos, len);
return 0;
}
}
len1 = blen1 >> hw->shift;
len2 = blen2 >> hw->shift;
if (!decr) {
return 0;
}
if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info,
fmd->old_pos, decr,
&p1, &p2,
&blen1, &blen2)) {
return 0;
}
len1 = blen1 >> hw->info.shift;
len2 = blen2 >> hw->info.shift;
ldebug ("%p %p %d %d %d %d\n", p1, p2, len1, len2, blen1, blen2);
decr = len1 + len2;
rpos = hw->rpos;
if (len1) {
rpos = fmod_write_sample (hw, p1, hw->mix_buf, hw->samples, rpos, len1);
if (p1 && len1) {
fmod_write_sample (hw, p1, len1);
}
if (len2) {
rpos = fmod_write_sample (hw, p2, hw->mix_buf, hw->samples, rpos, len2);
if (p2 && len2) {
fmod_write_sample (hw, p2, len2);
}
fmod_unlock_sample (fmd, p1, p2, blen1, blen2);
fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2);
pcm_hw_dec_live (hw, decr);
hw->rpos = rpos % hw->samples;
fmd->old_pos = (fmd->old_pos + decr) % hw->samples;
return decr;
}
static int AUD_to_fmodfmt (audfmt_e fmt, int stereo)
static int aud_to_fmodfmt (audfmt_e fmt, int stereo)
{
int mode = FSOUND_LOOP_NORMAL;
@ -270,16 +332,19 @@ static int AUD_to_fmodfmt (audfmt_e fmt, int stereo)
break;
default:
dolog ("Internal logic error: Bad audio format %d\nAborting\n", fmt);
exit (EXIT_FAILURE);
dolog ("Internal logic error: Bad audio format %d\n", fmt);
#ifdef DEBUG_FMOD
abort ();
#endif
mode |= FSOUND_8BITS;
}
mode |= stereo ? FSOUND_STEREO : FSOUND_MONO;
return mode;
}
static void fmod_hw_fini (HWVoice *hw)
static void fmod_fini_out (HWVoiceOut *hw)
{
FMODVoice *fmd = (FMODVoice *) hw;
FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
if (fmd->fmod_sample) {
FSOUND_Sample_Free (fmd->fmod_sample);
@ -291,12 +356,12 @@ static void fmod_hw_fini (HWVoice *hw)
}
}
static int fmod_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
static int fmod_init_out (HWVoiceOut *hw, int freq, int nchannels, audfmt_e fmt)
{
int bits16, mode, channel;
FMODVoice *fmd = (FMODVoice *) hw;
FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
mode = AUD_to_fmodfmt (fmt, nchannels == 2 ? 1 : 0);
mode = aud_to_fmodfmt (fmt, nchannels == 2 ? 1 : 0);
fmd->fmod_sample = FSOUND_Sample_Alloc (
FSOUND_FREE, /* index */
conf.nb_samples, /* length */
@ -308,52 +373,145 @@ static int fmod_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
);
if (!fmd->fmod_sample) {
dolog ("Failed to allocate FMOD sample\nReason: %s\n", errstr ());
fmod_logerr2 ("DAC", "Failed to allocate FMOD sample\n");
return -1;
}
channel = FSOUND_PlaySoundEx (FSOUND_FREE, fmd->fmod_sample, 0, 1);
if (channel < 0) {
dolog ("Failed to start playing sound\nReason: %s\n", errstr ());
fmod_logerr2 ("DAC", "Failed to start playing sound\n");
FSOUND_Sample_Free (fmd->fmod_sample);
return -1;
}
fmd->channel = channel;
hw->freq = freq;
hw->fmt = fmt;
hw->nchannels = nchannels;
bits16 = fmt == AUD_FMT_U16 || fmt == AUD_FMT_S16;
/* FMOD always operates on little endian frames? */
audio_pcm_init_info (&hw->info, freq, nchannels, fmt,
audio_need_to_swap_endian (0));
bits16 = (mode & FSOUND_16BITS) != 0;
hw->bufsize = conf.nb_samples << (nchannels == 2) << bits16;
return 0;
}
static int fmod_hw_ctl (HWVoice *hw, int cmd, ...)
static int fmod_ctl_out (HWVoiceOut *hw, int cmd, ...)
{
int status;
FMODVoice *fmd = (FMODVoice *) hw;
FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
switch (cmd) {
case VOICE_ENABLE:
fmod_clear_sample (fmd);
status = FSOUND_SetPaused (fmd->channel, 0);
if (!status) {
dolog ("Failed to resume channel %d\nReason: %s\n",
fmd->channel, errstr ());
fmod_logerr ("Failed to resume channel %d\n", fmd->channel);
}
break;
case VOICE_DISABLE:
status = FSOUND_SetPaused (fmd->channel, 1);
if (!status) {
dolog ("Failed to pause channel %d\nReason: %s\n",
fmd->channel, errstr ());
fmod_logerr ("Failed to pause channel %d\n", fmd->channel);
}
break;
}
return 0;
}
static int fmod_init_in (HWVoiceIn *hw, int freq, int nchannels, audfmt_e fmt)
{
int bits16, mode;
FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
if (conf.broken_adc) {
return -1;
}
mode = aud_to_fmodfmt (fmt, nchannels == 2 ? 1 : 0);
fmd->fmod_sample = FSOUND_Sample_Alloc (
FSOUND_FREE, /* index */
conf.nb_samples, /* length */
mode, /* mode */
freq, /* freq */
255, /* volume */
128, /* pan */
255 /* priority */
);
if (!fmd->fmod_sample) {
fmod_logerr2 ("ADC", "Failed to allocate FMOD sample\n");
return -1;
}
/* FMOD always operates on little endian frames? */
audio_pcm_init_info (&hw->info, freq, nchannels, fmt,
audio_need_to_swap_endian (0));
bits16 = (mode & FSOUND_16BITS) != 0;
hw->bufsize = conf.nb_samples << (nchannels == 2) << bits16;
return 0;
}
static void fmod_fini_in (HWVoiceIn *hw)
{
FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
if (fmd->fmod_sample) {
FSOUND_Record_Stop ();
FSOUND_Sample_Free (fmd->fmod_sample);
fmd->fmod_sample = 0;
}
}
static int fmod_run_in (HWVoiceIn *hw)
{
FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
int hwshift = hw->info.shift;
int live, dead, new_pos, len;
unsigned int blen1 = 0, blen2 = 0;
unsigned int len1, len2;
unsigned int decr;
void *p1, *p2;
live = audio_pcm_hw_get_live_in (hw);
dead = hw->samples - live;
if (!dead) {
return 0;
}
new_pos = FSOUND_Record_GetPosition ();
if (new_pos < 0) {
fmod_logerr ("Can not get recording position\n");
return 0;
}
len = audio_ring_dist (new_pos, hw->wpos, hw->samples);
if (!len) {
return 0;
}
len = audio_MIN (len, dead);
if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info,
hw->wpos, len,
&p1, &p2,
&blen1, &blen2)) {
return 0;
}
len1 = blen1 >> hwshift;
len2 = blen2 >> hwshift;
decr = len1 + len2;
if (p1 && blen1) {
hw->conv (hw->conv_buf + hw->wpos, p1, len1, &nominal_volume);
}
if (p2 && len2) {
hw->conv (hw->conv_buf, p2, len2, &nominal_volume);
}
fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2);
hw->wpos = (hw->wpos + decr) % hw->samples;
return decr;
}
static struct {
const char *name;
int type;
@ -378,16 +536,16 @@ static struct {
{"ps2", FSOUND_OUTPUT_PS2},
{"gcube", FSOUND_OUTPUT_GC},
#endif
{"nort", FSOUND_OUTPUT_NOSOUND_NONREALTIME}
{"none-realtime", FSOUND_OUTPUT_NOSOUND_NONREALTIME}
};
static void *fmod_audio_init (void)
{
int i;
size_t i;
double ver;
int status;
int output_type = -1;
const char *drv = audio_get_conf_str (QC_FMOD_DRV, NULL);
const char *drv = conf.drvname;
ver = FSOUND_GetVersion ();
if (ver < FMOD_VERSION) {
@ -395,6 +553,14 @@ static void *fmod_audio_init (void)
return NULL;
}
#ifdef __linux__
if (ver < 3.75) {
dolog ("FMOD before 3.75 has bug preventing ADC from working\n"
"ADC will be disabled.\n");
conf.broken_adc = 1;
}
#endif
if (drv) {
int found = 0;
for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) {
@ -405,65 +571,115 @@ static void *fmod_audio_init (void)
}
}
if (!found) {
dolog ("Unknown FMOD output driver `%s'\n", drv);
dolog ("Unknown FMOD driver `%s'\n", drv);
dolog ("Valid drivers:\n");
for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) {
dolog (" %s\n", drvtab[i].name);
}
}
}
if (output_type != -1) {
status = FSOUND_SetOutput (output_type);
if (!status) {
dolog ("FSOUND_SetOutput(%d) failed\nReason: %s\n",
output_type, errstr ());
fmod_logerr ("FSOUND_SetOutput(%d) failed\n", output_type);
return NULL;
}
}
conf.freq = audio_get_conf_int (QC_FMOD_FREQ, conf.freq);
conf.nb_samples = audio_get_conf_int (QC_FMOD_SAMPLES, conf.nb_samples);
conf.nb_channels =
audio_get_conf_int (QC_FMOD_CHANNELS,
(audio_state.nb_hw_voices > 1
? audio_state.nb_hw_voices
: conf.nb_channels));
conf.bufsize = audio_get_conf_int (QC_FMOD_BUFSIZE, conf.bufsize);
conf.threshold = audio_get_conf_int (QC_FMOD_THRESHOLD, conf.threshold);
if (conf.bufsize) {
status = FSOUND_SetBufferSize (conf.bufsize);
if (!status) {
dolog ("FSOUND_SetBufferSize (%d) failed\nReason: %s\n",
conf.bufsize, errstr ());
fmod_logerr ("FSOUND_SetBufferSize (%d) failed\n", conf.bufsize);
}
}
status = FSOUND_Init (conf.freq, conf.nb_channels, 0);
if (!status) {
dolog ("FSOUND_Init failed\nReason: %s\n", errstr ());
fmod_logerr ("FSOUND_Init failed\n");
return NULL;
}
return &conf;
}
static int fmod_read (SWVoiceIn *sw, void *buf, int size)
{
return audio_pcm_sw_read (sw, buf, size);
}
static int fmod_ctl_in (HWVoiceIn *hw, int cmd, ...)
{
int status;
FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
switch (cmd) {
case VOICE_ENABLE:
status = FSOUND_Record_StartSample (fmd->fmod_sample, 1);
if (!status) {
fmod_logerr ("Failed to start recording\n");
}
break;
case VOICE_DISABLE:
status = FSOUND_Record_Stop ();
if (!status) {
fmod_logerr ("Failed to stop recording\n");
}
break;
}
return 0;
}
static void fmod_audio_fini (void *opaque)
{
(void) opaque;
FSOUND_Close ();
}
struct pcm_ops fmod_pcm_ops = {
fmod_hw_init,
fmod_hw_fini,
fmod_hw_run,
fmod_hw_write,
fmod_hw_ctl
static struct audio_option fmod_options[] = {
{"DRV", AUD_OPT_STR, &conf.drvname,
"FMOD driver", NULL, 0},
{"FREQ", AUD_OPT_INT, &conf.freq,
"Default frequency", NULL, 0},
{"SAMPLES", AUD_OPT_INT, &conf.nb_samples,
"Buffer size in samples", NULL, 0},
{"CHANNELS", AUD_OPT_INT, &conf.nb_channels,
"Number of default channels (1 - mono, 2 - stereo)", NULL, 0},
{"BUFSIZE", AUD_OPT_INT, &conf.bufsize,
"(undocumented)", NULL, 0},
#if 0
{"THRESHOLD", AUD_OPT_INT, &conf.threshold,
"(undocumented)"},
#endif
{NULL, 0, NULL, NULL, NULL, 0}
};
struct audio_output_driver fmod_output_driver = {
"fmod",
fmod_audio_init,
fmod_audio_fini,
&fmod_pcm_ops,
1,
INT_MAX,
sizeof (FMODVoice)
static struct audio_pcm_ops fmod_pcm_ops = {
fmod_init_out,
fmod_fini_out,
fmod_run_out,
fmod_write,
fmod_ctl_out,
fmod_init_in,
fmod_fini_in,
fmod_run_in,
fmod_read,
fmod_ctl_in
};
struct audio_driver fmod_audio_driver = {
INIT_FIELD (name = ) "fmod",
INIT_FIELD (descr = ) "FMOD 3.xx http://www.fmod.org",
INIT_FIELD (options = ) fmod_options,
INIT_FIELD (init = ) fmod_audio_init,
INIT_FIELD (fini = ) fmod_audio_fini,
INIT_FIELD (pcm_ops = ) &fmod_pcm_ops,
INIT_FIELD (can_be_default = ) 1,
INIT_FIELD (max_voices_out = ) INT_MAX,
INIT_FIELD (max_voices_in = ) INT_MAX,
INIT_FIELD (voice_size_out = ) sizeof (FMODVoiceOut),
INIT_FIELD (voice_size_in = ) sizeof (FMODVoiceIn)
};

View File

@ -1,7 +1,7 @@
/*
* QEMU Mixing engine
*
* Copyright (c) 2004 Vassili Karpov (malc)
* Copyright (c) 2004-2005 Vassili Karpov (malc)
* Copyright (c) 1998 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
@ -23,87 +23,174 @@
* THE SOFTWARE.
*/
#include "vl.h"
//#define DEBUG_FP
#include "audio/mixeng.h"
#define AUDIO_CAP "mixeng"
#include "audio_int.h"
#define NOVOL
/* 8 bit */
#define ENDIAN_CONVERSION natural
#define ENDIAN_CONVERT(v) (v)
/* Signed 8 bit */
#define IN_T int8_t
#define IN_MIN CHAR_MIN
#define IN_MAX CHAR_MAX
#define IN_MIN SCHAR_MIN
#define IN_MAX SCHAR_MAX
#define SIGNED
#define SHIFT 8
#include "mixeng_template.h"
#undef SIGNED
#undef IN_MAX
#undef IN_MIN
#undef IN_T
#undef SHIFT
/* Unsigned 8 bit */
#define IN_T uint8_t
#define IN_MIN 0
#define IN_MAX UCHAR_MAX
#define SHIFT 8
#include "mixeng_template.h"
#undef IN_MAX
#undef IN_MIN
#undef IN_T
#undef SHIFT
#undef ENDIAN_CONVERT
#undef ENDIAN_CONVERSION
/* Signed 16 bit */
#define IN_T int16_t
#define IN_MIN SHRT_MIN
#define IN_MAX SHRT_MAX
#define SIGNED
#define SHIFT 16
#define ENDIAN_CONVERSION natural
#define ENDIAN_CONVERT(v) (v)
#include "mixeng_template.h"
#undef ENDIAN_CONVERT
#undef ENDIAN_CONVERSION
#define ENDIAN_CONVERSION swap
#define ENDIAN_CONVERT(v) bswap16 (v)
#include "mixeng_template.h"
#undef ENDIAN_CONVERT
#undef ENDIAN_CONVERSION
#undef SIGNED
#undef IN_MAX
#undef IN_MIN
#undef IN_T
#undef SHIFT
#define IN_T uint16_t
#define IN_MIN 0
#define IN_MAX USHRT_MAX
#define SHIFT 16
#define ENDIAN_CONVERSION natural
#define ENDIAN_CONVERT(v) (v)
#include "mixeng_template.h"
#undef ENDIAN_CONVERT
#undef ENDIAN_CONVERSION
#define ENDIAN_CONVERSION swap
#define ENDIAN_CONVERT(v) bswap16 (v)
#include "mixeng_template.h"
#undef ENDIAN_CONVERT
#undef ENDIAN_CONVERSION
#undef IN_MAX
#undef IN_MIN
#undef IN_T
#undef SHIFT
t_sample *mixeng_conv[2][2][2] = {
t_sample *mixeng_conv[2][2][2][2] = {
{
{
conv_uint8_t_to_mono,
conv_uint16_t_to_mono
{
conv_natural_uint8_t_to_mono,
conv_natural_uint16_t_to_mono
},
{
conv_int8_t_to_mono,
conv_int16_t_to_mono
conv_natural_uint8_t_to_mono,
conv_swap_uint16_t_to_mono
}
},
{
{
conv_uint8_t_to_stereo,
conv_uint16_t_to_stereo
conv_natural_int8_t_to_mono,
conv_natural_int16_t_to_mono
},
{
conv_int8_t_to_stereo,
conv_int16_t_to_stereo
conv_natural_int8_t_to_mono,
conv_swap_int16_t_to_mono
}
}
},
{
{
{
conv_natural_uint8_t_to_stereo,
conv_natural_uint16_t_to_stereo
},
{
conv_natural_uint8_t_to_stereo,
conv_swap_uint16_t_to_stereo
}
},
{
{
conv_natural_int8_t_to_stereo,
conv_natural_int16_t_to_stereo
},
{
conv_natural_int8_t_to_stereo,
conv_swap_int16_t_to_stereo
}
}
}
};
f_sample *mixeng_clip[2][2][2] = {
f_sample *mixeng_clip[2][2][2][2] = {
{
{
clip_uint8_t_from_mono,
clip_uint16_t_from_mono
{
clip_natural_uint8_t_from_mono,
clip_natural_uint16_t_from_mono
},
{
clip_int8_t_from_mono,
clip_int16_t_from_mono
clip_natural_uint8_t_from_mono,
clip_swap_uint16_t_from_mono
}
},
{
{
clip_uint8_t_from_stereo,
clip_uint16_t_from_stereo
clip_natural_int8_t_from_mono,
clip_natural_int16_t_from_mono
},
{
clip_int8_t_from_stereo,
clip_int16_t_from_stereo
clip_natural_int8_t_from_mono,
clip_swap_int16_t_from_mono
}
}
},
{
{
{
clip_natural_uint8_t_from_stereo,
clip_natural_uint16_t_from_stereo
},
{
clip_natural_uint8_t_from_stereo,
clip_swap_uint16_t_from_stereo
}
},
{
{
clip_natural_int8_t_from_stereo,
clip_natural_int16_t_from_stereo
},
{
clip_natural_int8_t_from_stereo,
clip_swap_int16_t_from_stereo
}
}
}
};
@ -156,21 +243,13 @@ void *st_rate_start (int inrate, int outrate)
rate_t rate = (rate_t) qemu_mallocz (sizeof (struct ratestuff));
if (!rate) {
exit (EXIT_FAILURE);
}
if (inrate == outrate) {
// exit (EXIT_FAILURE);
}
if (inrate >= 65535 || outrate >= 65535) {
// exit (EXIT_FAILURE);
return NULL;
}
rate->opos = 0;
/* increment */
rate->opos_inc = (inrate * ((int64_t) UINT_MAX)) / outrate;
rate->opos_inc = ((uint64_t) inrate << 32) / outrate;
rate->ipos = 0;
rate->ilast.l = 0;
@ -178,78 +257,20 @@ void *st_rate_start (int inrate, int outrate)
return rate;
}
/*
* Processed signed long samples from ibuf to obuf.
* Return number of samples processed.
*/
void st_rate_flow (void *opaque, st_sample_t *ibuf, st_sample_t *obuf,
int *isamp, int *osamp)
{
rate_t rate = (rate_t) opaque;
st_sample_t *istart, *iend;
st_sample_t *ostart, *oend;
st_sample_t ilast, icur, out;
int64_t t;
#define NAME st_rate_flow_mix
#define OP(a, b) a += b
#include "rate_template.h"
ilast = rate->ilast;
istart = ibuf;
iend = ibuf + *isamp;
ostart = obuf;
oend = obuf + *osamp;
if (rate->opos_inc == 1ULL << 32) {
int i, n = *isamp > *osamp ? *osamp : *isamp;
for (i = 0; i < n; i++) {
obuf[i].l += ibuf[i].r;
obuf[i].r += ibuf[i].r;
}
*isamp = n;
*osamp = n;
return;
}
while (obuf < oend) {
/* Safety catch to make sure we have input samples. */
if (ibuf >= iend)
break;
/* read as many input samples so that ipos > opos */
while (rate->ipos <= (rate->opos >> 32)) {
ilast = *ibuf++;
rate->ipos++;
/* See if we finished the input buffer yet */
if (ibuf >= iend) goto the_end;
}
icur = *ibuf;
/* interpolate */
t = rate->opos & 0xffffffff;
out.l = (ilast.l * (INT_MAX - t) + icur.l * t) / INT_MAX;
out.r = (ilast.r * (INT_MAX - t) + icur.r * t) / INT_MAX;
/* output sample & increment position */
#if 0
*obuf++ = out;
#else
obuf->l += out.l;
obuf->r += out.r;
obuf += 1;
#endif
rate->opos += rate->opos_inc;
}
the_end:
*isamp = ibuf - istart;
*osamp = obuf - ostart;
rate->ilast = ilast;
}
#define NAME st_rate_flow
#define OP(a, b) a = b
#include "rate_template.h"
void st_rate_stop (void *opaque)
{
qemu_free (opaque);
}
void mixeng_clear (st_sample_t *buf, int len)
{
memset (buf, 0, len * sizeof (st_sample_t));
}

View File

@ -1,7 +1,7 @@
/*
* QEMU Mixing engine header
*
* Copyright (c) 2004 Vassili Karpov (malc)
* Copyright (c) 2004-2005 Vassili Karpov (malc)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -24,16 +24,28 @@
#ifndef QEMU_MIXENG_H
#define QEMU_MIXENG_H
typedef void (t_sample) (void *dst, const void *src, int samples);
typedef void (f_sample) (void *dst, const void *src, int samples);
#ifdef FLOAT_MIXENG
typedef float real_t;
typedef struct { int mute; real_t r; real_t l; } volume_t;
typedef struct { real_t l; real_t r; } st_sample_t;
#else
typedef struct { int mute; int64_t r; int64_t l; } volume_t;
typedef struct { int64_t l; int64_t r; } st_sample_t;
#endif
extern t_sample *mixeng_conv[2][2][2];
extern f_sample *mixeng_clip[2][2][2];
typedef void (t_sample) (st_sample_t *dst, const void *src,
int samples, volume_t *vol);
typedef void (f_sample) (void *dst, const st_sample_t *src, int samples);
extern t_sample *mixeng_conv[2][2][2][2];
extern f_sample *mixeng_clip[2][2][2][2];
void *st_rate_start (int inrate, int outrate);
void st_rate_flow (void *opaque, st_sample_t *ibuf, st_sample_t *obuf,
int *isamp, int *osamp);
void st_rate_flow_mix (void *opaque, st_sample_t *ibuf, st_sample_t *obuf,
int *isamp, int *osamp);
void st_rate_stop (void *opaque);
void mixeng_clear (st_sample_t *buf, int len);
#endif /* mixeng.h */

View File

@ -1,7 +1,7 @@
/*
* QEMU Mixing engine
*
* Copyright (c) 2004 Vassili Karpov (malc)
* Copyright (c) 2004-2005 Vassili Karpov (malc)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -27,85 +27,151 @@
* dec++'ified by Dscho
*/
#ifdef SIGNED
#define HALFT IN_MAX
#define HALF IN_MAX
#else
#define HALFT ((IN_MAX)>>1)
#define HALF HALFT
#ifndef SIGNED
#define HALF (IN_MAX >> 1)
#endif
static int64_t inline glue(conv_,IN_T) (IN_T v)
{
#ifdef SIGNED
return (INT_MAX*(int64_t)v)/HALF;
#ifdef NOVOL
#define VOL(a, b) a
#else
return (INT_MAX*((int64_t)v-HALFT))/HALF;
#ifdef FLOAT_MIXENG
#define VOL(a, b) ((a) * (b))
#else
#define VOL(a, b) ((a) * (b)) >> 32
#endif
#endif
#define ET glue (ENDIAN_CONVERSION, glue (_, IN_T))
#ifdef FLOAT_MIXENG
static real_t inline glue (conv_, ET) (IN_T v)
{
IN_T nv = ENDIAN_CONVERT (v);
#ifdef RECIPROCAL
#ifdef SIGNED
return nv * (1.f / (real_t) (IN_MAX - IN_MIN));
#else
return (nv - HALF) * (1.f / (real_t) IN_MAX);
#endif
#else /* !RECIPROCAL */
#ifdef SIGNED
return nv / (real_t) (IN_MAX - IN_MIN);
#else
return (nv - HALF) / (real_t) IN_MAX;
#endif
#endif
}
static IN_T inline glue(clip_,IN_T) (int64_t v)
static IN_T inline glue (clip_, ET) (real_t v)
{
if (v >= INT_MAX)
if (v >= 0.5) {
return IN_MAX;
else if (v < -INT_MAX)
}
else if (v < -0.5) {
return IN_MIN;
}
#ifdef SIGNED
return (IN_T) (v*HALF/INT_MAX);
return ENDIAN_CONVERT ((IN_T) (v * (IN_MAX - IN_MIN)));
#else
return (IN_T) (v+INT_MAX/2)*HALF/INT_MAX;
return ENDIAN_CONVERT ((IN_T) ((v * IN_MAX) + HALF));
#endif
}
static void glue(glue(conv_,IN_T),_to_stereo) (void *dst, const void *src,
int samples)
#else /* !FLOAT_MIXENG */
static inline int64_t glue (conv_, ET) (IN_T v)
{
st_sample_t *out = (st_sample_t *) dst;
IN_T nv = ENDIAN_CONVERT (v);
#ifdef SIGNED
return ((int64_t) nv) << (32 - SHIFT);
#else
return ((int64_t) nv - HALF) << (32 - SHIFT);
#endif
}
static inline IN_T glue (clip_, ET) (int64_t v)
{
if (v >= 0x7f000000) {
return IN_MAX;
}
else if (v < -2147483648LL) {
return IN_MIN;
}
#ifdef SIGNED
return ENDIAN_CONVERT ((IN_T) (v >> (32 - SHIFT)));
#else
return ENDIAN_CONVERT ((IN_T) ((v >> (32 - SHIFT)) + HALF));
#endif
}
#endif
static void glue (glue (conv_, ET), _to_stereo)
(st_sample_t *dst, const void *src, int samples, volume_t *vol)
{
st_sample_t *out = dst;
IN_T *in = (IN_T *) src;
#ifndef NOVOL
if (vol->mute) {
mixeng_clear (dst, samples);
return;
}
#else
(void) vol;
#endif
while (samples--) {
out->l = glue(conv_,IN_T) (*in++);
out->r = glue(conv_,IN_T) (*in++);
out->l = VOL (glue (conv_, ET) (*in++), vol->l);
out->r = VOL (glue (conv_, ET) (*in++), vol->r);
out += 1;
}
}
static void glue(glue(conv_,IN_T),_to_mono) (void *dst, const void *src,
int samples)
static void glue (glue (conv_, ET), _to_mono)
(st_sample_t *dst, const void *src, int samples, volume_t *vol)
{
st_sample_t *out = (st_sample_t *) dst;
st_sample_t *out = dst;
IN_T *in = (IN_T *) src;
#ifndef NOVOL
if (vol->mute) {
mixeng_clear (dst, samples);
return;
}
#else
(void) vol;
#endif
while (samples--) {
out->l = glue(conv_,IN_T) (in[0]);
out->l = VOL (glue (conv_, ET) (in[0]), vol->l);
out->r = out->l;
out += 1;
in += 1;
}
}
static void glue(glue(clip_,IN_T),_from_stereo) (void *dst, const void *src,
int samples)
static void glue (glue (clip_, ET), _from_stereo)
(void *dst, const st_sample_t *src, int samples)
{
st_sample_t *in = (st_sample_t *) src;
const st_sample_t *in = src;
IN_T *out = (IN_T *) dst;
while (samples--) {
*out++ = glue(clip_,IN_T) (in->l);
*out++ = glue(clip_,IN_T) (in->r);
*out++ = glue (clip_, ET) (in->l);
*out++ = glue (clip_, ET) (in->r);
in += 1;
}
}
static void glue(glue(clip_,IN_T),_from_mono) (void *dst, const void *src,
int samples)
static void glue (glue (clip_, ET), _from_mono)
(void *dst, const st_sample_t *src, int samples)
{
st_sample_t *in = (st_sample_t *) src;
const st_sample_t *in = src;
IN_T *out = (IN_T *) dst;
while (samples--) {
*out++ = glue(clip_,IN_T) (in->l + in->r);
*out++ = glue (clip_, ET) (in->l + in->r);
in += 1;
}
}
#undef ET
#undef HALF
#undef HALFT
#undef VOL

View File

@ -1,7 +1,7 @@
/*
* QEMU NULL audio output driver
* QEMU Timer based audio emulation
*
* Copyright (c) 2004 Vassili Karpov (malc)
* Copyright (c) 2004-2005 Vassili Karpov (malc)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -23,77 +23,110 @@
*/
#include "vl.h"
#include "audio/audio_int.h"
#define AUDIO_CAP "noaudio"
#include "audio_int.h"
typedef struct NoVoice {
HWVoice hw;
typedef struct NoVoiceOut {
HWVoiceOut hw;
int64_t old_ticks;
} NoVoice;
} NoVoiceOut;
#define dolog(...) AUD_log ("noaudio", __VA_ARGS__)
#ifdef DEBUG
#define ldebug(...) dolog (__VA_ARGS__)
#else
#define ldebug(...)
#endif
typedef struct NoVoiceIn {
HWVoiceIn hw;
int64_t old_ticks;
} NoVoiceIn;
static void no_hw_run (HWVoice *hw)
static int no_run_out (HWVoiceOut *hw)
{
NoVoice *no = (NoVoice *) hw;
int rpos, live, decr, samples;
st_sample_t *src;
NoVoiceOut *no = (NoVoiceOut *) hw;
int live, decr, samples;
int64_t now = qemu_get_clock (vm_clock);
int64_t ticks = now - no->old_ticks;
int64_t bytes = (ticks * hw->bytes_per_second) / ticks_per_sec;
int64_t bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec;
if (bytes > INT_MAX)
samples = INT_MAX >> hw->shift;
else
samples = bytes >> hw->shift;
if (bytes > INT_MAX) {
samples = INT_MAX >> hw->info.shift;
}
else {
samples = bytes >> hw->info.shift;
}
live = pcm_hw_get_live (hw);
if (live <= 0)
return;
live = audio_pcm_hw_get_live_out (&no->hw);
if (!live) {
return 0;
}
no->old_ticks = now;
decr = audio_MIN (live, samples);
samples = decr;
rpos = hw->rpos;
while (samples) {
int left_till_end_samples = hw->samples - rpos;
int convert_samples = audio_MIN (samples, left_till_end_samples);
src = advance (hw->mix_buf, rpos * sizeof (st_sample_t));
memset (src, 0, convert_samples * sizeof (st_sample_t));
rpos = (rpos + convert_samples) % hw->samples;
samples -= convert_samples;
hw->rpos = (hw->rpos + decr) % hw->samples;
return decr;
}
pcm_hw_dec_live (hw, decr);
hw->rpos = rpos;
}
static int no_hw_write (SWVoice *sw, void *buf, int len)
static int no_write (SWVoiceOut *sw, void *buf, int len)
{
return pcm_hw_write (sw, buf, len);
return audio_pcm_sw_write (sw, buf, len);
}
static int no_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
static int no_init_out (HWVoiceOut *hw, int freq,
int nchannels, audfmt_e fmt)
{
hw->freq = freq;
hw->nchannels = nchannels;
hw->fmt = fmt;
audio_pcm_init_info (&hw->info, freq, nchannels, fmt, 0);
hw->bufsize = 4096;
return 0;
}
static void no_hw_fini (HWVoice *hw)
static void no_fini_out (HWVoiceOut *hw)
{
(void) hw;
}
static int no_hw_ctl (HWVoice *hw, int cmd, ...)
static int no_ctl_out (HWVoiceOut *hw, int cmd, ...)
{
(void) hw;
(void) cmd;
return 0;
}
static int no_init_in (HWVoiceIn *hw, int freq,
int nchannels, audfmt_e fmt)
{
audio_pcm_init_info (&hw->info, freq, nchannels, fmt, 0);
hw->bufsize = 4096;
return 0;
}
static void no_fini_in (HWVoiceIn *hw)
{
(void) hw;
}
static int no_run_in (HWVoiceIn *hw)
{
NoVoiceIn *no = (NoVoiceIn *) hw;
int64_t now = qemu_get_clock (vm_clock);
int64_t ticks = now - no->old_ticks;
int64_t bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec;
int live = audio_pcm_hw_get_live_in (hw);
int dead = hw->samples - live;
int samples;
bytes = audio_MIN (bytes, INT_MAX);
samples = bytes >> hw->info.shift;
samples = audio_MIN (samples, dead);
return samples;
}
static int no_read (SWVoiceIn *sw, void *buf, int size)
{
int samples = size >> sw->info.shift;
int total = sw->hw->total_samples_captured - sw->total_hw_samples_acquired;
int to_clear = audio_MIN (samples, total);
audio_pcm_info_clear_buf (&sw->info, buf, to_clear);
return to_clear;
}
static int no_ctl_in (HWVoiceIn *hw, int cmd, ...)
{
(void) hw;
(void) cmd;
@ -107,22 +140,33 @@ static void *no_audio_init (void)
static void no_audio_fini (void *opaque)
{
(void) opaque;
}
struct pcm_ops no_pcm_ops = {
no_hw_init,
no_hw_fini,
no_hw_run,
no_hw_write,
no_hw_ctl
static struct audio_pcm_ops no_pcm_ops = {
no_init_out,
no_fini_out,
no_run_out,
no_write,
no_ctl_out,
no_init_in,
no_fini_in,
no_run_in,
no_read,
no_ctl_in
};
struct audio_output_driver no_output_driver = {
"none",
no_audio_init,
no_audio_fini,
&no_pcm_ops,
1,
1,
sizeof (NoVoice)
struct audio_driver no_audio_driver = {
INIT_FIELD (name = ) "none",
INIT_FIELD (descr = ) "Timer based audio emulation",
INIT_FIELD (options = ) NULL,
INIT_FIELD (init = ) no_audio_init,
INIT_FIELD (fini = ) no_audio_fini,
INIT_FIELD (pcm_ops = ) &no_pcm_ops,
INIT_FIELD (can_be_default = ) 1,
INIT_FIELD (max_voices_out = ) INT_MAX,
INIT_FIELD (max_voices_in = ) INT_MAX,
INIT_FIELD (voice_size_out = ) sizeof (NoVoiceOut),
INIT_FIELD (voice_size_in = ) sizeof (NoVoiceIn)
};

View File

@ -1,7 +1,7 @@
/*
* QEMU OSS audio output driver
* QEMU OSS audio driver
*
* Copyright (c) 2003-2004 Vassili Karpov (malc)
* Copyright (c) 2003-2005 Vassili Karpov (malc)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -25,45 +25,42 @@
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>
#include <assert.h>
#include "vl.h"
#include "audio/audio_int.h"
#define AUDIO_CAP "oss"
#include "audio_int.h"
typedef struct OSSVoice {
HWVoice hw;
typedef struct OSSVoiceOut {
HWVoiceOut hw;
void *pcm_buf;
int fd;
int nfrags;
int fragsize;
int mmapped;
int old_optr;
} OSSVoice;
} OSSVoiceOut;
#define dolog(...) AUD_log ("oss", __VA_ARGS__)
#ifdef DEBUG
#define ldebug(...) dolog (__VA_ARGS__)
#else
#define ldebug(...)
#endif
#define QC_OSS_FRAGSIZE "QEMU_OSS_FRAGSIZE"
#define QC_OSS_NFRAGS "QEMU_OSS_NFRAGS"
#define QC_OSS_MMAP "QEMU_OSS_MMAP"
#define QC_OSS_DEV "QEMU_OSS_DEV"
#define errstr() strerror (errno)
typedef struct OSSVoiceIn {
HWVoiceIn hw;
void *pcm_buf;
int fd;
int nfrags;
int fragsize;
int old_optr;
} OSSVoiceIn;
static struct {
int try_mmap;
int nfrags;
int fragsize;
const char *dspname;
const char *devpath_out;
const char *devpath_in;
} conf = {
.try_mmap = 0,
.nfrags = 4,
.fragsize = 4096,
.dspname = "/dev/dsp"
.devpath_out = "/dev/dsp",
.devpath_in = "/dev/dsp"
};
struct oss_params {
@ -74,65 +71,141 @@ struct oss_params {
int fragsize;
};
static int oss_hw_write (SWVoice *sw, void *buf, int len)
static void GCC_FMT_ATTR (2, 3) oss_logerr (int err, const char *fmt, ...)
{
return pcm_hw_write (sw, buf, len);
va_list ap;
AUD_vlog (AUDIO_CAP, fmt, ap);
va_start (ap, fmt);
AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
va_end (ap);
}
static int AUD_to_ossfmt (audfmt_e fmt)
static void GCC_FMT_ATTR (3, 4) oss_logerr2 (
int err,
const char *typ,
const char *fmt,
...
)
{
va_list ap;
AUD_log (AUDIO_CAP, "Can not initialize %s\n", typ);
va_start (ap, fmt);
AUD_vlog (AUDIO_CAP, fmt, ap);
va_end (ap);
AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
}
static void oss_anal_close (int *fdp)
{
int err = close (*fdp);
if (err) {
oss_logerr (errno, "Failed to close file(fd=%d)\n", *fdp);
}
*fdp = -1;
}
static int oss_write (SWVoiceOut *sw, void *buf, int len)
{
return audio_pcm_sw_write (sw, buf, len);
}
static int aud_to_ossfmt (audfmt_e fmt)
{
switch (fmt) {
case AUD_FMT_S8: return AFMT_S8;
case AUD_FMT_U8: return AFMT_U8;
case AUD_FMT_S16: return AFMT_S16_LE;
case AUD_FMT_U16: return AFMT_U16_LE;
case AUD_FMT_S8:
return AFMT_S8;
case AUD_FMT_U8:
return AFMT_U8;
case AUD_FMT_S16:
return AFMT_S16_LE;
case AUD_FMT_U16:
return AFMT_U16_LE;
default:
dolog ("Internal logic error: Bad audio format %d\nAborting\n", fmt);
exit (EXIT_FAILURE);
dolog ("Internal logic error: Bad audio format %d\n", fmt);
#ifdef DEBUG_AUDIO
abort ();
#endif
return AFMT_U8;
}
}
static int oss_to_audfmt (int fmt)
static int oss_to_audfmt (int ossfmt, audfmt_e *fmt, int *endianness)
{
switch (fmt) {
case AFMT_S8: return AUD_FMT_S8;
case AFMT_U8: return AUD_FMT_U8;
case AFMT_S16_LE: return AUD_FMT_S16;
case AFMT_U16_LE: return AUD_FMT_U16;
switch (ossfmt) {
case AFMT_S8:
*endianness =0;
*fmt = AUD_FMT_S8;
break;
case AFMT_U8:
*endianness = 0;
*fmt = AUD_FMT_U8;
break;
case AFMT_S16_LE:
*endianness = 0;
*fmt = AUD_FMT_S16;
break;
case AFMT_U16_LE:
*endianness = 0;
*fmt = AUD_FMT_U16;
break;
case AFMT_S16_BE:
*endianness = 1;
*fmt = AUD_FMT_S16;
break;
case AFMT_U16_BE:
*endianness = 1;
*fmt = AUD_FMT_U16;
break;
default:
dolog ("Internal logic error: Unrecognized OSS audio format %d\n"
"Aborting\n",
fmt);
exit (EXIT_FAILURE);
}
dolog ("Unrecognized audio format %d\n", ossfmt);
return -1;
}
#ifdef DEBUG_PCM
static void oss_dump_pcm_info (struct oss_params *req, struct oss_params *obt)
return 0;
}
#ifdef DEBUG_MISMATCHES
static void oss_dump_info (struct oss_params *req, struct oss_params *obt)
{
dolog ("parameter | requested value | obtained value\n");
dolog ("format | %10d | %10d\n", req->fmt, obt->fmt);
dolog ("channels | %10d | %10d\n", req->nchannels, obt->nchannels);
dolog ("channels | %10d | %10d\n",
req->nchannels, obt->nchannels);
dolog ("frequency | %10d | %10d\n", req->freq, obt->freq);
dolog ("nfrags | %10d | %10d\n", req->nfrags, obt->nfrags);
dolog ("fragsize | %10d | %10d\n", req->fragsize, obt->fragsize);
dolog ("fragsize | %10d | %10d\n",
req->fragsize, obt->fragsize);
}
#endif
static int oss_open (struct oss_params *req, struct oss_params *obt, int *pfd)
static int oss_open (int in, struct oss_params *req,
struct oss_params *obt, int *pfd)
{
int fd;
int mmmmssss;
audio_buf_info abinfo;
int fmt, freq, nchannels;
const char *dspname = conf.dspname;
const char *dspname = in ? conf.devpath_in : conf.devpath_out;
const char *typ = in ? "ADC" : "DAC";
fd = open (dspname, O_WRONLY | O_NONBLOCK);
fd = open (dspname, (in ? O_RDONLY : O_WRONLY) | O_NONBLOCK);
if (-1 == fd) {
dolog ("Could not initialize audio hardware. Failed to open `%s':\n"
"Reason:%s\n",
dspname,
errstr ());
oss_logerr2 (errno, typ, "Failed to open `%s'\n", dspname);
return -1;
}
@ -141,52 +214,35 @@ static int oss_open (struct oss_params *req, struct oss_params *obt, int *pfd)
fmt = req->fmt;
if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) {
dolog ("Could not initialize audio hardware\n"
"Failed to set sample size\n"
"Reason: %s\n",
errstr ());
oss_logerr2 (errno, typ, "Failed to set sample size %d\n", req->fmt);
goto err;
}
if (ioctl (fd, SNDCTL_DSP_CHANNELS, &nchannels)) {
dolog ("Could not initialize audio hardware\n"
"Failed to set number of channels\n"
"Reason: %s\n",
errstr ());
oss_logerr2 (errno, typ, "Failed to set number of channels %d\n",
req->nchannels);
goto err;
}
if (ioctl (fd, SNDCTL_DSP_SPEED, &freq)) {
dolog ("Could not initialize audio hardware\n"
"Failed to set frequency\n"
"Reason: %s\n",
errstr ());
oss_logerr2 (errno, typ, "Failed to set frequency %d\n", req->freq);
goto err;
}
if (ioctl (fd, SNDCTL_DSP_NONBLOCK)) {
dolog ("Could not initialize audio hardware\n"
"Failed to set non-blocking mode\n"
"Reason: %s\n",
errstr ());
oss_logerr2 (errno, typ, "Failed to set non-blocking mode\n");
goto err;
}
mmmmssss = (req->nfrags << 16) | lsbindex (req->fragsize);
if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss)) {
dolog ("Could not initialize audio hardware\n"
"Failed to set buffer length (%d, %d)\n"
"Reason:%s\n",
conf.nfrags, conf.fragsize,
errstr ());
oss_logerr2 (errno, typ, "Failed to set buffer length (%d, %d)\n",
req->nfrags, req->fragsize);
goto err;
}
if (ioctl (fd, SNDCTL_DSP_GETOSPACE, &abinfo)) {
dolog ("Could not initialize audio hardware\n"
"Failed to get buffer length\n"
"Reason:%s\n",
errstr ());
if (ioctl (fd, in ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &abinfo)) {
oss_logerr2 (errno, typ, "Failed to get buffer length\n");
goto err;
}
@ -202,25 +258,25 @@ static int oss_open (struct oss_params *req, struct oss_params *obt, int *pfd)
(req->freq != obt->freq) ||
(req->fragsize != obt->fragsize) ||
(req->nfrags != obt->nfrags)) {
#ifdef DEBUG_PCM
#ifdef DEBUG_MISMATCHES
dolog ("Audio parameters mismatch\n");
oss_dump_pcm_info (req, obt);
oss_dump_info (req, obt);
#endif
}
#ifdef DEBUG_PCM
oss_dump_pcm_info (req, obt);
#ifdef DEBUG
oss_dump_info (req, obt);
#endif
return 0;
err:
close (fd);
oss_anal_close (&fd);
return -1;
}
static void oss_hw_run (HWVoice *hw)
static int oss_run_out (HWVoiceOut *hw)
{
OSSVoice *oss = (OSSVoice *) hw;
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
int err, rpos, live, decr;
int samples;
uint8_t *dst;
@ -228,23 +284,25 @@ static void oss_hw_run (HWVoice *hw)
struct audio_buf_info abinfo;
struct count_info cntinfo;
live = pcm_hw_get_live (hw);
if (live <= 0)
return;
live = audio_pcm_hw_get_live_out (hw);
if (!live) {
return 0;
}
if (oss->mmapped) {
int bytes;
err = ioctl (oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo);
if (err < 0) {
dolog ("SNDCTL_DSP_GETOPTR failed\nReason: %s\n", errstr ());
return;
oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
return 0;
}
if (cntinfo.ptr == oss->old_optr) {
if (abs (hw->samples - live) < 64)
dolog ("overrun\n");
return;
if (abs (hw->samples - live) < 64) {
dolog ("warning: overrun\n");
}
return 0;
}
if (cntinfo.ptr > oss->old_optr) {
@ -254,18 +312,25 @@ static void oss_hw_run (HWVoice *hw)
bytes = hw->bufsize + cntinfo.ptr - oss->old_optr;
}
decr = audio_MIN (bytes >> hw->shift, live);
decr = audio_MIN (bytes >> hw->info.shift, live);
}
else {
err = ioctl (oss->fd, SNDCTL_DSP_GETOSPACE, &abinfo);
if (err < 0) {
dolog ("SNDCTL_DSP_GETOSPACE failed\nReason: %s\n", errstr ());
return;
oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
return 0;
}
decr = audio_MIN (abinfo.bytes >> hw->shift, live);
if (decr <= 0)
return;
if (abinfo.bytes < 0 || abinfo.bytes > hw->bufsize) {
ldebug ("warning: invalid available size, size=%d bufsize=%d\n",
abinfo.bytes, hw->bufsize);
return 0;
}
decr = audio_MIN (abinfo.bytes >> hw->info.shift, live);
if (!decr) {
return 0;
}
}
samples = decr;
@ -274,33 +339,41 @@ static void oss_hw_run (HWVoice *hw)
int left_till_end_samples = hw->samples - rpos;
int convert_samples = audio_MIN (samples, left_till_end_samples);
src = advance (hw->mix_buf, rpos * sizeof (st_sample_t));
dst = advance (oss->pcm_buf, rpos << hw->shift);
src = hw->mix_buf + rpos;
dst = advance (oss->pcm_buf, rpos << hw->info.shift);
hw->clip (dst, src, convert_samples);
if (!oss->mmapped) {
int written;
written = write (oss->fd, dst, convert_samples << hw->shift);
written = write (oss->fd, dst, convert_samples << hw->info.shift);
/* XXX: follow errno recommendations ? */
if (written == -1) {
dolog ("Failed to write audio\nReason: %s\n", errstr ());
oss_logerr (
errno,
"Failed to write %d bytes of audio data from %p\n",
convert_samples << hw->info.shift,
dst
);
continue;
}
if (written != convert_samples << hw->shift) {
int wsamples = written >> hw->shift;
int wbytes = wsamples << hw->shift;
if (written != convert_samples << hw->info.shift) {
int wsamples = written >> hw->info.shift;
int wbytes = wsamples << hw->info.shift;
if (wbytes != written) {
dolog ("Unaligned write %d, %d\n", wbytes, written);
dolog ("warning: misaligned write %d (requested %d), "
"alignment %d\n",
wbytes, written, hw->info.align + 1);
}
memset (src, 0, wbytes);
decr -= samples;
mixeng_clear (src, wsamples);
decr -= wsamples;
rpos = (rpos + wsamples) % hw->samples;
break;
}
}
memset (src, 0, convert_samples * sizeof (st_sample_t));
mixeng_clear (src, convert_samples);
rpos = (rpos + convert_samples) % hw->samples;
samples -= convert_samples;
@ -309,28 +382,24 @@ static void oss_hw_run (HWVoice *hw)
oss->old_optr = cntinfo.ptr;
}
pcm_hw_dec_live (hw, decr);
hw->rpos = rpos;
return decr;
}
static void oss_hw_fini (HWVoice *hw)
static void oss_fini_out (HWVoiceOut *hw)
{
int err;
OSSVoice *oss = (OSSVoice *) hw;
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
ldebug ("oss_hw_fini\n");
err = close (oss->fd);
if (err) {
dolog ("Failed to close OSS descriptor\nReason: %s\n", errstr ());
}
oss->fd = -1;
ldebug ("oss_fini\n");
oss_anal_close (&oss->fd);
if (oss->pcm_buf) {
if (oss->mmapped) {
err = munmap (oss->pcm_buf, hw->bufsize);
if (err) {
dolog ("Failed to unmap OSS buffer\nReason: %s\n",
errstr ());
oss_logerr (errno, "Failed to unmap buffer %p, size %d\n",
oss->pcm_buf, hw->bufsize);
}
}
else {
@ -340,25 +409,38 @@ static void oss_hw_fini (HWVoice *hw)
}
}
static int oss_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
static int oss_init_out (HWVoiceOut *hw, int freq, int nchannels, audfmt_e fmt)
{
OSSVoice *oss = (OSSVoice *) hw;
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
struct oss_params req, obt;
int endianness;
int err;
int fd;
audfmt_e effective_fmt;
assert (!oss->fd);
req.fmt = AUD_to_ossfmt (fmt);
req.fmt = aud_to_ossfmt (fmt);
req.freq = freq;
req.nchannels = nchannels;
req.fragsize = conf.fragsize;
req.nfrags = conf.nfrags;
if (oss_open (&req, &obt, &oss->fd))
if (oss_open (0, &req, &obt, &fd)) {
return -1;
}
hw->freq = obt.freq;
hw->fmt = oss_to_audfmt (obt.fmt);
hw->nchannels = obt.nchannels;
err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness);
if (err) {
oss_anal_close (&fd);
return -1;
}
audio_pcm_init_info (
&hw->info,
obt.freq,
obt.nchannels,
effective_fmt,
audio_need_to_swap_endian (endianness)
);
oss->nfrags = obt.nfrags;
oss->fragsize = obt.fragsize;
hw->bufsize = obt.nfrags * obt.fragsize;
@ -366,22 +448,23 @@ static int oss_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
oss->mmapped = 0;
if (conf.try_mmap) {
oss->pcm_buf = mmap (0, hw->bufsize, PROT_READ | PROT_WRITE,
MAP_SHARED, oss->fd, 0);
MAP_SHARED, fd, 0);
if (oss->pcm_buf == MAP_FAILED) {
dolog ("Failed to mmap OSS device\nReason: %s\n",
errstr ());
oss_logerr (errno, "Failed to map %d bytes of DAC\n",
hw->bufsize);
} else {
int err;
int trig = 0;
if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
dolog ("SNDCTL_DSP_SETTRIGGER 0 failed\nReason: %s\n",
errstr ());
if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n");
}
else {
trig = PCM_ENABLE_OUTPUT;
if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
dolog ("SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
"Reason: %s\n", errstr ());
if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
oss_logerr (
errno,
"SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
);
}
else {
oss->mmapped = 1;
@ -391,8 +474,8 @@ static int oss_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
if (!oss->mmapped) {
err = munmap (oss->pcm_buf, hw->bufsize);
if (err) {
dolog ("Failed to unmap OSS device\nReason: %s\n",
errstr ());
oss_logerr (errno, "Failed to unmap buffer %p size %d\n",
oss->pcm_buf, hw->bufsize);
}
}
}
@ -401,31 +484,34 @@ static int oss_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
if (!oss->mmapped) {
oss->pcm_buf = qemu_mallocz (hw->bufsize);
if (!oss->pcm_buf) {
close (oss->fd);
oss->fd = -1;
oss_anal_close (&fd);
return -1;
}
}
oss->fd = fd;
return 0;
}
static int oss_hw_ctl (HWVoice *hw, int cmd, ...)
static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
{
int trig;
OSSVoice *oss = (OSSVoice *) hw;
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
if (!oss->mmapped)
if (!oss->mmapped) {
return 0;
}
switch (cmd) {
case VOICE_ENABLE:
ldebug ("enabling voice\n");
pcm_hw_clear (hw, oss->pcm_buf, hw->samples);
audio_pcm_info_clear_buf (&hw->info, oss->pcm_buf, hw->samples);
trig = PCM_ENABLE_OUTPUT;
if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
dolog ("SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
"Reason: %s\n", errstr ());
oss_logerr (
errno,
"SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
);
return -1;
}
break;
@ -434,8 +520,7 @@ static int oss_hw_ctl (HWVoice *hw, int cmd, ...)
ldebug ("disabling voice\n");
trig = 0;
if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
dolog ("SNDCTL_DSP_SETTRIGGER 0 failed\nReason: %s\n",
errstr ());
oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n");
return -1;
}
break;
@ -443,33 +528,194 @@ static int oss_hw_ctl (HWVoice *hw, int cmd, ...)
return 0;
}
static int oss_init_in (HWVoiceIn *hw,
int freq, int nchannels, audfmt_e fmt)
{
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
struct oss_params req, obt;
int endianness;
int err;
int fd;
audfmt_e effective_fmt;
req.fmt = aud_to_ossfmt (fmt);
req.freq = freq;
req.nchannels = nchannels;
req.fragsize = conf.fragsize;
req.nfrags = conf.nfrags;
if (oss_open (1, &req, &obt, &fd)) {
return -1;
}
err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness);
if (err) {
oss_anal_close (&fd);
return -1;
}
audio_pcm_init_info (
&hw->info,
obt.freq,
obt.nchannels,
effective_fmt,
audio_need_to_swap_endian (endianness)
);
oss->nfrags = obt.nfrags;
oss->fragsize = obt.fragsize;
hw->bufsize = obt.nfrags * obt.fragsize;
oss->pcm_buf = qemu_mallocz (hw->bufsize);
if (!oss->pcm_buf) {
oss_anal_close (&fd);
return -1;
}
oss->fd = fd;
return 0;
}
static void oss_fini_in (HWVoiceIn *hw)
{
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
oss_anal_close (&oss->fd);
if (oss->pcm_buf) {
qemu_free (oss->pcm_buf);
oss->pcm_buf = NULL;
}
}
static int oss_run_in (HWVoiceIn *hw)
{
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
int hwshift = hw->info.shift;
int i;
int live = audio_pcm_hw_get_live_in (hw);
int dead = hw->samples - live;
size_t read_samples = 0;
struct {
int add;
int len;
} bufs[2] = {
{ hw->wpos, 0 },
{ 0, 0 }
};
if (!dead) {
return 0;
}
if (hw->wpos + dead > hw->samples) {
bufs[0].len = (hw->samples - hw->wpos) << hwshift;
bufs[1].len = (dead - (hw->samples - hw->wpos)) << hwshift;
}
else {
bufs[0].len = dead << hwshift;
}
for (i = 0; i < 2; ++i) {
ssize_t nread;
if (bufs[i].len) {
void *p = advance (oss->pcm_buf, bufs[i].add << hwshift);
nread = read (oss->fd, p, bufs[i].len);
if (nread > 0) {
if (nread & hw->info.align) {
dolog ("warning: misaligned read %d (requested %d), "
"alignment %d\n", nread, bufs[i].add << hwshift,
hw->info.align + 1);
}
read_samples += nread >> hwshift;
hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift,
&nominal_volume);
}
if (bufs[i].len - nread) {
if (nread == -1) {
switch (errno) {
case EINTR:
case EAGAIN:
break;
default:
oss_logerr (
errno,
"Failed to read %d bytes of audio (to %p)\n",
bufs[i].len, p
);
break;
}
}
break;
}
}
}
hw->wpos = (hw->wpos + read_samples) % hw->samples;
return read_samples;
}
static int oss_read (SWVoiceIn *sw, void *buf, int size)
{
return audio_pcm_sw_read (sw, buf, size);
}
static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
{
(void) hw;
(void) cmd;
return 0;
}
static void *oss_audio_init (void)
{
conf.fragsize = audio_get_conf_int (QC_OSS_FRAGSIZE, conf.fragsize);
conf.nfrags = audio_get_conf_int (QC_OSS_NFRAGS, conf.nfrags);
conf.try_mmap = audio_get_conf_int (QC_OSS_MMAP, conf.try_mmap);
conf.dspname = audio_get_conf_str (QC_OSS_DEV, conf.dspname);
return &conf;
}
static void oss_audio_fini (void *opaque)
{
(void) opaque;
}
struct pcm_ops oss_pcm_ops = {
oss_hw_init,
oss_hw_fini,
oss_hw_run,
oss_hw_write,
oss_hw_ctl
static struct audio_option oss_options[] = {
{"FRAGSIZE", AUD_OPT_INT, &conf.fragsize,
"Fragment size in bytes", NULL, 0},
{"NFRAGS", AUD_OPT_INT, &conf.nfrags,
"Number of fragments", NULL, 0},
{"MMAP", AUD_OPT_BOOL, &conf.try_mmap,
"Try using memory mapped access", NULL, 0},
{"DAC_DEV", AUD_OPT_STR, &conf.devpath_out,
"Path to DAC device", NULL, 0},
{"ADC_DEV", AUD_OPT_STR, &conf.devpath_in,
"Path to ADC device", NULL, 0},
{NULL, 0, NULL, NULL, NULL, 0}
};
struct audio_output_driver oss_output_driver = {
"oss",
oss_audio_init,
oss_audio_fini,
&oss_pcm_ops,
1,
INT_MAX,
sizeof (OSSVoice)
static struct audio_pcm_ops oss_pcm_ops = {
oss_init_out,
oss_fini_out,
oss_run_out,
oss_write,
oss_ctl_out,
oss_init_in,
oss_fini_in,
oss_run_in,
oss_read,
oss_ctl_in
};
struct audio_driver oss_audio_driver = {
INIT_FIELD (name = ) "oss",
INIT_FIELD (descr = ) "OSS http://www.opensound.com",
INIT_FIELD (options = ) oss_options,
INIT_FIELD (init = ) oss_audio_init,
INIT_FIELD (fini = ) oss_audio_fini,
INIT_FIELD (pcm_ops = ) &oss_pcm_ops,
INIT_FIELD (can_be_default = ) 1,
INIT_FIELD (max_voices_out = ) INT_MAX,
INIT_FIELD (max_voices_in = ) INT_MAX,
INIT_FIELD (voice_size_out = ) sizeof (OSSVoiceOut),
INIT_FIELD (voice_size_in = ) sizeof (OSSVoiceIn)
};

111
audio/rate_template.h Normal file
View File

@ -0,0 +1,111 @@
/*
* QEMU Mixing engine
*
* Copyright (c) 2004-2005 Vassili Karpov (malc)
* Copyright (c) 1998 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/*
* Processed signed long samples from ibuf to obuf.
* Return number of samples processed.
*/
void NAME (void *opaque, st_sample_t *ibuf, st_sample_t *obuf,
int *isamp, int *osamp)
{
rate_t rate = (rate_t) opaque;
st_sample_t *istart, *iend;
st_sample_t *ostart, *oend;
st_sample_t ilast, icur, out;
#ifdef FLOAT_MIXENG
real_t t;
#else
int64_t t;
#endif
ilast = rate->ilast;
istart = ibuf;
iend = ibuf + *isamp;
ostart = obuf;
oend = obuf + *osamp;
if (rate->opos_inc == (1ULL + UINT_MAX)) {
int i, n = *isamp > *osamp ? *osamp : *isamp;
for (i = 0; i < n; i++) {
OP (obuf[i].l, ibuf[i].r);
OP (obuf[i].r, ibuf[i].r);
}
*isamp = n;
*osamp = n;
return;
}
while (obuf < oend) {
/* Safety catch to make sure we have input samples. */
if (ibuf >= iend) {
break;
}
/* read as many input samples so that ipos > opos */
while (rate->ipos <= (rate->opos >> 32)) {
ilast = *ibuf++;
rate->ipos++;
/* See if we finished the input buffer yet */
if (ibuf >= iend) {
goto the_end;
}
}
icur = *ibuf;
/* interpolate */
#ifdef FLOAT_MIXENG
#ifdef RECIPROCAL
t = (rate->opos & UINT_MAX) * (1.f / UINT_MAX);
#else
t = (rate->opos & UINT_MAX) / (real_t) UINT_MAX;
#endif
out.l = (ilast.l * (1.0 - t)) + icur.l * t;
out.r = (ilast.r * (1.0 - t)) + icur.r * t;
#else
t = rate->opos & 0xffffffff;
out.l = (ilast.l * ((int64_t) UINT_MAX - t) + icur.l * t) >> 32;
out.r = (ilast.r * ((int64_t) UINT_MAX - t) + icur.r * t) >> 32;
#endif
/* output sample & increment position */
OP (obuf->l, out.l);
OP (obuf->r, out.r);
obuf += 1;
rate->opos += rate->opos_inc;
}
the_end:
*isamp = ibuf - istart;
*osamp = obuf - ostart;
rate->ilast = ilast;
}
#undef NAME
#undef OP

View File

@ -1,7 +1,7 @@
/*
* QEMU SDL audio output driver
* QEMU SDL audio driver
*
* Copyright (c) 2004 Vassili Karpov (malc)
* Copyright (c) 2004-2005 Vassili Karpov (malc)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -25,22 +25,15 @@
#include <SDL_thread.h>
#include "vl.h"
#include "audio/audio_int.h"
#define AUDIO_CAP "sdl"
#include "audio_int.h"
typedef struct SDLVoice {
HWVoice hw;
} SDLVoice;
#define dolog(...) AUD_log ("sdl", __VA_ARGS__)
#ifdef DEBUG
#define ldebug(...) dolog (__VA_ARGS__)
#else
#define ldebug(...)
#endif
#define QC_SDL_SAMPLES "QEMU_SDL_SAMPLES"
#define errstr() SDL_GetError ()
typedef struct SDLVoiceOut {
HWVoiceOut hw;
int live;
int rpos;
int decr;
} SDLVoiceOut;
static struct {
int nb_samples;
@ -56,91 +49,129 @@ struct SDLAudioState {
} glob_sdl;
typedef struct SDLAudioState SDLAudioState;
static void sdl_hw_run (HWVoice *hw)
static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...)
{
(void) hw;
va_list ap;
va_start (ap, fmt);
AUD_vlog (AUDIO_CAP, fmt, ap);
va_end (ap);
AUD_log (AUDIO_CAP, "Reason: %s\n", SDL_GetError ());
}
static int sdl_lock (SDLAudioState *s)
static int sdl_lock (SDLAudioState *s, const char *forfn)
{
if (SDL_LockMutex (s->mutex)) {
dolog ("SDL_LockMutex failed\nReason: %s\n", errstr ());
sdl_logerr ("SDL_LockMutex for %s failed\n", forfn);
return -1;
}
return 0;
}
static int sdl_unlock (SDLAudioState *s)
static int sdl_unlock (SDLAudioState *s, const char *forfn)
{
if (SDL_UnlockMutex (s->mutex)) {
dolog ("SDL_UnlockMutex failed\nReason: %s\n", errstr ());
sdl_logerr ("SDL_UnlockMutex for %s failed\n", forfn);
return -1;
}
return 0;
}
static int sdl_post (SDLAudioState *s)
static int sdl_post (SDLAudioState *s, const char *forfn)
{
if (SDL_SemPost (s->sem)) {
dolog ("SDL_SemPost failed\nReason: %s\n", errstr ());
sdl_logerr ("SDL_SemPost for %s failed\n", forfn);
return -1;
}
return 0;
}
static int sdl_wait (SDLAudioState *s)
static int sdl_wait (SDLAudioState *s, const char *forfn)
{
if (SDL_SemWait (s->sem)) {
dolog ("SDL_SemWait failed\nReason: %s\n", errstr ());
sdl_logerr ("SDL_SemWait for %s failed\n", forfn);
return -1;
}
return 0;
}
static int sdl_unlock_and_post (SDLAudioState *s)
static int sdl_unlock_and_post (SDLAudioState *s, const char *forfn)
{
if (sdl_unlock (s))
if (sdl_unlock (s, forfn)) {
return -1;
return sdl_post (s);
}
static int sdl_hw_write (SWVoice *sw, void *buf, int len)
{
int ret;
SDLAudioState *s = &glob_sdl;
sdl_lock (s);
ret = pcm_hw_write (sw, buf, len);
sdl_unlock_and_post (s);
return ret;
return sdl_post (s, forfn);
}
static int AUD_to_sdlfmt (audfmt_e fmt, int *shift)
static int aud_to_sdlfmt (audfmt_e fmt, int *shift)
{
switch (fmt) {
case AUD_FMT_S8:
*shift = 0;
switch (fmt) {
case AUD_FMT_S8: return AUDIO_S8;
case AUD_FMT_U8: return AUDIO_U8;
case AUD_FMT_S16: *shift = 1; return AUDIO_S16LSB;
case AUD_FMT_U16: *shift = 1; return AUDIO_U16LSB;
return AUDIO_S8;
case AUD_FMT_U8:
*shift = 0;
return AUDIO_U8;
case AUD_FMT_S16:
*shift = 1;
return AUDIO_S16LSB;
case AUD_FMT_U16:
*shift = 1;
return AUDIO_U16LSB;
default:
dolog ("Internal logic error: Bad audio format %d\nAborting\n", fmt);
exit (EXIT_FAILURE);
dolog ("Internal logic error: Bad audio format %d\n", fmt);
#ifdef DEBUG_AUDIO
abort ();
#endif
return AUDIO_U8;
}
}
static int sdl_to_audfmt (int fmt)
static int sdl_to_audfmt (int sdlfmt, audfmt_e *fmt, int *endianess)
{
switch (fmt) {
case AUDIO_S8: return AUD_FMT_S8;
case AUDIO_U8: return AUD_FMT_U8;
case AUDIO_S16LSB: return AUD_FMT_S16;
case AUDIO_U16LSB: return AUD_FMT_U16;
switch (sdlfmt) {
case AUDIO_S8:
*endianess = 0;
*fmt = AUD_FMT_S8;
break;
case AUDIO_U8:
*endianess = 0;
*fmt = AUD_FMT_U8;
break;
case AUDIO_S16LSB:
*endianess = 0;
*fmt = AUD_FMT_S16;
break;
case AUDIO_U16LSB:
*endianess = 0;
*fmt = AUD_FMT_U16;
break;
case AUDIO_S16MSB:
*endianess = 1;
*fmt = AUD_FMT_S16;
break;
case AUDIO_U16MSB:
*endianess = 1;
*fmt = AUD_FMT_U16;
break;
default:
dolog ("Internal logic error: Unrecognized SDL audio format %d\n"
"Aborting\n", fmt);
exit (EXIT_FAILURE);
dolog ("Unrecognized SDL audio format %d\n", sdlfmt);
return -1;
}
return 0;
}
static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt)
@ -149,7 +180,7 @@ static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt)
status = SDL_OpenAudio (req, obt);
if (status) {
dolog ("SDL_OpenAudio failed\nReason: %s\n", errstr ());
sdl_logerr ("SDL_OpenAudio failed\n");
}
return status;
}
@ -157,9 +188,9 @@ static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt)
static void sdl_close (SDLAudioState *s)
{
if (s->initialized) {
sdl_lock (s);
sdl_lock (s, "sdl_close");
s->exit = 1;
sdl_unlock_and_post (s);
sdl_unlock_and_post (s, "sdl_close");
SDL_PauseAudio (1);
SDL_CloseAudio ();
s->initialized = 0;
@ -168,31 +199,40 @@ static void sdl_close (SDLAudioState *s)
static void sdl_callback (void *opaque, Uint8 *buf, int len)
{
SDLVoice *sdl = opaque;
SDLVoiceOut *sdl = opaque;
SDLAudioState *s = &glob_sdl;
HWVoice *hw = &sdl->hw;
int samples = len >> hw->shift;
HWVoiceOut *hw = &sdl->hw;
int samples = len >> hw->info.shift;
if (s->exit) {
return;
}
while (samples) {
int to_mix, live, decr;
int to_mix, decr;
/* dolog ("in callback samples=%d\n", samples); */
sdl_wait (s);
sdl_wait (s, "sdl_callback");
if (s->exit) {
return;
}
sdl_lock (s);
live = pcm_hw_get_live (hw);
if (live <= 0)
if (sdl_lock (s, "sdl_callback")) {
return;
}
if (audio_bug (AUDIO_FUNC, sdl->live < 0 || sdl->live > hw->samples)) {
dolog ("sdl->live=%d hw->samples=%d\n",
sdl->live, hw->samples);
return;
}
if (!sdl->live) {
goto again;
}
/* dolog ("in callback live=%d\n", live); */
to_mix = audio_MIN (samples, live);
to_mix = audio_MIN (samples, sdl->live);
decr = to_mix;
while (to_mix) {
int chunk = audio_MIN (to_mix, hw->samples - hw->rpos);
@ -200,44 +240,86 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len)
/* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */
hw->clip (buf, src, chunk);
memset (src, 0, chunk * sizeof (st_sample_t));
hw->rpos = (hw->rpos + chunk) % hw->samples;
mixeng_clear (src, chunk);
sdl->rpos = (sdl->rpos + chunk) % hw->samples;
to_mix -= chunk;
buf += chunk << hw->shift;
buf += chunk << hw->info.shift;
}
samples -= decr;
pcm_hw_dec_live (hw, decr);
sdl->live -= decr;
sdl->decr += decr;
again:
sdl_unlock (s);
if (sdl_unlock (s, "sdl_callback")) {
return;
}
}
/* dolog ("done len=%d\n", len); */
}
static void sdl_hw_fini (HWVoice *hw)
static int sdl_write_out (SWVoiceOut *sw, void *buf, int len)
{
ldebug ("sdl_hw_fini %d fixed=%d\n",
glob_sdl.initialized, audio_state.fixed_format);
return audio_pcm_sw_write (sw, buf, len);
}
static int sdl_run_out (HWVoiceOut *hw)
{
int decr, live;
SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
SDLAudioState *s = &glob_sdl;
if (sdl_lock (s, "sdl_callback")) {
return 0;
}
live = audio_pcm_hw_get_live_out (hw);
if (sdl->decr > live) {
ldebug ("sdl->decr %d live %d sdl->live %d\n",
sdl->decr,
live,
sdl->live);
}
decr = audio_MIN (sdl->decr, live);
sdl->decr -= decr;
sdl->live = live - decr;
hw->rpos = sdl->rpos;
if (sdl->live > 0) {
sdl_unlock_and_post (s, "sdl_callback");
}
else {
sdl_unlock (s, "sdl_callback");
}
return decr;
}
static void sdl_fini_out (HWVoiceOut *hw)
{
(void) hw;
sdl_close (&glob_sdl);
}
static int sdl_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
static int sdl_init_out (HWVoiceOut *hw, int freq, int nchannels, audfmt_e fmt)
{
SDLVoice *sdl = (SDLVoice *) hw;
SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
SDLAudioState *s = &glob_sdl;
SDL_AudioSpec req, obt;
int shift;
ldebug ("sdl_hw_init %d freq=%d fixed=%d\n",
s->initialized, freq, audio_state.fixed_format);
int endianess;
int err;
audfmt_e effective_fmt;
if (nchannels != 2) {
dolog ("Bogus channel count %d\n", nchannels);
dolog ("Can not init DAC. Bogus channel count %d\n", nchannels);
return -1;
}
req.freq = freq;
req.format = AUD_to_sdlfmt (fmt, &shift);
req.format = aud_to_sdlfmt (fmt, &shift);
req.channels = nchannels;
req.samples = conf.nb_samples;
shift <<= nchannels == 2;
@ -245,12 +327,23 @@ static int sdl_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
req.callback = sdl_callback;
req.userdata = sdl;
if (sdl_open (&req, &obt))
if (sdl_open (&req, &obt)) {
return -1;
}
hw->freq = obt.freq;
hw->fmt = sdl_to_audfmt (obt.format);
hw->nchannels = obt.channels;
err = sdl_to_audfmt (obt.format, &effective_fmt, &endianess);
if (err) {
sdl_close (s);
return -1;
}
audio_pcm_init_info (
&hw->info,
obt.freq,
obt.channels,
effective_fmt,
audio_need_to_swap_endian (endianess)
);
hw->bufsize = obt.samples << shift;
s->initialized = 1;
@ -259,7 +352,7 @@ static int sdl_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
return 0;
}
static int sdl_hw_ctl (HWVoice *hw, int cmd, ...)
static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...)
{
(void) hw;
@ -278,24 +371,22 @@ static int sdl_hw_ctl (HWVoice *hw, int cmd, ...)
static void *sdl_audio_init (void)
{
SDLAudioState *s = &glob_sdl;
conf.nb_samples = audio_get_conf_int (QC_SDL_SAMPLES, conf.nb_samples);
if (SDL_InitSubSystem (SDL_INIT_AUDIO)) {
dolog ("SDL failed to initialize audio subsystem\nReason: %s\n",
errstr ());
sdl_logerr ("SDL failed to initialize audio subsystem\n");
return NULL;
}
s->mutex = SDL_CreateMutex ();
if (!s->mutex) {
dolog ("Failed to create SDL mutex\nReason: %s\n", errstr ());
sdl_logerr ("Failed to create SDL mutex\n");
SDL_QuitSubSystem (SDL_INIT_AUDIO);
return NULL;
}
s->sem = SDL_CreateSemaphore (0);
if (!s->sem) {
dolog ("Failed to create SDL semaphore\nReason: %s\n", errstr ());
sdl_logerr ("Failed to create SDL semaphore\n");
SDL_DestroyMutex (s->mutex);
SDL_QuitSubSystem (SDL_INIT_AUDIO);
return NULL;
@ -313,20 +404,36 @@ static void sdl_audio_fini (void *opaque)
SDL_QuitSubSystem (SDL_INIT_AUDIO);
}
struct pcm_ops sdl_pcm_ops = {
sdl_hw_init,
sdl_hw_fini,
sdl_hw_run,
sdl_hw_write,
sdl_hw_ctl
static struct audio_option sdl_options[] = {
{"SAMPLES", AUD_OPT_INT, &conf.nb_samples,
"Size of SDL buffer in samples", NULL, 0},
{NULL, 0, NULL, NULL, NULL, 0}
};
struct audio_output_driver sdl_output_driver = {
"sdl",
sdl_audio_init,
sdl_audio_fini,
&sdl_pcm_ops,
1,
1,
sizeof (SDLVoice)
static struct audio_pcm_ops sdl_pcm_ops = {
sdl_init_out,
sdl_fini_out,
sdl_run_out,
sdl_write_out,
sdl_ctl_out,
NULL,
NULL,
NULL,
NULL,
NULL
};
struct audio_driver sdl_audio_driver = {
INIT_FIELD (name = ) "sdl",
INIT_FIELD (descr = ) "SDL http://www.libsdl.org",
INIT_FIELD (options = ) sdl_options,
INIT_FIELD (init = ) sdl_audio_init,
INIT_FIELD (fini = ) sdl_audio_fini,
INIT_FIELD (pcm_ops = ) &sdl_pcm_ops,
INIT_FIELD (can_be_default = ) 1,
INIT_FIELD (max_voices_out = ) 1,
INIT_FIELD (max_voices_in = ) 0,
INIT_FIELD (voice_size_out = ) sizeof (SDLVoiceOut),
INIT_FIELD (voice_size_in = ) 0
};

241
audio/sys-queue.h Normal file
View File

@ -0,0 +1,241 @@
/*
* Copyright (c) 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)queue.h 8.3 (Berkeley) 12/13/93
*/
#ifndef _SYS_QUEUE_H
#define _SYS_QUEUE_H 1
/*
* This file defines three types of data structures: lists, tail queues,
* and circular queues.
*
* A list is headed by a single forward pointer (or an array of forward
* pointers for a hash table header). The elements are doubly linked
* so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list after
* an existing element or at the head of the list. A list may only be
* traversed in the forward direction.
*
* A tail queue is headed by a pair of pointers, one to the head of the
* list and the other to the tail of the list. The elements are doubly
* linked so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list after
* an existing element, at the head of the list, or at the end of the
* list. A tail queue may only be traversed in the forward direction.
*
* A circle queue is headed by a pair of pointers, one to the head of the
* list and the other to the tail of the list. The elements are doubly
* linked so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before or after
* an existing element, at the head of the list, or at the end of the list.
* A circle queue may be traversed in either direction, but has a more
* complex end of list detection.
*
* For details on the use of these macros, see the queue(3) manual page.
*/
/*
* List definitions.
*/
#define LIST_HEAD(name, type) \
struct name { \
struct type *lh_first; /* first element */ \
}
#define LIST_ENTRY(type) \
struct { \
struct type *le_next; /* next element */ \
struct type **le_prev; /* address of previous next element */ \
}
/*
* List functions.
*/
#define LIST_INIT(head) { \
(head)->lh_first = NULL; \
}
#define LIST_INSERT_AFTER(listelm, elm, field) { \
if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
(listelm)->field.le_next->field.le_prev = \
&(elm)->field.le_next; \
(listelm)->field.le_next = (elm); \
(elm)->field.le_prev = &(listelm)->field.le_next; \
}
#define LIST_INSERT_HEAD(head, elm, field) { \
if (((elm)->field.le_next = (head)->lh_first) != NULL) \
(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
(head)->lh_first = (elm); \
(elm)->field.le_prev = &(head)->lh_first; \
}
#define LIST_REMOVE(elm, field) { \
if ((elm)->field.le_next != NULL) \
(elm)->field.le_next->field.le_prev = \
(elm)->field.le_prev; \
*(elm)->field.le_prev = (elm)->field.le_next; \
}
/*
* Tail queue definitions.
*/
#define TAILQ_HEAD(name, type) \
struct name { \
struct type *tqh_first; /* first element */ \
struct type **tqh_last; /* addr of last next element */ \
}
#define TAILQ_ENTRY(type) \
struct { \
struct type *tqe_next; /* next element */ \
struct type **tqe_prev; /* address of previous next element */ \
}
/*
* Tail queue functions.
*/
#define TAILQ_INIT(head) { \
(head)->tqh_first = NULL; \
(head)->tqh_last = &(head)->tqh_first; \
}
#define TAILQ_INSERT_HEAD(head, elm, field) { \
if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
(elm)->field.tqe_next->field.tqe_prev = \
&(elm)->field.tqe_next; \
else \
(head)->tqh_last = &(elm)->field.tqe_next; \
(head)->tqh_first = (elm); \
(elm)->field.tqe_prev = &(head)->tqh_first; \
}
#define TAILQ_INSERT_TAIL(head, elm, field) { \
(elm)->field.tqe_next = NULL; \
(elm)->field.tqe_prev = (head)->tqh_last; \
*(head)->tqh_last = (elm); \
(head)->tqh_last = &(elm)->field.tqe_next; \
}
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) { \
if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
(elm)->field.tqe_next->field.tqe_prev = \
&(elm)->field.tqe_next; \
else \
(head)->tqh_last = &(elm)->field.tqe_next; \
(listelm)->field.tqe_next = (elm); \
(elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
}
#define TAILQ_REMOVE(head, elm, field) { \
if (((elm)->field.tqe_next) != NULL) \
(elm)->field.tqe_next->field.tqe_prev = \
(elm)->field.tqe_prev; \
else \
(head)->tqh_last = (elm)->field.tqe_prev; \
*(elm)->field.tqe_prev = (elm)->field.tqe_next; \
}
/*
* Circular queue definitions.
*/
#define CIRCLEQ_HEAD(name, type) \
struct name { \
struct type *cqh_first; /* first element */ \
struct type *cqh_last; /* last element */ \
}
#define CIRCLEQ_ENTRY(type) \
struct { \
struct type *cqe_next; /* next element */ \
struct type *cqe_prev; /* previous element */ \
}
/*
* Circular queue functions.
*/
#define CIRCLEQ_INIT(head) { \
(head)->cqh_first = (void *)(head); \
(head)->cqh_last = (void *)(head); \
}
#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) { \
(elm)->field.cqe_next = (listelm)->field.cqe_next; \
(elm)->field.cqe_prev = (listelm); \
if ((listelm)->field.cqe_next == (void *)(head)) \
(head)->cqh_last = (elm); \
else \
(listelm)->field.cqe_next->field.cqe_prev = (elm); \
(listelm)->field.cqe_next = (elm); \
}
#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) { \
(elm)->field.cqe_next = (listelm); \
(elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
if ((listelm)->field.cqe_prev == (void *)(head)) \
(head)->cqh_first = (elm); \
else \
(listelm)->field.cqe_prev->field.cqe_next = (elm); \
(listelm)->field.cqe_prev = (elm); \
}
#define CIRCLEQ_INSERT_HEAD(head, elm, field) { \
(elm)->field.cqe_next = (head)->cqh_first; \
(elm)->field.cqe_prev = (void *)(head); \
if ((head)->cqh_last == (void *)(head)) \
(head)->cqh_last = (elm); \
else \
(head)->cqh_first->field.cqe_prev = (elm); \
(head)->cqh_first = (elm); \
}
#define CIRCLEQ_INSERT_TAIL(head, elm, field) { \
(elm)->field.cqe_next = (void *)(head); \
(elm)->field.cqe_prev = (head)->cqh_last; \
if ((head)->cqh_first == (void *)(head)) \
(head)->cqh_first = (elm); \
else \
(head)->cqh_last->field.cqe_next = (elm); \
(head)->cqh_last = (elm); \
}
#define CIRCLEQ_REMOVE(head, elm, field) { \
if ((elm)->field.cqe_next == (void *)(head)) \
(head)->cqh_last = (elm)->field.cqe_prev; \
else \
(elm)->field.cqe_next->field.cqe_prev = \
(elm)->field.cqe_prev; \
if ((elm)->field.cqe_prev == (void *)(head)) \
(head)->cqh_first = (elm)->field.cqe_next; \
else \
(elm)->field.cqe_prev->field.cqe_next = \
(elm)->field.cqe_next; \
}
#endif /* sys/queue.h */

View File

@ -1,7 +1,7 @@
/*
* QEMU WAV audio output driver
* QEMU WAV audio driver
*
* Copyright (c) 2004 Vassili Karpov (malc)
* Copyright (c) 2004-2005 Vassili Karpov (malc)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -23,22 +23,16 @@
*/
#include "vl.h"
#include "audio/audio_int.h"
#define AUDIO_CAP "wav"
#include "audio_int.h"
typedef struct WAVVoice {
HWVoice hw;
typedef struct WAVVoiceOut {
HWVoiceOut hw;
QEMUFile *f;
int64_t old_ticks;
void *pcm_buf;
int total_samples;
} WAVVoice;
#define dolog(...) AUD_log ("wav", __VA_ARGS__)
#ifdef DEBUG
#define ldebug(...) dolog (__VA_ARGS__)
#else
#define ldebug(...)
#endif
} WAVVoiceOut;
static struct {
const char *wav_path;
@ -46,24 +40,27 @@ static struct {
.wav_path = "qemu.wav"
};
static void wav_hw_run (HWVoice *hw)
static int wav_run_out (HWVoiceOut *hw)
{
WAVVoice *wav = (WAVVoice *) hw;
WAVVoiceOut *wav = (WAVVoiceOut *) hw;
int rpos, live, decr, samples;
uint8_t *dst;
st_sample_t *src;
int64_t now = qemu_get_clock (vm_clock);
int64_t ticks = now - wav->old_ticks;
int64_t bytes = (ticks * hw->bytes_per_second) / ticks_per_sec;
int64_t bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec;
if (bytes > INT_MAX)
samples = INT_MAX >> hw->shift;
else
samples = bytes >> hw->shift;
if (bytes > INT_MAX) {
samples = INT_MAX >> hw->info.shift;
}
else {
samples = bytes >> hw->info.shift;
}
live = pcm_hw_get_live (hw);
if (live <= 0)
return;
live = audio_pcm_hw_get_live_out (hw);
if (!live) {
return 0;
}
wav->old_ticks = now;
decr = audio_MIN (live, samples);
@ -73,25 +70,25 @@ static void wav_hw_run (HWVoice *hw)
int left_till_end_samples = hw->samples - rpos;
int convert_samples = audio_MIN (samples, left_till_end_samples);
src = advance (hw->mix_buf, rpos * sizeof (st_sample_t));
dst = advance (wav->pcm_buf, rpos << hw->shift);
src = hw->mix_buf + rpos;
dst = advance (wav->pcm_buf, rpos << hw->info.shift);
hw->clip (dst, src, convert_samples);
qemu_put_buffer (wav->f, dst, convert_samples << hw->shift);
memset (src, 0, convert_samples * sizeof (st_sample_t));
qemu_put_buffer (wav->f, dst, convert_samples << hw->info.shift);
mixeng_clear (src, convert_samples);
rpos = (rpos + convert_samples) % hw->samples;
samples -= convert_samples;
wav->total_samples += convert_samples;
}
pcm_hw_dec_live (hw, decr);
hw->rpos = rpos;
return decr;
}
static int wav_hw_write (SWVoice *sw, void *buf, int len)
static int wav_write_out (SWVoiceOut *sw, void *buf, int len)
{
return pcm_hw_write (sw, buf, len);
return audio_pcm_sw_write (sw, buf, len);
}
/* VICE code: Store number as little endian. */
@ -104,10 +101,10 @@ static void le_store (uint8_t *buf, uint32_t val, int len)
}
}
static int wav_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
static int wav_init_out (HWVoiceOut *hw, int freq, int nchannels, audfmt_e fmt)
{
WAVVoice *wav = (WAVVoice *) hw;
int bits16 = 0, stereo = audio_state.fixed_channels == 2;
WAVVoiceOut *wav = (WAVVoiceOut *) hw;
int bits16;
uint8_t hdr[] = {
0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
@ -115,34 +112,50 @@ static int wav_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
};
switch (audio_state.fixed_fmt) {
freq = audio_state.fixed_freq_out;
fmt = audio_state.fixed_fmt_out;
nchannels = audio_state.fixed_channels_out;
switch (fmt) {
case AUD_FMT_S8:
case AUD_FMT_U8:
bits16 = 0;
break;
case AUD_FMT_S16:
case AUD_FMT_U16:
bits16 = 1;
break;
default:
dolog ("Internal logic error bad format %d\n", fmt);
return -1;
}
hdr[34] = bits16 ? 0x10 : 0x08;
hw->freq = 44100;
hw->nchannels = stereo ? 2 : 1;
hw->fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8;
audio_pcm_init_info (
&hw->info,
freq,
nchannels,
bits16 ? AUD_FMT_S16 : AUD_FMT_U8,
audio_need_to_swap_endian (0)
);
hw->bufsize = 4096;
wav->pcm_buf = qemu_mallocz (hw->bufsize);
if (!wav->pcm_buf)
if (!wav->pcm_buf) {
dolog ("Can not initialize WAV buffer of %d bytes\n",
hw->bufsize);
return -1;
}
le_store (hdr + 22, hw->nchannels, 2);
le_store (hdr + 24, hw->freq, 4);
le_store (hdr + 28, hw->freq << (bits16 + stereo), 4);
le_store (hdr + 32, 1 << (bits16 + stereo), 2);
le_store (hdr + 22, hw->info.nchannels, 2);
le_store (hdr + 24, hw->info.freq, 4);
le_store (hdr + 28, hw->info.freq << (bits16 + (nchannels == 2)), 4);
le_store (hdr + 32, 1 << (bits16 + (nchannels == 2)), 2);
wav->f = fopen (conf.wav_path, "wb");
if (!wav->f) {
dolog ("failed to open wave file `%s'\nReason: %s\n",
dolog ("Failed to open wave file `%s'\nReason: %s\n",
conf.wav_path, strerror (errno));
qemu_free (wav->pcm_buf);
wav->pcm_buf = NULL;
@ -153,17 +166,18 @@ static int wav_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
return 0;
}
static void wav_hw_fini (HWVoice *hw)
static void wav_fini_out (HWVoiceOut *hw)
{
WAVVoice *wav = (WAVVoice *) hw;
int stereo = hw->nchannels == 2;
WAVVoiceOut *wav = (WAVVoiceOut *) hw;
int stereo = hw->info.nchannels == 2;
uint8_t rlen[4];
uint8_t dlen[4];
uint32_t rifflen = (wav->total_samples << stereo) + 36;
uint32_t datalen = wav->total_samples << stereo;
if (!wav->f || !hw->active)
if (!wav->f || !hw->active) {
return;
}
le_store (rlen, rifflen, 4);
le_store (dlen, datalen, 4);
@ -181,7 +195,7 @@ static void wav_hw_fini (HWVoice *hw)
wav->pcm_buf = NULL;
}
static int wav_hw_ctl (HWVoice *hw, int cmd, ...)
static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
{
(void) hw;
(void) cmd;
@ -195,23 +209,41 @@ static void *wav_audio_init (void)
static void wav_audio_fini (void *opaque)
{
(void) opaque;
ldebug ("wav_fini");
}
struct pcm_ops wav_pcm_ops = {
wav_hw_init,
wav_hw_fini,
wav_hw_run,
wav_hw_write,
wav_hw_ctl
struct audio_option wav_options[] = {
{"PATH", AUD_OPT_STR, &conf.wav_path,
"Path to wave file", NULL, 0},
{NULL, 0, NULL, NULL, NULL, 0}
};
struct audio_output_driver wav_output_driver = {
"wav",
wav_audio_init,
wav_audio_fini,
&wav_pcm_ops,
1,
1,
sizeof (WAVVoice)
struct audio_pcm_ops wav_pcm_ops = {
wav_init_out,
wav_fini_out,
wav_run_out,
wav_write_out,
wav_ctl_out,
NULL,
NULL,
NULL,
NULL,
NULL
};
struct audio_driver wav_audio_driver = {
INIT_FIELD (name = ) "wav",
INIT_FIELD (descr = )
"WAV renderer http://wikipedia.org/wiki/WAV",
INIT_FIELD (options = ) wav_options,
INIT_FIELD (init = ) wav_audio_init,
INIT_FIELD (fini = ) wav_audio_fini,
INIT_FIELD (pcm_ops = ) &wav_pcm_ops,
INIT_FIELD (can_be_default = ) 0,
INIT_FIELD (max_voices_out = ) 1,
INIT_FIELD (max_voices_in = ) 0,
INIT_FIELD (voice_size_out = ) sizeof (WAVVoiceOut),
INIT_FIELD (voice_size_in = ) 0
};

40
configure vendored
View File

@ -77,6 +77,9 @@ gdbstub="yes"
slirp="yes"
adlib="no"
oss="no"
dsound="no"
coreaudio="no"
alsa="no"
fmod="no"
fmod_lib=""
fmod_inc=""
@ -171,6 +174,12 @@ for opt do
;;
--disable-sdl) sdl="no"
;;
--enable-coreaudio) coreaudio="yes"
;;
--enable-alsa) alsa="yes"
;;
--enable-dsound) dsound="yes"
;;
--enable-fmod) fmod="yes"
;;
--fmod-lib=*) fmod_lib=${opt#--fmod-lib=}
@ -187,7 +196,7 @@ for opt do
;;
--kernel-path=*) kernel_path=${opt#--kernel-path=}
;;
--enable-cocoa) cocoa="yes" ; sdl="no"
--enable-cocoa) cocoa="yes" ; coreaudio="yes" ; sdl="no"
;;
--disable-gfx-check) check_gfx="no"
;;
@ -346,7 +355,10 @@ echo " --make=MAKE use specified make [$make]"
echo " --static enable static build [$static]"
echo " --enable-mingw32 enable Win32 cross compilation with mingw32"
echo " --enable-adlib enable Adlib emulation"
echo " --enable-fmod enable FMOD audio output driver"
echo " --enable-coreaudio enable Coreaudio audio driver"
echo " --enable-alsa enable ALSA audio driver"
echo " --enable-fmod enable FMOD audio driver"
echo " --enabled-dsound enable DirectSound audio driver"
echo " --fmod-lib path to FMOD library"
echo " --fmod-inc path to FMOD includes"
echo ""
@ -439,8 +451,18 @@ if test "$sdl" != "no" ; then
fi
echo "mingw32 support $mingw32"
echo "Adlib support $adlib"
echo "CoreAudio support $coreaudio"
echo "ALSA support $alsa"
echo "DSound support $dsound"
echo -n "FMOD support $fmod"
if test $fmod = "yes"; then
if test "$fmod" = "yes"; then
if test -z $fmod_lib || test -z $fmod_inc; then
echo
echo "Error: You must specify path to FMOD library and headers"
echo "Example: --fmod-inc=/path/include/fmod --fmod-lib=/path/lib/libfmod-3.74.so"
echo
exit 1
fi
echo -n " (lib='$fmod_lib' include='$fmod_inc')"
fi
echo ""
@ -568,6 +590,18 @@ if test "$oss" = "yes" ; then
echo "CONFIG_OSS=yes" >> $config_mak
echo "#define CONFIG_OSS 1" >> $config_h
fi
if test "$coreaudio" = "yes" ; then
echo "CONFIG_COREAUDIO=yes" >> $config_mak
echo "#define CONFIG_COREAUDIO 1" >> $config_h
fi
if test "$alsa" = "yes" ; then
echo "CONFIG_ALSA=yes" >> $config_mak
echo "#define CONFIG_ALSA 1" >> $config_h
fi
if test "$dsound" = "yes" ; then
echo "CONFIG_DSOUND=yes" >> $config_mak
echo "#define CONFIG_DSOUND 1" >> $config_h
fi
if test "$fmod" = "yes" ; then
echo "CONFIG_FMOD=yes" >> $config_mak
echo "CONFIG_FMOD_LIB=$fmod_lib" >> $config_mak

View File

@ -1,7 +1,7 @@
/*
* QEMU Adlib emulation
* QEMU Proxy for OPL2/3 emulation by MAME team
*
* Copyright (c) 2004 Vassili Karpov (malc)
* Copyright (c) 2004-2005 Vassili Karpov (malc)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -21,8 +21,11 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <assert.h>
#include "vl.h"
#define ADLIB_KILL_TIMERS 1
#define dolog(...) AUD_log ("adlib", __VA_ARGS__)
#ifdef DEBUG
#define ldebug(...) dolog (__VA_ARGS__)
@ -30,8 +33,7 @@
#define ldebug(...)
#endif
#ifdef USE_YMF262
#define HAS_YMF262 1
#ifdef HAS_YMF262
#include "ymf262.h"
void YMF262UpdateOneQEMU (int which, INT16 *dst, int length);
#define SHIFT 2
@ -40,13 +42,6 @@ void YMF262UpdateOneQEMU(int which, INT16 *dst, int length);
#define SHIFT 1
#endif
#ifdef _WIN32
#include <windows.h>
#define small_delay() Sleep (1)
#else
#define small_delay() usleep (1)
#endif
#define IO_READ_PROTO(name) \
uint32_t name (void *opaque, uint32_t nport)
#define IO_WRITE_PROTO(name) \
@ -58,35 +53,70 @@ static struct {
} conf = {0x220, 44100};
typedef struct {
int ticking[2];
int enabled;
int active;
int cparam;
int64_t ticks;
int bufpos;
#ifdef DEBUG
int64_t exp[2];
#endif
int16_t *mixbuf;
double interval;
QEMUTimer *ts, *opl_ts;
SWVoice *voice;
int left, pos, samples, bytes_per_second, old_free;
int refcount;
#ifndef USE_YMF262
uint64_t dexp[2];
SWVoiceOut *voice;
int left, pos, samples;
QEMUAudioTimeStamp ats;
#ifndef HAS_YMF262
FM_OPL *opl;
#endif
} AdlibState;
static AdlibState adlib;
static void adlib_stop_opl_timer (AdlibState *s, size_t n)
{
#ifdef HAS_YMF262
YMF262TimerOver (0, n);
#else
OPLTimerOver (s->opl, n);
#endif
s->ticking[n] = 0;
}
static void adlib_kill_timers (AdlibState *s)
{
size_t i;
for (i = 0; i < 2; ++i) {
if (s->ticking[i]) {
uint64_t delta;
delta = AUD_time_stamp_get_elapsed_usec_out (s->voice, &s->ats);
ldebug (
"delta = %f dexp = %f expired => %d\n",
delta / 1000000.0,
s->dexp[i] / 1000000.0,
delta >= s->dexp[i]
);
if (ADLIB_KILL_TIMERS || delta >= s->dexp[i]) {
adlib_stop_opl_timer (s, i);
AUD_init_time_stamp_out (s->voice, &s->ats);
}
}
}
}
static IO_WRITE_PROTO(adlib_write)
{
AdlibState *s = opaque;
int a = nport & 3;
int status;
s->ticks = qemu_get_clock (vm_clock);
s->active = 1;
AUD_enable (s->voice, 1);
AUD_set_active_out (s->voice, 1);
#ifdef USE_YMF262
adlib_kill_timers (s);
#ifdef HAS_YMF262
status = YMF262Write (0, a, val);
#else
status = OPLWrite (s->opl, a, val);
@ -99,8 +129,9 @@ static IO_READ_PROTO(adlib_read)
uint8_t data;
int a = nport & 3;
#ifdef USE_YMF262
(void) s;
adlib_kill_timers (s);
#ifdef HAS_YMF262
data = YMF262Read (0, a);
#else
data = OPLRead (s->opl, a);
@ -108,119 +139,115 @@ static IO_READ_PROTO(adlib_read)
return data;
}
static void OPL_timer (void *opaque)
{
AdlibState *s = opaque;
#ifdef USE_YMF262
YMF262TimerOver (s->cparam >> 1, s->cparam & 1);
#else
OPLTimerOver (s->opl, s->cparam);
#endif
qemu_mod_timer (s->opl_ts, qemu_get_clock (vm_clock) + s->interval);
}
static void YMF262TimerHandler (int c, double interval_Sec)
static void timer_handler (int c, double interval_Sec)
{
AdlibState *s = &adlib;
unsigned n = c & 1;
#ifdef DEBUG
double interval;
#endif
if (interval_Sec == 0.0) {
qemu_del_timer (s->opl_ts);
s->ticking[n] = 0;
return;
}
s->cparam = c;
s->interval = ticks_per_sec * interval_Sec;
qemu_mod_timer (s->opl_ts, qemu_get_clock (vm_clock) + s->interval);
small_delay ();
s->ticking[n] = 1;
#ifdef DEBUG
interval = ticks_per_sec * interval_Sec;
exp = qemu_get_clock (vm_clock) + interval;
s->exp[n] = exp;
#endif
s->dexp[n] = interval_Sec * 1000000.0;
AUD_init_time_stamp_out (s->voice, &s->ats);
}
static int write_audio (AdlibState *s, int samples)
{
int net = 0;
int ss = samples;
int pos = s->pos;
while (samples) {
int nbytes = samples << SHIFT;
int wbytes = AUD_write (s->voice,
s->mixbuf + (s->pos << (SHIFT - 1)),
nbytes);
int wsampl = wbytes >> SHIFT;
int nbytes, wbytes, wsampl;
nbytes = samples << SHIFT;
wbytes = AUD_write (
s->voice,
s->mixbuf + (pos << (SHIFT - 1)),
nbytes
);
if (wbytes) {
wsampl = wbytes >> SHIFT;
samples -= wsampl;
s->pos = (s->pos + wsampl) % s->samples;
pos = (pos + wsampl) % s->samples;
net += wsampl;
if (!wbytes)
}
else {
break;
}
if (net > ss) {
dolog ("WARNING: net > ss\n");
}
return net;
}
static void timer (void *opaque)
static void adlib_callback (void *opaque, int free)
{
AdlibState *s = opaque;
int elapsed, samples, net = 0;
int samples, net = 0, to_play, written;
if (s->refcount)
dolog ("refcount=%d\n", s->refcount);
s->refcount += 1;
if (!(s->active && s->enabled))
goto reset;
AUD_run ();
while (s->left) {
int written = write_audio (s, s->left);
net += written;
if (!written)
goto reset2;
s->left -= written;
samples = free >> SHIFT;
if (!(s->active && s->enabled) || !samples) {
return;
}
s->pos = 0;
elapsed = AUD_calc_elapsed (s->voice);
if (!elapsed)
goto reset2;
to_play = audio_MIN (s->left, samples);
while (to_play) {
written = write_audio (s, to_play);
/* elapsed = AUD_get_free (s->voice); */
samples = elapsed >> SHIFT;
if (!samples)
goto reset2;
if (written) {
s->left -= written;
samples -= written;
to_play -= written;
s->pos = (s->pos + written) % s->samples;
}
else {
return;
}
}
samples = audio_MIN (samples, s->samples - s->pos);
if (s->left)
dolog ("left=%d samples=%d elapsed=%d free=%d\n",
s->left, samples, elapsed, AUD_get_free (s->voice));
if (!samples) {
return;
}
if (!samples)
goto reset2;
#ifdef USE_YMF262
#ifdef HAS_YMF262
YMF262UpdateOneQEMU (0, s->mixbuf + s->pos * 2, samples);
#else
YM3812UpdateOne (s->opl, s->mixbuf + s->pos, samples);
#endif
while (samples) {
int written = write_audio (s, samples);
net += written;
if (!written)
break;
samples -= written;
}
if (!samples)
s->pos = 0;
s->left = samples;
written = write_audio (s, samples);
reset2:
AUD_adjust (s->voice, net << SHIFT);
reset:
qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + ticks_per_sec / 1024);
s->refcount -= 1;
if (written) {
net += written;
samples -= written;
s->pos = (s->pos + written) % s->samples;
}
else {
s->left = samples;
return;
}
}
}
static void Adlib_fini (AdlibState *s)
{
#ifdef USE_YMF262
#ifdef HAS_YMF262
YMF262Shutdown ();
#else
if (s->opl) {
@ -229,15 +256,9 @@ static void Adlib_fini (AdlibState *s)
}
#endif
if (s->opl_ts)
qemu_free_timer (s->opl_ts);
if (s->ts)
qemu_free_timer (s->ts);
#define maybe_free(p) if (p) qemu_free (p)
maybe_free (s->mixbuf);
#undef maybe_free
if (s->mixbuf) {
qemu_free (s->mixbuf);
}
s->active = 0;
s->enabled = 0;
@ -247,15 +268,13 @@ void Adlib_init (void)
{
AdlibState *s = &adlib;
memset (s, 0, sizeof (*s));
#ifdef USE_YMF262
#ifdef HAS_YMF262
if (YMF262Init (1, 14318180, conf.freq)) {
dolog ("YMF262Init %d failed\n", conf.freq);
return;
}
else {
YMF262SetTimerHandler (0, YMF262TimerHandler, 0);
YMF262SetTimerHandler (0, timer_handler, 0);
s->enabled = 1;
}
#else
@ -265,33 +284,26 @@ void Adlib_init (void)
return;
}
else {
OPLSetTimerHandler (s->opl, YMF262TimerHandler, 0);
OPLSetTimerHandler (s->opl, timer_handler, 0);
s->enabled = 1;
}
#endif
s->opl_ts = qemu_new_timer (vm_clock, OPL_timer, s);
if (!s->opl_ts) {
dolog ("Can not get timer for adlib emulation\n");
Adlib_fini (s);
return;
}
s->ts = qemu_new_timer (vm_clock, timer, s);
if (!s->opl_ts) {
dolog ("Can not get timer for adlib emulation\n");
Adlib_fini (s);
return;
}
s->voice = AUD_open (s->voice, "adlib", conf.freq, SHIFT, AUD_FMT_S16);
s->voice = AUD_open_out (
s->voice,
"adlib",
s,
adlib_callback,
conf.freq,
SHIFT,
AUD_FMT_S16
);
if (!s->voice) {
Adlib_fini (s);
return;
}
s->bytes_per_second = conf.freq << SHIFT;
s->samples = AUD_get_buffer_size (s->voice) >> SHIFT;
s->samples = AUD_get_buffer_size_out (s->voice) >> SHIFT;
s->mixbuf = qemu_mallocz (s->samples << SHIFT);
if (!s->mixbuf) {
@ -300,6 +312,7 @@ void Adlib_init (void)
Adlib_fini (s);
return;
}
register_ioport_read (0x388, 4, 1, adlib_read, s);
register_ioport_write (0x388, 4, 1, adlib_write, s);
@ -308,6 +321,4 @@ void Adlib_init (void)
register_ioport_read (conf.port + 8, 2, 1, adlib_read, s);
register_ioport_write (conf.port + 8, 2, 1, adlib_write, s);
qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + 1);
}

1007
hw/es1370.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -602,18 +602,18 @@ static void pc_init1(int ram_size, int vga_ram_size, int boot_device,
if (audio_enabled) {
AUD_init();
#ifdef USE_SB16
if (sb16_enabled)
SB16_init ();
#endif
#ifdef CONFIG_ADLIB
if (adlib_enabled)
Adlib_init ();
#endif
#ifdef USE_GUS
#ifdef CONFIG_GUS
if (gus_enabled)
GUS_init ();
#endif
if (pci_enabled && es1370_enabled)
es1370_init (pci_bus);
}
floppy_controller = fdctrl_init(6, 2, 0, 0x3f0, fd_table);

256
hw/sb16.c
View File

@ -1,7 +1,7 @@
/*
* QEMU Soundblaster 16 emulation
*
* Copyright (c) 2003-2004 Vassili Karpov (malc)
* Copyright (c) 2003-2005 Vassili Karpov (malc)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -99,9 +99,10 @@ typedef struct SB16State {
int dma_running;
int bytes_per_second;
int align;
SWVoice *voice;
int audio_free;
SWVoiceOut *voice;
QEMUTimer *ts, *aux_ts;
QEMUTimer *aux_ts;
/* mixer state */
int mixer_nreg;
uint8_t mixer_regs[256];
@ -110,6 +111,8 @@ typedef struct SB16State {
/* XXX: suppress that and use a context */
static struct SB16State dsp;
static void SB_audio_callback (void *opaque, int free);
static int magic_of_irq (int irq)
{
switch (irq) {
@ -174,11 +177,11 @@ static void control (SB16State *s, int hold)
if (hold) {
DMA_hold_DREQ (dma);
AUD_enable (s->voice, 1);
AUD_set_active_out (s->voice, 1);
}
else {
DMA_release_DREQ (dma);
AUD_enable (s->voice, 0);
AUD_set_active_out (s->voice, 0);
}
}
@ -207,8 +210,9 @@ static void dma_cmd8 (SB16State *s, int mask, int dma_len)
s->freq = (1000000 + (tmp / 2)) / tmp;
}
if (dma_len != -1)
if (dma_len != -1) {
s->block_size = dma_len << s->fmt_stereo;
}
else {
/* This is apparently the only way to make both Act1/PL
and SecondReality/FC work
@ -227,17 +231,28 @@ static void dma_cmd8 (SB16State *s, int mask, int dma_len)
s->dma_auto = (mask & DMA8_AUTO) != 0;
s->align = (1 << s->fmt_stereo) - 1;
if (s->block_size & s->align)
dolog ("warning: unaligned buffer\n");
if (s->block_size & s->align) {
dolog ("warning: misaligned block size %d, alignment %d\n",
s->block_size, s->align + 1);
}
ldebug ("freq %d, stereo %d, sign %d, bits %d, "
"dma %d, auto %d, fifo %d, high %d\n",
s->freq, s->fmt_stereo, s->fmt_signed, s->fmt_bits,
s->block_size, s->dma_auto, s->fifo, s->highspeed);
if (s->freq)
s->voice = AUD_open (s->voice, "sb16", s->freq,
1 << s->fmt_stereo, s->fmt);
if (s->freq) {
s->audio_free = 0;
s->voice = AUD_open_out (
s->voice,
"sb16",
s,
SB_audio_callback,
s->freq,
1 << s->fmt_stereo,
s->fmt
);
}
control (s, 1);
speaker (s, 1);
@ -309,12 +324,23 @@ static void dma_cmd (SB16State *s, uint8_t cmd, uint8_t d0, int dma_len)
s->bytes_per_second = (s->freq << s->fmt_stereo) << (s->fmt_bits == 16);
s->highspeed = 0;
s->align = (1 << (s->fmt_stereo + (s->fmt_bits == 16))) - 1;
if (s->block_size & s->align)
dolog ("warning: unaligned buffer\n");
if (s->block_size & s->align) {
dolog ("warning: misaligned block size %d, alignment %d\n",
s->block_size, s->align + 1);
}
if (s->freq)
s->voice = AUD_open (s->voice, "sb16", s->freq,
1 << s->fmt_stereo, s->fmt);
if (s->freq) {
s->audio_free = 0;
s->voice = AUD_open_out (
s->voice,
"sb16",
s,
SB_audio_callback,
s->freq,
1 << s->fmt_stereo,
s->fmt
);
}
control (s, 1);
speaker (s, 1);
@ -323,14 +349,16 @@ static void dma_cmd (SB16State *s, uint8_t cmd, uint8_t d0, int dma_len)
static inline void dsp_out_data (SB16State *s, uint8_t val)
{
ldebug ("outdata %#x\n", val);
if (s->out_data_len < sizeof (s->out_data))
if (s->out_data_len < sizeof (s->out_data)) {
s->out_data[s->out_data_len++] = val;
}
}
static inline uint8_t dsp_get_data (SB16State *s)
{
if (s->in_index)
if (s->in_index) {
return s->in2_data[--s->in_index];
}
else {
dolog ("buffer underflow\n");
return 0;
@ -356,6 +384,8 @@ static void command (SB16State *s, uint8_t cmd)
s->needed_bytes = 3;
}
else {
s->needed_bytes = 0;
switch (cmd) {
case 0x03:
dsp_out_data (s, 0x10); /* s->csp_param); */
@ -403,7 +433,7 @@ static void command (SB16State *s, uint8_t cmd)
goto warn;
case 0x35:
dolog ("MIDI command(0x35) not implemented\n");
dolog ("0x35 - MIDI command not implemented\n");
break;
case 0x40:
@ -435,6 +465,38 @@ static void command (SB16State *s, uint8_t cmd)
s->needed_bytes = 2;
break;
case 0x74:
s->needed_bytes = 2; /* DMA DAC, 4-bit ADPCM */
dolog ("0x75 - DMA DAC, 4-bit ADPCM not implemented\n");
break;
case 0x75: /* DMA DAC, 4-bit ADPCM Reference */
s->needed_bytes = 2;
dolog ("0x74 - DMA DAC, 4-bit ADPCM Reference not implemented\n");
break;
case 0x76: /* DMA DAC, 2.6-bit ADPCM */
s->needed_bytes = 2;
dolog ("0x74 - DMA DAC, 2.6-bit ADPCM not implemented\n");
break;
case 0x77: /* DMA DAC, 2.6-bit ADPCM Reference */
s->needed_bytes = 2;
dolog ("0x74 - DMA DAC, 2.6-bit ADPCM Reference not implemented\n");
break;
case 0x7d:
dolog ("0x7d - Autio-Initialize DMA DAC, 4-bit ADPCM Reference\n");
dolog ("not implemented\n");
break;
case 0x7f:
dolog (
"0x7d - Autio-Initialize DMA DAC, 2.6-bit ADPCM Reference\n"
);
dolog ("not implemented\n");
break;
case 0x80:
s->needed_bytes = 2;
break;
@ -476,9 +538,9 @@ static void command (SB16State *s, uint8_t cmd)
s->dma_auto = 0;
break;
case 0xe0:
case 0xe0: /* DSP identification */
s->needed_bytes = 1;
goto warn;
break;
case 0xe1:
dsp_out_data (s, s->ver & 0xff);
@ -503,7 +565,7 @@ static void command (SB16State *s, uint8_t cmd)
case 0xe7:
dolog ("Attempt to probe for ESS (0xe7)?\n");
return;
break;
case 0xe8: /* read test reg */
dsp_out_data (s, s->test_reg);
@ -529,21 +591,29 @@ static void command (SB16State *s, uint8_t cmd)
goto warn;
default:
dolog ("unrecognized command %#x\n", cmd);
return;
dolog ("Unrecognized command %#x\n", cmd);
break;
}
}
s->cmd = cmd;
if (!s->needed_bytes)
if (!s->needed_bytes) {
ldebug ("\n");
}
exit:
if (!s->needed_bytes) {
s->cmd = -1;
}
else {
s->cmd = cmd;
}
return;
warn:
dolog ("warning: command %#x,%d is not truly understood yet\n",
cmd, s->needed_bytes);
s->cmd = cmd;
return;
goto exit;
}
static uint16_t dsp_get_lohi (SB16State *s)
@ -607,8 +677,9 @@ static void complete (SB16State *s)
s->csp_reg83[s->csp_reg83r % 4] = d0;
s->csp_reg83r += 1;
}
else
else {
s->csp_regs[d1] = d0;
}
break;
case 0x0f:
@ -622,8 +693,9 @@ static void complete (SB16State *s)
dsp_out_data (s, s->csp_reg83[s->csp_reg83w % 4]);
s->csp_reg83w += 1;
}
else
else {
dsp_out_data (s, s->csp_regs[d0]);
}
break;
case 0x10:
@ -641,8 +713,9 @@ static void complete (SB16State *s)
break;
case 0x42: /* FT2 sets output freq with this, go figure */
#if 0
dolog ("cmd 0x42 might not do what it think it should\n");
#endif
case 0x41:
s->freq = dsp_get_hilo (s);
ldebug ("set freq %d\n", s->freq);
@ -653,6 +726,13 @@ static void complete (SB16State *s)
ldebug ("set dma block len %d\n", s->block_size);
break;
case 0x74:
case 0x75:
case 0x76:
case 0x77:
/* ADPCM stuff, ignore */
break;
case 0x80:
{
int freq, samples, bytes;
@ -662,10 +742,17 @@ static void complete (SB16State *s)
samples = dsp_get_lohi (s) + 1;
bytes = samples << s->fmt_stereo << (s->fmt_bits == 16);
ticks = (bytes * ticks_per_sec) / freq;
if (ticks < ticks_per_sec / 1024)
if (ticks < ticks_per_sec / 1024) {
pic_set_irq (s->irq, 1);
else
qemu_mod_timer (s->aux_ts, qemu_get_clock (vm_clock) + ticks);
}
else {
if (s->aux_ts) {
qemu_mod_timer (
s->aux_ts,
qemu_get_clock (vm_clock) + ticks
);
}
}
ldebug ("mix silence %d %d %lld\n", samples, bytes, ticks);
}
break;
@ -737,6 +824,7 @@ static void reset (SB16State *s)
s->nzero = 0;
s->highspeed = 0;
s->v2x6 = 0;
s->cmd = -1;
dsp_out_data(s, 0xaa);
speaker (s, 0);
@ -761,9 +849,10 @@ static IO_WRITE_PROTO (dsp_write)
pic_set_irq (s->irq, 0);
control (s, 0);
}
else
else {
reset (s);
}
}
s->v2x6 = 0;
break;
@ -845,7 +934,10 @@ static IO_READ_PROTO (dsp_read)
s->last_read_byte = retval;
}
else {
dolog ("empty output buffer\n");
if (s->cmd != -1) {
dolog ("empty output buffer for command %#x\n",
s->cmd);
}
retval = s->last_read_byte;
/* goto error; */
}
@ -882,13 +974,14 @@ static IO_READ_PROTO (dsp_read)
goto error;
}
if (!ack)
if (!ack) {
ldebug ("read %#x -> %#x\n", nport, retval);
}
return retval;
error:
dolog ("WARNING dsp_read %#x error\n", nport);
dolog ("warning: dsp_read %#x error\n", nport);
return 0xff;
}
@ -933,8 +1026,9 @@ static IO_WRITE_PROTO(mixer_write_datab)
SB16State *s = opaque;
ldebug ("mixer_write [%#x] <- %#x\n", s->mixer_nreg, val);
if (s->mixer_nreg > sizeof (s->mixer_regs))
if (s->mixer_nreg > sizeof (s->mixer_regs)) {
return;
}
switch (s->mixer_nreg) {
case 0x00:
@ -945,9 +1039,10 @@ static IO_WRITE_PROTO(mixer_write_datab)
{
int irq = irq_of_magic (val);
ldebug ("setting irq to %d (val=%#x)\n", irq, val);
if (irq > 0)
if (irq > 0) {
s->irq = irq;
}
}
break;
case 0x81:
@ -956,8 +1051,12 @@ static IO_WRITE_PROTO(mixer_write_datab)
dma = lsbindex (val & 0xf);
hdma = lsbindex (val & 0xf0);
dolog ("attempt to set DMA register 8bit %d, 16bit %d (val=%#x)\n",
dma, hdma, val);
if (dma != s->dma || hdma != s->hdma) {
dolog (
"attempt to change DMA "
"8bit %d(%d), 16bit %d(%d) (val=%#x)\n",
dma, s->dma, hdma, s->hdma, val);
}
#if 0
s->dma = dma;
s->hdma = hdma;
@ -971,8 +1070,9 @@ static IO_WRITE_PROTO(mixer_write_datab)
return;
default:
if (s->mixer_nreg >= 0x80)
dolog ("attempt to write mixer[%#x] <- %#x\n", s->mixer_nreg, val);
if (s->mixer_nreg >= 0x80) {
ldebug ("attempt to write mixer[%#x] <- %#x\n", s->mixer_nreg, val);
}
break;
}
@ -989,10 +1089,14 @@ static IO_READ_PROTO(mixer_read)
{
SB16State *s = opaque;
#ifndef DEBUG_SB16_MOST
if (s->mixer_nreg != 0x82)
#endif
if (s->mixer_nreg != 0x82) {
ldebug ("mixer_read[%#x] -> %#x\n",
s->mixer_nreg, s->mixer_regs[s->mixer_nreg]);
}
#else
ldebug ("mixer_read[%#x] -> %#x\n",
s->mixer_nreg, s->mixer_regs[s->mixer_nreg]);
#endif
return s->mixer_regs[s->mixer_nreg];
}
@ -1010,8 +1114,9 @@ static int write_audio (SB16State *s, int nchan, int dma_pos,
int to_copy, copied;
to_copy = audio_MIN (temp, left);
if (to_copy > sizeof(tmpbuf))
if (to_copy > sizeof(tmpbuf)) {
to_copy = sizeof(tmpbuf);
}
copied = DMA_read_memory (nchan, tmpbuf, dma_pos, to_copy);
copied = AUD_write (s->voice, tmpbuf, copied);
@ -1020,9 +1125,10 @@ static int write_audio (SB16State *s, int nchan, int dma_pos,
dma_pos = (dma_pos + copied) % dma_len;
net += copied;
if (!copied)
if (!copied) {
break;
}
}
return net;
}
@ -1030,27 +1136,28 @@ static int write_audio (SB16State *s, int nchan, int dma_pos,
static int SB_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len)
{
SB16State *s = opaque;
int free, rfree, till, copy, written, elapsed;
int till, copy, written, free;
if (s->left_till_irq < 0) {
s->left_till_irq = s->block_size;
}
elapsed = AUD_calc_elapsed (s->voice);
free = elapsed;/* AUD_get_free (s->voice); */
rfree = free;
free = audio_MIN (free, elapsed) & ~s->align;
if (s->voice) {
free = s->audio_free & ~s->align;
if ((free <= 0) || !dma_len) {
return dma_pos;
}
}
else {
free = dma_len;
}
copy = free;
till = s->left_till_irq;
#ifdef DEBUG_SB16_MOST
dolog ("pos:%06d free:%d,%d till:%d len:%d\n",
dma_pos, free, AUD_get_free (s->voice), till, dma_len);
dolog ("pos:%06d %d till:%d len:%d\n",
dma_pos, free, till, dma_len);
#endif
if (till <= copy) {
@ -1082,15 +1189,13 @@ static int SB_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len)
s->left_till_irq = s->block_size + s->left_till_irq;
}
AUD_adjust (s->voice, written);
return dma_pos;
}
void SB_timer (void *opaque)
static void SB_audio_callback (void *opaque, int free)
{
SB16State *s = opaque;
AUD_run ();
qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + 1);
s->audio_free = free;
}
static void SB_save (QEMUFile *f, void *opaque)
@ -1150,8 +1255,9 @@ static int SB_load (QEMUFile *f, void *opaque, int version_id)
{
SB16State *s = opaque;
if (version_id != 1)
if (version_id != 1) {
return -EINVAL;
}
qemu_get_be32s (f, &s->irq);
qemu_get_be32s (f, &s->dma);
@ -1202,14 +1308,23 @@ static int SB_load (QEMUFile *f, void *opaque, int version_id)
qemu_get_buffer (f, s->mixer_regs, 256);
if (s->voice) {
AUD_close (s->voice);
AUD_close_out (s->voice);
s->voice = NULL;
}
if (s->dma_running) {
if (s->freq)
s->voice = AUD_open (s->voice, "sb16", s->freq,
1 << s->fmt_stereo, s->fmt);
if (s->freq) {
s->audio_free = 0;
s->voice = AUD_open_out (
s->voice,
"sb16",
s,
SB_audio_callback,
s->freq,
1 << s->fmt_stereo,
s->fmt
);
}
control (s, 1);
speaker (s, s->speaker);
@ -1224,10 +1339,7 @@ void SB16_init (void)
static const uint8_t dsp_write_ports[] = {0x6, 0xc};
static const uint8_t dsp_read_ports[] = {0x6, 0xa, 0xc, 0xd, 0xe, 0xf};
s->ts = qemu_new_timer (vm_clock, SB_timer, s);
if (!s->ts)
return;
s->cmd = -1;
s->irq = conf.irq;
s->dma = conf.dma;
s->hdma = conf.hdma;
@ -1243,8 +1355,9 @@ void SB16_init (void)
reset_mixer (s);
s->aux_ts = qemu_new_timer (vm_clock, aux_timer, s);
if (!s->aux_ts)
return;
if (!s->aux_ts) {
dolog ("Can not create auxiliary timer\n");
}
for (i = 0; i < LENOFA (dsp_write_ports); i++) {
register_ioport_write (s->port + dsp_write_ports[i], 1, 1, dsp_write, s);
@ -1263,6 +1376,5 @@ void SB16_init (void)
DMA_register_channel (s->dma, SB_read_DMA, s);
s->can_write = 1;
qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + 1);
register_savevm ("sb16", 0, 1, SB_save, SB_load, s);
}

View File

@ -181,8 +181,23 @@ The default is @code{en-us}.
@item -enable-audio
The SB16 emulation is disabled by default as it may give problems with
Windows. You can enable it manually with this option.
Will enable audio and all the sound hardware QEMU was built with.
@item -audio-help
Will show the audio subsystem help: list of drivers, tunable
parameters.
@item -soundhw card1,card2,...
Enable audio and selected sound hardware. Use ? to print all
available sound hardware.
@example
qemu -soundhw sb16,adlib hda
qemu -soundhw es1370 hda
qemu -soundhw ?
@end example
@item -localtime
Set the real time clock to local time (the default is to UTC

109
vl.c
View File

@ -127,10 +127,13 @@ int nb_nics;
NetDriverState nd_table[MAX_NICS];
QEMUTimer *gui_timer;
int vm_running;
#ifdef HAS_AUDIO
int audio_enabled = 0;
int sb16_enabled = 1;
int adlib_enabled = 1;
int gus_enabled = 1;
int sb16_enabled = 0;
int adlib_enabled = 0;
int gus_enabled = 0;
int es1370_enabled = 0;
#endif
int pci_enabled = 1;
int prep_enabled = 0;
int rtc_utc = 1;
@ -930,7 +933,7 @@ static void init_timers(void)
#ifdef _WIN32
{
int count=0;
timerID = timeSetEvent(10, // interval (ms)
timerID = timeSetEvent(1, // interval (ms)
0, // resolution
host_alarm_handler, // function
(DWORD)&count, // user parameter
@ -2837,7 +2840,12 @@ void help(void)
#ifndef _WIN32
"-k language use keyboard layout (for example \"fr\" for French)\n"
#endif
#ifdef HAS_AUDIO
"-enable-audio enable audio support\n"
"-audio-help print list of audio drivers and their options\n"
"-soundhw c1,... comma separated list of sound card names\n"
" use -soundhw ? to get the list of supported sound cards\n"
#endif
"-localtime set the real time clock to local time [default=utc]\n"
"-full-screen start in full screen\n"
#ifdef TARGET_I386
@ -2935,7 +2943,11 @@ enum {
QEMU_OPTION_snapshot,
QEMU_OPTION_m,
QEMU_OPTION_nographic,
#ifdef HAS_AUDIO
QEMU_OPTION_enable_audio,
QEMU_OPTION_audio_help,
QEMU_OPTION_soundhw,
#endif
QEMU_OPTION_nics,
QEMU_OPTION_macaddr,
@ -2998,7 +3010,11 @@ const QEMUOption qemu_options[] = {
{ "m", HAS_ARG, QEMU_OPTION_m },
{ "nographic", 0, QEMU_OPTION_nographic },
{ "k", HAS_ARG, QEMU_OPTION_k },
#ifdef HAS_AUDIO
{ "enable-audio", 0, QEMU_OPTION_enable_audio },
{ "audio-help", 0, QEMU_OPTION_audio_help },
{ "soundhw", HAS_ARG, QEMU_OPTION_soundhw },
#endif
{ "nics", HAS_ARG, QEMU_OPTION_nics},
{ "macaddr", HAS_ARG, QEMU_OPTION_macaddr},
@ -3117,6 +3133,78 @@ void register_machines(void)
#endif
}
#ifdef HAS_AUDIO
static void select_soundhw (const char *optarg)
{
if (*optarg == '?') {
show_valid_cards:
printf ("Valid sound card names (comma separated):\n");
printf ("sb16 Creative Sound Blaster 16\n");
#ifdef CONFIG_ADLIB
#ifdef HAS_YMF262
printf ("adlib Ymaha YMF262 (OPL3)\n");
#else
printf ("adlib Ymaha YM3812 (OPL2)\n");
#endif
#endif
#ifdef CONFIG_GUS
printf ("gus Gravis Ultrasound GF1\n");
#endif
printf ("es1370 ENSONIQ AudioPCI ES1370\n");
exit (*optarg != '?');
}
else {
struct {
char *name;
int *enabledp;
} soundhw_tab[] = {
{ "sb16", &sb16_enabled },
#ifdef CONFIG_ADLIB
{ "adlib", &adlib_enabled },
#endif
#ifdef CONFIG_GUS
{ "gus", &gus_enabled },
#endif
{ "es1370", &es1370_enabled },
};
size_t tablen, l, i;
const char *p;
char *e;
int bad_card = 0;
p = optarg;
tablen = sizeof (soundhw_tab) / sizeof (soundhw_tab[0]);
while (*p) {
e = strchr (p, ',');
l = !e ? strlen (p) : (size_t) (e - p);
for (i = 0; i < tablen; ++i) {
if (!strncmp (soundhw_tab[i].name, p, l)) {
audio_enabled = 1;
*soundhw_tab[i].enabledp = 1;
break;
}
}
if (i == tablen) {
if (l > 80) {
fprintf (stderr,
"Unknown sound card name (too big to show)\n");
}
else {
fprintf (stderr, "Unknown sound card name `%.*s'\n",
(int) l, p);
}
bad_card = 1;
}
p += l + (e != NULL);
}
if (bad_card)
goto show_valid_cards;
}
}
#endif
#define NET_IF_TUN 0
#define NET_IF_USER 1
#define NET_IF_DUMMY 2
@ -3401,9 +3489,22 @@ int main(int argc, char **argv)
case QEMU_OPTION_dummy_net:
net_if_type = NET_IF_DUMMY;
break;
#ifdef HAS_AUDIO
case QEMU_OPTION_enable_audio:
audio_enabled = 1;
sb16_enabled = 1;
adlib_enabled = 1;
gus_enabled = 1;
es1370_enabled = 1;
break;
case QEMU_OPTION_audio_help:
AUD_help ();
exit (0);
break;
case QEMU_OPTION_soundhw:
select_soundhw (optarg);
break;
#endif
case QEMU_OPTION_h:
help();
break;

4
vl.h
View File

@ -124,6 +124,7 @@ extern int audio_enabled;
extern int sb16_enabled;
extern int adlib_enabled;
extern int gus_enabled;
extern int es1370_enabled;
extern int ram_size;
extern int bios_size;
extern int rtc_utc;
@ -628,6 +629,9 @@ void pci_piix3_ide_init(PCIBus *bus, BlockDriverState **hd_table);
int pmac_ide_init (BlockDriverState **hd_table,
SetIRQFunc *set_irq, void *irq_opaque, int irq);
/* es1370.c */
int es1370_init (PCIBus *bus);
/* sb16.c */
void SB16_init (void);