audio merge (malc)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1125 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
		| @@ -1,7 +1,17 @@ | |||||||
| include config.mak | include config.mak | ||||||
|  |  | ||||||
|  | #After enabling Adlib and/or FMOD rebuild QEMU from scratch | ||||||
|  | #Uncomment following for adlib support | ||||||
|  | #USE_ADLIB=1 | ||||||
|  |  | ||||||
|  | #Uncomment following and specify proper paths/names for FMOD support | ||||||
|  | #USE_FMOD=1 | ||||||
|  | #FMOD_INCLUDE=/net/include/fmod | ||||||
|  | #FMOD_LIBPATH=/net/lib | ||||||
|  | #FMOD_VERSION=3.74 | ||||||
|  |  | ||||||
| TARGET_PATH=$(SRC_PATH)/target-$(TARGET_ARCH) | TARGET_PATH=$(SRC_PATH)/target-$(TARGET_ARCH) | ||||||
| VPATH=$(SRC_PATH):$(TARGET_PATH):$(SRC_PATH)/hw | VPATH=$(SRC_PATH):$(TARGET_PATH):$(SRC_PATH)/hw:$(SRC_PATH)/audio | ||||||
| DEFINES=-I. -I$(TARGET_PATH) -I$(SRC_PATH) | DEFINES=-I. -I$(TARGET_PATH) -I$(SRC_PATH) | ||||||
| ifdef CONFIG_USER_ONLY | ifdef CONFIG_USER_ONLY | ||||||
| VPATH+=:$(SRC_PATH)/linux-user | VPATH+=:$(SRC_PATH)/linux-user | ||||||
| @@ -267,16 +277,31 @@ endif | |||||||
| VL_OBJS=vl.o osdep.o block.o readline.o monitor.o pci.o console.o  | 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 | VL_OBJS+=block-cow.o block-qcow.o aes.o block-vmdk.o block-cloop.o | ||||||
|  |  | ||||||
|  | SOUND_HW = sb16.o | ||||||
|  | AUDIODRV = audio.o ossaudio.o sdlaudio.o wavaudio.o | ||||||
|  |  | ||||||
|  | ifeq ($(USE_ADLIB),1) | ||||||
|  | SOUND_HW += fmopl.o adlib.o | ||||||
|  | audio.o: DEFINES := -DUSE_ADLIB $(DEFINES) | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | ifeq ($(USE_FMOD),1) | ||||||
|  | AUDIODRV += fmodaudio.o | ||||||
|  | audio.o fmodaudio.o: DEFINES := -DUSE_FMOD_AUDIO -I$(FMOD_INCLUDE) $(DEFINES) | ||||||
|  | LDFLAGS += -L$(FMOD_LIBPATH) -Wl,-rpath,$(FMOD_LIBPATH) | ||||||
|  | LIBS += -lfmod-$(FMOD_VERSION) | ||||||
|  | endif | ||||||
|  |  | ||||||
| ifeq ($(TARGET_ARCH), i386) | ifeq ($(TARGET_ARCH), i386) | ||||||
| # Hardware support | # Hardware support | ||||||
| VL_OBJS+= ide.o ne2000.o pckbd.o vga.o sb16.o dma.o oss.o | 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+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pc.o | ||||||
| VL_OBJS+= cirrus_vga.o | VL_OBJS+= cirrus_vga.o mixeng.o | ||||||
| endif | endif | ||||||
| ifeq ($(TARGET_ARCH), ppc) | ifeq ($(TARGET_ARCH), ppc) | ||||||
| VL_OBJS+= ppc.o ide.o ne2000.o pckbd.o vga.o sb16.o dma.o oss.o | 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+= 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 | VL_OBJS+= ppc_prep.o ppc_chrp.o cuda.o adb.o openpic.o mixeng.o | ||||||
| endif | endif | ||||||
| ifeq ($(TARGET_ARCH), sparc) | ifeq ($(TARGET_ARCH), sparc) | ||||||
| VL_OBJS+= sun4m.o tcx.o lance.o iommu.o sched.o m48t08.o magic-load.o timer.o | VL_OBJS+= sun4m.o tcx.o lance.o iommu.o sched.o m48t08.o magic-load.o timer.o | ||||||
| @@ -360,6 +385,8 @@ op.o: op.c op_template.h op_mem.h | |||||||
| op_helper.o: op_helper_mem.h | op_helper.o: op_helper_mem.h | ||||||
| endif | endif | ||||||
|  |  | ||||||
|  | mixeng.o: mixeng.c mixeng.h mixeng_template.h | ||||||
|  |  | ||||||
| %.o: %.c | %.o: %.c | ||||||
| 	$(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< | 	$(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										935
									
								
								audio/audio.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										935
									
								
								audio/audio.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,935 @@ | |||||||
|  | /* | ||||||
|  |  * QEMU Audio subsystem | ||||||
|  |  *  | ||||||
|  |  * Copyright (c) 2003-2004 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 <assert.h> | ||||||
|  | #include <limits.h> | ||||||
|  | #include "vl.h" | ||||||
|  |  | ||||||
|  | #define AUDIO_CAP "audio" | ||||||
|  | #include "audio/audio.h" | ||||||
|  |  | ||||||
|  | #define USE_SDL_AUDIO | ||||||
|  | #define USE_WAV_AUDIO | ||||||
|  |  | ||||||
|  | #if defined __linux__ || (defined _BSD && !defined __APPLE__) | ||||||
|  | #define USE_OSS_AUDIO | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #ifdef USE_OSS_AUDIO | ||||||
|  | #include "audio/ossaudio.h" | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #ifdef USE_SDL_AUDIO | ||||||
|  | #include "audio/sdlaudio.h" | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #ifdef USE_WAV_AUDIO | ||||||
|  | #include "audio/wavaudio.h" | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #ifdef USE_FMOD_AUDIO | ||||||
|  | #include "audio/fmodaudio.h" | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #define QC_AUDIO_DRV    "QEMU_AUDIO_DRV" | ||||||
|  | #define QC_VOICES    "QEMU_VOICES" | ||||||
|  | #define QC_FIXED_FORMAT "QEMU_FIXED_FORMAT" | ||||||
|  | #define QC_FIXED_FREQ   "QEMU_FIXED_FREQ" | ||||||
|  |  | ||||||
|  | extern void SB16_init (void); | ||||||
|  |  | ||||||
|  | #ifdef USE_ADLIB | ||||||
|  | extern void Adlib_init (void); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #ifdef USE_GUS | ||||||
|  | extern void GUS_init (void); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | static void (*hw_ctors[]) (void) = { | ||||||
|  |     SB16_init, | ||||||
|  | #ifdef USE_ADLIB | ||||||
|  |     Adlib_init, | ||||||
|  | #endif | ||||||
|  | #ifdef USE_GUS | ||||||
|  |     GUS_init, | ||||||
|  | #endif | ||||||
|  |     NULL | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static HWVoice *hw_voice; | ||||||
|  |  | ||||||
|  | AudioState audio_state = { | ||||||
|  |     1,                          /* use fixed settings */ | ||||||
|  |     44100,                      /* fixed frequency */ | ||||||
|  |     2,                          /* fixed channels */ | ||||||
|  |     AUD_FMT_S16,                /* fixed format */ | ||||||
|  |     1,                          /* number of hw voices */ | ||||||
|  |     -1                          /* voice size */ | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* http://www.df.lth.se/~john_e/gems/gem002d.html */ | ||||||
|  | /* http://www.multi-platforms.com/Tips/PopCount.htm */ | ||||||
|  | uint32_t popcount (uint32_t u) | ||||||
|  | { | ||||||
|  |     u = ((u&0x55555555) + ((u>>1)&0x55555555)); | ||||||
|  |     u = ((u&0x33333333) + ((u>>2)&0x33333333)); | ||||||
|  |     u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f)); | ||||||
|  |     u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff)); | ||||||
|  |     u = ( u&0x0000ffff) + (u>>16); | ||||||
|  |     return u; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | inline uint32_t lsbindex (uint32_t u) | ||||||
|  | { | ||||||
|  |     return popcount ((u&-u)-1); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int audio_get_conf_int (const char *key, int defval) | ||||||
|  | { | ||||||
|  |     int val = defval; | ||||||
|  |     char *strval; | ||||||
|  |  | ||||||
|  |     strval = getenv (key); | ||||||
|  |     if (strval) { | ||||||
|  |         val = atoi (strval); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return val; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const char *audio_get_conf_str (const char *key, const char *defval) | ||||||
|  | { | ||||||
|  |     const char *val = getenv (key); | ||||||
|  |     if (!val) | ||||||
|  |         return defval; | ||||||
|  |     else | ||||||
|  |         return val; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void audio_log (const char *fmt, ...) | ||||||
|  | { | ||||||
|  |     va_list ap; | ||||||
|  |     va_start (ap, fmt); | ||||||
|  |     vfprintf (stderr, fmt, ap); | ||||||
|  |     va_end (ap); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Soft Voice | ||||||
|  |  */ | ||||||
|  | void pcm_sw_free_resources (SWVoice *sw) | ||||||
|  | { | ||||||
|  |     if (sw->buf) qemu_free (sw->buf); | ||||||
|  |     if (sw->rate) st_rate_stop (sw->rate); | ||||||
|  |     sw->buf = NULL; | ||||||
|  |     sw->rate = NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int pcm_sw_alloc_resources (SWVoice *sw) | ||||||
|  | { | ||||||
|  |     sw->buf = qemu_mallocz (sw->hw->samples * sizeof (st_sample_t)); | ||||||
|  |     if (!sw->buf) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|  |     sw->rate = st_rate_start (sw->freq, sw->hw->freq); | ||||||
|  |     if (!sw->rate) { | ||||||
|  |         qemu_free (sw->buf); | ||||||
|  |         sw->buf = NULL; | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void pcm_sw_fini (SWVoice *sw) | ||||||
|  | { | ||||||
|  |     pcm_sw_free_resources (sw); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int pcm_sw_init (SWVoice *sw, HWVoice *hw, int freq, | ||||||
|  |                  int nchannels, audfmt_e fmt) | ||||||
|  | { | ||||||
|  |     int bits = 8, sign = 0; | ||||||
|  |  | ||||||
|  |     switch (fmt) { | ||||||
|  |     case AUD_FMT_S8: | ||||||
|  |         sign = 1; | ||||||
|  |     case AUD_FMT_U8: | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     case AUD_FMT_S16: | ||||||
|  |         sign = 1; | ||||||
|  |     case AUD_FMT_U16: | ||||||
|  |         bits = 16; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     sw->hw = hw; | ||||||
|  |     sw->freq = freq; | ||||||
|  |     sw->fmt = fmt; | ||||||
|  |     sw->nchannels = nchannels; | ||||||
|  |     sw->shift = (nchannels == 2) + (bits == 16); | ||||||
|  |     sw->align = (1 << sw->shift) - 1; | ||||||
|  |     sw->left = 0; | ||||||
|  |     sw->pos = 0; | ||||||
|  |     sw->wpos = 0; | ||||||
|  |     sw->live = 0; | ||||||
|  |     sw->ratio = (sw->hw->freq * ((int64_t) INT_MAX)) / sw->freq; | ||||||
|  |     sw->bytes_per_second = sw->freq << sw->shift; | ||||||
|  |     sw->conv = mixeng_conv[nchannels == 2][sign][bits == 16]; | ||||||
|  |  | ||||||
|  |     pcm_sw_free_resources (sw); | ||||||
|  |     return pcm_sw_alloc_resources (sw); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Hard voice */ | ||||||
|  | void pcm_hw_free_resources (HWVoice *hw) | ||||||
|  | { | ||||||
|  |     if (hw->mix_buf) | ||||||
|  |         qemu_free (hw->mix_buf); | ||||||
|  |     hw->mix_buf = NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int pcm_hw_alloc_resources (HWVoice *hw) | ||||||
|  | { | ||||||
|  |     hw->mix_buf = qemu_mallocz (hw->samples * sizeof (st_sample_t)); | ||||||
|  |     if (!hw->mix_buf) | ||||||
|  |         return -1; | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | void pcm_hw_fini (HWVoice *hw) | ||||||
|  | { | ||||||
|  |     if (hw->active) { | ||||||
|  |         ldebug ("pcm_hw_fini: %d %d %d\n", hw->freq, hw->nchannels, hw->fmt); | ||||||
|  |         pcm_hw_free_resources (hw); | ||||||
|  |         hw->pcm_ops->fini (hw); | ||||||
|  |         memset (hw, 0, audio_state.drv->voice_size); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void pcm_hw_gc (HWVoice *hw) | ||||||
|  | { | ||||||
|  |     if (hw->nb_voices) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|  |     pcm_hw_fini (hw); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int pcm_hw_get_live (HWVoice *hw) | ||||||
|  | { | ||||||
|  |     int i, alive = 0, live = hw->samples; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < hw->nb_voices; i++) { | ||||||
|  |         if (hw->pvoice[i]->live) { | ||||||
|  |             live = audio_MIN (hw->pvoice[i]->live, live); | ||||||
|  |             alive += 1; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (alive) | ||||||
|  |         return live; | ||||||
|  |     else | ||||||
|  |         return -1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int pcm_hw_get_live2 (HWVoice *hw, int *nb_active) | ||||||
|  | { | ||||||
|  |     int i, alive = 0, live = hw->samples; | ||||||
|  |  | ||||||
|  |     *nb_active = 0; | ||||||
|  |     for (i = 0; i < hw->nb_voices; i++) { | ||||||
|  |         if (hw->pvoice[i]->live) { | ||||||
|  |             if (hw->pvoice[i]->live < live) { | ||||||
|  |                 *nb_active = hw->pvoice[i]->active != 0; | ||||||
|  |                 live = hw->pvoice[i]->live; | ||||||
|  |             } | ||||||
|  |             alive += 1; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (alive) | ||||||
|  |         return live; | ||||||
|  |     else | ||||||
|  |         return -1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void pcm_hw_dec_live (HWVoice *hw, int decr) | ||||||
|  | { | ||||||
|  |     int i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < hw->nb_voices; i++) { | ||||||
|  |         if (hw->pvoice[i]->live) { | ||||||
|  |             hw->pvoice[i]->live -= decr; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void pcm_hw_clear (HWVoice *hw, void *buf, int len) | ||||||
|  | { | ||||||
|  |     if (!len) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|  |     switch (hw->fmt) { | ||||||
|  |     case AUD_FMT_S16: | ||||||
|  |     case AUD_FMT_S8: | ||||||
|  |         memset (buf, len << hw->shift, 0x00); | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     case AUD_FMT_U8: | ||||||
|  |         memset (buf, len << hw->shift, 0x80); | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     case AUD_FMT_U16: | ||||||
|  |         { | ||||||
|  |             unsigned int i; | ||||||
|  |             uint16_t *p = buf; | ||||||
|  |             int shift = hw->nchannels - 1; | ||||||
|  |  | ||||||
|  |             for (i = 0; i < len << shift; i++) { | ||||||
|  |                 p[i] = INT16_MAX; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int pcm_hw_write (SWVoice *sw, void *buf, int size) | ||||||
|  | { | ||||||
|  |     int hwsamples, samples, isamp, osamp, wpos, live, dead, left, swlim, blck; | ||||||
|  |     int ret = 0, pos = 0; | ||||||
|  |     if (!sw) | ||||||
|  |         return size; | ||||||
|  |  | ||||||
|  |     hwsamples = sw->hw->samples; | ||||||
|  |     samples = size >> sw->shift; | ||||||
|  |  | ||||||
|  |     if (!sw->live) { | ||||||
|  |         sw->wpos = sw->hw->rpos; | ||||||
|  |     } | ||||||
|  |     wpos = sw->wpos; | ||||||
|  |     live = sw->live; | ||||||
|  |     dead = hwsamples - live; | ||||||
|  |     swlim = (dead * ((int64_t) INT_MAX)) / sw->ratio; | ||||||
|  |     swlim = audio_MIN (swlim, samples); | ||||||
|  |  | ||||||
|  |     ldebug ("size=%d live=%d dead=%d swlim=%d wpos=%d\n", | ||||||
|  |            size, live, dead, swlim, wpos); | ||||||
|  |     if (swlim) | ||||||
|  |         sw->conv (sw->buf, buf, swlim); | ||||||
|  |  | ||||||
|  |     while (swlim) { | ||||||
|  |         dead = hwsamples - live; | ||||||
|  |         left = hwsamples - wpos; | ||||||
|  |         blck = audio_MIN (dead, left); | ||||||
|  |         if (!blck) { | ||||||
|  |             /* dolog ("swlim=%d\n", swlim); */ | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         isamp = swlim; | ||||||
|  |         osamp = blck; | ||||||
|  |         st_rate_flow (sw->rate, sw->buf + pos, sw->hw->mix_buf + wpos, &isamp, &osamp); | ||||||
|  |         ret += isamp; | ||||||
|  |         swlim -= isamp; | ||||||
|  |         pos += isamp; | ||||||
|  |         live += osamp; | ||||||
|  |         wpos = (wpos + osamp) % hwsamples; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     sw->wpos = wpos; | ||||||
|  |     sw->live = live; | ||||||
|  |     return ret << sw->shift; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int pcm_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt) | ||||||
|  | { | ||||||
|  |     int sign = 0, bits = 8; | ||||||
|  |  | ||||||
|  |     pcm_hw_fini (hw); | ||||||
|  |     ldebug ("pcm_hw_init: %d %d %d\n", freq, nchannels, fmt); | ||||||
|  |     if (hw->pcm_ops->init (hw, freq, nchannels, fmt)) { | ||||||
|  |         memset (hw, 0, audio_state.drv->voice_size); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     switch (hw->fmt) { | ||||||
|  |     case AUD_FMT_S8: | ||||||
|  |         sign = 1; | ||||||
|  |     case AUD_FMT_U8: | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     case AUD_FMT_S16: | ||||||
|  |         sign = 1; | ||||||
|  |     case AUD_FMT_U16: | ||||||
|  |         bits = 16; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     hw->nb_voices = 0; | ||||||
|  |     hw->active = 1; | ||||||
|  |     hw->shift = (hw->nchannels == 2) + (bits == 16); | ||||||
|  |     hw->bytes_per_second = hw->freq << hw->shift; | ||||||
|  |     hw->align = (1 << hw->shift) - 1; | ||||||
|  |     hw->samples = hw->bufsize >> hw->shift; | ||||||
|  |     hw->clip = mixeng_clip[hw->nchannels == 2][sign][bits == 16]; | ||||||
|  |     if (pcm_hw_alloc_resources (hw)) { | ||||||
|  |         pcm_hw_fini (hw); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int dist (void *hw) | ||||||
|  | { | ||||||
|  |     if (hw) { | ||||||
|  |         return (((uint8_t *) hw - (uint8_t *) hw_voice) | ||||||
|  |                 / audio_state.voice_size) + 1; | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #define ADVANCE(hw) hw ? advance (hw, audio_state.voice_size) : hw_voice | ||||||
|  |  | ||||||
|  | HWVoice *pcm_hw_find_any (HWVoice *hw) | ||||||
|  | { | ||||||
|  |     int i = dist (hw); | ||||||
|  |     for (; i < audio_state.nb_hw_voices; i++) { | ||||||
|  |         hw = ADVANCE (hw); | ||||||
|  |         return hw; | ||||||
|  |     } | ||||||
|  |     return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | HWVoice *pcm_hw_find_any_active (HWVoice *hw) | ||||||
|  | { | ||||||
|  |     int i = dist (hw); | ||||||
|  |     for (; i < audio_state.nb_hw_voices; i++) { | ||||||
|  |         hw = ADVANCE (hw); | ||||||
|  |         if (hw->active) | ||||||
|  |             return hw; | ||||||
|  |     } | ||||||
|  |     return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | HWVoice *pcm_hw_find_any_active_enabled (HWVoice *hw) | ||||||
|  | { | ||||||
|  |     int i = dist (hw); | ||||||
|  |     for (; i < audio_state.nb_hw_voices; i++) { | ||||||
|  |         hw = ADVANCE (hw); | ||||||
|  |         if (hw->active && hw->enabled) | ||||||
|  |             return hw; | ||||||
|  |     } | ||||||
|  |     return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | HWVoice *pcm_hw_find_any_passive (HWVoice *hw) | ||||||
|  | { | ||||||
|  |     int i = dist (hw); | ||||||
|  |     for (; i < audio_state.nb_hw_voices; i++) { | ||||||
|  |         hw = ADVANCE (hw); | ||||||
|  |         if (!hw->active) | ||||||
|  |             return hw; | ||||||
|  |     } | ||||||
|  |     return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | HWVoice *pcm_hw_find_specific (HWVoice *hw, int freq, | ||||||
|  |                                int nchannels, audfmt_e fmt) | ||||||
|  | { | ||||||
|  |     while ((hw = pcm_hw_find_any_active (hw))) { | ||||||
|  |         if (hw->freq == freq && | ||||||
|  |             hw->nchannels == nchannels && | ||||||
|  |             hw->fmt == fmt) | ||||||
|  |             return hw; | ||||||
|  |     } | ||||||
|  |     return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | HWVoice *pcm_hw_add (int freq, int nchannels, audfmt_e fmt) | ||||||
|  | { | ||||||
|  |     HWVoice *hw; | ||||||
|  |  | ||||||
|  |     if (audio_state.fixed_format) { | ||||||
|  |         freq = audio_state.fixed_freq; | ||||||
|  |         nchannels = audio_state.fixed_channels; | ||||||
|  |         fmt = audio_state.fixed_fmt; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     hw = pcm_hw_find_specific (NULL, freq, nchannels, fmt); | ||||||
|  |  | ||||||
|  |     if (hw) | ||||||
|  |         return hw; | ||||||
|  |  | ||||||
|  |     hw = pcm_hw_find_any_passive (NULL); | ||||||
|  |     if (hw) { | ||||||
|  |         hw->pcm_ops = audio_state.drv->pcm_ops; | ||||||
|  |         if (!hw->pcm_ops) | ||||||
|  |             return NULL; | ||||||
|  |  | ||||||
|  |         if (pcm_hw_init (hw, freq, nchannels, fmt)) { | ||||||
|  |             pcm_hw_gc (hw); | ||||||
|  |             return NULL; | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |             return hw; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return pcm_hw_find_any (NULL); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int pcm_hw_add_sw (HWVoice *hw, SWVoice *sw) | ||||||
|  | { | ||||||
|  |     SWVoice **pvoice = qemu_mallocz ((hw->nb_voices + 1) * sizeof (sw)); | ||||||
|  |     if (!pvoice) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|  |     memcpy (pvoice, hw->pvoice, hw->nb_voices * sizeof (sw)); | ||||||
|  |     qemu_free (hw->pvoice); | ||||||
|  |     hw->pvoice = pvoice; | ||||||
|  |     hw->pvoice[hw->nb_voices++] = sw; | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int pcm_hw_del_sw (HWVoice *hw, SWVoice *sw) | ||||||
|  | { | ||||||
|  |     int i, j; | ||||||
|  |     if (hw->nb_voices > 1) { | ||||||
|  |         SWVoice **pvoice = qemu_mallocz ((hw->nb_voices - 1) * sizeof (sw)); | ||||||
|  |  | ||||||
|  |         if (!pvoice) { | ||||||
|  |             dolog ("Can not maintain consistent state (not enough memory)\n"); | ||||||
|  |             return -1; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         for (i = 0, j = 0; i < hw->nb_voices; i++) { | ||||||
|  |             if (j >= hw->nb_voices - 1) { | ||||||
|  |                 dolog ("Can not maintain consistent state " | ||||||
|  |                        "(invariant violated)\n"); | ||||||
|  |                 return -1; | ||||||
|  |             } | ||||||
|  |             if (hw->pvoice[i] != sw) | ||||||
|  |                 pvoice[j++] = hw->pvoice[i]; | ||||||
|  |         } | ||||||
|  |         qemu_free (hw->pvoice); | ||||||
|  |         hw->pvoice = pvoice; | ||||||
|  |         hw->nb_voices -= 1; | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |         qemu_free (hw->pvoice); | ||||||
|  |         hw->pvoice = NULL; | ||||||
|  |         hw->nb_voices = 0; | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | SWVoice *pcm_create_voice_pair (int freq, int nchannels, audfmt_e fmt) | ||||||
|  | { | ||||||
|  |     SWVoice *sw; | ||||||
|  |     HWVoice *hw; | ||||||
|  |  | ||||||
|  |     sw = qemu_mallocz (sizeof (*sw)); | ||||||
|  |     if (!sw) | ||||||
|  |         goto err1; | ||||||
|  |  | ||||||
|  |     hw = pcm_hw_add (freq, nchannels, fmt); | ||||||
|  |     if (!hw) | ||||||
|  |         goto err2; | ||||||
|  |  | ||||||
|  |     if (pcm_hw_add_sw (hw, sw)) | ||||||
|  |         goto err3; | ||||||
|  |  | ||||||
|  |     if (pcm_sw_init (sw, hw, freq, nchannels, fmt)) | ||||||
|  |         goto err4; | ||||||
|  |  | ||||||
|  |     return sw; | ||||||
|  |  | ||||||
|  | err4: | ||||||
|  |     pcm_hw_del_sw (hw, sw); | ||||||
|  | err3: | ||||||
|  |     pcm_hw_gc (hw); | ||||||
|  | err2: | ||||||
|  |     qemu_free (sw); | ||||||
|  | err1: | ||||||
|  |     return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | SWVoice *AUD_open (SWVoice *sw, const char *name, | ||||||
|  |                    int freq, int nchannels, audfmt_e fmt) | ||||||
|  | { | ||||||
|  |     if (!audio_state.drv) { | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (sw && freq == sw->freq && sw->nchannels == nchannels && sw->fmt == fmt) { | ||||||
|  |         return sw; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (sw) { | ||||||
|  |         ldebug ("Different format %s %d %d %d\n", | ||||||
|  |                 name, | ||||||
|  |                 sw->freq == freq, | ||||||
|  |                 sw->nchannels == nchannels, | ||||||
|  |                 sw->fmt == fmt); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (nchannels != 1 && nchannels != 2) { | ||||||
|  |         dolog ("Bogus channel count %d for voice %s\n", nchannels, name); | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!audio_state.fixed_format && sw) { | ||||||
|  |         pcm_sw_fini (sw); | ||||||
|  |         pcm_hw_del_sw (sw->hw, sw); | ||||||
|  |         pcm_hw_gc (sw->hw); | ||||||
|  |         if (sw->name) { | ||||||
|  |             qemu_free (sw->name); | ||||||
|  |             sw->name = NULL; | ||||||
|  |         } | ||||||
|  |         qemu_free (sw); | ||||||
|  |         sw = NULL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (sw) { | ||||||
|  |         HWVoice *hw = sw->hw; | ||||||
|  |         if (!hw) { | ||||||
|  |             dolog ("Internal logic error voice %s has no hardware store\n", | ||||||
|  |                    name); | ||||||
|  |             return sw; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (pcm_sw_init (sw, hw, freq, nchannels, fmt)) { | ||||||
|  |             pcm_sw_fini (sw); | ||||||
|  |             pcm_hw_del_sw (hw, sw); | ||||||
|  |             pcm_hw_gc (hw); | ||||||
|  |             if (sw->name) { | ||||||
|  |                 qemu_free (sw->name); | ||||||
|  |                 sw->name = NULL; | ||||||
|  |             } | ||||||
|  |             qemu_free (sw); | ||||||
|  |             return NULL; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |         sw = pcm_create_voice_pair (freq, nchannels, fmt); | ||||||
|  |         if (!sw) { | ||||||
|  |             dolog ("Failed to create voice %s\n", name); | ||||||
|  |             return NULL; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (sw->name) { | ||||||
|  |         qemu_free (sw->name); | ||||||
|  |         sw->name = NULL; | ||||||
|  |     } | ||||||
|  |     sw->name = qemu_strdup (name); | ||||||
|  |     return sw; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int AUD_write (SWVoice *sw, void *buf, int size) | ||||||
|  | { | ||||||
|  |     int bytes; | ||||||
|  |  | ||||||
|  |     if (!sw->hw->enabled) | ||||||
|  |         dolog ("Writing to disabled voice %s\n", sw->name); | ||||||
|  |     bytes = sw->hw->pcm_ops->write (sw, buf, size); | ||||||
|  |     return bytes; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void AUD_run (void) | ||||||
|  | { | ||||||
|  |     HWVoice *hw = NULL; | ||||||
|  |  | ||||||
|  |     while ((hw = pcm_hw_find_any_active_enabled (hw))) { | ||||||
|  |         int i; | ||||||
|  |         if (hw->pending_disable && pcm_hw_get_live (hw) <= 0) { | ||||||
|  |             hw->enabled = 0; | ||||||
|  |             hw->pcm_ops->ctl (hw, VOICE_DISABLE); | ||||||
|  |             for (i = 0; i < hw->nb_voices; i++) { | ||||||
|  |                 hw->pvoice[i]->live = 0; | ||||||
|  |                 /* hw->pvoice[i]->old_ticks = 0; */ | ||||||
|  |             } | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         hw->pcm_ops->run (hw); | ||||||
|  |         assert (hw->rpos < hw->samples); | ||||||
|  |         for (i = 0; i < hw->nb_voices; i++) { | ||||||
|  |             SWVoice *sw = hw->pvoice[i]; | ||||||
|  |             if (!sw->active && !sw->live && sw->old_ticks) { | ||||||
|  |                 int64_t delta = qemu_get_clock (vm_clock) - sw->old_ticks; | ||||||
|  |                 if (delta > audio_state.ticks_threshold) { | ||||||
|  |                     ldebug ("resetting old_ticks for %s\n", sw->name); | ||||||
|  |                     sw->old_ticks = 0; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int AUD_get_free (SWVoice *sw) | ||||||
|  | { | ||||||
|  |     int free; | ||||||
|  |  | ||||||
|  |     if (!sw) | ||||||
|  |         return 4096; | ||||||
|  |  | ||||||
|  |     free = ((sw->hw->samples - sw->live) << sw->hw->shift) * sw->ratio | ||||||
|  |         / INT_MAX; | ||||||
|  |  | ||||||
|  |     free &= ~sw->hw->align; | ||||||
|  |     if (!free) return 0; | ||||||
|  |  | ||||||
|  |     return free; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int AUD_get_buffer_size (SWVoice *sw) | ||||||
|  | { | ||||||
|  |     return sw->hw->bufsize; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void AUD_adjust (SWVoice *sw, int bytes) | ||||||
|  | { | ||||||
|  |     if (!sw) | ||||||
|  |         return; | ||||||
|  |     sw->old_ticks += (ticks_per_sec * (int64_t) bytes) / sw->bytes_per_second; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void AUD_reset (SWVoice *sw) | ||||||
|  | { | ||||||
|  |     sw->active = 0; | ||||||
|  |     sw->old_ticks = 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int AUD_calc_elapsed (SWVoice *sw) | ||||||
|  | { | ||||||
|  |     int64_t now, delta, bytes; | ||||||
|  |     int dead, swlim; | ||||||
|  |  | ||||||
|  |     if (!sw) | ||||||
|  |         return 0; | ||||||
|  |  | ||||||
|  |     now = qemu_get_clock (vm_clock); | ||||||
|  |     delta = now - sw->old_ticks; | ||||||
|  |     bytes = (delta * sw->bytes_per_second) / ticks_per_sec; | ||||||
|  |     if (delta < 0) { | ||||||
|  |         dolog ("whoops delta(<0)=%lld\n", delta); | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     dead = sw->hw->samples - sw->live; | ||||||
|  |     swlim = ((dead * (int64_t) INT_MAX) / sw->ratio); | ||||||
|  |  | ||||||
|  |     if (bytes > swlim) { | ||||||
|  |         return swlim; | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |         return bytes; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void AUD_enable (SWVoice *sw, int on) | ||||||
|  | { | ||||||
|  |     int i; | ||||||
|  |     HWVoice *hw; | ||||||
|  |  | ||||||
|  |     if (!sw) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|  |     hw = sw->hw; | ||||||
|  |     if (on) { | ||||||
|  |         if (!sw->live) | ||||||
|  |             sw->wpos = sw->hw->rpos; | ||||||
|  |         if (!sw->old_ticks) { | ||||||
|  |             sw->old_ticks = qemu_get_clock (vm_clock); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (sw->active != on) { | ||||||
|  |         if (on) { | ||||||
|  |             hw->pending_disable = 0; | ||||||
|  |             if (!hw->enabled) { | ||||||
|  |                 hw->enabled = 1; | ||||||
|  |                 for (i = 0; i < hw->nb_voices; i++) { | ||||||
|  |                     ldebug ("resetting voice\n"); | ||||||
|  |                     sw = hw->pvoice[i]; | ||||||
|  |                     sw->old_ticks = qemu_get_clock (vm_clock); | ||||||
|  |                 } | ||||||
|  |                 hw->pcm_ops->ctl (hw, VOICE_ENABLE); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |             if (hw->enabled && !hw->pending_disable) { | ||||||
|  |                 int nb_active = 0; | ||||||
|  |                 for (i = 0; i < hw->nb_voices; i++) { | ||||||
|  |                     nb_active += hw->pvoice[i]->active != 0; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 if (nb_active == 1) { | ||||||
|  |                     hw->pending_disable = 1; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         sw->active = on; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static struct audio_output_driver *drvtab[] = { | ||||||
|  | #ifdef USE_OSS_AUDIO | ||||||
|  |     &oss_output_driver, | ||||||
|  | #endif | ||||||
|  | #ifdef USE_FMOD_AUDIO | ||||||
|  |     &fmod_output_driver, | ||||||
|  | #endif | ||||||
|  | #ifdef USE_SDL_AUDIO | ||||||
|  |     &sdl_output_driver, | ||||||
|  | #endif | ||||||
|  | #ifdef USE_WAV_AUDIO | ||||||
|  |     &wav_output_driver, | ||||||
|  | #endif | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static int voice_init (struct audio_output_driver *drv) | ||||||
|  | { | ||||||
|  |     audio_state.opaque = drv->init (); | ||||||
|  |     if (audio_state.opaque) { | ||||||
|  |         if (audio_state.nb_hw_voices > drv->max_voices) { | ||||||
|  |             dolog ("`%s' does not support %d multiple hardware channels\n" | ||||||
|  |                    "Resetting to %d\n", | ||||||
|  |                    drv->name, audio_state.nb_hw_voices, drv->max_voices); | ||||||
|  |             audio_state.nb_hw_voices = drv->max_voices; | ||||||
|  |         } | ||||||
|  |         hw_voice = qemu_mallocz (audio_state.nb_hw_voices * drv->voice_size); | ||||||
|  |         if (hw_voice) { | ||||||
|  |             audio_state.drv = drv; | ||||||
|  |             return 1; | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |             dolog ("Not enough memory for %d `%s' voices (each %d bytes)\n", | ||||||
|  |                    audio_state.nb_hw_voices, drv->name, drv->voice_size); | ||||||
|  |             drv->fini (audio_state.opaque); | ||||||
|  |             return 0; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |         dolog ("Could not init `%s' audio\n", drv->name); | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void audio_vm_stop_handler (void *opaque, int reason) | ||||||
|  | { | ||||||
|  |     HWVoice *hw = NULL; | ||||||
|  |  | ||||||
|  |     while ((hw = pcm_hw_find_any (hw))) { | ||||||
|  |         if (!hw->pcm_ops) | ||||||
|  |             continue; | ||||||
|  |  | ||||||
|  |         hw->pcm_ops->ctl (hw, reason ? VOICE_ENABLE : VOICE_DISABLE); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void audio_atexit (void) | ||||||
|  | { | ||||||
|  |     HWVoice *hw = NULL; | ||||||
|  |  | ||||||
|  |     while ((hw = pcm_hw_find_any (hw))) { | ||||||
|  |         if (!hw->pcm_ops) | ||||||
|  |             continue; | ||||||
|  |  | ||||||
|  |         hw->pcm_ops->ctl (hw, VOICE_DISABLE); | ||||||
|  |         hw->pcm_ops->fini (hw); | ||||||
|  |     } | ||||||
|  |     audio_state.drv->fini (audio_state.opaque); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void audio_save (QEMUFile *f, void *opaque) | ||||||
|  | { | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int audio_load (QEMUFile *f, void *opaque, int version_id) | ||||||
|  | { | ||||||
|  |     if (version_id != 1) | ||||||
|  |         return -EINVAL; | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void AUD_init (void) | ||||||
|  | { | ||||||
|  |     int i; | ||||||
|  |     int done = 0; | ||||||
|  |     const char *drvname; | ||||||
|  |  | ||||||
|  |     audio_state.fixed_format = | ||||||
|  |         !!audio_get_conf_int (QC_FIXED_FORMAT, audio_state.fixed_format); | ||||||
|  |     audio_state.fixed_freq = | ||||||
|  |         audio_get_conf_int (QC_FIXED_FREQ, audio_state.fixed_freq); | ||||||
|  |     audio_state.nb_hw_voices = | ||||||
|  |         audio_get_conf_int (QC_VOICES, audio_state.nb_hw_voices); | ||||||
|  |  | ||||||
|  |     if (audio_state.nb_hw_voices <= 0) { | ||||||
|  |         dolog ("Bogus number of voices %d, resetting to 1\n", | ||||||
|  |                audio_state.nb_hw_voices); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     drvname = audio_get_conf_str (QC_AUDIO_DRV, NULL); | ||||||
|  |     if (drvname) { | ||||||
|  |         int found = 0; | ||||||
|  |         for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) { | ||||||
|  |             if (!strcmp (drvname, drvtab[i]->name)) { | ||||||
|  |                 done = voice_init (drvtab[i]); | ||||||
|  |                 found = 1; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if (!found) { | ||||||
|  |             dolog ("Unknown audio driver `%s'\n", drvname); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     qemu_add_vm_stop_handler (audio_vm_stop_handler, NULL); | ||||||
|  |     atexit (audio_atexit); | ||||||
|  |  | ||||||
|  |     if (!done) { | ||||||
|  |         for (i = 0; !done && i < sizeof (drvtab) / sizeof (drvtab[0]); i++) { | ||||||
|  |             if (drvtab[i]->can_be_default) | ||||||
|  |                 done = voice_init (drvtab[i]); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     audio_state.ticks_threshold = ticks_per_sec / 50; | ||||||
|  |     audio_state.freq_threshold = 100; | ||||||
|  |  | ||||||
|  |     register_savevm ("audio", 0, 1, audio_save, audio_load, NULL); | ||||||
|  |     if (!done) { | ||||||
|  |         dolog ("Can not initialize audio subsystem\n"); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (i = 0; hw_ctors[i]; i++) { | ||||||
|  |         hw_ctors[i] (); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										188
									
								
								audio/audio.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										188
									
								
								audio/audio.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,188 @@ | |||||||
|  | /* | ||||||
|  |  * QEMU Audio subsystem header | ||||||
|  |  *  | ||||||
|  |  * Copyright (c) 2003-2004 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. | ||||||
|  |  */ | ||||||
|  | #ifndef QEMU_AUDIO_H | ||||||
|  | #define QEMU_AUDIO_H | ||||||
|  |  | ||||||
|  | #include "mixeng.h" | ||||||
|  |  | ||||||
|  | #define dolog(...) fprintf (stderr, AUDIO_CAP ": " __VA_ARGS__) | ||||||
|  | #ifdef DEBUG | ||||||
|  | #define ldebug(...) dolog (__VA_ARGS__) | ||||||
|  | #else | ||||||
|  | #define ldebug(...) | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | typedef enum { | ||||||
|  |   AUD_FMT_U8, | ||||||
|  |   AUD_FMT_S8, | ||||||
|  |   AUD_FMT_U16, | ||||||
|  |   AUD_FMT_S16 | ||||||
|  | } audfmt_e; | ||||||
|  |  | ||||||
|  | typedef struct HWVoice HWVoice; | ||||||
|  | struct audio_output_driver; | ||||||
|  |  | ||||||
|  | typedef struct AudioState { | ||||||
|  |     int fixed_format; | ||||||
|  |     int fixed_freq; | ||||||
|  |     int fixed_channels; | ||||||
|  |     int fixed_fmt; | ||||||
|  |     int nb_hw_voices; | ||||||
|  |     int voice_size; | ||||||
|  |     int64_t ticks_threshold; | ||||||
|  |     int freq_threshold; | ||||||
|  |     void *opaque; | ||||||
|  |     struct audio_output_driver *drv; | ||||||
|  | } AudioState; | ||||||
|  |  | ||||||
|  | extern AudioState audio_state; | ||||||
|  |  | ||||||
|  | typedef struct SWVoice { | ||||||
|  |     int freq; | ||||||
|  |     audfmt_e fmt; | ||||||
|  |     int nchannels; | ||||||
|  |  | ||||||
|  |     int shift; | ||||||
|  |     int align; | ||||||
|  |  | ||||||
|  |     t_sample *conv; | ||||||
|  |  | ||||||
|  |     int left; | ||||||
|  |     int pos; | ||||||
|  |     int bytes_per_second; | ||||||
|  |     int64_t ratio; | ||||||
|  |     st_sample_t *buf; | ||||||
|  |     void *rate; | ||||||
|  |  | ||||||
|  |     int wpos; | ||||||
|  |     int live; | ||||||
|  |     int active; | ||||||
|  |     int64_t old_ticks; | ||||||
|  |     HWVoice *hw; | ||||||
|  |     char *name; | ||||||
|  | } SWVoice; | ||||||
|  |  | ||||||
|  | #define VOICE_ENABLE 1 | ||||||
|  | #define VOICE_DISABLE 2 | ||||||
|  |  | ||||||
|  | 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 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; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct HWVoice { | ||||||
|  |     int active; | ||||||
|  |     int enabled; | ||||||
|  |     int pending_disable; | ||||||
|  |     int valid; | ||||||
|  |     int freq; | ||||||
|  |  | ||||||
|  |     f_sample *clip; | ||||||
|  |     audfmt_e fmt; | ||||||
|  |     int nchannels; | ||||||
|  |  | ||||||
|  |     int align; | ||||||
|  |     int shift; | ||||||
|  |  | ||||||
|  |     int rpos; | ||||||
|  |     int bufsize; | ||||||
|  |  | ||||||
|  |     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; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | void      audio_log (const char *fmt, ...); | ||||||
|  | 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); | ||||||
|  |  | ||||||
|  | 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); | ||||||
|  |  | ||||||
|  | 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); | ||||||
|  |  | ||||||
|  | int         audio_get_conf_int (const char *key, int defval); | ||||||
|  | const char *audio_get_conf_str (const char *key, const char *defval); | ||||||
|  |  | ||||||
|  | /* Public API */ | ||||||
|  | SWVoice * AUD_open (SWVoice *sw, const char *name, int freq, | ||||||
|  |                     int nchannels, audfmt_e fmt); | ||||||
|  | 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); | ||||||
|  |  | ||||||
|  | static inline void *advance (void *p, int incr) | ||||||
|  | { | ||||||
|  |     uint8_t *d = p; | ||||||
|  |     return (d + incr); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | uint32_t popcount (uint32_t u); | ||||||
|  | inline uint32_t lsbindex (uint32_t u); | ||||||
|  |  | ||||||
|  | #define audio_MIN(a, b) ((a)>(b)?(b):(a)) | ||||||
|  | #define audio_MAX(a, b) ((a)<(b)?(b):(a)) | ||||||
|  |  | ||||||
|  | #endif  /* audio.h */ | ||||||
							
								
								
									
										457
									
								
								audio/fmodaudio.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										457
									
								
								audio/fmodaudio.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,457 @@ | |||||||
|  | /* | ||||||
|  |  * QEMU FMOD audio output driver | ||||||
|  |  *  | ||||||
|  |  * Copyright (c) 2004 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 <fmod.h> | ||||||
|  | #include <fmod_errors.h> | ||||||
|  | #include "vl.h" | ||||||
|  |  | ||||||
|  | #define AUDIO_CAP "fmod" | ||||||
|  | #include "audio/audio.h" | ||||||
|  | #include "audio/fmodaudio.h" | ||||||
|  |  | ||||||
|  | #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" | ||||||
|  |  | ||||||
|  | static struct { | ||||||
|  |     int nb_samples; | ||||||
|  |     int freq; | ||||||
|  |     int nb_channels; | ||||||
|  |     int bufsize; | ||||||
|  |     int threshold; | ||||||
|  | } conf = { | ||||||
|  |     2048, | ||||||
|  |     44100, | ||||||
|  |     1, | ||||||
|  |     0, | ||||||
|  |     128 | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #define errstr() FMOD_ErrorString (FSOUND_GetError ()) | ||||||
|  |  | ||||||
|  | static int fmod_hw_write (SWVoice *sw, void *buf, int len) | ||||||
|  | { | ||||||
|  |     return pcm_hw_write (sw, buf, len); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void fmod_clear_sample (FMODVoice *fmd) | ||||||
|  | { | ||||||
|  |     HWVoice *hw = &fmd->hw; | ||||||
|  |     int status; | ||||||
|  |     void *p1 = 0, *p2 = 0; | ||||||
|  |     unsigned int len1 = 0, len2 = 0; | ||||||
|  |  | ||||||
|  |     status = FSOUND_Sample_Lock ( | ||||||
|  |         fmd->fmod_sample, | ||||||
|  |         0, | ||||||
|  |         hw->samples << hw->shift, | ||||||
|  |         &p1, | ||||||
|  |         &p2, | ||||||
|  |         &len1, | ||||||
|  |         &len2 | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |     if (!status) { | ||||||
|  |         dolog ("Failed to lock sample\nReason: %s\n", errstr ()); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if ((len1 & hw->align) || (len2 & hw->align)) { | ||||||
|  |         dolog ("Locking sample returned unaligned length %d, %d\n", | ||||||
|  |                len1, len2); | ||||||
|  |         goto fail; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (len1 + len2 != hw->samples << hw->shift) { | ||||||
|  |         dolog ("Locking sample returned incomplete length %d, %d\n", | ||||||
|  |                len1 + len2, hw->samples << hw->shift); | ||||||
|  |         goto fail; | ||||||
|  |     } | ||||||
|  |     pcm_hw_clear (hw, 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 ()); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int fmod_write_sample (HWVoice *hw, uint8_t *dst, st_sample_t *src, | ||||||
|  |                               int src_size, int src_pos, 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; | ||||||
|  |  | ||||||
|  |     if (src_pos + dst_len > src_size) { | ||||||
|  |         src_len1 = src_size - src_pos; | ||||||
|  |         src2 = src; | ||||||
|  |         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); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (src_len2) { | ||||||
|  |         hw->clip (dst, src2, src_len2); | ||||||
|  |         memset (src2, 0, src_len2 * sizeof (st_sample_t)); | ||||||
|  |     } | ||||||
|  |     return pos; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int fmod_unlock_sample (FMODVoice *fmd, void *p1, void *p2, | ||||||
|  |                                unsigned int blen1, unsigned int blen2) | ||||||
|  | { | ||||||
|  |     int status = FSOUND_Sample_Unlock (fmd->fmod_sample, p1, p2, blen1, blen2); | ||||||
|  |     if (!status) { | ||||||
|  |         dolog ("Failed to unlock sample\nReason: %s\n", errstr ()); | ||||||
|  |         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) | ||||||
|  | { | ||||||
|  |     HWVoice *hw = &fmd->hw; | ||||||
|  |     int status; | ||||||
|  |  | ||||||
|  |     status = FSOUND_Sample_Lock ( | ||||||
|  |         fmd->fmod_sample, | ||||||
|  |         pos << hw->shift, | ||||||
|  |         len << hw->shift, | ||||||
|  |         p1, | ||||||
|  |         p2, | ||||||
|  |         blen1, | ||||||
|  |         blen2 | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |     if (!status) { | ||||||
|  |         dolog ("Failed to lock sample\nReason: %s\n", errstr ()); | ||||||
|  |         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); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void fmod_hw_run (HWVoice *hw) | ||||||
|  | { | ||||||
|  |     FMODVoice *fmd = (FMODVoice *) hw; | ||||||
|  |     int rpos, live, decr; | ||||||
|  |     void *p1 = 0, *p2 = 0; | ||||||
|  |     unsigned int blen1 = 0, blen2 = 0; | ||||||
|  |     unsigned int len1 = 0, len2 = 0; | ||||||
|  |     int nb_active; | ||||||
|  |  | ||||||
|  |     live = pcm_hw_get_live2 (hw, &nb_active); | ||||||
|  |     if (live <= 0) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!hw->pending_disable | ||||||
|  |         && nb_active | ||||||
|  |         && conf.threshold | ||||||
|  |         && live <= conf.threshold) { | ||||||
|  |         ldebug ("live=%d nb_active=%d\n", live, nb_active); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     decr = live; | ||||||
|  |  | ||||||
|  | #if 1 | ||||||
|  |     if (fmd->channel >= 0) { | ||||||
|  |         int pos2 = (fmd->old_pos + decr) % hw->samples; | ||||||
|  |         int pos = 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 (fmod_lock_sample (fmd, fmd->old_pos, decr, &p1, &p2, &blen1, &blen2)) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     len1 = blen1 >> hw->shift; | ||||||
|  |     len2 = blen2 >> hw->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 (len2) { | ||||||
|  |         rpos = fmod_write_sample (hw, p2, hw->mix_buf, hw->samples, rpos, len2); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fmod_unlock_sample (fmd, p1, p2, blen1, blen2); | ||||||
|  |  | ||||||
|  |     pcm_hw_dec_live (hw, decr); | ||||||
|  |     hw->rpos = rpos % hw->samples; | ||||||
|  |     fmd->old_pos = (fmd->old_pos + decr) % hw->samples; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int AUD_to_fmodfmt (audfmt_e fmt, int stereo) | ||||||
|  | { | ||||||
|  |     int mode = FSOUND_LOOP_NORMAL; | ||||||
|  |  | ||||||
|  |     switch (fmt) { | ||||||
|  |     case AUD_FMT_S8: | ||||||
|  |         mode |= FSOUND_SIGNED | FSOUND_8BITS; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     case AUD_FMT_U8: | ||||||
|  |         mode |= FSOUND_UNSIGNED | FSOUND_8BITS; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     case AUD_FMT_S16: | ||||||
|  |         mode |= FSOUND_SIGNED | FSOUND_16BITS; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     case AUD_FMT_U16: | ||||||
|  |         mode |= FSOUND_UNSIGNED | FSOUND_16BITS; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     default: | ||||||
|  |         dolog ("Internal logic error: Bad audio format %d\nAborting\n", fmt); | ||||||
|  |         exit (EXIT_FAILURE); | ||||||
|  |     } | ||||||
|  |     mode |= stereo ? FSOUND_STEREO : FSOUND_MONO; | ||||||
|  |     return mode; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void fmod_hw_fini (HWVoice *hw) | ||||||
|  | { | ||||||
|  |     FMODVoice *fmd = (FMODVoice *) hw; | ||||||
|  |  | ||||||
|  |     if (fmd->fmod_sample) { | ||||||
|  |         FSOUND_Sample_Free (fmd->fmod_sample); | ||||||
|  |         fmd->fmod_sample = 0; | ||||||
|  |  | ||||||
|  |         if (fmd->channel >= 0) { | ||||||
|  |             FSOUND_StopSound (fmd->channel); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int fmod_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt) | ||||||
|  | { | ||||||
|  |     int bits16, mode, channel; | ||||||
|  |     FMODVoice *fmd = (FMODVoice *) hw; | ||||||
|  |  | ||||||
|  |     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) { | ||||||
|  |         dolog ("Failed to allocate FMOD sample\nReason: %s\n", errstr ()); | ||||||
|  |         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 ()); | ||||||
|  |         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; | ||||||
|  |     hw->bufsize = conf.nb_samples << (nchannels == 2) << bits16; | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int fmod_hw_ctl (HWVoice *hw, int cmd, ...) | ||||||
|  | { | ||||||
|  |     int status; | ||||||
|  |     FMODVoice *fmd = (FMODVoice *) 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 ()); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     case VOICE_DISABLE: | ||||||
|  |         status = FSOUND_SetPaused (fmd->channel, 1); | ||||||
|  |         if (!status) { | ||||||
|  |             dolog ("Failed to pause channel %d\nReason: %s\n", | ||||||
|  |                    fmd->channel, errstr ()); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static struct { | ||||||
|  |     const char *name; | ||||||
|  |     int type; | ||||||
|  | } drvtab[] = { | ||||||
|  |     {"none", FSOUND_OUTPUT_NOSOUND}, | ||||||
|  | #ifdef _WIN32 | ||||||
|  |     {"winmm", FSOUND_OUTPUT_WINMM}, | ||||||
|  |     {"dsound", FSOUND_OUTPUT_DSOUND}, | ||||||
|  |     {"a3d", FSOUND_OUTPUT_A3D}, | ||||||
|  |     {"asio", FSOUND_OUTPUT_ASIO}, | ||||||
|  | #endif | ||||||
|  | #ifdef __linux__ | ||||||
|  |     {"oss", FSOUND_OUTPUT_OSS}, | ||||||
|  |     {"alsa", FSOUND_OUTPUT_ALSA}, | ||||||
|  |     {"esd", FSOUND_OUTPUT_ESD}, | ||||||
|  | #endif | ||||||
|  | #ifdef __APPLE__ | ||||||
|  |     {"mac", FSOUND_OUTPUT_MAC}, | ||||||
|  | #endif | ||||||
|  | #if 0 | ||||||
|  |     {"xbox", FSOUND_OUTPUT_XBOX}, | ||||||
|  |     {"ps2", FSOUND_OUTPUT_PS2}, | ||||||
|  |     {"gcube", FSOUND_OUTPUT_GC}, | ||||||
|  | #endif | ||||||
|  |     {"nort", FSOUND_OUTPUT_NOSOUND_NONREALTIME} | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static void *fmod_audio_init (void) | ||||||
|  | { | ||||||
|  |     int i; | ||||||
|  |     double ver; | ||||||
|  |     int status; | ||||||
|  |     int output_type = -1; | ||||||
|  |     const char *drv = audio_get_conf_str (QC_FMOD_DRV, NULL); | ||||||
|  |  | ||||||
|  |     ver = FSOUND_GetVersion (); | ||||||
|  |     if (ver < FMOD_VERSION) { | ||||||
|  |         dolog ("Wrong FMOD version %f, need at least %f\n", ver, FMOD_VERSION); | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (drv) { | ||||||
|  |         int found = 0; | ||||||
|  |         for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) { | ||||||
|  |             if (!strcmp (drv, drvtab[i].name)) { | ||||||
|  |                 output_type = drvtab[i].type; | ||||||
|  |                 found = 1; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if (!found) { | ||||||
|  |             dolog ("Unknown FMOD output driver `%s'\n", drv); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (output_type != -1) { | ||||||
|  |         status = FSOUND_SetOutput (output_type); | ||||||
|  |         if (!status) { | ||||||
|  |             dolog ("FSOUND_SetOutput(%d) failed\nReason: %s\n", | ||||||
|  |                    output_type, errstr ()); | ||||||
|  |             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 ()); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     status = FSOUND_Init (conf.freq, conf.nb_channels, 0); | ||||||
|  |     if (!status) { | ||||||
|  |         dolog ("FSOUND_Init failed\nReason: %s\n", errstr ()); | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return &conf; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void fmod_audio_fini (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 | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct audio_output_driver fmod_output_driver = { | ||||||
|  |     "fmod", | ||||||
|  |     fmod_audio_init, | ||||||
|  |     fmod_audio_fini, | ||||||
|  |     &fmod_pcm_ops, | ||||||
|  |     1, | ||||||
|  |     INT_MAX, | ||||||
|  |     sizeof (FMODVoice) | ||||||
|  | }; | ||||||
							
								
								
									
										39
									
								
								audio/fmodaudio.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								audio/fmodaudio.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | |||||||
|  | /* | ||||||
|  |  * QEMU FMOD audio output driver header | ||||||
|  |  *  | ||||||
|  |  * Copyright (c) 2004 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. | ||||||
|  |  */ | ||||||
|  | #ifndef QEMU_FMODAUDIO_H | ||||||
|  | #define QEMU_FMODAUDIO_H | ||||||
|  |  | ||||||
|  | #include <fmod.h> | ||||||
|  |  | ||||||
|  | typedef struct FMODVoice { | ||||||
|  |     struct HWVoice hw; | ||||||
|  |     unsigned int old_pos; | ||||||
|  |     FSOUND_SAMPLE *fmod_sample; | ||||||
|  |     int channel; | ||||||
|  | } FMODVoice; | ||||||
|  |  | ||||||
|  | extern struct pcm_ops fmod_pcm_ops; | ||||||
|  | extern struct audio_output_driver fmod_output_driver; | ||||||
|  |  | ||||||
|  | #endif  /* fmodaudio.h */ | ||||||
							
								
								
									
										255
									
								
								audio/mixeng.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										255
									
								
								audio/mixeng.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,255 @@ | |||||||
|  | /* | ||||||
|  |  * QEMU Mixing engine | ||||||
|  |  * | ||||||
|  |  * Copyright (c) 2004 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. | ||||||
|  |  */ | ||||||
|  | #include "vl.h" | ||||||
|  | //#define DEBUG_FP | ||||||
|  | #include "audio/mixeng.h" | ||||||
|  |  | ||||||
|  | #define IN_T int8_t | ||||||
|  | #define IN_MIN CHAR_MIN | ||||||
|  | #define IN_MAX CHAR_MAX | ||||||
|  | #define SIGNED | ||||||
|  | #include "mixeng_template.h" | ||||||
|  | #undef SIGNED | ||||||
|  | #undef IN_MAX | ||||||
|  | #undef IN_MIN | ||||||
|  | #undef IN_T | ||||||
|  |  | ||||||
|  | #define IN_T uint8_t | ||||||
|  | #define IN_MIN 0 | ||||||
|  | #define IN_MAX UCHAR_MAX | ||||||
|  | #include "mixeng_template.h" | ||||||
|  | #undef IN_MAX | ||||||
|  | #undef IN_MIN | ||||||
|  | #undef IN_T | ||||||
|  |  | ||||||
|  | #define IN_T int16_t | ||||||
|  | #define IN_MIN SHRT_MIN | ||||||
|  | #define IN_MAX SHRT_MAX | ||||||
|  | #define SIGNED | ||||||
|  | #include "mixeng_template.h" | ||||||
|  | #undef SIGNED | ||||||
|  | #undef IN_MAX | ||||||
|  | #undef IN_MIN | ||||||
|  | #undef IN_T | ||||||
|  |  | ||||||
|  | #define IN_T uint16_t | ||||||
|  | #define IN_MIN 0 | ||||||
|  | #define IN_MAX USHRT_MAX | ||||||
|  | #include "mixeng_template.h" | ||||||
|  | #undef IN_MAX | ||||||
|  | #undef IN_MIN | ||||||
|  | #undef IN_T | ||||||
|  |  | ||||||
|  | t_sample *mixeng_conv[2][2][2] = { | ||||||
|  |     { | ||||||
|  |         { | ||||||
|  |             conv_uint8_t_to_mono, | ||||||
|  |             conv_uint16_t_to_mono | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             conv_int8_t_to_mono, | ||||||
|  |             conv_int16_t_to_mono | ||||||
|  |         } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         { | ||||||
|  |             conv_uint8_t_to_stereo, | ||||||
|  |             conv_uint16_t_to_stereo | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             conv_int8_t_to_stereo, | ||||||
|  |             conv_int16_t_to_stereo | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | f_sample *mixeng_clip[2][2][2] = { | ||||||
|  |     { | ||||||
|  |         { | ||||||
|  |             clip_uint8_t_from_mono, | ||||||
|  |             clip_uint16_t_from_mono | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             clip_int8_t_from_mono, | ||||||
|  |             clip_int16_t_from_mono | ||||||
|  |         } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         { | ||||||
|  |             clip_uint8_t_from_stereo, | ||||||
|  |             clip_uint16_t_from_stereo | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             clip_int8_t_from_stereo, | ||||||
|  |             clip_int16_t_from_stereo | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * August 21, 1998 | ||||||
|  |  * Copyright 1998 Fabrice Bellard. | ||||||
|  |  * | ||||||
|  |  * [Rewrote completly the code of Lance Norskog And Sundry | ||||||
|  |  * Contributors with a more efficient algorithm.] | ||||||
|  |  * | ||||||
|  |  * This source code is freely redistributable and may be used for | ||||||
|  |  * any purpose.  This copyright notice must be maintained.  | ||||||
|  |  * Lance Norskog And Sundry Contributors are not responsible for  | ||||||
|  |  * the consequences of using this software.   | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Sound Tools rate change effect file. | ||||||
|  |  */ | ||||||
|  | /* | ||||||
|  |  * Linear Interpolation. | ||||||
|  |  * | ||||||
|  |  * The use of fractional increment allows us to use no buffer. It | ||||||
|  |  * avoid the problems at the end of the buffer we had with the old | ||||||
|  |  * method which stored a possibly big buffer of size | ||||||
|  |  * lcm(in_rate,out_rate). | ||||||
|  |  * | ||||||
|  |  * Limited to 16 bit samples and sampling frequency <= 65535 Hz. If | ||||||
|  |  * the input & output frequencies are equal, a delay of one sample is | ||||||
|  |  * introduced.  Limited to processing 32-bit count worth of samples. | ||||||
|  |  * | ||||||
|  |  * 1 << FRAC_BITS evaluating to zero in several places.  Changed with | ||||||
|  |  * an (unsigned long) cast to make it safe.  MarkMLl 2/1/99 | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | /* Private data */ | ||||||
|  | typedef struct ratestuff { | ||||||
|  |     uint64_t opos; | ||||||
|  |     uint64_t opos_inc; | ||||||
|  |     uint32_t ipos;              /* position in the input stream (integer) */ | ||||||
|  |     st_sample_t ilast;          /* last sample in the input stream */ | ||||||
|  | } *rate_t; | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Prepare processing. | ||||||
|  |  */ | ||||||
|  | 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); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     rate->opos = 0; | ||||||
|  |  | ||||||
|  |     /* increment */ | ||||||
|  |     rate->opos_inc = (inrate * ((int64_t) UINT_MAX)) / outrate; | ||||||
|  |  | ||||||
|  |     rate->ipos = 0; | ||||||
|  |     rate->ilast.l = 0; | ||||||
|  |     rate->ilast.r = 0; | ||||||
|  |     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; | ||||||
|  |  | ||||||
|  |     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; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void st_rate_stop (void *opaque) | ||||||
|  | { | ||||||
|  |     qemu_free (opaque); | ||||||
|  | } | ||||||
							
								
								
									
										39
									
								
								audio/mixeng.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								audio/mixeng.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | |||||||
|  | /* | ||||||
|  |  * QEMU Mixing engine header | ||||||
|  |  *  | ||||||
|  |  * Copyright (c) 2004 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. | ||||||
|  |  */ | ||||||
|  | #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); | ||||||
|  | typedef struct { int64_t l; int64_t r; } st_sample_t; | ||||||
|  |  | ||||||
|  | extern t_sample *mixeng_conv[2][2][2]; | ||||||
|  | extern f_sample *mixeng_clip[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_stop (void *opaque); | ||||||
|  |  | ||||||
|  | #endif  /* mixeng.h */ | ||||||
							
								
								
									
										111
									
								
								audio/mixeng_template.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								audio/mixeng_template.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,111 @@ | |||||||
|  | /* | ||||||
|  |  * QEMU Mixing engine | ||||||
|  |  *  | ||||||
|  |  * Copyright (c) 2004 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. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Tusen tack till Mike Nordell | ||||||
|  |  * dec++'ified by Dscho | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #ifdef SIGNED | ||||||
|  | #define HALFT IN_MAX | ||||||
|  | #define HALF IN_MAX | ||||||
|  | #else | ||||||
|  | #define HALFT ((IN_MAX)>>1) | ||||||
|  | #define HALF HALFT | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | static int64_t inline glue(conv_,IN_T) (IN_T v) | ||||||
|  | { | ||||||
|  | #ifdef SIGNED | ||||||
|  |     return (INT_MAX*(int64_t)v)/HALF; | ||||||
|  | #else | ||||||
|  |     return (INT_MAX*((int64_t)v-HALFT))/HALF; | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static IN_T inline glue(clip_,IN_T) (int64_t v) | ||||||
|  | { | ||||||
|  |     if (v >= INT_MAX) | ||||||
|  |         return IN_MAX; | ||||||
|  |     else if (v < -INT_MAX) | ||||||
|  |         return IN_MIN; | ||||||
|  |  | ||||||
|  | #ifdef SIGNED | ||||||
|  |     return (IN_T) (v*HALF/INT_MAX); | ||||||
|  | #else | ||||||
|  |     return (IN_T) (v+INT_MAX/2)*HALF/INT_MAX; | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void glue(glue(conv_,IN_T),_to_stereo) (void *dst, const void *src, | ||||||
|  |                                                int samples) | ||||||
|  | { | ||||||
|  |     st_sample_t *out = (st_sample_t *) dst; | ||||||
|  |     IN_T *in = (IN_T *) src; | ||||||
|  |     while (samples--) { | ||||||
|  |         out->l = glue(conv_,IN_T) (*in++); | ||||||
|  |         out->r = glue(conv_,IN_T) (*in++); | ||||||
|  |         out += 1; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void glue(glue(conv_,IN_T),_to_mono) (void *dst, const void *src, | ||||||
|  |                                              int samples) | ||||||
|  | { | ||||||
|  |     st_sample_t *out = (st_sample_t *) dst; | ||||||
|  |     IN_T *in = (IN_T *) src; | ||||||
|  |     while (samples--) { | ||||||
|  |         out->l = glue(conv_,IN_T) (in[0]); | ||||||
|  |         out->r = out->l; | ||||||
|  |         out += 1; | ||||||
|  |         in += 1; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void glue(glue(clip_,IN_T),_from_stereo) (void *dst, const void *src, | ||||||
|  |                                                  int samples) | ||||||
|  | { | ||||||
|  |     st_sample_t *in = (st_sample_t *) src; | ||||||
|  |     IN_T *out = (IN_T *) dst; | ||||||
|  |     while (samples--) { | ||||||
|  |         *out++ = glue(clip_,IN_T) (in->l); | ||||||
|  |         *out++ = glue(clip_,IN_T) (in->r); | ||||||
|  |         in += 1; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void glue(glue(clip_,IN_T),_from_mono) (void *dst, const void *src, | ||||||
|  |                                                int samples) | ||||||
|  | { | ||||||
|  |     st_sample_t *in = (st_sample_t *) src; | ||||||
|  |     IN_T *out = (IN_T *) dst; | ||||||
|  |     while (samples--) { | ||||||
|  |         *out++ = glue(clip_,IN_T) (in->l + in->r); | ||||||
|  |         in += 1; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #undef HALF | ||||||
|  | #undef HALFT | ||||||
|  |  | ||||||
							
								
								
									
										466
									
								
								audio/ossaudio.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										466
									
								
								audio/ossaudio.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,466 @@ | |||||||
|  | /* | ||||||
|  |  * QEMU OSS audio output driver | ||||||
|  |  *  | ||||||
|  |  * Copyright (c) 2003-2004 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. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | /* Temporary kludge */ | ||||||
|  | #if defined __linux__ || (defined _BSD && !defined __APPLE__) | ||||||
|  | #include <assert.h> | ||||||
|  | #include "vl.h" | ||||||
|  |  | ||||||
|  | #include <sys/mman.h> | ||||||
|  | #include <sys/types.h> | ||||||
|  | #include <sys/ioctl.h> | ||||||
|  | #include <sys/soundcard.h> | ||||||
|  |  | ||||||
|  | #define AUDIO_CAP "oss" | ||||||
|  | #include "audio/audio.h" | ||||||
|  | #include "audio/ossaudio.h" | ||||||
|  |  | ||||||
|  | #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) | ||||||
|  |  | ||||||
|  | static struct { | ||||||
|  |     int try_mmap; | ||||||
|  |     int nfrags; | ||||||
|  |     int fragsize; | ||||||
|  |     const char *dspname; | ||||||
|  | } conf = { | ||||||
|  |     .try_mmap = 0, | ||||||
|  |     .nfrags = 4, | ||||||
|  |     .fragsize = 4096, | ||||||
|  |     .dspname = "/dev/dsp" | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct oss_params { | ||||||
|  |     int freq; | ||||||
|  |     audfmt_e fmt; | ||||||
|  |     int nchannels; | ||||||
|  |     int nfrags; | ||||||
|  |     int fragsize; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static int oss_hw_write (SWVoice *sw, void *buf, int len) | ||||||
|  | { | ||||||
|  |     return pcm_hw_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; | ||||||
|  |     default: | ||||||
|  |         dolog ("Internal logic error: Bad audio format %d\nAborting\n", fmt); | ||||||
|  |         exit (EXIT_FAILURE); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int oss_to_audfmt (int fmt) | ||||||
|  | { | ||||||
|  |     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; | ||||||
|  |     default: | ||||||
|  |         dolog ("Internal logic error: Unrecognized OSS audio format %d\n" | ||||||
|  |                "Aborting\n", | ||||||
|  |                fmt); | ||||||
|  |         exit (EXIT_FAILURE); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #ifdef DEBUG_PCM | ||||||
|  | static void oss_dump_pcm_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 ("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); | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | static int oss_open (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; | ||||||
|  |  | ||||||
|  |     fd = open (dspname, O_RDWR | O_NONBLOCK); | ||||||
|  |     if (-1 == fd) { | ||||||
|  |         dolog ("Could not initialize audio hardware. Failed to open `%s':\n" | ||||||
|  |                "Reason:%s\n", | ||||||
|  |                dspname, | ||||||
|  |                errstr ()); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     freq = req->freq; | ||||||
|  |     nchannels = req->nchannels; | ||||||
|  |     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 ()); | ||||||
|  |         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 ()); | ||||||
|  |         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 ()); | ||||||
|  |         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 ()); | ||||||
|  |         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 ()); | ||||||
|  |         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 ()); | ||||||
|  |         goto err; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     obt->fmt = fmt; | ||||||
|  |     obt->nchannels = nchannels; | ||||||
|  |     obt->freq = freq; | ||||||
|  |     obt->nfrags = abinfo.fragstotal; | ||||||
|  |     obt->fragsize = abinfo.fragsize; | ||||||
|  |     *pfd = fd; | ||||||
|  |  | ||||||
|  |     if ((req->fmt != obt->fmt) || | ||||||
|  |         (req->nchannels != obt->nchannels) || | ||||||
|  |         (req->freq != obt->freq) || | ||||||
|  |         (req->fragsize != obt->fragsize) || | ||||||
|  |         (req->nfrags != obt->nfrags)) { | ||||||
|  | #ifdef DEBUG_PCM | ||||||
|  |         dolog ("Audio parameters mismatch\n"); | ||||||
|  |         oss_dump_pcm_info (req, obt); | ||||||
|  | #endif | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | #ifdef DEBUG_PCM | ||||||
|  |     oss_dump_pcm_info (req, obt); | ||||||
|  | #endif | ||||||
|  |     return 0; | ||||||
|  |  | ||||||
|  | err: | ||||||
|  |     close (fd); | ||||||
|  |     return -1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void oss_hw_run (HWVoice *hw) | ||||||
|  | { | ||||||
|  |     OSSVoice *oss = (OSSVoice *) hw; | ||||||
|  |     int err, rpos, live, decr; | ||||||
|  |     int samples; | ||||||
|  |     uint8_t *dst; | ||||||
|  |     st_sample_t *src; | ||||||
|  |     struct audio_buf_info abinfo; | ||||||
|  |     struct count_info cntinfo; | ||||||
|  |  | ||||||
|  |     live = pcm_hw_get_live (hw); | ||||||
|  |     if (live <= 0) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|  |     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; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (cntinfo.ptr == oss->old_optr) { | ||||||
|  |             if (abs (hw->samples - live) < 64) | ||||||
|  |                 dolog ("overrun\n"); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (cntinfo.ptr > oss->old_optr) { | ||||||
|  |             bytes = cntinfo.ptr - oss->old_optr; | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |             bytes = hw->bufsize + cntinfo.ptr - oss->old_optr; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         decr = audio_MIN (bytes >> hw->shift, live); | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |         err = ioctl (oss->fd, SNDCTL_DSP_GETOSPACE, &abinfo); | ||||||
|  |         if (err < 0) { | ||||||
|  |             dolog ("SNDCTL_DSP_GETOSPACE failed\nReason: %s\n", errstr ()); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         decr = audio_MIN (abinfo.bytes >> hw->shift, live); | ||||||
|  |         if (decr <= 0) | ||||||
|  |             return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     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)); | ||||||
|  |         dst = advance (oss->pcm_buf, rpos << hw->shift); | ||||||
|  |  | ||||||
|  |         hw->clip (dst, src, convert_samples); | ||||||
|  |         if (!oss->mmapped) { | ||||||
|  |             int written; | ||||||
|  |  | ||||||
|  |             written = write (oss->fd, dst, convert_samples << hw->shift); | ||||||
|  |             /* XXX: follow errno recommendations ? */ | ||||||
|  |             if (written == -1) { | ||||||
|  |                 dolog ("Failed to write audio\nReason: %s\n", errstr ()); | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (written != convert_samples << hw->shift) { | ||||||
|  |                 int wsamples = written >> hw->shift; | ||||||
|  |                 int wbytes = wsamples << hw->shift; | ||||||
|  |                 if (wbytes != written) { | ||||||
|  |                     dolog ("Unaligned write %d, %d\n", wbytes, written); | ||||||
|  |                 } | ||||||
|  |                 memset (src, 0, wbytes); | ||||||
|  |                 decr -= samples; | ||||||
|  |                 rpos = (rpos + wsamples) % hw->samples; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         memset (src, 0, convert_samples * sizeof (st_sample_t)); | ||||||
|  |  | ||||||
|  |         rpos = (rpos + convert_samples) % hw->samples; | ||||||
|  |         samples -= convert_samples; | ||||||
|  |     } | ||||||
|  |     if (oss->mmapped) { | ||||||
|  |         oss->old_optr = cntinfo.ptr; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pcm_hw_dec_live (hw, decr); | ||||||
|  |     hw->rpos = rpos; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void oss_hw_fini (HWVoice *hw) | ||||||
|  | { | ||||||
|  |     int err; | ||||||
|  |     OSSVoice *oss = (OSSVoice *) 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; | ||||||
|  |  | ||||||
|  |     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 ()); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |             qemu_free (oss->pcm_buf); | ||||||
|  |         } | ||||||
|  |         oss->pcm_buf = NULL; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int oss_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt) | ||||||
|  | { | ||||||
|  |     OSSVoice *oss = (OSSVoice *) hw; | ||||||
|  |     struct oss_params req, obt; | ||||||
|  |  | ||||||
|  |     assert (!oss->fd); | ||||||
|  |     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)) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|  |     hw->freq = obt.freq; | ||||||
|  |     hw->fmt = oss_to_audfmt (obt.fmt); | ||||||
|  |     hw->nchannels = obt.nchannels; | ||||||
|  |  | ||||||
|  |     oss->nfrags = obt.nfrags; | ||||||
|  |     oss->fragsize = obt.fragsize; | ||||||
|  |     hw->bufsize = obt.nfrags * obt.fragsize; | ||||||
|  |  | ||||||
|  |     oss->mmapped = 0; | ||||||
|  |     if (conf.try_mmap) { | ||||||
|  |         oss->pcm_buf = mmap (0, hw->bufsize, PROT_READ | PROT_WRITE, | ||||||
|  |                              MAP_SHARED, oss->fd, 0); | ||||||
|  |         if (oss->pcm_buf == MAP_FAILED) { | ||||||
|  |             dolog ("Failed to mmap OSS device\nReason: %s\n", | ||||||
|  |                    errstr ()); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         for (;;) { | ||||||
|  |             int err; | ||||||
|  |             int trig = 0; | ||||||
|  |             if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { | ||||||
|  |                 dolog ("SNDCTL_DSP_SETTRIGGER 0 failed\nReason: %s\n", | ||||||
|  |                        errstr ()); | ||||||
|  |                 goto fail; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             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 ()); | ||||||
|  |                 goto fail; | ||||||
|  |             } | ||||||
|  |             oss->mmapped = 1; | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         fail: | ||||||
|  |             err = munmap (oss->pcm_buf, hw->bufsize); | ||||||
|  |             if (err) { | ||||||
|  |                 dolog ("Failed to unmap OSS device\nReason: %s\n", | ||||||
|  |                        errstr ()); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!oss->mmapped) { | ||||||
|  |         oss->pcm_buf = qemu_mallocz (hw->bufsize); | ||||||
|  |         if (!oss->pcm_buf) { | ||||||
|  |             close (oss->fd); | ||||||
|  |             oss->fd = -1; | ||||||
|  |             return -1; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int oss_hw_ctl (HWVoice *hw, int cmd, ...) | ||||||
|  | { | ||||||
|  |     int trig; | ||||||
|  |     OSSVoice *oss = (OSSVoice *) hw; | ||||||
|  |  | ||||||
|  |     if (!oss->mmapped) | ||||||
|  |         return 0; | ||||||
|  |  | ||||||
|  |     switch (cmd) { | ||||||
|  |     case VOICE_ENABLE: | ||||||
|  |         ldebug ("enabling voice\n"); | ||||||
|  |         pcm_hw_clear (hw, 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 ()); | ||||||
|  |             return -1; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     case VOICE_DISABLE: | ||||||
|  |         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 ()); | ||||||
|  |             return -1; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     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) | ||||||
|  | { | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct pcm_ops oss_pcm_ops = { | ||||||
|  |     oss_hw_init, | ||||||
|  |     oss_hw_fini, | ||||||
|  |     oss_hw_run, | ||||||
|  |     oss_hw_write, | ||||||
|  |     oss_hw_ctl | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct audio_output_driver oss_output_driver = { | ||||||
|  |     "oss", | ||||||
|  |     oss_audio_init, | ||||||
|  |     oss_audio_fini, | ||||||
|  |     &oss_pcm_ops, | ||||||
|  |     1, | ||||||
|  |     INT_MAX, | ||||||
|  |     sizeof (OSSVoice) | ||||||
|  | }; | ||||||
|  | #endif | ||||||
							
								
								
									
										40
									
								
								audio/ossaudio.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								audio/ossaudio.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | |||||||
|  | /* | ||||||
|  |  * QEMU OSS audio output driver header | ||||||
|  |  *  | ||||||
|  |  * Copyright (c) 2003-2004 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. | ||||||
|  |  */ | ||||||
|  | #ifndef QEMU_OSSAUDIO_H | ||||||
|  | #define QEMU_OSSAUDIO_H | ||||||
|  |  | ||||||
|  | typedef struct OSSVoice { | ||||||
|  |     struct HWVoice hw; | ||||||
|  |     void *pcm_buf; | ||||||
|  |     int fd; | ||||||
|  |     int nfrags; | ||||||
|  |     int fragsize; | ||||||
|  |     int mmapped; | ||||||
|  |     int old_optr; | ||||||
|  | } OSSVoice; | ||||||
|  |  | ||||||
|  | extern struct pcm_ops oss_pcm_ops; | ||||||
|  | extern struct audio_output_driver oss_output_driver; | ||||||
|  |  | ||||||
|  | #endif  /* ossaudio.h */ | ||||||
							
								
								
									
										323
									
								
								audio/sdlaudio.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										323
									
								
								audio/sdlaudio.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,323 @@ | |||||||
|  | /* | ||||||
|  |  * QEMU SDL audio output driver | ||||||
|  |  *  | ||||||
|  |  * Copyright (c) 2004 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 <SDL/SDL.h> | ||||||
|  | #include <SDL/SDL_thread.h> | ||||||
|  | #include "vl.h" | ||||||
|  |  | ||||||
|  | #define AUDIO_CAP "sdl" | ||||||
|  | #include "audio/audio.h" | ||||||
|  | #include "audio/sdlaudio.h" | ||||||
|  |  | ||||||
|  | #define QC_SDL_SAMPLES "QEMU_SDL_SAMPLES" | ||||||
|  |  | ||||||
|  | #define errstr() SDL_GetError () | ||||||
|  |  | ||||||
|  | static struct { | ||||||
|  |     int nb_samples; | ||||||
|  | } conf = { | ||||||
|  |     1024 | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct SDLAudioState { | ||||||
|  |     int exit; | ||||||
|  |     SDL_mutex *mutex; | ||||||
|  |     SDL_sem *sem; | ||||||
|  |     int initialized; | ||||||
|  | } glob_sdl; | ||||||
|  | typedef struct SDLAudioState SDLAudioState; | ||||||
|  |  | ||||||
|  | static void sdl_hw_run (HWVoice *hw) | ||||||
|  | { | ||||||
|  |     (void) hw; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int sdl_lock (SDLAudioState *s) | ||||||
|  | { | ||||||
|  |     if (SDL_LockMutex (s->mutex)) { | ||||||
|  |         dolog ("SDL_LockMutex failed\nReason: %s\n", errstr ()); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int sdl_unlock (SDLAudioState *s) | ||||||
|  | { | ||||||
|  |     if (SDL_UnlockMutex (s->mutex)) { | ||||||
|  |         dolog ("SDL_UnlockMutex failed\nReason: %s\n", errstr ()); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int sdl_post (SDLAudioState *s) | ||||||
|  | { | ||||||
|  |     if (SDL_SemPost (s->sem)) { | ||||||
|  |         dolog ("SDL_SemPost failed\nReason: %s\n", errstr ()); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int sdl_wait (SDLAudioState *s) | ||||||
|  | { | ||||||
|  |     if (SDL_SemWait (s->sem)) { | ||||||
|  |         dolog ("SDL_SemWait failed\nReason: %s\n", errstr ()); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int sdl_unlock_and_post (SDLAudioState *s) | ||||||
|  | { | ||||||
|  |     if (sdl_unlock (s)) | ||||||
|  |         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; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int AUD_to_sdlfmt (audfmt_e fmt, int *shift) | ||||||
|  | { | ||||||
|  |     *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; | ||||||
|  |     default: | ||||||
|  |         dolog ("Internal logic error: Bad audio format %d\nAborting\n", fmt); | ||||||
|  |         exit (EXIT_FAILURE); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int sdl_to_audfmt (int fmt) | ||||||
|  | { | ||||||
|  |     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; | ||||||
|  |     default: | ||||||
|  |         dolog ("Internal logic error: Unrecognized SDL audio format %d\n" | ||||||
|  |                "Aborting\n", fmt); | ||||||
|  |         exit (EXIT_FAILURE); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt) | ||||||
|  | { | ||||||
|  |     int status; | ||||||
|  |  | ||||||
|  |     status = SDL_OpenAudio (req, obt); | ||||||
|  |     if (status) { | ||||||
|  |         dolog ("SDL_OpenAudio failed\nReason: %s\n", errstr ()); | ||||||
|  |     } | ||||||
|  |     return status; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void sdl_close (SDLAudioState *s) | ||||||
|  | { | ||||||
|  |     if (s->initialized) { | ||||||
|  |         sdl_lock (s); | ||||||
|  |         s->exit = 1; | ||||||
|  |         sdl_unlock_and_post (s); | ||||||
|  |         SDL_PauseAudio (1); | ||||||
|  |         SDL_CloseAudio (); | ||||||
|  |         s->initialized = 0; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void sdl_callback (void *opaque, Uint8 *buf, int len) | ||||||
|  | { | ||||||
|  |     SDLVoice *sdl = opaque; | ||||||
|  |     SDLAudioState *s = &glob_sdl; | ||||||
|  |     HWVoice *hw = &sdl->hw; | ||||||
|  |     int samples = len >> hw->shift; | ||||||
|  |  | ||||||
|  |     if (s->exit) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     while (samples) { | ||||||
|  |         int to_mix, live, decr; | ||||||
|  |  | ||||||
|  |         /* dolog ("in callback samples=%d\n", samples); */ | ||||||
|  |         sdl_wait (s); | ||||||
|  |         if (s->exit) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         sdl_lock (s); | ||||||
|  |         live = pcm_hw_get_live (hw); | ||||||
|  |         if (live <= 0) | ||||||
|  |             goto again; | ||||||
|  |  | ||||||
|  |         /* dolog ("in callback live=%d\n", live); */ | ||||||
|  |         to_mix = audio_MIN (samples, live); | ||||||
|  |         decr = to_mix; | ||||||
|  |         while (to_mix) { | ||||||
|  |             int chunk = audio_MIN (to_mix, hw->samples - hw->rpos); | ||||||
|  |             st_sample_t *src = hw->mix_buf + hw->rpos; | ||||||
|  |  | ||||||
|  |             /* 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; | ||||||
|  |             to_mix -= chunk; | ||||||
|  |             buf += chunk << hw->shift; | ||||||
|  |         } | ||||||
|  |         samples -= decr; | ||||||
|  |         pcm_hw_dec_live (hw, decr); | ||||||
|  |  | ||||||
|  |     again: | ||||||
|  |         sdl_unlock (s); | ||||||
|  |     } | ||||||
|  |     /* dolog ("done len=%d\n", len); */ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void sdl_hw_fini (HWVoice *hw) | ||||||
|  | { | ||||||
|  |     ldebug ("sdl_hw_fini %d fixed=%d\n", | ||||||
|  |              glob_sdl.initialized, audio_conf.fixed_format); | ||||||
|  |     sdl_close (&glob_sdl); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int sdl_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt) | ||||||
|  | { | ||||||
|  |     SDLVoice *sdl = (SDLVoice *) 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_conf.fixed_format); | ||||||
|  |  | ||||||
|  |     if (nchannels != 2) { | ||||||
|  |         dolog ("Bogus channel count %d\n", nchannels); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     req.freq = freq; | ||||||
|  |     req.format = AUD_to_sdlfmt (fmt, &shift); | ||||||
|  |     req.channels = nchannels; | ||||||
|  |     req.samples = conf.nb_samples; | ||||||
|  |     shift <<= nchannels == 2; | ||||||
|  |  | ||||||
|  |     req.callback = sdl_callback; | ||||||
|  |     req.userdata = sdl; | ||||||
|  |  | ||||||
|  |     if (sdl_open (&req, &obt)) | ||||||
|  |         return -1; | ||||||
|  |  | ||||||
|  |     hw->freq = obt.freq; | ||||||
|  |     hw->fmt = sdl_to_audfmt (obt.format); | ||||||
|  |     hw->nchannels = obt.channels; | ||||||
|  |     hw->bufsize = obt.samples << shift; | ||||||
|  |  | ||||||
|  |     s->initialized = 1; | ||||||
|  |     s->exit = 0; | ||||||
|  |     SDL_PauseAudio (0); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int sdl_hw_ctl (HWVoice *hw, int cmd, ...) | ||||||
|  | { | ||||||
|  |     (void) hw; | ||||||
|  |  | ||||||
|  |     switch (cmd) { | ||||||
|  |     case VOICE_ENABLE: | ||||||
|  |         SDL_PauseAudio (0); | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     case VOICE_DISABLE: | ||||||
|  |         SDL_PauseAudio (1); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | 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 ()); | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     s->mutex = SDL_CreateMutex (); | ||||||
|  |     if (!s->mutex) { | ||||||
|  |         dolog ("Failed to create SDL mutex\nReason: %s\n", errstr ()); | ||||||
|  |         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_DestroyMutex (s->mutex); | ||||||
|  |         SDL_QuitSubSystem (SDL_INIT_AUDIO); | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return s; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void sdl_audio_fini (void *opaque) | ||||||
|  | { | ||||||
|  |     SDLAudioState *s = opaque; | ||||||
|  |     sdl_close (s); | ||||||
|  |     SDL_DestroySemaphore (s->sem); | ||||||
|  |     SDL_DestroyMutex (s->mutex); | ||||||
|  |     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 | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct audio_output_driver sdl_output_driver = { | ||||||
|  |     "sdl", | ||||||
|  |     sdl_audio_init, | ||||||
|  |     sdl_audio_fini, | ||||||
|  |     &sdl_pcm_ops, | ||||||
|  |     1, | ||||||
|  |     1, | ||||||
|  |     sizeof (SDLVoice) | ||||||
|  | }; | ||||||
							
								
								
									
										34
									
								
								audio/sdlaudio.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								audio/sdlaudio.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | |||||||
|  | /* | ||||||
|  |  * QEMU SDL audio output driver header | ||||||
|  |  *  | ||||||
|  |  * Copyright (c) 2004 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. | ||||||
|  |  */ | ||||||
|  | #ifndef QEMU_SDLAUDIO_H | ||||||
|  | #define QEMU_SDLAUDIO_H | ||||||
|  |  | ||||||
|  | typedef struct SDLVoice { | ||||||
|  |     struct HWVoice hw; | ||||||
|  | } SDLVoice; | ||||||
|  |  | ||||||
|  | extern struct pcm_ops sdl_pcm_ops; | ||||||
|  | extern struct audio_output_driver sdl_output_driver; | ||||||
|  |  | ||||||
|  | #endif  /* sdlaudio.h */ | ||||||
							
								
								
									
										200
									
								
								audio/wavaudio.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										200
									
								
								audio/wavaudio.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,200 @@ | |||||||
|  | /* | ||||||
|  |  * QEMU WAV audio output driver | ||||||
|  |  *  | ||||||
|  |  * Copyright (c) 2004 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 "vl.h" | ||||||
|  |  | ||||||
|  | #define AUDIO_CAP "wav" | ||||||
|  | #include "audio/audio.h" | ||||||
|  | #include "audio/wavaudio.h" | ||||||
|  |  | ||||||
|  | static struct { | ||||||
|  |     const char *wav_path; | ||||||
|  | } conf = { | ||||||
|  |     .wav_path = "qemu.wav" | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static void wav_hw_run (HWVoice *hw) | ||||||
|  | { | ||||||
|  |     WAVVoice *wav = (WAVVoice *) 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; | ||||||
|  |     wav->old_ticks = now; | ||||||
|  |  | ||||||
|  |     if (bytes > INT_MAX) | ||||||
|  |         samples = INT_MAX >> hw->shift; | ||||||
|  |     else | ||||||
|  |         samples = bytes >> hw->shift; | ||||||
|  |  | ||||||
|  |     live = pcm_hw_get_live (hw); | ||||||
|  |     if (live <= 0) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|  |     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)); | ||||||
|  |         dst = advance (wav->pcm_buf, rpos << hw->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)); | ||||||
|  |  | ||||||
|  |         rpos = (rpos + convert_samples) % hw->samples; | ||||||
|  |         samples -= convert_samples; | ||||||
|  |         wav->total_samples += convert_samples; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pcm_hw_dec_live (hw, decr); | ||||||
|  |     hw->rpos = rpos; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int wav_hw_write (SWVoice *sw, void *buf, int len) | ||||||
|  | { | ||||||
|  |     return pcm_hw_write (sw, buf, len); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* VICE code: Store number as little endian. */ | ||||||
|  | static void le_store (uint8_t *buf, uint32_t val, int len) | ||||||
|  | { | ||||||
|  |     int i; | ||||||
|  |     for (i = 0; i < len; i++) { | ||||||
|  |         buf[i] = (uint8_t) (val & 0xff); | ||||||
|  |         val >>= 8; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int wav_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt) | ||||||
|  | { | ||||||
|  |     WAVVoice *wav = (WAVVoice *) hw; | ||||||
|  |     int bits16 = 0, stereo = audio_state.fixed_channels == 2; | ||||||
|  |     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, | ||||||
|  |         0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04, | ||||||
|  |         0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00 | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     switch (audio_state.fixed_fmt) { | ||||||
|  |     case AUD_FMT_S8: | ||||||
|  |     case AUD_FMT_U8: | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     case AUD_FMT_S16: | ||||||
|  |     case AUD_FMT_U16: | ||||||
|  |         bits16 = 1; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     hdr[34] = bits16 ? 0x10 : 0x08; | ||||||
|  |     hw->freq = 44100; | ||||||
|  |     hw->nchannels = stereo ? 2 : 1; | ||||||
|  |     hw->fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8; | ||||||
|  |     hw->bufsize = 4096; | ||||||
|  |     wav->pcm_buf = qemu_mallocz (hw->bufsize); | ||||||
|  |     if (!wav->pcm_buf) | ||||||
|  |         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); | ||||||
|  |  | ||||||
|  |     wav->f = fopen (conf.wav_path, "wb"); | ||||||
|  |     if (!wav->f) { | ||||||
|  |         dolog ("failed to open wave file `%s'\nReason: %s\n", | ||||||
|  |                conf.wav_path, strerror (errno)); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     qemu_put_buffer (wav->f, hdr, sizeof (hdr)); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void wav_hw_fini (HWVoice *hw) | ||||||
|  | { | ||||||
|  |     WAVVoice *wav = (WAVVoice *) hw; | ||||||
|  |     int stereo = hw->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) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|  |     le_store (rlen, rifflen, 4); | ||||||
|  |     le_store (dlen, datalen, 4); | ||||||
|  |  | ||||||
|  |     qemu_fseek (wav->f, 4, SEEK_SET); | ||||||
|  |     qemu_put_buffer (wav->f, rlen, 4); | ||||||
|  |  | ||||||
|  |     qemu_fseek (wav->f, 32, SEEK_CUR); | ||||||
|  |     qemu_put_buffer (wav->f, dlen, 4); | ||||||
|  |  | ||||||
|  |     fclose (wav->f); | ||||||
|  |     wav->f = NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int wav_hw_ctl (HWVoice *hw, int cmd, ...) | ||||||
|  | { | ||||||
|  |     (void) hw; | ||||||
|  |     (void) cmd; | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void *wav_audio_init (void) | ||||||
|  | { | ||||||
|  |     return &conf; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void wav_audio_fini (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_output_driver wav_output_driver = { | ||||||
|  |     "wav", | ||||||
|  |     wav_audio_init, | ||||||
|  |     wav_audio_fini, | ||||||
|  |     &wav_pcm_ops, | ||||||
|  |     1, | ||||||
|  |     1, | ||||||
|  |     sizeof (WAVVoice) | ||||||
|  | }; | ||||||
							
								
								
									
										38
									
								
								audio/wavaudio.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								audio/wavaudio.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | |||||||
|  | /* | ||||||
|  |  * QEMU WAV audio output driver header | ||||||
|  |  *  | ||||||
|  |  * Copyright (c) 2004 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. | ||||||
|  |  */ | ||||||
|  | #ifndef QEMU_WAVAUDIO_H | ||||||
|  | #define QEMU_WAVAUDIO_H | ||||||
|  |  | ||||||
|  | typedef struct WAVVoice { | ||||||
|  |     struct HWVoice hw; | ||||||
|  |     QEMUFile *f; | ||||||
|  |     int64_t old_ticks; | ||||||
|  |     void *pcm_buf; | ||||||
|  |     int total_samples; | ||||||
|  | } WAVVoice; | ||||||
|  |  | ||||||
|  | extern struct pcm_ops wav_pcm_ops; | ||||||
|  | extern struct audio_output_driver wav_output_driver; | ||||||
|  |  | ||||||
|  | #endif  /* wavaudio.h */ | ||||||
							
								
								
									
										309
									
								
								hw/adlib.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										309
									
								
								hw/adlib.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,309 @@ | |||||||
|  | /* | ||||||
|  |  * QEMU Adlib emulation | ||||||
|  |  *  | ||||||
|  |  * Copyright (c) 2004 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 "vl.h" | ||||||
|  |  | ||||||
|  | #define AUDIO_CAP "adlib" | ||||||
|  | #include "audio/audio.h" | ||||||
|  |  | ||||||
|  | #ifdef USE_YMF262 | ||||||
|  | #define HAS_YMF262 1 | ||||||
|  | #include "ymf262.h" | ||||||
|  | void YMF262UpdateOneQEMU(int which, INT16 *dst, int length); | ||||||
|  | #define SHIFT 2 | ||||||
|  | #else | ||||||
|  | #include "fmopl.h" | ||||||
|  | #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) \ | ||||||
|  |     void name (void *opaque, uint32_t nport, uint32_t val) | ||||||
|  |  | ||||||
|  | static struct { | ||||||
|  |     int port; | ||||||
|  |     int freq; | ||||||
|  | } conf = {0x220, 44100}; | ||||||
|  |  | ||||||
|  | typedef struct { | ||||||
|  |     int enabled; | ||||||
|  |     int active; | ||||||
|  |     int cparam; | ||||||
|  |     int64_t ticks; | ||||||
|  |     int bufpos; | ||||||
|  |     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 | ||||||
|  |     FM_OPL *opl; | ||||||
|  | #endif | ||||||
|  | } AdlibState; | ||||||
|  |  | ||||||
|  | static AdlibState adlib; | ||||||
|  |  | ||||||
|  | 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); | ||||||
|  |  | ||||||
|  | #ifdef USE_YMF262 | ||||||
|  |     status = YMF262Write (0, a, val); | ||||||
|  | #else | ||||||
|  |     status = OPLWrite (s->opl, a, val); | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static IO_READ_PROTO(adlib_read) | ||||||
|  | { | ||||||
|  |     AdlibState *s = opaque; | ||||||
|  |     uint8_t data; | ||||||
|  |     int a = nport & 3; | ||||||
|  |  | ||||||
|  | #ifdef USE_YMF262 | ||||||
|  |     (void) s; | ||||||
|  |     data = YMF262Read (0, a); | ||||||
|  | #else | ||||||
|  |     data = OPLRead (s->opl, a); | ||||||
|  | #endif | ||||||
|  |     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) | ||||||
|  | { | ||||||
|  |     AdlibState *s = &adlib; | ||||||
|  |     if (interval_Sec == 0.0) { | ||||||
|  |         qemu_del_timer (s->opl_ts); | ||||||
|  |         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 (); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int write_audio (AdlibState *s, int samples) | ||||||
|  | { | ||||||
|  |     int net = 0; | ||||||
|  |     int ss = samples; | ||||||
|  |     while (samples) { | ||||||
|  |         int nbytes = samples << SHIFT; | ||||||
|  |         int wbytes = AUD_write (s->voice, | ||||||
|  |                                 s->mixbuf + (s->pos << (SHIFT - 1)), | ||||||
|  |                                 nbytes); | ||||||
|  |         int wsampl = wbytes >> SHIFT; | ||||||
|  |         samples -= wsampl; | ||||||
|  |         s->pos = (s->pos + wsampl) % s->samples; | ||||||
|  |         net += wsampl; | ||||||
|  |         if (!wbytes) | ||||||
|  |             break; | ||||||
|  |     } | ||||||
|  |     if (net > ss) { | ||||||
|  |         dolog ("WARNING: net > ss\n"); | ||||||
|  |     } | ||||||
|  |     return net; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void timer (void *opaque) | ||||||
|  | { | ||||||
|  |     AdlibState *s = opaque; | ||||||
|  |     int elapsed, samples, net = 0; | ||||||
|  |  | ||||||
|  |     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; | ||||||
|  |     } | ||||||
|  |     s->pos = 0; | ||||||
|  |  | ||||||
|  |     elapsed = AUD_calc_elapsed (s->voice); | ||||||
|  |     if (!elapsed) | ||||||
|  |         goto reset2; | ||||||
|  |  | ||||||
|  |     /* elapsed = AUD_get_free (s->voice); */ | ||||||
|  |     samples = elapsed >> SHIFT; | ||||||
|  |     if (!samples) | ||||||
|  |         goto reset2; | ||||||
|  |  | ||||||
|  |     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) | ||||||
|  |         goto reset2; | ||||||
|  |  | ||||||
|  | #ifdef USE_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; | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void Adlib_fini (AdlibState *s) | ||||||
|  | { | ||||||
|  | #ifdef USE_YMF262 | ||||||
|  |     YMF262Shutdown (); | ||||||
|  | #else | ||||||
|  |     if (s->opl) { | ||||||
|  |         OPLDestroy (s->opl); | ||||||
|  |         s->opl = NULL; | ||||||
|  |     } | ||||||
|  | #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 | ||||||
|  |  | ||||||
|  |     s->active = 0; | ||||||
|  |     s->enabled = 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Adlib_init (void) | ||||||
|  | { | ||||||
|  |     AdlibState *s = &adlib; | ||||||
|  |  | ||||||
|  |     memset (s, 0, sizeof (*s)); | ||||||
|  |  | ||||||
|  | #ifdef USE_YMF262 | ||||||
|  |     if (YMF262Init (1, 14318180, conf.freq)) { | ||||||
|  |         dolog ("YMF262Init %d failed\n", conf.freq); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |         YMF262SetTimerHandler (0, YMF262TimerHandler, 0); | ||||||
|  |         s->enabled = 1; | ||||||
|  |     } | ||||||
|  | #else | ||||||
|  |     s->opl = OPLCreate (OPL_TYPE_YM3812, 3579545, conf.freq); | ||||||
|  |     if (!s->opl) { | ||||||
|  |         dolog ("OPLCreate %d failed\n", conf.freq); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |         OPLSetTimerHandler (s->opl, YMF262TimerHandler, 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); | ||||||
|  |     if (!s->voice) { | ||||||
|  |         Adlib_fini (s); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     s->bytes_per_second = conf.freq << SHIFT; | ||||||
|  |     s->samples = AUD_get_buffer_size (s->voice) >> SHIFT; | ||||||
|  |     s->mixbuf = qemu_mallocz (s->samples << SHIFT); | ||||||
|  |  | ||||||
|  |     if (!s->mixbuf) { | ||||||
|  |         dolog ("not enough memory for adlib mixing buffer (%d)\n", | ||||||
|  |                s->samples << SHIFT); | ||||||
|  |         Adlib_fini (s); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     register_ioport_read (0x388, 4, 1, adlib_read, s); | ||||||
|  |     register_ioport_write (0x388, 4, 1, adlib_write, s); | ||||||
|  |  | ||||||
|  |     register_ioport_read (conf.port, 4, 1, adlib_read, s); | ||||||
|  |     register_ioport_write (conf.port, 4, 1, adlib_write, s); | ||||||
|  |  | ||||||
|  |     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); | ||||||
|  | } | ||||||
							
								
								
									
										191
									
								
								hw/dma.c
									
									
									
									
									
								
							
							
						
						
									
										191
									
								
								hw/dma.c
									
									
									
									
									
								
							| @@ -1,7 +1,7 @@ | |||||||
| /* | /* | ||||||
|  * QEMU DMA emulation |  * QEMU DMA emulation | ||||||
|  * |  * | ||||||
|  * Copyright (c) 2003 Vassili Karpov (malc) |  * Copyright (c) 2003-2004 Vassili Karpov (malc) | ||||||
|  * |  * | ||||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy |  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  * of this software and associated documentation files (the "Software"), to deal |  * of this software and associated documentation files (the "Software"), to deal | ||||||
| @@ -23,9 +23,9 @@ | |||||||
|  */ |  */ | ||||||
| #include "vl.h" | #include "vl.h" | ||||||
|  |  | ||||||
| //#define DEBUG_DMA | /* #define DEBUG_DMA */ | ||||||
|  |  | ||||||
| #define log(...) fprintf (stderr, "dma: " __VA_ARGS__) | #define dolog(...) fprintf (stderr, "dma: " __VA_ARGS__) | ||||||
| #ifdef DEBUG_DMA | #ifdef DEBUG_DMA | ||||||
| #define lwarn(...) fprintf (stderr, "dma: " __VA_ARGS__) | #define lwarn(...) fprintf (stderr, "dma: " __VA_ARGS__) | ||||||
| #define linfo(...) fprintf (stderr, "dma: " __VA_ARGS__) | #define linfo(...) fprintf (stderr, "dma: " __VA_ARGS__) | ||||||
| @@ -86,7 +86,7 @@ static void write_page (void *opaque, uint32_t nport, uint32_t data) | |||||||
|  |  | ||||||
|     ichan = channels[nport & 7]; |     ichan = channels[nport & 7]; | ||||||
|     if (-1 == ichan) { |     if (-1 == ichan) { | ||||||
|         log ("invalid channel %#x %#x\n", nport, data); |         dolog ("invalid channel %#x %#x\n", nport, data); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     d->regs[ichan].page = data; |     d->regs[ichan].page = data; | ||||||
| @@ -99,7 +99,7 @@ static void write_pageh (void *opaque, uint32_t nport, uint32_t data) | |||||||
|  |  | ||||||
|     ichan = channels[nport & 7]; |     ichan = channels[nport & 7]; | ||||||
|     if (-1 == ichan) { |     if (-1 == ichan) { | ||||||
|         log ("invalid channel %#x %#x\n", nport, data); |         dolog ("invalid channel %#x %#x\n", nport, data); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     d->regs[ichan].pageh = data; |     d->regs[ichan].pageh = data; | ||||||
| @@ -112,7 +112,7 @@ static uint32_t read_page (void *opaque, uint32_t nport) | |||||||
|  |  | ||||||
|     ichan = channels[nport & 7]; |     ichan = channels[nport & 7]; | ||||||
|     if (-1 == ichan) { |     if (-1 == ichan) { | ||||||
|         log ("invalid channel read %#x\n", nport); |         dolog ("invalid channel read %#x\n", nport); | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|     return d->regs[ichan].page; |     return d->regs[ichan].page; | ||||||
| @@ -125,7 +125,7 @@ static uint32_t read_pageh (void *opaque, uint32_t nport) | |||||||
|  |  | ||||||
|     ichan = channels[nport & 7]; |     ichan = channels[nport & 7]; | ||||||
|     if (-1 == ichan) { |     if (-1 == ichan) { | ||||||
|         log ("invalid channel read %#x\n", nport); |         dolog ("invalid channel read %#x\n", nport); | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|     return d->regs[ichan].pageh; |     return d->regs[ichan].pageh; | ||||||
| @@ -136,7 +136,7 @@ static inline void init_chan (struct dma_cont *d, int ichan) | |||||||
|     struct dma_regs *r; |     struct dma_regs *r; | ||||||
|  |  | ||||||
|     r = d->regs + ichan; |     r = d->regs + ichan; | ||||||
|     r->now[ADDR] = r->base[0] << d->dshift; |     r->now[ADDR] = r->base[ADDR] << d->dshift; | ||||||
|     r->now[COUNT] = 0; |     r->now[COUNT] = 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -152,7 +152,7 @@ static inline int getff (struct dma_cont *d) | |||||||
| static uint32_t read_chan (void *opaque, uint32_t nport) | static uint32_t read_chan (void *opaque, uint32_t nport) | ||||||
| { | { | ||||||
|     struct dma_cont *d = opaque; |     struct dma_cont *d = opaque; | ||||||
|     int ichan, nreg, iport, ff, val; |     int ichan, nreg, iport, ff, val, dir; | ||||||
|     struct dma_regs *r; |     struct dma_regs *r; | ||||||
|  |  | ||||||
|     iport = (nport >> d->dshift) & 0x0f; |     iport = (nport >> d->dshift) & 0x0f; | ||||||
| @@ -160,12 +160,14 @@ static uint32_t read_chan (void *opaque, uint32_t nport) | |||||||
|     nreg = iport & 1; |     nreg = iport & 1; | ||||||
|     r = d->regs + ichan; |     r = d->regs + ichan; | ||||||
|  |  | ||||||
|  |     dir = ((r->mode >> 5) & 1) ? -1 : 1; | ||||||
|     ff = getff (d); |     ff = getff (d); | ||||||
|     if (nreg) |     if (nreg) | ||||||
|         val = (r->base[COUNT] << d->dshift) - r->now[COUNT]; |         val = (r->base[COUNT] << d->dshift) - r->now[COUNT]; | ||||||
|     else |     else | ||||||
|         val = r->now[ADDR] + r->now[COUNT]; |         val = r->now[ADDR] + r->now[COUNT] * dir; | ||||||
|  |  | ||||||
|  |     ldebug ("read_chan %#x -> %d\n", iport, val); | ||||||
|     return (val >> (d->dshift + (ff << 3))) & 0xff; |     return (val >> (d->dshift + (ff << 3))) & 0xff; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -190,19 +192,19 @@ static void write_chan (void *opaque, uint32_t nport, uint32_t data) | |||||||
| static void write_cont (void *opaque, uint32_t nport, uint32_t data) | static void write_cont (void *opaque, uint32_t nport, uint32_t data) | ||||||
| { | { | ||||||
|     struct dma_cont *d = opaque; |     struct dma_cont *d = opaque; | ||||||
|     int iport, ichan; |     int iport, ichan = 0; | ||||||
|  |  | ||||||
|     iport = (nport >> d->dshift) & 0x0f; |     iport = (nport >> d->dshift) & 0x0f; | ||||||
|     switch (iport) { |     switch (iport) { | ||||||
|     case 8:                     /* command */ |     case 0x08:                  /* command */ | ||||||
|         if ((data != 0) && (data & CMD_NOT_SUPPORTED)) { |         if ((data != 0) && (data & CMD_NOT_SUPPORTED)) { | ||||||
|             log ("command %#x not supported\n", data); |             dolog ("command %#x not supported\n", data); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|         d->command = data; |         d->command = data; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case 9: |     case 0x09: | ||||||
|         ichan = data & 3; |         ichan = data & 3; | ||||||
|         if (data & 4) { |         if (data & 4) { | ||||||
|             d->status |= 1 << (ichan + 4); |             d->status |= 1 << (ichan + 4); | ||||||
| @@ -213,22 +215,19 @@ static void write_cont (void *opaque, uint32_t nport, uint32_t data) | |||||||
|         d->status &= ~(1 << ichan); |         d->status &= ~(1 << ichan); | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case 0xa:                   /* single mask */ |     case 0x0a:                  /* single mask */ | ||||||
|         if (data & 4) |         if (data & 4) | ||||||
|             d->mask |= 1 << (data & 3); |             d->mask |= 1 << (data & 3); | ||||||
|         else |         else | ||||||
|             d->mask &= ~(1 << (data & 3)); |             d->mask &= ~(1 << (data & 3)); | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case 0xb:                   /* mode */ |     case 0x0b:                  /* mode */ | ||||||
|         { |         { | ||||||
|             ichan = data & 3; |             ichan = data & 3; | ||||||
| #ifdef DEBUG_DMA | #ifdef DEBUG_DMA | ||||||
|             int op; |             { | ||||||
|             int ai; |                 int op, ai, dir, opmode; | ||||||
|             int dir; |  | ||||||
|             int opmode; |  | ||||||
|  |  | ||||||
|             op = (data >> 2) & 3; |             op = (data >> 2) & 3; | ||||||
|             ai = (data >> 4) & 1; |             ai = (data >> 4) & 1; | ||||||
|             dir = (data >> 5) & 1; |             dir = (data >> 5) & 1; | ||||||
| @@ -236,39 +235,39 @@ static void write_cont (void *opaque, uint32_t nport, uint32_t data) | |||||||
|  |  | ||||||
|             linfo ("ichan %d, op %d, ai %d, dir %d, opmode %d\n", |             linfo ("ichan %d, op %d, ai %d, dir %d, opmode %d\n", | ||||||
|                    ichan, op, ai, dir, opmode); |                    ichan, op, ai, dir, opmode); | ||||||
|  |             } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|             d->regs[ichan].mode = data; |             d->regs[ichan].mode = data; | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|     case 0xc:                   /* clear flip flop */ |     case 0x0c:                  /* clear flip flop */ | ||||||
|         d->flip_flop = 0; |         d->flip_flop = 0; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case 0xd:                   /* reset */ |     case 0x0d:                  /* reset */ | ||||||
|         d->flip_flop = 0; |         d->flip_flop = 0; | ||||||
|         d->mask = ~0; |         d->mask = ~0; | ||||||
|         d->status = 0; |         d->status = 0; | ||||||
|         d->command = 0; |         d->command = 0; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case 0xe:                   /* clear mask for all channels */ |     case 0x0e:                  /* clear mask for all channels */ | ||||||
|         d->mask = 0; |         d->mask = 0; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case 0xf:                   /* write mask for all channels */ |     case 0x0f:                  /* write mask for all channels */ | ||||||
|         d->mask = data; |         d->mask = data; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     default: |     default: | ||||||
|         log ("dma: unknown iport %#x\n", iport); |         dolog ("unknown iport %#x\n", iport); | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| #ifdef DEBUG_DMA | #ifdef DEBUG_DMA | ||||||
|     if (0xc != iport) { |     if (0xc != iport) { | ||||||
|         linfo ("nport %#06x, ichan % 2d, val %#06x\n", |         linfo ("write_cont: nport %#06x, ichan % 2d, val %#06x\n", | ||||||
|                nport, ichan, data); |                nport, ichan, data); | ||||||
|     } |     } | ||||||
| #endif | #endif | ||||||
| @@ -281,17 +280,19 @@ static uint32_t read_cont (void *opaque, uint32_t nport) | |||||||
|  |  | ||||||
|     iport = (nport >> d->dshift) & 0x0f; |     iport = (nport >> d->dshift) & 0x0f; | ||||||
|     switch (iport) { |     switch (iport) { | ||||||
|     case 0x08: /* status */ |     case 0x08:                  /* status */ | ||||||
|         val = d->status; |         val = d->status; | ||||||
|         d->status &= 0xf0; |         d->status &= 0xf0; | ||||||
|         break; |         break; | ||||||
|     case 0x0f: /* mask */ |     case 0x0f:                  /* mask */ | ||||||
|         val = d->mask; |         val = d->mask; | ||||||
|         break; |         break; | ||||||
|     default: |     default: | ||||||
|         val = 0; |         val = 0; | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     ldebug ("read_cont: nport %#06x, iport %#04x val %#x\n", nport, iport, val); | ||||||
|     return val; |     return val; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -322,23 +323,27 @@ void DMA_release_DREQ (int nchan) | |||||||
|  |  | ||||||
| static void channel_run (int ncont, int ichan) | static void channel_run (int ncont, int ichan) | ||||||
| { | { | ||||||
|     struct dma_regs *r; |  | ||||||
|     int n; |     int n; | ||||||
|     target_ulong addr; |     struct dma_regs *r = &dma_controllers[ncont].regs[ichan]; | ||||||
| /*     int ai, dir; */ | #ifdef DEBUG_DMA | ||||||
|  |     int dir, opmode; | ||||||
|  |  | ||||||
|  |     dir = (r->mode >> 5) & 1; | ||||||
|  |     opmode = (r->mode >> 6) & 3; | ||||||
|  |  | ||||||
|  |     if (dir) { | ||||||
|  |         dolog ("DMA in address decrement mode\n"); | ||||||
|  |     } | ||||||
|  |     if (opmode != 1) { | ||||||
|  |         dolog ("DMA not in single mode select %#x\n", opmode); | ||||||
|  |     } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|     r = dma_controllers[ncont].regs + ichan; |     r = dma_controllers[ncont].regs + ichan; | ||||||
| /*   ai = r->mode & 16; */ |     n = r->transfer_handler (r->opaque, ichan + (ncont << 2), | ||||||
| /*   dir = r->mode & 32 ? -1 : 1; */ |                              r->now[COUNT], (r->base[COUNT] + 1) << ncont); | ||||||
|  |  | ||||||
|     /* NOTE: pageh is only used by PPC PREP */ |  | ||||||
|     addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR]; |  | ||||||
|     n = r->transfer_handler (r->opaque, addr,  |  | ||||||
|                              (r->base[COUNT] << ncont) + (1 << ncont)); |  | ||||||
|     r->now[COUNT] = n; |     r->now[COUNT] = n; | ||||||
|  |     ldebug ("dma_pos %d size %d\n", n, (r->base[COUNT] + 1) << ncont); | ||||||
|     ldebug ("dma_pos %d size %d\n", |  | ||||||
|             n, (r->base[1] << ncont) + (1 << ncont)); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void DMA_run (void) | void DMA_run (void) | ||||||
| @@ -375,6 +380,50 @@ void DMA_register_channel (int nchan, | |||||||
|     r->opaque = opaque; |     r->opaque = opaque; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | int DMA_read_memory (int nchan, void *buf, int pos, int len) | ||||||
|  | { | ||||||
|  |     struct dma_regs *r = &dma_controllers[nchan > 3].regs[nchan & 3]; | ||||||
|  |     target_ulong addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR]; | ||||||
|  |  | ||||||
|  |     if (r->mode & 0x20) { | ||||||
|  |         int i; | ||||||
|  |         uint8_t *p = buf; | ||||||
|  |  | ||||||
|  |         cpu_physical_memory_read (addr - pos - len, buf, len); | ||||||
|  |         /* What about 16bit transfers? */ | ||||||
|  |         for (i = 0; i < len >> 1; i++) { | ||||||
|  |             uint8_t b = p[len - i - 1]; | ||||||
|  |             p[i] = b; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     else | ||||||
|  |         cpu_physical_memory_read (addr + pos, buf, len); | ||||||
|  |  | ||||||
|  |     return len; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int DMA_write_memory (int nchan, void *buf, int pos, int len) | ||||||
|  | { | ||||||
|  |     struct dma_regs *r = &dma_controllers[nchan > 3].regs[nchan & 3]; | ||||||
|  |     target_ulong addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR]; | ||||||
|  |  | ||||||
|  |     if (r->mode & 0x20) { | ||||||
|  |         int i; | ||||||
|  |         uint8_t *p = buf; | ||||||
|  |  | ||||||
|  |         cpu_physical_memory_write (addr - pos - len, buf, len); | ||||||
|  |         /* What about 16bit transfers? */ | ||||||
|  |         for (i = 0; i < len; i++) { | ||||||
|  |             uint8_t b = p[len - i - 1]; | ||||||
|  |             p[i] = b; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     else | ||||||
|  |         cpu_physical_memory_write (addr + pos, buf, len); | ||||||
|  |  | ||||||
|  |     return len; | ||||||
|  | } | ||||||
|  |  | ||||||
| /* request the emulator to transfer a new DMA memory block ASAP */ | /* request the emulator to transfer a new DMA memory block ASAP */ | ||||||
| void DMA_schedule(int nchan) | void DMA_schedule(int nchan) | ||||||
| { | { | ||||||
| @@ -421,10 +470,66 @@ static void dma_init2(struct dma_cont *d, int base, int dshift, | |||||||
|     dma_reset(d); |     dma_reset(d); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static void dma_save (QEMUFile *f, void *opaque) | ||||||
|  | { | ||||||
|  |     struct dma_cont *d = opaque; | ||||||
|  |     int i; | ||||||
|  |  | ||||||
|  |     /* qemu_put_8s (f, &d->status); */ | ||||||
|  |     qemu_put_8s (f, &d->command); | ||||||
|  |     qemu_put_8s (f, &d->mask); | ||||||
|  |     qemu_put_8s (f, &d->flip_flop); | ||||||
|  |     qemu_put_be32s (f, &d->dshift); | ||||||
|  |  | ||||||
|  |     for (i = 0; i < 4; ++i) { | ||||||
|  |         struct dma_regs *r = &d->regs[i]; | ||||||
|  |         qemu_put_be32s (f, &r->now[0]); | ||||||
|  |         qemu_put_be32s (f, &r->now[1]); | ||||||
|  |         qemu_put_be16s (f, &r->base[0]); | ||||||
|  |         qemu_put_be16s (f, &r->base[1]); | ||||||
|  |         qemu_put_8s (f, &r->mode); | ||||||
|  |         qemu_put_8s (f, &r->page); | ||||||
|  |         qemu_put_8s (f, &r->pageh); | ||||||
|  |         qemu_put_8s (f, &r->dack); | ||||||
|  |         qemu_put_8s (f, &r->eop); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int dma_load (QEMUFile *f, void *opaque, int version_id) | ||||||
|  | { | ||||||
|  |     struct dma_cont *d = opaque; | ||||||
|  |     int i; | ||||||
|  |  | ||||||
|  |     if (version_id != 1) | ||||||
|  |         return -EINVAL; | ||||||
|  |  | ||||||
|  |     /* qemu_get_8s (f, &d->status); */ | ||||||
|  |     qemu_get_8s (f, &d->command); | ||||||
|  |     qemu_get_8s (f, &d->mask); | ||||||
|  |     qemu_get_8s (f, &d->flip_flop); | ||||||
|  |     qemu_get_be32s (f, &d->dshift); | ||||||
|  |  | ||||||
|  |     for (i = 0; i < 4; ++i) { | ||||||
|  |         struct dma_regs *r = &d->regs[i]; | ||||||
|  |         qemu_get_be32s (f, &r->now[0]); | ||||||
|  |         qemu_get_be32s (f, &r->now[1]); | ||||||
|  |         qemu_get_be16s (f, &r->base[0]); | ||||||
|  |         qemu_get_be16s (f, &r->base[1]); | ||||||
|  |         qemu_get_8s (f, &r->mode); | ||||||
|  |         qemu_get_8s (f, &r->page); | ||||||
|  |         qemu_get_8s (f, &r->pageh); | ||||||
|  |         qemu_get_8s (f, &r->dack); | ||||||
|  |         qemu_get_8s (f, &r->eop); | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
| void DMA_init (int high_page_enable) | void DMA_init (int high_page_enable) | ||||||
| { | { | ||||||
|     dma_init2(&dma_controllers[0], 0x00, 0, 0x80, |     dma_init2(&dma_controllers[0], 0x00, 0, 0x80, | ||||||
|               high_page_enable ? 0x480 : -1); |               high_page_enable ? 0x480 : -1); | ||||||
|     dma_init2(&dma_controllers[1], 0xc0, 1, 0x88, |     dma_init2(&dma_controllers[1], 0xc0, 1, 0x88, | ||||||
|               high_page_enable ? 0x488 : -1); |               high_page_enable ? 0x488 : -1); | ||||||
|  |     register_savevm ("dma", 0, 1, dma_save, dma_load, &dma_controllers[0]); | ||||||
|  |     register_savevm ("dma", 1, 1, dma_save, dma_load, &dma_controllers[1]); | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										31
									
								
								hw/fdc.c
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								hw/fdc.c
									
									
									
									
									
								
							| @@ -313,7 +313,8 @@ static void fd_reset (fdrive_t *drv) | |||||||
|  |  | ||||||
| static void fdctrl_reset (fdctrl_t *fdctrl, int do_irq); | static void fdctrl_reset (fdctrl_t *fdctrl, int do_irq); | ||||||
| static void fdctrl_reset_fifo (fdctrl_t *fdctrl); | static void fdctrl_reset_fifo (fdctrl_t *fdctrl); | ||||||
| static int fdctrl_transfer_handler (void *opaque, target_ulong addr, int size); | static int fdctrl_transfer_handler (void *opaque, int nchan, | ||||||
|  |                                     int dma_pos, int dma_len); | ||||||
| static void fdctrl_raise_irq (fdctrl_t *fdctrl, uint8_t status); | static void fdctrl_raise_irq (fdctrl_t *fdctrl, uint8_t status); | ||||||
| static void fdctrl_result_timer(void *opaque); | static void fdctrl_result_timer(void *opaque); | ||||||
|  |  | ||||||
| @@ -908,7 +909,8 @@ static void fdctrl_start_transfer_del (fdctrl_t *fdctrl, int direction) | |||||||
| } | } | ||||||
|  |  | ||||||
| /* handlers for DMA transfers */ | /* handlers for DMA transfers */ | ||||||
| static int fdctrl_transfer_handler (void *opaque, target_ulong addr, int size) | static int fdctrl_transfer_handler (void *opaque, int nchan, | ||||||
|  |                                     int dma_pos, int dma_len) | ||||||
| { | { | ||||||
|     fdctrl_t *fdctrl; |     fdctrl_t *fdctrl; | ||||||
|     fdrive_t *cur_drv; |     fdrive_t *cur_drv; | ||||||
| @@ -924,8 +926,8 @@ static int fdctrl_transfer_handler (void *opaque, target_ulong addr, int size) | |||||||
|     if (fdctrl->data_dir == FD_DIR_SCANE || fdctrl->data_dir == FD_DIR_SCANL || |     if (fdctrl->data_dir == FD_DIR_SCANE || fdctrl->data_dir == FD_DIR_SCANL || | ||||||
|         fdctrl->data_dir == FD_DIR_SCANH) |         fdctrl->data_dir == FD_DIR_SCANH) | ||||||
|         status2 = 0x04; |         status2 = 0x04; | ||||||
|     if (size > fdctrl->data_len) |     if (dma_len > fdctrl->data_len) | ||||||
| 	size = fdctrl->data_len; |         dma_len = fdctrl->data_len; | ||||||
|     if (cur_drv->bs == NULL) { |     if (cur_drv->bs == NULL) { | ||||||
| 	if (fdctrl->data_dir == FD_DIR_WRITE) | 	if (fdctrl->data_dir == FD_DIR_WRITE) | ||||||
| 	    fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00); | 	    fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00); | ||||||
| @@ -935,8 +937,8 @@ static int fdctrl_transfer_handler (void *opaque, target_ulong addr, int size) | |||||||
|         goto transfer_error; |         goto transfer_error; | ||||||
|     } |     } | ||||||
|     rel_pos = fdctrl->data_pos % FD_SECTOR_LEN; |     rel_pos = fdctrl->data_pos % FD_SECTOR_LEN; | ||||||
|     for (start_pos = fdctrl->data_pos; fdctrl->data_pos < size;) { |     for (start_pos = fdctrl->data_pos; fdctrl->data_pos < dma_len;) { | ||||||
|         len = size - fdctrl->data_pos; |         len = dma_len - fdctrl->data_pos; | ||||||
|         if (len + rel_pos > FD_SECTOR_LEN) |         if (len + rel_pos > FD_SECTOR_LEN) | ||||||
|             len = FD_SECTOR_LEN - rel_pos; |             len = FD_SECTOR_LEN - rel_pos; | ||||||
|         FLOPPY_DPRINTF("copy %d bytes (%d %d %d) %d pos %d %02x %02x " |         FLOPPY_DPRINTF("copy %d bytes (%d %d %d) %d pos %d %02x %02x " | ||||||
| @@ -958,13 +960,17 @@ static int fdctrl_transfer_handler (void *opaque, target_ulong addr, int size) | |||||||
| 	switch (fdctrl->data_dir) { | 	switch (fdctrl->data_dir) { | ||||||
| 	case FD_DIR_READ: | 	case FD_DIR_READ: | ||||||
| 	    /* READ commands */ | 	    /* READ commands */ | ||||||
| 	    cpu_physical_memory_write(addr + fdctrl->data_pos, |             DMA_write_memory (nchan, fdctrl->fifo + rel_pos, | ||||||
| 				      fdctrl->fifo + rel_pos, len); |                               fdctrl->data_pos, len); | ||||||
|  | /* 	    cpu_physical_memory_write(addr + fdctrl->data_pos, */ | ||||||
|  | /* 				      fdctrl->fifo + rel_pos, len); */ | ||||||
| 	    break; | 	    break; | ||||||
| 	case FD_DIR_WRITE: | 	case FD_DIR_WRITE: | ||||||
|             /* WRITE commands */ |             /* WRITE commands */ | ||||||
|             cpu_physical_memory_read(addr + fdctrl->data_pos, |             DMA_read_memory (nchan, fdctrl->fifo + rel_pos, | ||||||
| 				     fdctrl->fifo + rel_pos, len); |                              fdctrl->data_pos, len); | ||||||
|  | /*             cpu_physical_memory_read(addr + fdctrl->data_pos, */ | ||||||
|  | /* 				     fdctrl->fifo + rel_pos, len); */ | ||||||
|             if (bdrv_write(cur_drv->bs, fd_sector(cur_drv), |             if (bdrv_write(cur_drv->bs, fd_sector(cur_drv), | ||||||
| 			   fdctrl->fifo, 1) < 0) { | 			   fdctrl->fifo, 1) < 0) { | ||||||
|                 FLOPPY_ERROR("writting sector %d\n", fd_sector(cur_drv)); |                 FLOPPY_ERROR("writting sector %d\n", fd_sector(cur_drv)); | ||||||
| @@ -977,8 +983,9 @@ static int fdctrl_transfer_handler (void *opaque, target_ulong addr, int size) | |||||||
|             { |             { | ||||||
| 		uint8_t tmpbuf[FD_SECTOR_LEN]; | 		uint8_t tmpbuf[FD_SECTOR_LEN]; | ||||||
|                 int ret; |                 int ret; | ||||||
|                 cpu_physical_memory_read(addr + fdctrl->data_pos, |                 DMA_read_memory (nchan, tmpbuf, fdctrl->data_pos, len); | ||||||
|                                          tmpbuf, len); | /*                 cpu_physical_memory_read(addr + fdctrl->data_pos, */ | ||||||
|  | /*                                          tmpbuf, len); */ | ||||||
|                 ret = memcmp(tmpbuf, fdctrl->fifo + rel_pos, len); |                 ret = memcmp(tmpbuf, fdctrl->fifo + rel_pos, len); | ||||||
|                 if (ret == 0) { |                 if (ret == 0) { | ||||||
|                     status2 = 0x08; |                     status2 = 0x08; | ||||||
|   | |||||||
							
								
								
									
										1390
									
								
								hw/fmopl.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1390
									
								
								hw/fmopl.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										174
									
								
								hw/fmopl.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								hw/fmopl.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,174 @@ | |||||||
|  | #ifndef __FMOPL_H_ | ||||||
|  | #define __FMOPL_H_ | ||||||
|  |  | ||||||
|  | /* --- select emulation chips --- */ | ||||||
|  | #define BUILD_YM3812 (HAS_YM3812) | ||||||
|  | //#define BUILD_YM3526 (HAS_YM3526) | ||||||
|  | //#define BUILD_Y8950  (HAS_Y8950) | ||||||
|  |  | ||||||
|  | /* --- system optimize --- */ | ||||||
|  | /* select bit size of output : 8 or 16 */ | ||||||
|  | #define OPL_OUTPUT_BIT 16 | ||||||
|  |  | ||||||
|  | /* compiler dependence */ | ||||||
|  | #ifndef OSD_CPU_H | ||||||
|  | #define OSD_CPU_H | ||||||
|  | typedef unsigned char	UINT8;   /* unsigned  8bit */ | ||||||
|  | typedef unsigned short	UINT16;  /* unsigned 16bit */ | ||||||
|  | typedef unsigned int	UINT32;  /* unsigned 32bit */ | ||||||
|  | typedef signed char		INT8;    /* signed  8bit   */ | ||||||
|  | typedef signed short	INT16;   /* signed 16bit   */ | ||||||
|  | typedef signed int		INT32;   /* signed 32bit   */ | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #if (OPL_OUTPUT_BIT==16) | ||||||
|  | typedef INT16 OPLSAMPLE; | ||||||
|  | #endif | ||||||
|  | #if (OPL_OUTPUT_BIT==8) | ||||||
|  | typedef unsigned char  OPLSAMPLE; | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #if BUILD_Y8950 | ||||||
|  | #include "ymdeltat.h" | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | typedef void (*OPL_TIMERHANDLER)(int channel,double interval_Sec); | ||||||
|  | typedef void (*OPL_IRQHANDLER)(int param,int irq); | ||||||
|  | typedef void (*OPL_UPDATEHANDLER)(int param,int min_interval_us); | ||||||
|  | typedef void (*OPL_PORTHANDLER_W)(int param,unsigned char data); | ||||||
|  | typedef unsigned char (*OPL_PORTHANDLER_R)(int param); | ||||||
|  |  | ||||||
|  | /* !!!!! here is private section , do not access there member direct !!!!! */ | ||||||
|  |  | ||||||
|  | #define OPL_TYPE_WAVESEL   0x01  /* waveform select    */ | ||||||
|  | #define OPL_TYPE_ADPCM     0x02  /* DELTA-T ADPCM unit */ | ||||||
|  | #define OPL_TYPE_KEYBOARD  0x04  /* keyboard interface */ | ||||||
|  | #define OPL_TYPE_IO        0x08  /* I/O port */ | ||||||
|  |  | ||||||
|  | /* Saving is necessary for member of the 'R' mark for suspend/resume */ | ||||||
|  | /* ---------- OPL one of slot  ---------- */ | ||||||
|  | typedef struct fm_opl_slot { | ||||||
|  | 	INT32 TL;		/* total level     :TL << 8            */ | ||||||
|  | 	INT32 TLL;		/* adjusted now TL                     */ | ||||||
|  | 	UINT8  KSR;		/* key scale rate  :(shift down bit)   */ | ||||||
|  | 	INT32 *AR;		/* attack rate     :&AR_TABLE[AR<<2]   */ | ||||||
|  | 	INT32 *DR;		/* decay rate      :&DR_TALBE[DR<<2]   */ | ||||||
|  | 	INT32 SL;		/* sustin level    :SL_TALBE[SL]       */ | ||||||
|  | 	INT32 *RR;		/* release rate    :&DR_TABLE[RR<<2]   */ | ||||||
|  | 	UINT8 ksl;		/* keyscale level  :(shift down bits)  */ | ||||||
|  | 	UINT8 ksr;		/* key scale rate  :kcode>>KSR         */ | ||||||
|  | 	UINT32 mul;		/* multiple        :ML_TABLE[ML]       */ | ||||||
|  | 	UINT32 Cnt;		/* frequency count :                   */ | ||||||
|  | 	UINT32 Incr;	/* frequency step  :                   */ | ||||||
|  | 	/* envelope generator state */ | ||||||
|  | 	UINT8 eg_typ;	/* envelope type flag                  */ | ||||||
|  | 	UINT8 evm;		/* envelope phase                      */ | ||||||
|  | 	INT32 evc;		/* envelope counter                    */ | ||||||
|  | 	INT32 eve;		/* envelope counter end point          */ | ||||||
|  | 	INT32 evs;		/* envelope counter step               */ | ||||||
|  | 	INT32 evsa;	/* envelope step for AR :AR[ksr]           */ | ||||||
|  | 	INT32 evsd;	/* envelope step for DR :DR[ksr]           */ | ||||||
|  | 	INT32 evsr;	/* envelope step for RR :RR[ksr]           */ | ||||||
|  | 	/* LFO */ | ||||||
|  | 	UINT8 ams;		/* ams flag                            */ | ||||||
|  | 	UINT8 vib;		/* vibrate flag                        */ | ||||||
|  | 	/* wave selector */ | ||||||
|  | 	INT32 **wavetable; | ||||||
|  | }OPL_SLOT; | ||||||
|  |  | ||||||
|  | /* ---------- OPL one of channel  ---------- */ | ||||||
|  | typedef struct fm_opl_channel { | ||||||
|  | 	OPL_SLOT SLOT[2]; | ||||||
|  | 	UINT8 CON;			/* connection type                     */ | ||||||
|  | 	UINT8 FB;			/* feed back       :(shift down bit)   */ | ||||||
|  | 	INT32 *connect1;	/* slot1 output pointer                */ | ||||||
|  | 	INT32 *connect2;	/* slot2 output pointer                */ | ||||||
|  | 	INT32 op1_out[2];	/* slot1 output for selfeedback        */ | ||||||
|  | 	/* phase generator state */ | ||||||
|  | 	UINT32  block_fnum;	/* block+fnum      :                   */ | ||||||
|  | 	UINT8 kcode;		/* key code        : KeyScaleCode      */ | ||||||
|  | 	UINT32  fc;			/* Freq. Increment base                */ | ||||||
|  | 	UINT32  ksl_base;	/* KeyScaleLevel Base step             */ | ||||||
|  | 	UINT8 keyon;		/* key on/off flag                     */ | ||||||
|  | } OPL_CH; | ||||||
|  |  | ||||||
|  | /* OPL state */ | ||||||
|  | typedef struct fm_opl_f { | ||||||
|  | 	UINT8 type;			/* chip type                         */ | ||||||
|  | 	int clock;			/* master clock  (Hz)                */ | ||||||
|  | 	int rate;			/* sampling rate (Hz)                */ | ||||||
|  | 	double freqbase;	/* frequency base                    */ | ||||||
|  | 	double TimerBase;	/* Timer base time (==sampling time) */ | ||||||
|  | 	UINT8 address;		/* address register                  */ | ||||||
|  | 	UINT8 status;		/* status flag                       */ | ||||||
|  | 	UINT8 statusmask;	/* status mask                       */ | ||||||
|  | 	UINT32 mode;		/* Reg.08 : CSM , notesel,etc.       */ | ||||||
|  | 	/* Timer */ | ||||||
|  | 	int T[2];			/* timer counter                     */ | ||||||
|  | 	UINT8 st[2];		/* timer enable                      */ | ||||||
|  | 	/* FM channel slots */ | ||||||
|  | 	OPL_CH *P_CH;		/* pointer of CH                     */ | ||||||
|  | 	int	max_ch;			/* maximum channel                   */ | ||||||
|  | 	/* Rythm sention */ | ||||||
|  | 	UINT8 rythm;		/* Rythm mode , key flag */ | ||||||
|  | #if BUILD_Y8950 | ||||||
|  | 	/* Delta-T ADPCM unit (Y8950) */ | ||||||
|  | 	YM_DELTAT *deltat;			/* DELTA-T ADPCM       */ | ||||||
|  | #endif | ||||||
|  | 	/* Keyboard / I/O interface unit (Y8950) */ | ||||||
|  | 	UINT8 portDirection; | ||||||
|  | 	UINT8 portLatch; | ||||||
|  | 	OPL_PORTHANDLER_R porthandler_r; | ||||||
|  | 	OPL_PORTHANDLER_W porthandler_w; | ||||||
|  | 	int port_param; | ||||||
|  | 	OPL_PORTHANDLER_R keyboardhandler_r; | ||||||
|  | 	OPL_PORTHANDLER_W keyboardhandler_w; | ||||||
|  | 	int keyboard_param; | ||||||
|  | 	/* time tables */ | ||||||
|  | 	INT32 AR_TABLE[75];	/* atttack rate tables */ | ||||||
|  | 	INT32 DR_TABLE[75];	/* decay rate tables   */ | ||||||
|  | 	UINT32 FN_TABLE[1024];  /* fnumber -> increment counter */ | ||||||
|  | 	/* LFO */ | ||||||
|  | 	INT32 *ams_table; | ||||||
|  | 	INT32 *vib_table; | ||||||
|  | 	INT32 amsCnt; | ||||||
|  | 	INT32 amsIncr; | ||||||
|  | 	INT32 vibCnt; | ||||||
|  | 	INT32 vibIncr; | ||||||
|  | 	/* wave selector enable flag */ | ||||||
|  | 	UINT8 wavesel; | ||||||
|  | 	/* external event callback handler */ | ||||||
|  | 	OPL_TIMERHANDLER  TimerHandler;		/* TIMER handler   */ | ||||||
|  | 	int TimerParam;						/* TIMER parameter */ | ||||||
|  | 	OPL_IRQHANDLER    IRQHandler;		/* IRQ handler    */ | ||||||
|  | 	int IRQParam;						/* IRQ parameter  */ | ||||||
|  | 	OPL_UPDATEHANDLER UpdateHandler;	/* stream update handler   */ | ||||||
|  | 	int UpdateParam;					/* stream update parameter */ | ||||||
|  | } FM_OPL; | ||||||
|  |  | ||||||
|  | /* ---------- Generic interface section ---------- */ | ||||||
|  | #define OPL_TYPE_YM3526 (0) | ||||||
|  | #define OPL_TYPE_YM3812 (OPL_TYPE_WAVESEL) | ||||||
|  | #define OPL_TYPE_Y8950  (OPL_TYPE_ADPCM|OPL_TYPE_KEYBOARD|OPL_TYPE_IO) | ||||||
|  |  | ||||||
|  | FM_OPL *OPLCreate(int type, int clock, int rate); | ||||||
|  | void OPLDestroy(FM_OPL *OPL); | ||||||
|  | void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER TimerHandler,int channelOffset); | ||||||
|  | void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,int param); | ||||||
|  | void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,int param); | ||||||
|  | /* Y8950 port handlers */ | ||||||
|  | void OPLSetPortHandler(FM_OPL *OPL,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,int param); | ||||||
|  | void OPLSetKeyboardHandler(FM_OPL *OPL,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,int param); | ||||||
|  |  | ||||||
|  | void OPLResetChip(FM_OPL *OPL); | ||||||
|  | int OPLWrite(FM_OPL *OPL,int a,int v); | ||||||
|  | unsigned char OPLRead(FM_OPL *OPL,int a); | ||||||
|  | int OPLTimerOver(FM_OPL *OPL,int c); | ||||||
|  |  | ||||||
|  | /* YM3626/YM3812 local section */ | ||||||
|  | void YM3812UpdateOne(FM_OPL *OPL, INT16 *buffer, int length); | ||||||
|  |  | ||||||
|  | void Y8950UpdateOne(FM_OPL *OPL, INT16 *buffer, int length); | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										3
									
								
								hw/pc.c
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								hw/pc.c
									
									
									
									
									
								
							| @@ -554,13 +554,10 @@ void pc_init(int ram_size, int vga_ram_size, int boot_device, | |||||||
|     kbd_init(); |     kbd_init(); | ||||||
|     DMA_init(0); |     DMA_init(0); | ||||||
|  |  | ||||||
| #ifndef _WIN32 |  | ||||||
|     if (audio_enabled) { |     if (audio_enabled) { | ||||||
|         /* no audio supported yet for win32 */ |         /* no audio supported yet for win32 */ | ||||||
|         AUD_init(); |         AUD_init(); | ||||||
|         SB16_init(); |  | ||||||
|     } |     } | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     floppy_controller = fdctrl_init(6, 2, 0, 0x3f0, fd_table); |     floppy_controller = fdctrl_init(6, 2, 0, 0x3f0, fd_table); | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										978
									
								
								oss.c
									
									
									
									
									
								
							
							
						
						
									
										978
									
								
								oss.c
									
									
									
									
									
								
							| @@ -1,978 +0,0 @@ | |||||||
| /* |  | ||||||
|  * QEMU OSS Audio output driver |  | ||||||
|  *  |  | ||||||
|  * Copyright (c) 2003 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 "vl.h" |  | ||||||
|  |  | ||||||
| #include <stdio.h> |  | ||||||
| #include <limits.h> |  | ||||||
| #include <stdlib.h> |  | ||||||
| #include <string.h> |  | ||||||
|  |  | ||||||
| /* TODO: Graceful error handling */ |  | ||||||
|  |  | ||||||
| #if defined(_WIN32) |  | ||||||
| #define USE_SDL_AUDIO |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #define MIN(a, b) ((a)>(b)?(b):(a)) |  | ||||||
| #define MAX(a, b) ((a)<(b)?(b):(a)) |  | ||||||
|  |  | ||||||
| #define DEREF(x) (void)x |  | ||||||
| #define dolog(...) fprintf (stderr, "audio: " __VA_ARGS__) |  | ||||||
| #define ERRFail(...) do {                                               \ |  | ||||||
|     int _errno = errno;                                                 \ |  | ||||||
|     fprintf (stderr, "audio: " __VA_ARGS__);                            \ |  | ||||||
|     fprintf (stderr, "\nsystem error: %s\n", strerror (_errno));        \ |  | ||||||
|     abort ();                                                           \ |  | ||||||
| } while (0) |  | ||||||
| #define Fail(...) do {                          \ |  | ||||||
|     fprintf (stderr, "audio: " __VA_ARGS__);    \ |  | ||||||
|     fprintf (stderr, "\n");                     \ |  | ||||||
|     abort ();                                   \ |  | ||||||
| } while (0) |  | ||||||
|  |  | ||||||
| #ifdef DEBUG_AUDIO |  | ||||||
| #define lwarn(...) fprintf (stderr, "audio: " __VA_ARGS__) |  | ||||||
| #define linfo(...) fprintf (stderr, "audio: " __VA_ARGS__) |  | ||||||
| #define ldebug(...) fprintf (stderr, "audio: " __VA_ARGS__) |  | ||||||
| #else |  | ||||||
| #define lwarn(...) |  | ||||||
| #define linfo(...) |  | ||||||
| #define ldebug(...) |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| static int get_conf_val (const char *key, int defval) |  | ||||||
| { |  | ||||||
|     int val = defval; |  | ||||||
|     char *strval; |  | ||||||
|  |  | ||||||
|     strval = getenv (key); |  | ||||||
|     if (strval) { |  | ||||||
|         val = atoi (strval); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return val; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void copy_no_conversion (void *dst, void *src, int size) |  | ||||||
| { |  | ||||||
|     memcpy (dst, src, size); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void copy_u16_to_s16 (void *dst, void *src, int size) |  | ||||||
| { |  | ||||||
|     int i; |  | ||||||
|     uint16_t *out, *in; |  | ||||||
|  |  | ||||||
|     out = dst; |  | ||||||
|     in = src; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < size / 2; i++) { |  | ||||||
|         out[i] = in[i] + 0x8000; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #ifdef USE_SDL_AUDIO |  | ||||||
| #include <SDL/SDL.h> |  | ||||||
| #include <SDL/SDL_thread.h> |  | ||||||
|  |  | ||||||
| static struct { |  | ||||||
|     int samples; |  | ||||||
| } conf = { |  | ||||||
|     .samples = 4096 |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| typedef struct AudioState { |  | ||||||
|     int freq; |  | ||||||
|     int bits16; |  | ||||||
|     int nchannels; |  | ||||||
|     int rpos; |  | ||||||
|     int wpos; |  | ||||||
|     volatile int live; |  | ||||||
|     volatile int exit; |  | ||||||
|     int bytes_per_second; |  | ||||||
|     Uint8 *buf; |  | ||||||
|     int bufsize; |  | ||||||
|     int leftover; |  | ||||||
|     uint64_t old_ticks; |  | ||||||
|     SDL_AudioSpec spec; |  | ||||||
|     SDL_mutex *mutex; |  | ||||||
|     SDL_sem *sem; |  | ||||||
|     void (*copy_fn)(void *, void *, int); |  | ||||||
| } AudioState; |  | ||||||
|  |  | ||||||
| static AudioState sdl_audio; |  | ||||||
|  |  | ||||||
| void AUD_run (void) |  | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void own (AudioState *s) |  | ||||||
| { |  | ||||||
|     /* SDL_LockAudio (); */ |  | ||||||
|     if (SDL_mutexP (s->mutex)) |  | ||||||
|         dolog ("SDL_mutexP: %s\n", SDL_GetError ()); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void disown (AudioState *s) |  | ||||||
| { |  | ||||||
|     /* SDL_UnlockAudio (); */ |  | ||||||
|     if (SDL_mutexV (s->mutex)) |  | ||||||
|         dolog ("SDL_mutexV: %s\n", SDL_GetError ()); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void sem_wait (AudioState *s) |  | ||||||
| { |  | ||||||
|     if (SDL_SemWait (s->sem)) |  | ||||||
|         dolog ("SDL_SemWait: %s\n", SDL_GetError ()); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void sem_post (AudioState *s) |  | ||||||
| { |  | ||||||
|     if (SDL_SemPost (s->sem)) |  | ||||||
|         dolog ("SDL_SemPost: %s\n", SDL_GetError ()); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void audio_callback (void *data, Uint8 *stream, int len) |  | ||||||
| { |  | ||||||
|     int to_mix; |  | ||||||
|     AudioState *s = data; |  | ||||||
|  |  | ||||||
|     if (s->exit) return; |  | ||||||
|     while (len) { |  | ||||||
|         sem_wait (s); |  | ||||||
|         if (s->exit) return; |  | ||||||
|         own (s); |  | ||||||
|         to_mix = MIN (len, s->live); |  | ||||||
|         len -= to_mix; |  | ||||||
|         /* printf ("to_mix=%d len=%d live=%d\n", to_mix, len, s->live); */ |  | ||||||
|         while (to_mix) { |  | ||||||
|             int chunk = MIN (to_mix, s->bufsize - s->rpos); |  | ||||||
|             /* SDL_MixAudio (stream, buf, chunk, SDL_MIX_MAXVOLUME); */ |  | ||||||
|             memcpy (stream, s->buf + s->rpos, chunk); |  | ||||||
|  |  | ||||||
|             s->rpos += chunk; |  | ||||||
|             s->live -= chunk; |  | ||||||
|  |  | ||||||
|             stream += chunk; |  | ||||||
|             to_mix -= chunk; |  | ||||||
|  |  | ||||||
|             if (s->rpos == s->bufsize) s->rpos = 0; |  | ||||||
|         } |  | ||||||
|         disown (s); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void sem_zero (AudioState *s) |  | ||||||
| { |  | ||||||
|     int res; |  | ||||||
|  |  | ||||||
|     do { |  | ||||||
|         res = SDL_SemTryWait (s->sem); |  | ||||||
|         if (res < 0) { |  | ||||||
|             dolog ("SDL_SemTryWait: %s\n", SDL_GetError ()); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|     } while (res != SDL_MUTEX_TIMEDOUT); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void do_open (AudioState *s) |  | ||||||
| { |  | ||||||
|     int status; |  | ||||||
|     SDL_AudioSpec obtained; |  | ||||||
|  |  | ||||||
|     SDL_PauseAudio (1); |  | ||||||
|     if (s->buf) { |  | ||||||
|         s->exit = 1; |  | ||||||
|         sem_post (s); |  | ||||||
|         SDL_CloseAudio (); |  | ||||||
|         s->exit = 0; |  | ||||||
|         qemu_free (s->buf); |  | ||||||
|         s->buf = NULL; |  | ||||||
|         sem_zero (s); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     s->bytes_per_second = (s->spec.freq << (s->spec.channels >> 1)) << s->bits16; |  | ||||||
|     s->spec.samples = conf.samples; |  | ||||||
|     s->spec.userdata = s; |  | ||||||
|     s->spec.callback = audio_callback; |  | ||||||
|  |  | ||||||
|     status = SDL_OpenAudio (&s->spec, &obtained); |  | ||||||
|     if (status < 0) { |  | ||||||
|         dolog ("SDL_OpenAudio: %s\n", SDL_GetError ()); |  | ||||||
|         goto exit; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (obtained.freq != s->spec.freq || |  | ||||||
|         obtained.channels != s->spec.channels || |  | ||||||
|         obtained.format != s->spec.format) { |  | ||||||
|         dolog ("Audio spec mismatch requested obtained\n" |  | ||||||
|                "freq                %5d    %5d\n" |  | ||||||
|                "channels            %5d    %5d\n" |  | ||||||
|                "fmt                 %5d    %5d\n", |  | ||||||
|                s->spec.freq, obtained.freq, |  | ||||||
|                s->spec.channels, obtained.channels, |  | ||||||
|                s->spec.format, obtained.format |  | ||||||
|             ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     s->bufsize = obtained.size; |  | ||||||
|     s->buf = qemu_mallocz (s->bufsize); |  | ||||||
|     if (!s->buf) { |  | ||||||
|         dolog ("qemu_mallocz(%d)\n", s->bufsize); |  | ||||||
|         goto exit; |  | ||||||
|     } |  | ||||||
|     SDL_PauseAudio (0); |  | ||||||
|  |  | ||||||
| exit: |  | ||||||
|     s->rpos = 0; |  | ||||||
|     s->wpos = 0; |  | ||||||
|     s->live = 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int AUD_write (void *in_buf, int size) |  | ||||||
| { |  | ||||||
|     AudioState *s = &sdl_audio; |  | ||||||
|     int to_copy, temp; |  | ||||||
|     uint8_t *in, *out; |  | ||||||
|  |  | ||||||
|     own (s); |  | ||||||
|     to_copy = MIN (s->bufsize - s->live, size); |  | ||||||
|  |  | ||||||
|     temp = to_copy; |  | ||||||
|  |  | ||||||
|     in = in_buf; |  | ||||||
|     out = s->buf; |  | ||||||
|  |  | ||||||
|     while (temp) { |  | ||||||
|         int copy; |  | ||||||
|  |  | ||||||
|         copy = MIN (temp, s->bufsize - s->wpos); |  | ||||||
|         s->copy_fn (out + s->wpos, in, copy); |  | ||||||
|  |  | ||||||
|         s->wpos += copy; |  | ||||||
|         if (s->wpos == s->bufsize) { |  | ||||||
|             s->wpos = 0; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         temp -= copy; |  | ||||||
|         in += copy; |  | ||||||
|         s->live += copy; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     disown (s); |  | ||||||
|     sem_post (s); |  | ||||||
|     return to_copy; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void maybe_open (AudioState *s, int req_freq, int req_nchannels, |  | ||||||
|                         audfmt_e req_fmt, int force_open) |  | ||||||
| { |  | ||||||
|     int sdl_fmt, bits16; |  | ||||||
|  |  | ||||||
|     switch (req_fmt) { |  | ||||||
|     case AUD_FMT_U8: |  | ||||||
|         bits16 = 0; |  | ||||||
|         sdl_fmt = AUDIO_U8; |  | ||||||
|         s->copy_fn = copy_no_conversion; |  | ||||||
|         break; |  | ||||||
|  |  | ||||||
|     case AUD_FMT_S8: |  | ||||||
|         fprintf (stderr, "audio: can not play 8bit signed\n"); |  | ||||||
|         return; |  | ||||||
|  |  | ||||||
|     case AUD_FMT_S16: |  | ||||||
|         bits16 = 1; |  | ||||||
|         sdl_fmt = AUDIO_S16; |  | ||||||
|         s->copy_fn = copy_no_conversion; |  | ||||||
|         break; |  | ||||||
|  |  | ||||||
|     case AUD_FMT_U16: |  | ||||||
|         bits16 = 1; |  | ||||||
|         sdl_fmt = AUDIO_S16; |  | ||||||
|         s->copy_fn = copy_u16_to_s16; |  | ||||||
|         break; |  | ||||||
|  |  | ||||||
|     default: |  | ||||||
|         abort (); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (force_open |  | ||||||
|         || (NULL == s->buf) |  | ||||||
|         || (sdl_fmt != s->spec.format) |  | ||||||
|         || (req_nchannels != s->spec.channels) |  | ||||||
|         || (req_freq != s->spec.freq) |  | ||||||
|         || (bits16 != s->bits16)) { |  | ||||||
|  |  | ||||||
|         s->spec.format = sdl_fmt; |  | ||||||
|         s->spec.channels = req_nchannels; |  | ||||||
|         s->spec.freq = req_freq; |  | ||||||
|         s->bits16 = bits16; |  | ||||||
|         do_open (s); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void AUD_reset (int req_freq, int req_nchannels, audfmt_e req_fmt) |  | ||||||
| { |  | ||||||
|     AudioState *s = &sdl_audio; |  | ||||||
|     own (s); |  | ||||||
|     maybe_open (s, req_freq, req_nchannels, req_fmt, 0); |  | ||||||
|     disown (s); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void AUD_open (int req_freq, int req_nchannels, audfmt_e req_fmt) |  | ||||||
| { |  | ||||||
|     AudioState *s = &sdl_audio; |  | ||||||
|     own (s); |  | ||||||
|     maybe_open (s, req_freq, req_nchannels, req_fmt, 1); |  | ||||||
|     disown (s); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void AUD_adjust_estimate (int leftover) |  | ||||||
| { |  | ||||||
|     AudioState *s = &sdl_audio; |  | ||||||
|     own (s); |  | ||||||
|     s->leftover = leftover; |  | ||||||
|     disown (s); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int AUD_get_free (void) |  | ||||||
| { |  | ||||||
|     int free, elapsed; |  | ||||||
|     uint64_t ticks, delta; |  | ||||||
|     uint64_t ua_elapsed; |  | ||||||
|     uint64_t al_elapsed; |  | ||||||
|     AudioState *s = &sdl_audio; |  | ||||||
|  |  | ||||||
|     own (s); |  | ||||||
|     free = s->bufsize - s->live; |  | ||||||
|  |  | ||||||
|     if (0 == free) { |  | ||||||
|         disown (s); |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     elapsed = free; |  | ||||||
|     ticks = qemu_get_clock(rt_clock); |  | ||||||
|     delta = ticks - s->old_ticks; |  | ||||||
|     s->old_ticks = ticks; |  | ||||||
|  |  | ||||||
|     ua_elapsed = (delta * s->bytes_per_second) / 1000; |  | ||||||
|     al_elapsed = ua_elapsed & ~3ULL; |  | ||||||
|  |  | ||||||
|     ldebug ("tid elapsed %llu bytes\n", ua_elapsed); |  | ||||||
|  |  | ||||||
|     if (al_elapsed > (uint64_t) INT_MAX) |  | ||||||
|         elapsed = INT_MAX; |  | ||||||
|     else |  | ||||||
|         elapsed = al_elapsed; |  | ||||||
|  |  | ||||||
|     elapsed += s->leftover; |  | ||||||
|     disown (s); |  | ||||||
|  |  | ||||||
|     if (elapsed > free) { |  | ||||||
|         lwarn ("audio can not keep up elapsed %d free %d\n", elapsed, free); |  | ||||||
|         return free; |  | ||||||
|     } |  | ||||||
|     else { |  | ||||||
|         return elapsed; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int AUD_get_live (void) |  | ||||||
| { |  | ||||||
|     int live; |  | ||||||
|     AudioState *s = &sdl_audio; |  | ||||||
|  |  | ||||||
|     own (s); |  | ||||||
|     live = s->live; |  | ||||||
|     disown (s); |  | ||||||
|     return live; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int AUD_get_buffer_size (void) |  | ||||||
| { |  | ||||||
|     int bufsize; |  | ||||||
|     AudioState *s = &sdl_audio; |  | ||||||
|  |  | ||||||
|     own (s); |  | ||||||
|     bufsize = s->bufsize; |  | ||||||
|     disown (s); |  | ||||||
|     return bufsize; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #define QC_SDL_NSAMPLES "QEMU_SDL_NSAMPLES" |  | ||||||
|  |  | ||||||
| static void cleanup (void) |  | ||||||
| { |  | ||||||
|     AudioState *s = &sdl_audio; |  | ||||||
|     own (s); |  | ||||||
|     s->exit = 1; |  | ||||||
|     sem_post (s); |  | ||||||
|     disown (s); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void AUD_init (void) |  | ||||||
| { |  | ||||||
|     AudioState *s = &sdl_audio; |  | ||||||
|  |  | ||||||
|     atexit (cleanup); |  | ||||||
|     SDL_InitSubSystem (SDL_INIT_AUDIO); |  | ||||||
|     s->mutex = SDL_CreateMutex (); |  | ||||||
|     if (!s->mutex) { |  | ||||||
|         dolog ("SDL_CreateMutex: %s\n", SDL_GetError ()); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     s->sem = SDL_CreateSemaphore (0); |  | ||||||
|     if (!s->sem) { |  | ||||||
|         dolog ("SDL_CreateSemaphore: %s\n", SDL_GetError ()); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     conf.samples = get_conf_val (QC_SDL_NSAMPLES, conf.samples); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #elif !defined(_WIN32) && !defined(__APPLE__) |  | ||||||
|  |  | ||||||
| #include <fcntl.h> |  | ||||||
| #include <errno.h> |  | ||||||
| #include <unistd.h> |  | ||||||
| #include <sys/mman.h> |  | ||||||
| #include <sys/types.h> |  | ||||||
| #include <sys/ioctl.h> |  | ||||||
| #include <sys/soundcard.h> |  | ||||||
|  |  | ||||||
| /* http://www.df.lth.se/~john_e/gems/gem002d.html */ |  | ||||||
| /* http://www.multi-platforms.com/Tips/PopCount.htm */ |  | ||||||
| static inline uint32_t popcount (uint32_t u) |  | ||||||
| { |  | ||||||
|   u = ((u&0x55555555) + ((u>>1)&0x55555555)); |  | ||||||
|   u = ((u&0x33333333) + ((u>>2)&0x33333333)); |  | ||||||
|   u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f)); |  | ||||||
|   u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff)); |  | ||||||
|   u = ( u&0x0000ffff) + (u>>16); |  | ||||||
|   return u; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline uint32_t lsbindex (uint32_t u) |  | ||||||
| { |  | ||||||
|   return popcount ((u&-u)-1); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #define IOCTL(args) do {                        \ |  | ||||||
|   int ret = ioctl args;                         \ |  | ||||||
|   if (-1 == ret) {                              \ |  | ||||||
|     ERRFail (#args);                            \ |  | ||||||
|   }                                             \ |  | ||||||
|   ldebug ("ioctl " #args " = %d\n", ret);       \ |  | ||||||
| } while (0) |  | ||||||
|  |  | ||||||
| typedef struct AudioState { |  | ||||||
|     int fd; |  | ||||||
|     int freq; |  | ||||||
|     int bits16; |  | ||||||
|     int nchannels; |  | ||||||
|     int rpos; |  | ||||||
|     int wpos; |  | ||||||
|     int live; |  | ||||||
|     int oss_fmt; |  | ||||||
|     int bytes_per_second; |  | ||||||
|     int is_mapped; |  | ||||||
|     void *buf; |  | ||||||
|     int bufsize; |  | ||||||
|     int nfrags; |  | ||||||
|     int fragsize; |  | ||||||
|     int old_optr; |  | ||||||
|     int leftover; |  | ||||||
|     uint64_t old_ticks; |  | ||||||
|     void (*copy_fn)(void *, void *, int); |  | ||||||
| } AudioState; |  | ||||||
|  |  | ||||||
| static AudioState oss_audio = { .fd = -1 }; |  | ||||||
|  |  | ||||||
| static struct { |  | ||||||
|     int try_mmap; |  | ||||||
|     int nfrags; |  | ||||||
|     int fragsize; |  | ||||||
| } conf = { |  | ||||||
|     .try_mmap = 0, |  | ||||||
|     .nfrags = 4, |  | ||||||
|     .fragsize = 4096 |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static enum {DONT, DSP, TID} est = DONT; |  | ||||||
|  |  | ||||||
| static void pab (AudioState *s, struct audio_buf_info *abinfo) |  | ||||||
| { |  | ||||||
|     DEREF (abinfo); |  | ||||||
|  |  | ||||||
|     ldebug ("fragments %d, fragstotal %d, fragsize %d, bytes %d\n" |  | ||||||
|             "rpos %d, wpos %d, live %d\n", |  | ||||||
|             abinfo->fragments, |  | ||||||
|             abinfo->fragstotal, |  | ||||||
|             abinfo->fragsize, |  | ||||||
|             abinfo->bytes, |  | ||||||
|             s->rpos, s->wpos, s->live); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void do_open (AudioState *s) |  | ||||||
| { |  | ||||||
|     int mmmmssss; |  | ||||||
|     audio_buf_info abinfo; |  | ||||||
|     int fmt, freq, nchannels; |  | ||||||
|  |  | ||||||
|     if (s->buf) { |  | ||||||
|         if (s->is_mapped) { |  | ||||||
|             if (-1 == munmap (s->buf, s->bufsize)) { |  | ||||||
|             ERRFail ("failed to unmap audio buffer %p %d", |  | ||||||
|                          s->buf, s->bufsize); |  | ||||||
|         } |  | ||||||
|         } |  | ||||||
|         else { |  | ||||||
|             qemu_free (s->buf); |  | ||||||
|         } |  | ||||||
|         s->buf = NULL; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (-1 != s->fd) |  | ||||||
|         close (s->fd); |  | ||||||
|  |  | ||||||
|     s->fd = open ("/dev/dsp", O_RDWR | O_NONBLOCK); |  | ||||||
|     if (-1 == s->fd) { |  | ||||||
|         ERRFail ("can not open /dev/dsp"); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fmt = s->oss_fmt; |  | ||||||
|     freq = s->freq; |  | ||||||
|     nchannels = s->nchannels; |  | ||||||
|  |  | ||||||
|     IOCTL ((s->fd, SNDCTL_DSP_RESET, 1)); |  | ||||||
|     IOCTL ((s->fd, SNDCTL_DSP_SAMPLESIZE, &fmt)); |  | ||||||
|     IOCTL ((s->fd, SNDCTL_DSP_CHANNELS, &nchannels)); |  | ||||||
|     IOCTL ((s->fd, SNDCTL_DSP_SPEED, &freq)); |  | ||||||
|     IOCTL ((s->fd, SNDCTL_DSP_NONBLOCK)); |  | ||||||
|  |  | ||||||
|     mmmmssss = (conf.nfrags << 16) | conf.fragsize; |  | ||||||
|     IOCTL ((s->fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss)); |  | ||||||
|  |  | ||||||
|     if ((s->oss_fmt != fmt) |  | ||||||
|         || (s->nchannels != nchannels) |  | ||||||
|         || (s->freq != freq)) { |  | ||||||
|         Fail ("failed to set audio parameters\n" |  | ||||||
|               "parameter | requested value | obtained value\n" |  | ||||||
|               "format    |      %10d |     %10d\n" |  | ||||||
|               "channels  |      %10d |     %10d\n" |  | ||||||
|               "frequency |      %10d |     %10d\n", |  | ||||||
|               s->oss_fmt, fmt, |  | ||||||
|               s->nchannels, nchannels, |  | ||||||
|               s->freq, freq); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     IOCTL ((s->fd, SNDCTL_DSP_GETOSPACE, &abinfo)); |  | ||||||
|  |  | ||||||
|     s->nfrags = abinfo.fragstotal; |  | ||||||
|     s->fragsize = abinfo.fragsize; |  | ||||||
|     s->bufsize = s->nfrags * s->fragsize; |  | ||||||
|     s->old_optr = 0; |  | ||||||
|  |  | ||||||
|     s->bytes_per_second = (freq << (nchannels >> 1)) << s->bits16; |  | ||||||
|  |  | ||||||
|     linfo ("bytes per second %d\n", s->bytes_per_second); |  | ||||||
|  |  | ||||||
|     linfo ("fragments %d, fragstotal %d, fragsize %d, bytes %d, bufsize %d\n", |  | ||||||
|            abinfo.fragments, |  | ||||||
|            abinfo.fragstotal, |  | ||||||
|            abinfo.fragsize, |  | ||||||
|            abinfo.bytes, |  | ||||||
|            s->bufsize); |  | ||||||
|  |  | ||||||
|     s->buf = MAP_FAILED; |  | ||||||
|     s->is_mapped = 0; |  | ||||||
|  |  | ||||||
|     if (conf.try_mmap) { |  | ||||||
|         s->buf = mmap (NULL, s->bufsize, PROT_WRITE, MAP_SHARED, s->fd, 0); |  | ||||||
|         if (MAP_FAILED == s->buf) { |  | ||||||
|             int err; |  | ||||||
|  |  | ||||||
|             err = errno; |  | ||||||
|             dolog ("failed to mmap audio, size %d, fd %d\n" |  | ||||||
|                  "syserr: %s\n", |  | ||||||
|                    s->bufsize, s->fd, strerror (err)); |  | ||||||
|         } |  | ||||||
|     else { |  | ||||||
|             est = TID; |  | ||||||
|             s->is_mapped = 1; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (MAP_FAILED == s->buf) { |  | ||||||
|         est = TID; |  | ||||||
|         s->buf = qemu_mallocz (s->bufsize); |  | ||||||
|         if (!s->buf) { |  | ||||||
|             ERRFail ("audio buf malloc failed, size %d", s->bufsize); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     s->rpos = 0; |  | ||||||
|     s->wpos = 0; |  | ||||||
|     s->live = 0; |  | ||||||
|  |  | ||||||
|     if (s->is_mapped) { |  | ||||||
|         int trig; |  | ||||||
|  |  | ||||||
|         trig = 0; |  | ||||||
|         IOCTL ((s->fd, SNDCTL_DSP_SETTRIGGER, &trig)); |  | ||||||
|         trig = PCM_ENABLE_OUTPUT; |  | ||||||
|         IOCTL ((s->fd, SNDCTL_DSP_SETTRIGGER, &trig)); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void maybe_open (AudioState *s, int req_freq, int req_nchannels, |  | ||||||
|                         audfmt_e req_fmt, int force_open) |  | ||||||
| { |  | ||||||
|     int oss_fmt, bits16; |  | ||||||
|  |  | ||||||
|     switch (req_fmt) { |  | ||||||
|     case AUD_FMT_U8: |  | ||||||
|         bits16 = 0; |  | ||||||
|         oss_fmt = AFMT_U8; |  | ||||||
|         s->copy_fn = copy_no_conversion; |  | ||||||
|         break; |  | ||||||
|  |  | ||||||
|     case AUD_FMT_S8: |  | ||||||
|         Fail ("can not play 8bit signed"); |  | ||||||
|  |  | ||||||
|     case AUD_FMT_S16: |  | ||||||
|         bits16 = 1; |  | ||||||
|         oss_fmt = AFMT_S16_LE; |  | ||||||
|         s->copy_fn = copy_no_conversion; |  | ||||||
|         break; |  | ||||||
|  |  | ||||||
|     case AUD_FMT_U16: |  | ||||||
|         bits16 = 1; |  | ||||||
|         oss_fmt = AFMT_S16_LE; |  | ||||||
|         s->copy_fn = copy_u16_to_s16; |  | ||||||
|         break; |  | ||||||
|  |  | ||||||
|     default: |  | ||||||
|         abort (); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (force_open |  | ||||||
|         || (-1 == s->fd) |  | ||||||
|         || (oss_fmt != s->oss_fmt) |  | ||||||
|         || (req_nchannels != s->nchannels) |  | ||||||
|         || (req_freq != s->freq) |  | ||||||
|         || (bits16 != s->bits16)) { |  | ||||||
|         s->oss_fmt = oss_fmt; |  | ||||||
|         s->nchannels = req_nchannels; |  | ||||||
|         s->freq = req_freq; |  | ||||||
|         s->bits16 = bits16; |  | ||||||
|         do_open (s); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void AUD_reset (int req_freq, int req_nchannels, audfmt_e req_fmt) |  | ||||||
| { |  | ||||||
|     AudioState *s = &oss_audio; |  | ||||||
|     maybe_open (s, req_freq, req_nchannels, req_fmt, 0); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void AUD_open (int req_freq, int req_nchannels, audfmt_e req_fmt) |  | ||||||
| { |  | ||||||
|     AudioState *s = &oss_audio; |  | ||||||
|     maybe_open (s, req_freq, req_nchannels, req_fmt, 1); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int AUD_write (void *in_buf, int size) |  | ||||||
| { |  | ||||||
|     AudioState *s = &oss_audio; |  | ||||||
|     int to_copy, temp; |  | ||||||
|     uint8_t *in, *out; |  | ||||||
|  |  | ||||||
|     to_copy = MIN (s->bufsize - s->live, size); |  | ||||||
|  |  | ||||||
|     temp = to_copy; |  | ||||||
|  |  | ||||||
|     in = in_buf; |  | ||||||
|     out = s->buf; |  | ||||||
|  |  | ||||||
|     while (temp) { |  | ||||||
|         int copy; |  | ||||||
|  |  | ||||||
|         copy = MIN (temp, s->bufsize - s->wpos); |  | ||||||
|         s->copy_fn (out + s->wpos, in, copy); |  | ||||||
|  |  | ||||||
|         s->wpos += copy; |  | ||||||
|         if (s->wpos == s->bufsize) { |  | ||||||
|             s->wpos = 0; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         temp -= copy; |  | ||||||
|         in += copy; |  | ||||||
|         s->live += copy; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return to_copy; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void AUD_run (void) |  | ||||||
| { |  | ||||||
|     int res; |  | ||||||
|     int bytes; |  | ||||||
|     struct audio_buf_info abinfo; |  | ||||||
|     AudioState *s = &oss_audio; |  | ||||||
|  |  | ||||||
|     if (0 == s->live) |  | ||||||
|         return; |  | ||||||
|  |  | ||||||
|     if (s->is_mapped) { |  | ||||||
|         count_info info; |  | ||||||
|  |  | ||||||
|         res = ioctl (s->fd, SNDCTL_DSP_GETOPTR, &info); |  | ||||||
|         if (res < 0) { |  | ||||||
|             int err; |  | ||||||
|  |  | ||||||
|             err = errno; |  | ||||||
|             lwarn ("SNDCTL_DSP_GETOPTR failed with %s\n", strerror (err)); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (info.ptr > s->old_optr) { |  | ||||||
|             bytes = info.ptr - s->old_optr; |  | ||||||
|         } |  | ||||||
|         else { |  | ||||||
|             bytes = s->bufsize + info.ptr - s->old_optr; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         s->old_optr = info.ptr; |  | ||||||
|         s->live -= bytes; |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     res = ioctl (s->fd, SNDCTL_DSP_GETOSPACE, &abinfo); |  | ||||||
|  |  | ||||||
|     if (res < 0) { |  | ||||||
|         int err; |  | ||||||
|  |  | ||||||
|         err = errno; |  | ||||||
|         lwarn ("SNDCTL_DSP_GETOSPACE failed with %s\n", strerror (err)); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     bytes = abinfo.bytes; |  | ||||||
|     bytes = MIN (s->live, bytes); |  | ||||||
| #if 0 |  | ||||||
|     bytes = (bytes / fragsize) * fragsize; |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     while (bytes) { |  | ||||||
|         int left, play, written; |  | ||||||
|  |  | ||||||
|         left = s->bufsize - s->rpos; |  | ||||||
|         play = MIN (left, bytes); |  | ||||||
|         written = write (s->fd, (uint8_t *)s->buf + s->rpos, play); |  | ||||||
|  |  | ||||||
|         if (-1 == written) { |  | ||||||
|             if (EAGAIN == errno || EINTR == errno) { |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
|             else { |  | ||||||
|                 ERRFail ("write audio"); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         play = written; |  | ||||||
|         s->live -= play; |  | ||||||
|         s->rpos += play; |  | ||||||
|         bytes -= play; |  | ||||||
|  |  | ||||||
|         if (s->rpos == s->bufsize) { |  | ||||||
|             s->rpos = 0; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int get_dsp_bytes (void) |  | ||||||
| { |  | ||||||
|     int res; |  | ||||||
|     struct count_info info; |  | ||||||
|     AudioState *s = &oss_audio; |  | ||||||
|  |  | ||||||
|     res = ioctl (s->fd, SNDCTL_DSP_GETOPTR, &info); |  | ||||||
|     if (-1 == res) { |  | ||||||
|         int err; |  | ||||||
|  |  | ||||||
|         err = errno; |  | ||||||
|         lwarn ("SNDCTL_DSP_GETOPTR failed with %s\n", strerror (err)); |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|     else { |  | ||||||
|         ldebug ("bytes %d\n", info.bytes); |  | ||||||
|         return info.bytes; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void AUD_adjust_estimate (int leftover) |  | ||||||
| { |  | ||||||
|     AudioState *s = &oss_audio; |  | ||||||
|     s->leftover = leftover; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int AUD_get_free (void) |  | ||||||
| { |  | ||||||
|     int free, elapsed; |  | ||||||
|     AudioState *s = &oss_audio; |  | ||||||
|  |  | ||||||
|     free = s->bufsize - s->live; |  | ||||||
|  |  | ||||||
|     if (free <= 0) |  | ||||||
|         return 0; |  | ||||||
|  |  | ||||||
|     elapsed = free; |  | ||||||
|     switch (est) { |  | ||||||
|     case DONT: |  | ||||||
|         break; |  | ||||||
|  |  | ||||||
|     case DSP: |  | ||||||
|         { |  | ||||||
|             static int old_bytes; |  | ||||||
|             int bytes; |  | ||||||
|  |  | ||||||
|             bytes = get_dsp_bytes (); |  | ||||||
|             if (bytes <= 0) |  | ||||||
|                 return free; |  | ||||||
|  |  | ||||||
|             elapsed = bytes - old_bytes; |  | ||||||
|             old_bytes = bytes; |  | ||||||
|             ldebug ("dsp elapsed %d bytes\n", elapsed); |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|     case TID: |  | ||||||
|         { |  | ||||||
|             uint64_t ticks, delta; |  | ||||||
|             uint64_t ua_elapsed; |  | ||||||
|             uint64_t al_elapsed; |  | ||||||
|  |  | ||||||
|             ticks = qemu_get_clock(rt_clock); |  | ||||||
|             delta = ticks - s->old_ticks; |  | ||||||
|             s->old_ticks = ticks; |  | ||||||
|  |  | ||||||
|             ua_elapsed = (delta * s->bytes_per_second) / 1000; |  | ||||||
|             al_elapsed = ua_elapsed & ~3ULL; |  | ||||||
|  |  | ||||||
|             ldebug ("tid elapsed %llu bytes\n", ua_elapsed); |  | ||||||
|  |  | ||||||
|             if (al_elapsed > (uint64_t) INT_MAX) |  | ||||||
|                 elapsed = INT_MAX; |  | ||||||
|             else |  | ||||||
|                 elapsed = al_elapsed; |  | ||||||
|  |  | ||||||
|             elapsed += s->leftover; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (elapsed > free) { |  | ||||||
|         lwarn ("audio can not keep up elapsed %d free %d\n", elapsed, free); |  | ||||||
|         return free; |  | ||||||
|     } |  | ||||||
|     else { |  | ||||||
|         return elapsed; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int AUD_get_live (void) |  | ||||||
| { |  | ||||||
|     AudioState *s = &oss_audio; |  | ||||||
|     return s->live; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int AUD_get_buffer_size (void) |  | ||||||
| { |  | ||||||
|     AudioState *s = &oss_audio; |  | ||||||
|     return s->bufsize; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #define QC_OSS_FRAGSIZE "QEMU_OSS_FRAGSIZE" |  | ||||||
| #define QC_OSS_NFRAGS "QEMU_OSS_NFRAGS" |  | ||||||
| #define QC_OSS_MMAP "QEMU_OSS_MMAP" |  | ||||||
|  |  | ||||||
| void AUD_init (void) |  | ||||||
| { |  | ||||||
|     int fsp; |  | ||||||
|  |  | ||||||
|     DEREF (pab); |  | ||||||
|  |  | ||||||
|     conf.fragsize = get_conf_val (QC_OSS_FRAGSIZE, conf.fragsize); |  | ||||||
|     conf.nfrags = get_conf_val (QC_OSS_NFRAGS, conf.nfrags); |  | ||||||
|     conf.try_mmap = get_conf_val (QC_OSS_MMAP, conf.try_mmap); |  | ||||||
|  |  | ||||||
|     fsp = conf.fragsize; |  | ||||||
|     if (0 != (fsp & (fsp - 1))) { |  | ||||||
|         Fail ("fragment size %d is not power of 2", fsp); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     conf.fragsize = lsbindex (fsp); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #else |  | ||||||
|  |  | ||||||
| void AUD_run (void) |  | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int AUD_write (void *in_buf, int size) |  | ||||||
| { |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void AUD_reset (int rfreq, int rnchannels, audfmt_e rfmt) |  | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void AUD_adjust_estimate (int _leftover) |  | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int AUD_get_free (void) |  | ||||||
| { |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int AUD_get_live (void) |  | ||||||
| { |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int AUD_get_buffer_size (void) |  | ||||||
| { |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void AUD_init (void) |  | ||||||
| { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #endif |  | ||||||
							
								
								
									
										6
									
								
								vl.c
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								vl.c
									
									
									
									
									
								
							| @@ -2438,12 +2438,6 @@ void main_loop_wait(int timeout) | |||||||
|         if (vm_running) { |         if (vm_running) { | ||||||
|             qemu_run_timers(&active_timers[QEMU_TIMER_VIRTUAL],  |             qemu_run_timers(&active_timers[QEMU_TIMER_VIRTUAL],  | ||||||
|                             qemu_get_clock(vm_clock)); |                             qemu_get_clock(vm_clock)); | ||||||
|              |  | ||||||
|             if (audio_enabled) { |  | ||||||
|                 /* XXX: add explicit timer */ |  | ||||||
|                 SB16_run(); |  | ||||||
|             } |  | ||||||
|              |  | ||||||
|             /* run dma transfers, if any */ |             /* run dma transfers, if any */ | ||||||
|             DMA_run(); |             DMA_run(); | ||||||
|         } |         } | ||||||
|   | |||||||
							
								
								
									
										30
									
								
								vl.h
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								vl.h
									
									
									
									
									
								
							| @@ -30,6 +30,7 @@ | |||||||
| #include <stdarg.h> | #include <stdarg.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <inttypes.h> | #include <inttypes.h> | ||||||
|  | #include <limits.h> | ||||||
| #include <time.h> | #include <time.h> | ||||||
| #include <ctype.h> | #include <ctype.h> | ||||||
| #include <errno.h> | #include <errno.h> | ||||||
| @@ -553,39 +554,22 @@ void pci_piix3_ide_init(PCIBus *bus, BlockDriverState **hd_table); | |||||||
| int pmac_ide_init (BlockDriverState **hd_table, | int pmac_ide_init (BlockDriverState **hd_table, | ||||||
|                    openpic_t *openpic, int irq); |                    openpic_t *openpic, int irq); | ||||||
|  |  | ||||||
| /* oss.c */ | /* audio.c */ | ||||||
| typedef enum { |  | ||||||
|   AUD_FMT_U8, |  | ||||||
|   AUD_FMT_S8, |  | ||||||
|   AUD_FMT_U16, |  | ||||||
|   AUD_FMT_S16 |  | ||||||
| } audfmt_e; |  | ||||||
|  |  | ||||||
| void AUD_open (int rfreq, int rnchannels, audfmt_e rfmt); |  | ||||||
| void AUD_reset (int rfreq, int rnchannels, audfmt_e rfmt); |  | ||||||
| int AUD_write (void *in_buf, int size); |  | ||||||
| void AUD_run (void); |  | ||||||
| void AUD_adjust_estimate (int _leftover); |  | ||||||
| int AUD_get_free (void); |  | ||||||
| int AUD_get_live (void); |  | ||||||
| int AUD_get_buffer_size (void); |  | ||||||
| void AUD_init (void); | void AUD_init (void); | ||||||
|  |  | ||||||
| /* dma.c */ | /* dma.c */ | ||||||
| typedef int (*DMA_transfer_handler) (void *opaque, target_ulong addr, int size); | typedef int (*DMA_transfer_handler) (void *opaque, int nchan, int pos, int size); | ||||||
| int DMA_get_channel_mode (int nchan); | int DMA_get_channel_mode (int nchan); | ||||||
|  | int DMA_read_memory (int nchan, void *buf, int pos, int size); | ||||||
|  | int DMA_write_memory (int nchan, void *buf, int pos, int size); | ||||||
| void DMA_hold_DREQ (int nchan); | void DMA_hold_DREQ (int nchan); | ||||||
| void DMA_release_DREQ (int nchan); | void DMA_release_DREQ (int nchan); | ||||||
| void DMA_schedule(int nchan); | void DMA_schedule(int nchan); | ||||||
| void DMA_run (void); | void DMA_run (void); | ||||||
| void DMA_init (int high_page_enable); | void DMA_init (int high_page_enable); | ||||||
| void DMA_register_channel (int nchan, | void DMA_register_channel (int nchan, | ||||||
|                            DMA_transfer_handler transfer_handler, void *opaque); |                            DMA_transfer_handler transfer_handler, | ||||||
|  |                            void *opaque); | ||||||
| /* sb16.c */ |  | ||||||
| void SB16_run (void); |  | ||||||
| void SB16_init (void); |  | ||||||
|   |  | ||||||
| /* fdc.c */ | /* fdc.c */ | ||||||
| #define MAX_FD 2 | #define MAX_FD 2 | ||||||
| extern BlockDriverState *fd_table[MAX_FD]; | extern BlockDriverState *fd_table[MAX_FD]; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user