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