/* * sound/gus_wave.c * * Driver for the Gravis UltraSound wave table synth. * * Copyright by Hannu Savolainen 1993 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include "sound_config.h" #ifdef __FreeBSD__ #include #else #include "ultrasound.h" #endif #include "gus_hw.h" #if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_GUS) #define MAX_SAMPLE 128 #define MAX_PATCH 256 struct voice_info { unsigned long orig_freq; unsigned long current_freq; unsigned long mode; int bender; int bender_range; int panning; int midi_volume; unsigned int initial_volume; unsigned int current_volume; int loop_irq_mode, loop_irq_parm; #define LMODE_FINISH 1 #define LMODE_PCM 2 #define LMODE_PCM_STOP 3 int volume_irq_mode, volume_irq_parm; #define VMODE_HALT 1 #define VMODE_ENVELOPE 2 #define VMODE_START_NOTE 3 int env_phase; unsigned char env_rate[6]; unsigned char env_offset[6]; /* * Volume computation parameters for gus_adagio_vol() */ int main_vol, expression_vol, patch_vol; /* Variables for "Ultraclick" removal */ int dev_pending, note_pending, volume_pending, sample_pending; char kill_pending; long offset_pending; }; extern int gus_base; extern int gus_irq, gus_dma; extern char *snd_raw_buf[MAX_DSP_DEV][DSP_BUFFCOUNT]; extern unsigned long snd_raw_buf_phys[MAX_DSP_DEV][DSP_BUFFCOUNT]; extern int snd_raw_count[MAX_DSP_DEV]; static long gus_mem_size = 0; static long free_mem_ptr = 0; static int gus_busy = 0; static int nr_voices = 0; static int gus_devnum = 0; static int volume_base, volume_scale, volume_method; static int gus_line_vol = 100, gus_mic_vol = 0; static int gus_recmask = SOUND_MASK_MIC; static int recording_active = 0; int gus_wave_volume = 60; int gus_pcm_volume = 80; static unsigned char mix_image = 0x00; /* * Current version of this driver doesn't allow synth and PCM functions * at the same time. The active_device specifies the active driver */ static int active_device = 0; #define GUS_DEV_WAVE 1 /* * * * Wave table synth */ #define GUS_DEV_PCM_DONE 2 /* * * * PCM device, transfer done */ #define GUS_DEV_PCM_CONTINUE 3 /* * * * PCM device, transfer the * second * * * chn */ static int gus_sampling_speed; static int gus_sampling_channels; static int gus_sampling_bits; DEFINE_WAIT_QUEUE (dram_sleeper, dram_sleep_flag); /* * Variables and buffers for PCM output */ #define MAX_PCM_BUFFERS (32*MAX_REALTIME_FACTOR) /* * * * Don't * * * change * */ static int pcm_bsize, /* * Current blocksize */ pcm_nblk, /* * Current # of blocks */ pcm_banksize; /* * * * * # bytes allocated for channels */ static int pcm_datasize[MAX_PCM_BUFFERS]; /* * * * * Actual # of bytes * in blk * */ static volatile int pcm_head, pcm_tail, pcm_qlen; /* * * * * DRAM queue * */ static volatile int pcm_active; static int pcm_opened = 0; static int pcm_current_dev; static int pcm_current_block; static unsigned long pcm_current_buf; static int pcm_current_count; static int pcm_current_intrflag; struct voice_info voices[32]; static int freq_div_table[] = { 44100, /* * 14 */ 41160, /* * 15 */ 38587, /* * 16 */ 36317, /* * 17 */ 34300, /* * 18 */ 32494, /* * 19 */ 30870, /* * 20 */ 29400, /* * 21 */ 28063, /* * 22 */ 26843, /* * 23 */ 25725, /* * 24 */ 24696, /* * 25 */ 23746, /* * 26 */ 22866, /* * 27 */ 22050, /* * 28 */ 21289, /* * 29 */ 20580, /* * 30 */ 19916, /* * 31 */ 19293 /* * 32 */ }; static struct patch_info *samples; static long sample_ptrs[MAX_SAMPLE + 1]; static int sample_map[32]; static int free_sample; static int patch_table[MAX_PATCH]; static int patch_map[32]; static struct synth_info gus_info = {"Gravis UltraSound", 0, SYNTH_TYPE_SAMPLE, SAMPLE_TYPE_GUS, 0, 16, 0, MAX_PATCH}; static void gus_poke (long addr, unsigned char data); static void compute_and_set_volume (int voice, int volume, int ramp_time); extern unsigned short gus_adagio_vol (int vel, int mainv, int xpn, int voicev); extern unsigned short gus_linear_vol (int vol, int mainvol); static void compute_volume (int voice, int volume); static void do_volume_irq (int voice); static void set_input_volumes (void); #define INSTANT_RAMP -1 /* * * * Dont use ramping */ #define FAST_RAMP 0 /* * * * Fastest possible ramp */ static void reset_sample_memory (void) { int i; for (i = 0; i <= MAX_SAMPLE; i++) sample_ptrs[i] = -1; for (i = 0; i < 32; i++) sample_map[i] = -1; for (i = 0; i < 32; i++) patch_map[i] = -1; gus_poke (0, 0); /* * Put silence here */ gus_poke (1, 0); free_mem_ptr = 2; free_sample = 0; for (i = 0; i < MAX_PATCH; i++) patch_table[i] = -1; } void gus_delay (void) { int i; for (i = 0; i < 7; i++) INB (u_DRAMIO); } static void gus_poke (long addr, unsigned char data) { unsigned long flags; DISABLE_INTR (flags); OUTB (0x43, u_Command); OUTB (addr & 0xff, u_DataLo); OUTB ((addr >> 8) & 0xff, u_DataHi); OUTB (0x44, u_Command); OUTB ((addr >> 16) & 0xff, u_DataHi); OUTB (data, u_DRAMIO); RESTORE_INTR (flags); } static unsigned char gus_peek (long addr) { unsigned long flags; unsigned char tmp; DISABLE_INTR (flags); OUTB (0x43, u_Command); OUTB (addr & 0xff, u_DataLo); OUTB ((addr >> 8) & 0xff, u_DataHi); OUTB (0x44, u_Command); OUTB ((addr >> 16) & 0xff, u_DataHi); tmp = INB (u_DRAMIO); RESTORE_INTR (flags); return tmp; } void gus_write8 (int reg, unsigned int data) { unsigned long flags; DISABLE_INTR (flags); OUTB (reg, u_Command); OUTB ((unsigned char) (data & 0xff), u_DataHi); RESTORE_INTR (flags); } unsigned char gus_read8 (int reg) { unsigned long flags; unsigned char val; DISABLE_INTR (flags); OUTB (reg | 0x80, u_Command); val = INB (u_DataHi); RESTORE_INTR (flags); return val; } unsigned char gus_look8 (int reg) { unsigned long flags; unsigned char val; DISABLE_INTR (flags); OUTB (reg, u_Command); val = INB (u_DataHi); RESTORE_INTR (flags); return val; } void gus_write16 (int reg, unsigned int data) { unsigned long flags; DISABLE_INTR (flags); OUTB (reg, u_Command); OUTB ((unsigned char) (data & 0xff), u_DataLo); OUTB ((unsigned char) ((data >> 8) & 0xff), u_DataHi); RESTORE_INTR (flags); } unsigned short gus_read16 (int reg) { unsigned long flags; unsigned char hi, lo; DISABLE_INTR (flags); OUTB (reg | 0x80, u_Command); lo = INB (u_DataLo); hi = INB (u_DataHi); RESTORE_INTR (flags); return ((hi << 8) & 0xff00) | lo; } void gus_write_addr (int reg, unsigned long address, int is16bit) { unsigned long hold_address; if (is16bit) { /* * Special processing required for 16 bit patches */ hold_address = address; address = address >> 1; address &= 0x0001ffffL; address |= (hold_address & 0x000c0000L); } gus_write16 (reg, (unsigned short) ((address >> 7) & 0xffff)); gus_write16 (reg + 1, (unsigned short) ((address << 9) & 0xffff)); /* Could writing twice fix problems with GUS_VOICE_POS() ? Lets try... */ gus_delay (); gus_write16 (reg, (unsigned short) ((address >> 7) & 0xffff)); gus_write16 (reg + 1, (unsigned short) ((address << 9) & 0xffff)); } static void gus_select_voice (int voice) { if (voice < 0 || voice > 31) return; OUTB (voice, u_Voice); } static void gus_select_max_voices (int nvoices) { if (nvoices < 14) nvoices = 14; if (nvoices > 32) nvoices = 32; nr_voices = nvoices; gus_write8 (0x0e, (nvoices - 1) | 0xc0); } static void gus_voice_on (unsigned int mode) { gus_write8 (0x00, (unsigned char) (mode & 0xfc)); gus_delay (); gus_write8 (0x00, (unsigned char) (mode & 0xfc)); } static void gus_voice_off (void) { gus_write8 (0x00, gus_read8 (0x00) | 0x03); } static void gus_voice_mode (unsigned int m) { unsigned char mode = (unsigned char) (m & 0xff); gus_write8 (0x00, (gus_read8 (0x00) & 0x03) | (mode & 0xfc)); /* * Don't * start * or * stop * * * voice */ gus_delay (); gus_write8 (0x00, (gus_read8 (0x00) & 0x03) | (mode & 0xfc)); } static void gus_voice_freq (unsigned long freq) { unsigned long divisor = freq_div_table[nr_voices - 14]; unsigned short fc; fc = (unsigned short) (((freq << 9) + (divisor >> 1)) / divisor); fc = fc << 1; gus_write16 (0x01, fc); } static void gus_voice_volume (unsigned int vol) { gus_write8 (0x0d, 0x03); /* * Stop ramp before setting volume */ gus_write16 (0x09, (unsigned short) (vol << 4)); } static void gus_voice_balance (unsigned int balance) { gus_write8 (0x0c, (unsigned char) (balance & 0xff)); } static void gus_ramp_range (unsigned int low, unsigned int high) { gus_write8 (0x07, (unsigned char) ((low >> 4) & 0xff)); gus_write8 (0x08, (unsigned char) ((high >> 4) & 0xff)); } static void gus_ramp_rate (unsigned int scale, unsigned int rate) { gus_write8 (0x06, (unsigned char) (((scale & 0x03) << 6) | (rate & 0x3f))); } static void gus_rampon (unsigned int m) { unsigned char mode = (unsigned char) (m & 0xff); gus_write8 (0x0d, mode & 0xfc); gus_delay (); gus_write8 (0x0d, mode & 0xfc); } static void gus_ramp_mode (unsigned int m) { unsigned char mode = (unsigned char) (m & 0xff); gus_write8 (0x0d, (gus_read8 (0x0d) & 0x03) | (mode & 0xfc)); /* * Don't * start * or * stop * * * ramping */ gus_delay (); gus_write8 (0x0d, (gus_read8 (0x0d) & 0x03) | (mode & 0xfc)); } static void gus_rampoff (void) { gus_write8 (0x0d, 0x03); } static void gus_set_voice_pos (int voice, long position) { int sample_no; if ((sample_no = sample_map[voice]) != -1) if (position < samples[sample_no].len) if (voices[voice].volume_irq_mode == VMODE_START_NOTE) voices[voice].offset_pending = position; else gus_write_addr (0x0a, sample_ptrs[sample_no] + position, samples[sample_no].mode & WAVE_16_BITS); } static void gus_voice_init (int voice) { unsigned long flags; DISABLE_INTR (flags); gus_select_voice (voice); gus_voice_volume (0); gus_write_addr (0x0a, 0, 0); /* * Set current position to 0 */ gus_write8 (0x00, 0x03); /* * Voice off */ gus_write8 (0x0d, 0x03); /* * Ramping off */ RESTORE_INTR (flags); } static void gus_voice_init2 (int voice) { voices[voice].panning = 0; voices[voice].mode = 0; voices[voice].orig_freq = 20000; voices[voice].current_freq = 20000; voices[voice].bender = 0; voices[voice].bender_range = 200; voices[voice].initial_volume = 0; voices[voice].current_volume = 0; voices[voice].loop_irq_mode = 0; voices[voice].loop_irq_parm = 0; voices[voice].volume_irq_mode = 0; voices[voice].volume_irq_parm = 0; voices[voice].env_phase = 0; voices[voice].main_vol = 127; voices[voice].patch_vol = 127; voices[voice].expression_vol = 127; voices[voice].sample_pending = -1; } static void step_envelope (int voice) { unsigned vol, prev_vol, phase; unsigned char rate; long int flags; if (voices[voice].mode & WAVE_SUSTAIN_ON && voices[voice].env_phase == 2) { DISABLE_INTR (flags); gus_select_voice (voice); gus_rampoff (); RESTORE_INTR (flags); return; /* * Sustain */ } if (voices[voice].env_phase >= 5) { /* * Shoot the voice off */ gus_voice_init (voice); return; } prev_vol = voices[voice].current_volume; phase = ++voices[voice].env_phase; compute_volume (voice, voices[voice].midi_volume); vol = voices[voice].initial_volume * voices[voice].env_offset[phase] / 255; rate = voices[voice].env_rate[phase]; DISABLE_INTR (flags); gus_select_voice (voice); gus_voice_volume (prev_vol); gus_write8 (0x06, rate); /* * Ramping rate */ voices[voice].volume_irq_mode = VMODE_ENVELOPE; if (((vol - prev_vol) / 64) == 0) /* * No significant volume change */ { RESTORE_INTR (flags); step_envelope (voice); /* * Continue with the next phase */ return; } if (vol > prev_vol) { if (vol >= (4096 - 64)) vol = 4096 - 65; gus_ramp_range (0, vol); gus_rampon (0x20); /* * Increasing, irq */ } else { if (vol <= 64) vol = 65; gus_ramp_range (vol, 4030); gus_rampon (0x60); /* * Decreasing, irq */ } voices[voice].current_volume = vol; RESTORE_INTR (flags); } static void init_envelope (int voice) { voices[voice].env_phase = -1; voices[voice].current_volume = 64; step_envelope (voice); } static void start_release (int voice, long int flags) { if (gus_read8 (0x00) & 0x03) return; /* * Voice already stopped */ voices[voice].env_phase = 2; /* * Will be incremented by step_envelope */ voices[voice].current_volume = voices[voice].initial_volume = gus_read16 (0x09) >> 4; /* * Get current volume */ voices[voice].mode &= ~WAVE_SUSTAIN_ON; gus_rampoff (); RESTORE_INTR (flags); step_envelope (voice); } static void gus_voice_fade (int voice) { int instr_no = sample_map[voice], is16bits; long int flags; DISABLE_INTR (flags); gus_select_voice (voice); if (instr_no < 0 || instr_no > MAX_SAMPLE) { gus_write8 (0x00, 0x03); /* * Hard stop */ RESTORE_INTR (flags); return; } is16bits = (samples[instr_no].mode & WAVE_16_BITS) ? 1 : 0; /* * 8 or 16 * bit * samples */ if (voices[voice].mode & WAVE_ENVELOPES) { start_release (voice, flags); return; } /* * Ramp the volume down but not too quickly. */ if ((gus_read16 (0x09) >> 4) < 100) /* * Get current volume */ { gus_voice_off (); gus_rampoff (); gus_voice_init (voice); return; } gus_ramp_range (65, 4030); gus_ramp_rate (2, 4); gus_rampon (0x40 | 0x20); /* * Down, once, irq */ voices[voice].volume_irq_mode = VMODE_HALT; RESTORE_INTR (flags); } static void gus_reset (void) { int i; gus_select_max_voices (24); volume_base = 3071; volume_scale = 4; volume_method = VOL_METHOD_ADAGIO; for (i = 0; i < 32; i++) { gus_voice_init (i); /* * Turn voice off */ gus_voice_init2 (i); } INB (u_Status); /* * Touch the status register */ gus_look8 (0x41); /* * Clear any pending DMA IRQs */ gus_look8 (0x49); /* * Clear any pending sample IRQs */ gus_read8 (0x0f); /* * Clear pending IRQs */ } static void gus_initialize (void) { unsigned long flags; register unsigned char dma_image, irq_image, tmp; static unsigned char gus_irq_map[16] = {0, 0, 1, 3, 0, 2, 0, 4, 0, 0, 0, 5, 6, 0, 0, 7}; static unsigned char gus_dma_map[8] = {0, 1, 0, 2, 0, 3, 4, 5}; DISABLE_INTR (flags); gus_write8 (0x4c, 0); /* * Reset GF1 */ gus_delay (); gus_delay (); gus_write8 (0x4c, 1); /* * Release Reset */ gus_delay (); gus_delay (); /* * Clear all interrupts */ gus_write8 (0x41, 0); /* * DMA control */ gus_write8 (0x45, 0); /* * Timer control */ gus_write8 (0x49, 0); /* * Sample control */ gus_select_max_voices (24); INB (u_Status); /* * Touch the status register */ gus_look8 (0x41); /* * Clear any pending DMA IRQs */ gus_look8 (0x49); /* * Clear any pending sample IRQs */ gus_read8 (0x0f); /* * Clear pending IRQs */ gus_reset (); /* * Resets all voices */ gus_look8 (0x41); /* * Clear any pending DMA IRQs */ gus_look8 (0x49); /* * Clear any pending sample IRQs */ gus_read8 (0x0f); /* * Clear pending IRQs */ gus_write8 (0x4c, 7); /* * Master reset | DAC enable | IRQ enable */ /* * Set up for Digital ASIC */ OUTB (0x05, gus_base + 0x0f); mix_image |= 0x02; /* * Disable line out */ OUTB (mix_image, u_Mixer); OUTB (0x00, u_IRQDMAControl); OUTB (0x00, gus_base + 0x0f); /* * Now set up the DMA and IRQ interface * * The GUS supports two IRQs and two DMAs. * * Just one DMA channel is used. This prevents simultaneous ADC and DAC. * Adding this support requires significant changes to the dmabuf.c, dsp.c * and audio.c also. */ irq_image = 0; tmp = gus_irq_map[gus_irq]; if (!tmp) printk ("Warning! GUS IRQ not selected\n"); irq_image |= tmp; irq_image |= 0x40; /* * Combine IRQ1 (GF1) and IRQ2 (Midi) */ dma_image = 0x40; /* * Combine DMA1 (DRAM) and IRQ2 (ADC) */ tmp = gus_dma_map[gus_dma]; if (!tmp) printk ("Warning! GUS DMA not selected\n"); dma_image |= tmp; /* * For some reason the IRQ and DMA addresses must be written twice */ /* * Doing it first time */ OUTB (mix_image, u_Mixer); /* * Select DMA control */ OUTB (dma_image | 0x80, u_IRQDMAControl); /* * Set DMA address */ OUTB (mix_image | 0x40, u_Mixer); /* * Select IRQ control */ OUTB (irq_image, u_IRQDMAControl); /* * Set IRQ address */ /* * Doing it second time */ OUTB (mix_image, u_Mixer); /* * Select DMA control */ OUTB (dma_image, u_IRQDMAControl); /* * Set DMA address */ OUTB (mix_image | 0x40, u_Mixer); /* * Select IRQ control */ OUTB (irq_image, u_IRQDMAControl); /* * Set IRQ address */ gus_select_voice (0); /* * This disables writes to IRQ/DMA reg */ mix_image &= ~0x02; /* * Enable line out */ mix_image |= 0x08; /* * Enable IRQ */ OUTB (mix_image, u_Mixer); /* * Turn mixer channels on * Note! Mic in is left off. */ gus_select_voice (0); /* * This disables writes to IRQ/DMA reg */ gusintr (0); /* * Serve pending interrupts */ RESTORE_INTR (flags); } int gus_wave_detect (int baseaddr) { unsigned long i; unsigned long loc; gus_base = baseaddr; gus_write8 (0x4c, 0); /* Reset GF1 */ gus_delay (); gus_delay (); gus_write8 (0x4c, 1); /* Release Reset */ gus_delay (); gus_delay (); /* See if there is first block there.... */ gus_poke (0L, 0xaa); if (gus_peek (0L) != 0xaa) return (0); /* Now zero it out so that I can check for mirroring .. */ gus_poke (0L, 0x00); for (i = 1L; i < 1024L; i++) { int n, failed; /* check for mirroring ... */ if (gus_peek (0L) != 0) break; loc = i << 10; for (n = loc - 1, failed = 0; n <= loc; n++) { gus_poke (loc, 0xaa); if (gus_peek (loc) != 0xaa) failed = 1; gus_poke (loc, 0x55); if (gus_peek (loc) != 0x55) failed = 1; } if (failed) break; } gus_mem_size = i << 10; return 1; } static int guswave_ioctl (int dev, unsigned int cmd, unsigned int arg) { switch (cmd) { case SNDCTL_SYNTH_INFO: gus_info.nr_voices = nr_voices; IOCTL_TO_USER ((char *) arg, 0, &gus_info, sizeof (gus_info)); return 0; break; case SNDCTL_SEQ_RESETSAMPLES: reset_sample_memory (); return 0; break; case SNDCTL_SEQ_PERCMODE: return 0; break; case SNDCTL_SYNTH_MEMAVL: return gus_mem_size - free_mem_ptr - 32; default: return RET_ERROR (EINVAL); } } static int guswave_set_instr (int dev, int voice, int instr_no) { int sample_no; if (instr_no < 0 || instr_no > MAX_PATCH) return RET_ERROR (EINVAL); if (voice < 0 || voice > 31) return RET_ERROR (EINVAL); if (voices[voice].volume_irq_mode == VMODE_START_NOTE) { voices[voice].sample_pending = instr_no; return 0; } sample_no = patch_table[instr_no]; patch_map[voice] = -1; if (sample_no < 0) { printk ("GUS: Undefined patch %d for voice %d\n", instr_no, voice); return RET_ERROR (EINVAL);/* * Patch not defined */ } if (sample_ptrs[sample_no] == -1) /* * Sample not loaded */ { printk ("GUS: Sample #%d not loaded for patch %d (voice %d)\n", sample_no, instr_no, voice); return RET_ERROR (EINVAL); } sample_map[voice] = sample_no; patch_map[voice] = instr_no; return 0; } static int #ifdef FUTURE_VERSION guswave_kill_note (int dev, int voice, int note, int velocity) #else guswave_kill_note (int dev, int voice, int velocity) #endif { unsigned long flags; DISABLE_INTR (flags); if (voices[voice].volume_irq_mode == VMODE_START_NOTE) { voices[voice].kill_pending = 1; RESTORE_INTR (flags); } else { RESTORE_INTR (flags); gus_voice_fade (voice); } return 0; } static void guswave_aftertouch (int dev, int voice, int pressure) { short lo_limit, hi_limit; unsigned long flags; return; /* * Currently disabled */ if (voice < 0 || voice > 31) return; if (voices[voice].mode & WAVE_ENVELOPES && voices[voice].env_phase != 2) return; /* * Don't mix with envelopes */ if (pressure < 32) { DISABLE_INTR (flags); gus_select_voice (voice); gus_rampoff (); compute_and_set_volume (voice, 255, 0); /* * Back to original volume */ RESTORE_INTR (flags); return; } hi_limit = voices[voice].current_volume; lo_limit = hi_limit * 99 / 100; if (lo_limit < 65) lo_limit = 65; DISABLE_INTR (flags); gus_select_voice (voice); if (hi_limit > (4095 - 65)) { hi_limit = 4095 - 65; gus_voice_volume (hi_limit); } gus_ramp_range (lo_limit, hi_limit); gus_ramp_rate (3, 8); gus_rampon (0x58); /* * Bidirectional, Down, Loop */ RESTORE_INTR (flags); } static void guswave_panning (int dev, int voice, int value) { if (voice >= 0 || voice < 32) voices[voice].panning = value; } static void guswave_volume_method (int dev, int mode) { if (mode == VOL_METHOD_LINEAR || mode == VOL_METHOD_ADAGIO) volume_method = mode; } static void compute_volume (int voice, int volume) { if (volume < 128) voices[voice].midi_volume = volume; switch (volume_method) { case VOL_METHOD_ADAGIO: voices[voice].initial_volume = gus_adagio_vol (voices[voice].midi_volume, voices[voice].main_vol, voices[voice].expression_vol, voices[voice].patch_vol); break; case VOL_METHOD_LINEAR: /* Totally ignores patch-volume and expression */ voices[voice].initial_volume = gus_linear_vol (volume, voices[voice].main_vol); break; default: voices[voice].initial_volume = volume_base + (voices[voice].midi_volume * volume_scale); } if (voices[voice].initial_volume > 4030) voices[voice].initial_volume = 4030; } static void compute_and_set_volume (int voice, int volume, int ramp_time) { int current, target, rate; unsigned long flags; compute_volume (voice, volume); voices[voice].current_volume = voices[voice].initial_volume; DISABLE_INTR (flags); /* * CAUTION! Interrupts disabled. Enable them before returning */ gus_select_voice (voice); current = gus_read16 (0x09) >> 4; target = voices[voice].initial_volume; if (ramp_time == INSTANT_RAMP) { gus_rampoff (); gus_voice_volume (target); RESTORE_INTR (flags); return; } if (ramp_time == FAST_RAMP) rate = 63; else rate = 16; gus_ramp_rate (0, rate); if ((target - current) / 64 == 0) /* * Too close */ { gus_rampoff (); gus_voice_volume (target); RESTORE_INTR (flags); return; } if (target > current) { if (target > (4095 - 65)) target = 4095 - 65; gus_ramp_range (current, target); gus_rampon (0x00); /* * Ramp up, once, no irq */ } else { if (target < 65) target = 65; gus_ramp_range (target, current); gus_rampon (0x40); /* * Ramp down, once, no irq */ } RESTORE_INTR (flags); } static void dynamic_volume_change (int voice) { unsigned char status; unsigned long flags; DISABLE_INTR (flags); gus_select_voice (voice); status = gus_read8 (0x00); /* * Voice status */ RESTORE_INTR (flags); if (status & 0x03) return; /* * Voice not started */ if (!(voices[voice].mode & WAVE_ENVELOPES)) { compute_and_set_volume (voice, voices[voice].midi_volume, 1); return; } /* * Voice is running and has envelopes. */ DISABLE_INTR (flags); gus_select_voice (voice); status = gus_read8 (0x0d); /* * Ramping status */ RESTORE_INTR (flags); if (status & 0x03) /* * Sustain phase? */ { compute_and_set_volume (voice, voices[voice].midi_volume, 1); return; } if (voices[voice].env_phase < 0) return; compute_volume (voice, voices[voice].midi_volume); #if 0 /* * * * Is this really required */ voices[voice].current_volume = gus_read16 (0x09) >> 4; /* * Get current volume */ voices[voice].env_phase--; step_envelope (voice); #endif } static void guswave_controller (int dev, int voice, int ctrl_num, int value) { unsigned long flags; unsigned long freq; if (voice < 0 || voice > 31) return; switch (ctrl_num) { case CTRL_PITCH_BENDER: voices[voice].bender = value; if (voices[voice].volume_irq_mode != VMODE_START_NOTE) { freq = compute_finetune (voices[voice].orig_freq, value, voices[voice].bender_range); voices[voice].current_freq = freq; DISABLE_INTR (flags); gus_select_voice (voice); gus_voice_freq (freq); RESTORE_INTR (flags); } break; case CTRL_PITCH_BENDER_RANGE: voices[voice].bender_range = value; break; #ifdef FUTURE_VERSION case CTL_EXPRESSION: value /= 128; #endif case CTRL_EXPRESSION: if (volume_method == VOL_METHOD_ADAGIO) { voices[voice].expression_vol = value; if (voices[voice].volume_irq_mode != VMODE_START_NOTE) dynamic_volume_change (voice); } break; #ifdef FUTURE_VERSION case CTL_PAN: voices[voice].panning = (value * 2) - 128; break; case CTL_MAIN_VOLUME: value = (value * 100) / 16383; #endif case CTRL_MAIN_VOLUME: voices[voice].main_vol = value; if (voices[voice].volume_irq_mode != VMODE_START_NOTE) dynamic_volume_change (voice); break; default: /* * Ignore */ break; } } static int guswave_start_note2 (int dev, int voice, int note_num, int volume) { int sample, best_sample, best_delta, delta_freq; int is16bits, samplep, patch, pan; unsigned long note_freq, base_note, freq, flags; unsigned char mode = 0; if (voice < 0 || voice > 31) { printk ("GUS: Invalid voice\n"); return RET_ERROR (EINVAL); } if (note_num == 255) { if (voices[voice].mode & WAVE_ENVELOPES) { voices[voice].midi_volume = volume; dynamic_volume_change (voice); return 0; } compute_and_set_volume (voice, volume, 1); return 0; } if ((patch = patch_map[voice]) == -1) { return RET_ERROR (EINVAL); } if ((samplep = patch_table[patch]) == -1) { return RET_ERROR (EINVAL); } note_freq = note_to_freq (note_num); /* * Find a sample within a patch so that the note_freq is between low_note * and high_note. */ sample = -1; best_sample = samplep; best_delta = 1000000; while (samplep >= 0 && sample == -1) { delta_freq = note_freq - samples[samplep].base_note; if (delta_freq < 0) delta_freq = -delta_freq; if (delta_freq < best_delta) { best_sample = samplep; best_delta = delta_freq; } if (samples[samplep].low_note <= note_freq && note_freq <= samples[samplep].high_note) sample = samplep; else samplep = samples[samplep].key; /* * Follow link */ } if (sample == -1) sample = best_sample; if (sample == -1) { printk ("GUS: Patch %d not defined for note %d\n", patch, note_num); return 0; /* * Should play default patch ??? */ } is16bits = (samples[sample].mode & WAVE_16_BITS) ? 1 : 0; /* * 8 or 16 * bit * samples */ voices[voice].mode = samples[sample].mode; voices[voice].patch_vol = samples[sample].volume; if (voices[voice].mode & WAVE_ENVELOPES) { int i; for (i = 0; i < 6; i++) { voices[voice].env_rate[i] = samples[sample].env_rate[i]; voices[voice].env_offset[i] = samples[sample].env_offset[i]; } } sample_map[voice] = sample; base_note = samples[sample].base_note / 100; /* * To avoid overflows */ note_freq /= 100; freq = samples[sample].base_freq * note_freq / base_note; voices[voice].orig_freq = freq; /* * Since the pitch bender may have been set before playing the note, we * have to calculate the bending now. */ freq = compute_finetune (voices[voice].orig_freq, voices[voice].bender, voices[voice].bender_range); voices[voice].current_freq = freq; pan = (samples[sample].panning + voices[voice].panning) / 32; pan += 7; if (pan < 0) pan = 0; if (pan > 15) pan = 15; if (samples[sample].mode & WAVE_16_BITS) { mode |= 0x04; /* * 16 bits */ if ((sample_ptrs[sample] >> 18) != ((sample_ptrs[sample] + samples[sample].len) >> 18)) printk ("GUS: Sample address error\n"); } /************************************************************************* * CAUTION! Interrupts disabled. Don't return before enabling *************************************************************************/ DISABLE_INTR (flags); gus_select_voice (voice); gus_voice_off (); /* * It may still be running */ gus_rampoff (); RESTORE_INTR (flags); if (voices[voice].mode & WAVE_ENVELOPES) { compute_volume (voice, volume); init_envelope (voice); } else compute_and_set_volume (voice, volume, 0); DISABLE_INTR (flags); gus_select_voice (voice); if (samples[sample].mode & WAVE_LOOP_BACK) gus_write_addr (0x0a, sample_ptrs[sample] + samples[sample].len - voices[voice].offset_pending, is16bits); /* Sample * start=end */ else gus_write_addr (0x0a, sample_ptrs[sample] + voices[voice].offset_pending, is16bits); /* Sample start=begin */ if (samples[sample].mode & WAVE_LOOPING) { mode |= 0x08; /* * Looping on */ if (samples[sample].mode & WAVE_BIDIR_LOOP) mode |= 0x10; /* * Bidirectional looping on */ if (samples[sample].mode & WAVE_LOOP_BACK) { gus_write_addr (0x0a, sample_ptrs[sample] + samples[sample].loop_end - voices[voice].offset_pending, is16bits); mode |= 0x40; } gus_write_addr (0x02, sample_ptrs[sample] + samples[sample].loop_start, is16bits); /* * Loop * start * location */ gus_write_addr (0x04, sample_ptrs[sample] + samples[sample].loop_end, is16bits); /* * Loop * end * location */ } else { mode |= 0x20; /* * Loop irq at the end */ voices[voice].loop_irq_mode = LMODE_FINISH; /* * Ramp it down at * the * end */ voices[voice].loop_irq_parm = 1; gus_write_addr (0x02, sample_ptrs[sample], is16bits); /* * Loop start * location */ gus_write_addr (0x04, sample_ptrs[sample] + samples[sample].len - 1, is16bits); /* * Loop * end * location */ } gus_voice_freq (freq); gus_voice_balance (pan); gus_voice_on (mode); RESTORE_INTR (flags); return 0; } /* * * New guswave_start_note by Andrew J. Robinson attempts to minimize * clicking * when the note playing on the voice is changed. It uses volume * ramping. */ static int guswave_start_note (int dev, int voice, int note_num, int volume) { long int flags; int mode; int ret_val = 0; DISABLE_INTR (flags); if (note_num == 255) { if (voices[voice].volume_irq_mode == VMODE_START_NOTE) voices[voice].volume_pending = volume; else { RESTORE_INTR (flags); ret_val = guswave_start_note2 (dev, voice, note_num, volume); } } else { gus_select_voice (voice); mode = gus_read8 (0x00); if (mode & 0x20) gus_write8 (0x00, mode & 0xdf); /* No interrupt! */ voices[voice].offset_pending = 0; voices[voice].kill_pending = 0; voices[voice].volume_irq_mode = 0; voices[voice].loop_irq_mode = 0; if (voices[voice].sample_pending >= 0) { RESTORE_INTR (flags); guswave_set_instr (voices[voice].dev_pending, voice, voices[voice].sample_pending); voices[voice].sample_pending = -1; DISABLE_INTR (flags); } if ((mode & 0x01) || ((gus_read16 (0x09) >> 4) < 2065)) { ret_val = guswave_start_note2 (dev, voice, note_num, volume); } else { voices[voice].dev_pending = dev; voices[voice].note_pending = note_num; voices[voice].volume_pending = volume; voices[voice].volume_irq_mode = VMODE_START_NOTE; gus_rampoff (); gus_ramp_range (2000, 4065); gus_ramp_rate (0, 63);/* Fastest possible rate */ gus_rampon (0x20 | 0x40); /* Ramp down, once, irq */ RESTORE_INTR (flags); } } return ret_val; } static void guswave_reset (int dev) { int i; for (i = 0; i < 32; i++) { gus_voice_init (i); gus_voice_init2 (i); } } static int guswave_open (int dev, int mode) { int err; if (gus_busy) return RET_ERROR (EBUSY); gus_initialize (); if ((err = DMAbuf_open_dma (gus_devnum))) return err; RESET_WAIT_QUEUE (dram_sleeper, dram_sleep_flag); gus_busy = 1; active_device = GUS_DEV_WAVE; gus_reset (); return 0; } static void guswave_close (int dev) { gus_busy = 0; active_device = 0; gus_reset (); DMAbuf_close_dma (gus_devnum); } static int guswave_load_patch (int dev, int format, snd_rw_buf * addr, int offs, int count, int pmgr_flag) { struct patch_info patch; int instr; long sizeof_patch; unsigned long blk_size, blk_end, left, src_offs, target; sizeof_patch = (long) &patch.data[0] - (long) &patch; /* * Size of * the header * * info */ if (format != GUS_PATCH) { printk ("GUS Error: Invalid patch format (key) 0x%x\n", format); return RET_ERROR (EINVAL); } if (count < sizeof_patch) { printk ("GUS Error: Patch header too short\n"); return RET_ERROR (EINVAL); } count -= sizeof_patch; if (free_sample >= MAX_SAMPLE) { printk ("GUS: Sample table full\n"); return RET_ERROR (ENOSPC); } /* * Copy the header from user space but ignore the first bytes which have * been transferred already. */ COPY_FROM_USER (&((char *) &patch)[offs], addr, offs, sizeof_patch - offs); instr = patch.instr_no; if (instr < 0 || instr > MAX_PATCH) { printk ("GUS: Invalid patch number %d\n", instr); return RET_ERROR (EINVAL); } if (count < patch.len) { printk ("GUS Warning: Patch record too short (%d<%d)\n", count, (int) patch.len); patch.len = count; } if (patch.len <= 0 || patch.len > gus_mem_size) { printk ("GUS: Invalid sample length %d\n", (int) patch.len); return RET_ERROR (EINVAL); } if (patch.mode & WAVE_LOOPING) { if (patch.loop_start < 0 || patch.loop_start >= patch.len) { printk ("GUS: Invalid loop start\n"); return RET_ERROR (EINVAL); } if (patch.loop_end < patch.loop_start || patch.loop_end > patch.len) { printk ("GUS: Invalid loop end\n"); return RET_ERROR (EINVAL); } } free_mem_ptr = (free_mem_ptr + 31) & ~31; /* * Alignment 32 bytes */ #define GUS_BANK_SIZE (256*1024) if (patch.mode & WAVE_16_BITS) { /* * 16 bit samples must fit one 256k bank. */ if (patch.len >= GUS_BANK_SIZE) { printk ("GUS: Sample (16 bit) too long %d\n", (int) patch.len); return RET_ERROR (ENOSPC); } if ((free_mem_ptr / GUS_BANK_SIZE) != ((free_mem_ptr + patch.len) / GUS_BANK_SIZE)) { unsigned long tmp_mem = /* * Align to 256K*N */ ((free_mem_ptr / GUS_BANK_SIZE) + 1) * GUS_BANK_SIZE; if ((tmp_mem + patch.len) > gus_mem_size) return RET_ERROR (ENOSPC); free_mem_ptr = tmp_mem; /* * This leaves unusable memory */ } } if ((free_mem_ptr + patch.len) > gus_mem_size) return RET_ERROR (ENOSPC); sample_ptrs[free_sample] = free_mem_ptr; /* * Tremolo is not possible with envelopes */ if (patch.mode & WAVE_ENVELOPES) patch.mode &= ~WAVE_TREMOLO; memcpy ((char *) &samples[free_sample], &patch, sizeof_patch); /* * Link this_one sample to the list of samples for patch 'instr'. */ samples[free_sample].key = patch_table[instr]; patch_table[instr] = free_sample; /* * Use DMA to transfer the wave data to the DRAM */ left = patch.len; src_offs = 0; target = free_mem_ptr; while (left) /* * Not all moved */ { blk_size = sound_buffsizes[gus_devnum]; if (blk_size > left) blk_size = left; /* * DMA cannot cross 256k bank boundaries. Check for that. */ blk_end = target + blk_size; if ((target >> 18) != (blk_end >> 18)) { /* * Have to split the block */ blk_end &= ~(256 * 1024 - 1); blk_size = blk_end - target; } #if defined(GUS_NO_DMA) || defined(GUS_PATCH_NO_DMA) /* * For some reason the DMA is not possible. We have to use PIO. */ { long i; unsigned char data; for (i = 0; i < blk_size; i++) { GET_BYTE_FROM_USER (data, addr, sizeof_patch + i); if (patch.mode & WAVE_UNSIGNED) if (!(patch.mode & WAVE_16_BITS) || (i & 0x01)) data ^= 0x80; /* * Convert to signed */ gus_poke (target + i, data); } } #else /* * * * GUS_NO_DMA */ { unsigned long address, hold_address; unsigned char dma_command; unsigned long flags; /* * OK, move now. First in and then out. */ COPY_FROM_USER (snd_raw_buf[gus_devnum][0], addr, sizeof_patch + src_offs, blk_size); DISABLE_INTR (flags); /******** INTERRUPTS DISABLED NOW ********/ gus_write8 (0x41, 0); /* * Disable GF1 DMA */ DMAbuf_start_dma (gus_devnum, snd_raw_buf_phys[gus_devnum][0], blk_size, DMA_MODE_WRITE); /* * Set the DRAM address for the wave data */ address = target; if (sound_dsp_dmachan[gus_devnum] > 3) { hold_address = address; address = address >> 1; address &= 0x0001ffffL; address |= (hold_address & 0x000c0000L); } gus_write16 (0x42, (address >> 4) & 0xffff); /* * DRAM DMA address */ /* * Start the DMA transfer */ dma_command = 0x21; /* * IRQ enable, DMA start */ if (patch.mode & WAVE_UNSIGNED) dma_command |= 0x80; /* * Invert MSB */ if (patch.mode & WAVE_16_BITS) dma_command |= 0x40; /* * 16 bit _DATA_ */ if (sound_dsp_dmachan[gus_devnum] > 3) dma_command |= 0x04; /* * 16 bit DMA channel */ gus_write8 (0x41, dma_command); /* * Let's go luteet (=bugs) */ /* * Sleep here until the DRAM DMA done interrupt is served */ active_device = GUS_DEV_WAVE; DO_SLEEP (dram_sleeper, dram_sleep_flag, HZ); if (TIMED_OUT (dram_sleeper, dram_sleep_flag)) printk ("GUS: DMA Transfer timed out\n"); RESTORE_INTR (flags); } #endif /* * * * GUS_NO_DMA */ /* * Now the next part */ left -= blk_size; src_offs += blk_size; target += blk_size; gus_write8 (0x41, 0); /* * Stop DMA */ } free_mem_ptr += patch.len; if (!pmgr_flag) pmgr_inform (dev, PM_E_PATCH_LOADED, instr, free_sample, 0, 0); free_sample++; return 0; } static void guswave_hw_control (int dev, unsigned char *event) { int voice, cmd; unsigned short p1, p2; unsigned long plong, flags; cmd = event[2]; voice = event[3]; p1 = *(unsigned short *) &event[4]; p2 = *(unsigned short *) &event[6]; plong = *(unsigned long *) &event[4]; if ((voices[voice].volume_irq_mode == VMODE_START_NOTE) && (cmd != _GUS_VOICESAMPLE) && (cmd != _GUS_VOICE_POS)) do_volume_irq (voice); switch (cmd) { case _GUS_NUMVOICES: DISABLE_INTR (flags); gus_select_voice (voice); gus_select_max_voices (p1); RESTORE_INTR (flags); break; case _GUS_VOICESAMPLE: guswave_set_instr (dev, voice, p1); break; case _GUS_VOICEON: DISABLE_INTR (flags); gus_select_voice (voice); p1 &= ~0x20; /* * Disable intr */ gus_voice_on (p1); RESTORE_INTR (flags); break; case _GUS_VOICEOFF: DISABLE_INTR (flags); gus_select_voice (voice); gus_voice_off (); RESTORE_INTR (flags); break; case _GUS_VOICEFADE: gus_voice_fade (voice); break; case _GUS_VOICEMODE: DISABLE_INTR (flags); gus_select_voice (voice); p1 &= ~0x20; /* * Disable intr */ gus_voice_mode (p1); RESTORE_INTR (flags); break; case _GUS_VOICEBALA: DISABLE_INTR (flags); gus_select_voice (voice); gus_voice_balance (p1); RESTORE_INTR (flags); break; case _GUS_VOICEFREQ: DISABLE_INTR (flags); gus_select_voice (voice); gus_voice_freq (plong); RESTORE_INTR (flags); break; case _GUS_VOICEVOL: DISABLE_INTR (flags); gus_select_voice (voice); gus_voice_volume (p1); RESTORE_INTR (flags); break; case _GUS_VOICEVOL2: /* * Just update the voice value */ voices[voice].initial_volume = voices[voice].current_volume = p1; break; case _GUS_RAMPRANGE: if (voices[voice].mode & WAVE_ENVELOPES) break; /* * NO-NO */ DISABLE_INTR (flags); gus_select_voice (voice); gus_ramp_range (p1, p2); RESTORE_INTR (flags); break; case _GUS_RAMPRATE: if (voices[voice].mode & WAVE_ENVELOPES) break; /* * NO-NO */ DISABLE_INTR (flags); gus_select_voice (voice); gus_ramp_rate (p1, p2); RESTORE_INTR (flags); break; case _GUS_RAMPMODE: if (voices[voice].mode & WAVE_ENVELOPES) break; /* * NO-NO */ DISABLE_INTR (flags); gus_select_voice (voice); p1 &= ~0x20; /* * Disable intr */ gus_ramp_mode (p1); RESTORE_INTR (flags); break; case _GUS_RAMPON: if (voices[voice].mode & WAVE_ENVELOPES) break; /* * NO-NO */ DISABLE_INTR (flags); gus_select_voice (voice); p1 &= ~0x20; /* * Disable intr */ gus_rampon (p1); RESTORE_INTR (flags); break; case _GUS_RAMPOFF: if (voices[voice].mode & WAVE_ENVELOPES) break; /* * NO-NO */ DISABLE_INTR (flags); gus_select_voice (voice); gus_rampoff (); RESTORE_INTR (flags); break; case _GUS_VOLUME_SCALE: volume_base = p1; volume_scale = p2; break; case _GUS_VOICE_POS: DISABLE_INTR (flags); gus_select_voice (voice); gus_set_voice_pos (voice, plong); RESTORE_INTR (flags); break; default:; } } static int gus_sampling_set_speed (int speed) { if (speed <= 0) return gus_sampling_speed; if (speed > 44100) speed = 44100; gus_sampling_speed = speed; return speed; } static int gus_sampling_set_channels (int channels) { if (!channels) return gus_sampling_channels; if (channels > 2) channels = 2; if (channels < 1) channels = 1; gus_sampling_channels = channels; return channels; } static int gus_sampling_set_bits (int bits) { if (!bits) return gus_sampling_bits; if (bits != 8 && bits != 16) bits = 8; gus_sampling_bits = bits; return bits; } static int gus_sampling_ioctl (int dev, unsigned int cmd, unsigned int arg, int local) { switch (cmd) { case SOUND_PCM_WRITE_RATE: if (local) return gus_sampling_set_speed (arg); return IOCTL_OUT (arg, gus_sampling_set_speed (IOCTL_IN (arg))); break; case SOUND_PCM_READ_RATE: if (local) return gus_sampling_speed; return IOCTL_OUT (arg, gus_sampling_speed); break; case SNDCTL_DSP_STEREO: if (local) return gus_sampling_set_channels (arg + 1) - 1; return IOCTL_OUT (arg, gus_sampling_set_channels (IOCTL_IN (arg) + 1) - 1); break; case SOUND_PCM_WRITE_CHANNELS: if (local) return gus_sampling_set_channels (arg); return IOCTL_OUT (arg, gus_sampling_set_channels (IOCTL_IN (arg))); break; case SOUND_PCM_READ_CHANNELS: if (local) return gus_sampling_channels; return IOCTL_OUT (arg, gus_sampling_channels); break; case SNDCTL_DSP_SAMPLESIZE: if (local) return gus_sampling_set_bits (arg); return IOCTL_OUT (arg, gus_sampling_set_bits (IOCTL_IN (arg))); break; case SOUND_PCM_READ_BITS: if (local) return gus_sampling_bits; return IOCTL_OUT (arg, gus_sampling_bits); case SOUND_PCM_WRITE_FILTER: /* * NOT YET IMPLEMENTED */ return IOCTL_OUT (arg, RET_ERROR (EINVAL)); break; case SOUND_PCM_READ_FILTER: return IOCTL_OUT (arg, RET_ERROR (EINVAL)); break; default: return RET_ERROR (EINVAL); } return RET_ERROR (EINVAL); } static void gus_sampling_reset (int dev) { } static int gus_sampling_open (int dev, int mode) { #ifdef GUS_NO_DMA printk ("GUS: DMA mode not enabled. Device not supported\n"); return RET_ERROR (ENXIO); #endif if (gus_busy) return RET_ERROR (EBUSY); gus_initialize (); gus_busy = 1; active_device = 0; gus_reset (); reset_sample_memory (); gus_select_max_voices (14); pcm_active = 0; pcm_opened = 1; if (mode & OPEN_READ) { recording_active = 1; set_input_volumes (); } return 0; } static void gus_sampling_close (int dev) { gus_reset (); gus_busy = 0; pcm_opened = 0; active_device = 0; if (recording_active) set_input_volumes (); recording_active = 0; } static void gus_sampling_update_volume (void) { unsigned long flags; int voice; DISABLE_INTR (flags); if (pcm_active && pcm_opened) for (voice = 0; voice < gus_sampling_channels; voice++) { gus_select_voice (voice); gus_rampoff (); gus_voice_volume (1530 + (25 * gus_pcm_volume)); gus_ramp_range (65, 1530 + (25 * gus_pcm_volume)); } RESTORE_INTR (flags); } static void play_next_pcm_block (void) { unsigned long flags; int speed = gus_sampling_speed; int this_one, is16bits, chn; unsigned long dram_loc; unsigned char mode[2], ramp_mode[2]; if (!pcm_qlen) return; this_one = pcm_head; for (chn = 0; chn < gus_sampling_channels; chn++) { mode[chn] = 0x00; ramp_mode[chn] = 0x03; /* * Ramping and rollover off */ if (chn == 0) { mode[chn] |= 0x20; /* * Loop irq */ voices[chn].loop_irq_mode = LMODE_PCM; } if (gus_sampling_bits != 8) { is16bits = 1; mode[chn] |= 0x04; /* * 16 bit data */ } else is16bits = 0; dram_loc = this_one * pcm_bsize; dram_loc += chn * pcm_banksize; if (this_one == (pcm_nblk - 1)) /* * Last of the DRAM buffers */ { mode[chn] |= 0x08; /* * Enable loop */ ramp_mode[chn] = 0x03;/* * Disable rollover */ } else { if (chn == 0) ramp_mode[chn] = 0x04; /* * Enable rollover bit */ } DISABLE_INTR (flags); gus_select_voice (chn); gus_voice_freq (speed); if (gus_sampling_channels == 1) gus_voice_balance (7); /* * mono */ else if (chn == 0) gus_voice_balance (0); /* * left */ else gus_voice_balance (15); /* * right */ if (!pcm_active) /* * Voice not started yet */ { /* * The playback was not started yet (or there has been a pause). * Start the voice (again) and ask for a rollover irq at the end of * this_one block. If this_one one is last of the buffers, use just * the normal loop with irq. */ gus_voice_off (); /* * It could already be running */ gus_rampoff (); gus_voice_volume (1530 + (25 * gus_pcm_volume)); gus_ramp_range (65, 1530 + (25 * gus_pcm_volume)); gus_write_addr (0x0a, dram_loc, is16bits); /* * Starting position */ gus_write_addr (0x02, chn * pcm_banksize, is16bits); /* * Loop start * location */ if (chn != 0) gus_write_addr (0x04, pcm_banksize + (pcm_bsize * pcm_nblk), is16bits); /* * Loop end location */ } if (chn == 0) gus_write_addr (0x04, dram_loc + pcm_datasize[this_one], is16bits); /* * Loop * end * location */ else mode[chn] |= 0x08; /* * Enable loop */ if (pcm_datasize[this_one] != pcm_bsize) { /* * Incomplete block. Possibly the last one. */ if (chn == 0) { mode[chn] &= ~0x08; /* * Disable loop */ mode[chn] |= 0x20;/* * Enable loop IRQ */ voices[0].loop_irq_mode = LMODE_PCM_STOP; ramp_mode[chn] = 0x03; /* * No rollover bit */ } else { gus_write_addr (0x04, dram_loc + pcm_datasize[this_one], is16bits); /* * Loop * end * location */ mode[chn] &= ~0x08; /* * Disable loop */ } } RESTORE_INTR (flags); } for (chn = 0; chn < gus_sampling_channels; chn++) { DISABLE_INTR (flags); gus_select_voice (chn); gus_write8 (0x0d, ramp_mode[chn]); gus_voice_on (mode[chn]); RESTORE_INTR (flags); } pcm_active = 1; } static void gus_transfer_output_block (int dev, unsigned long buf, int total_count, int intrflag, int chn) { /* * This routine transfers one block of audio data to the DRAM. In mono mode * it's called just once. When in stereo mode, this_one routine is called * once for both channels. * * The left/mono channel data is transferred to the beginning of dram and the * right data to the area pointed by gus_page_size. */ int this_one, count; unsigned long flags; unsigned char dma_command; unsigned long address, hold_address; DISABLE_INTR (flags); count = total_count / gus_sampling_channels; if (chn == 0) { if (pcm_qlen >= pcm_nblk) printk ("GUS Warning: PCM buffers out of sync\n"); this_one = pcm_current_block = pcm_tail; pcm_qlen++; pcm_tail = (pcm_tail + 1) % pcm_nblk; pcm_datasize[this_one] = count; } else this_one = pcm_current_block; gus_write8 (0x41, 0); /* * Disable GF1 DMA */ DMAbuf_start_dma (dev, buf + (chn * count), count, DMA_MODE_WRITE); address = this_one * pcm_bsize; address += chn * pcm_banksize; if (sound_dsp_dmachan[dev] > 3) { hold_address = address; address = address >> 1; address &= 0x0001ffffL; address |= (hold_address & 0x000c0000L); } gus_write16 (0x42, (address >> 4) & 0xffff); /* * DRAM DMA address */ dma_command = 0x21; /* * IRQ enable, DMA start */ if (gus_sampling_bits != 8) dma_command |= 0x40; /* * 16 bit _DATA_ */ else dma_command |= 0x80; /* * Invert MSB */ if (sound_dsp_dmachan[dev] > 3) dma_command |= 0x04; /* * 16 bit DMA channel */ gus_write8 (0x41, dma_command); /* * Kick on */ if (chn == (gus_sampling_channels - 1)) /* * Last channel */ { /* * Last (right or mono) channel data */ active_device = GUS_DEV_PCM_DONE; if (!pcm_active && (pcm_qlen > 2 || count < pcm_bsize)) { play_next_pcm_block (); } } else /* * * * Left channel data. The right channel * is * * * transferred after DMA interrupt */ active_device = GUS_DEV_PCM_CONTINUE; RESTORE_INTR (flags); } static void gus_sampling_output_block (int dev, unsigned long buf, int total_count, int intrflag, int restart_dma) { pcm_current_buf = buf; pcm_current_count = total_count; pcm_current_intrflag = intrflag; pcm_current_dev = dev; gus_transfer_output_block (dev, buf, total_count, intrflag, 0); } static void gus_sampling_start_input (int dev, unsigned long buf, int count, int intrflag, int restart_dma) { unsigned long flags; unsigned char mode; DISABLE_INTR (flags); DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); mode = 0xa0; /* * DMA IRQ enable, invert MSB */ if (sound_dsp_dmachan[dev] > 3) mode |= 0x04; /* * 16 bit DMA channel */ if (gus_sampling_channels > 1) mode |= 0x02; /* * Stereo */ mode |= 0x01; /* * DMA enable */ gus_write8 (0x49, mode); RESTORE_INTR (flags); } static int gus_sampling_prepare_for_input (int dev, int bsize, int bcount) { unsigned int rate; rate = (9878400 / (gus_sampling_speed + 2)) / 16; gus_write8 (0x48, rate & 0xff); /* * Set sampling frequency */ if (gus_sampling_bits != 8) { printk ("GUS Error: 16 bit recording not supported\n"); return RET_ERROR (EINVAL); } return 0; } static int gus_sampling_prepare_for_output (int dev, int bsize, int bcount) { int i; long mem_ptr, mem_size; mem_ptr = 0; mem_size = gus_mem_size / gus_sampling_channels; if (mem_size > (256 * 1024)) mem_size = 256 * 1024; pcm_bsize = bsize / gus_sampling_channels; pcm_head = pcm_tail = pcm_qlen = 0; pcm_nblk = MAX_PCM_BUFFERS; if ((pcm_bsize * pcm_nblk) > mem_size) pcm_nblk = mem_size / pcm_bsize; for (i = 0; i < pcm_nblk; i++) pcm_datasize[i] = 0; pcm_banksize = pcm_nblk * pcm_bsize; if (gus_sampling_bits != 8 && pcm_banksize == (256 * 1024)) pcm_nblk--; return 0; } static int gus_has_output_drained (int dev) { return !pcm_qlen; } static void gus_copy_from_user (int dev, char *localbuf, int localoffs, snd_rw_buf * userbuf, int useroffs, int len) { if (gus_sampling_channels == 1) { COPY_FROM_USER (&localbuf[localoffs], userbuf, useroffs, len); } else if (gus_sampling_bits == 8) { int in_left = useroffs; int in_right = useroffs + 1; char *out_left, *out_right; int i; len /= 2; localoffs /= 2; out_left = &localbuf[localoffs]; out_right = out_left + pcm_bsize; for (i = 0; i < len; i++) { GET_BYTE_FROM_USER (*out_left++, userbuf, in_left); in_left += 2; GET_BYTE_FROM_USER (*out_right++, userbuf, in_right); in_right += 2; } } else { int in_left = useroffs; int in_right = useroffs + 1; short *out_left, *out_right; int i; len /= 4; localoffs /= 4; out_left = (short *) &localbuf[localoffs]; out_right = out_left + (pcm_bsize / 2); for (i = 0; i < len; i++) { GET_SHORT_FROM_USER (*out_left++, (short *) userbuf, in_left); in_left += 2; GET_SHORT_FROM_USER (*out_right++, (short *) userbuf, in_right); in_right += 2; } } } static struct audio_operations gus_sampling_operations = { "Gravis UltraSound", NEEDS_RESTART, gus_sampling_open, gus_sampling_close, gus_sampling_output_block, gus_sampling_start_input, gus_sampling_ioctl, gus_sampling_prepare_for_input, gus_sampling_prepare_for_output, gus_sampling_reset, gus_sampling_reset, gus_has_output_drained, gus_copy_from_user }; #ifdef FUTURE_VERSION static void guswave_bender (int dev, int voice, int value) { int freq; unsigned long flags; voices[voice].bender = value - 8192; freq = compute_finetune (voices[voice].orig_freq, value, voices[voice].bender_range); voices[voice].current_freq = freq; DISABLE_INTR (flags); gus_select_voice (voice); gus_voice_freq (freq); RESTORE_INTR (flags); } #endif static int guswave_patchmgr (int dev, struct patmgr_info *rec) { int i, n; switch (rec->command) { case PM_GET_DEVTYPE: rec->parm1 = PMTYPE_WAVE; return 0; break; case PM_GET_NRPGM: rec->parm1 = MAX_PATCH; return 0; break; case PM_GET_PGMMAP: rec->parm1 = MAX_PATCH; for (i = 0; i < MAX_PATCH; i++) { int ptr = patch_table[i]; rec->data.data8[i] = 0; while (ptr >= 0 && ptr < free_sample) { rec->data.data8[i]++; ptr = samples[ptr].key; /* * Follow link */ } } return 0; break; case PM_GET_PGM_PATCHES: { int ptr = patch_table[rec->parm1]; n = 0; while (ptr >= 0 && ptr < free_sample) { rec->data.data32[n++] = ptr; ptr = samples[ptr].key; /* * Follow link */ } } rec->parm1 = n; return 0; break; case PM_GET_PATCH: { int ptr = rec->parm1; struct patch_info *pat; if (ptr < 0 || ptr >= free_sample) return RET_ERROR (EINVAL); memcpy (rec->data.data8, (char *) &samples[ptr], sizeof (struct patch_info)); pat = (struct patch_info *) rec->data.data8; pat->key = GUS_PATCH; /* * Restore patch type */ rec->parm1 = sample_ptrs[ptr]; /* * DRAM address */ rec->parm2 = sizeof (struct patch_info); } return 0; break; case PM_SET_PATCH: { int ptr = rec->parm1; struct patch_info *pat; if (ptr < 0 || ptr >= free_sample) return RET_ERROR (EINVAL); pat = (struct patch_info *) rec->data.data8; if (pat->len > samples[ptr].len) /* * Cannot expand sample */ return RET_ERROR (EINVAL); pat->key = samples[ptr].key; /* * Ensure the link is correct */ memcpy ((char *) &samples[ptr], rec->data.data8, sizeof (struct patch_info)); pat->key = GUS_PATCH; } return 0; break; case PM_READ_PATCH: /* * Returns a block of wave data from the DRAM */ { int sample = rec->parm1; int n; long offs = rec->parm2; int l = rec->parm3; if (sample < 0 || sample >= free_sample) return RET_ERROR (EINVAL); if (offs < 0 || offs >= samples[sample].len) return RET_ERROR (EINVAL); /* * Invalid offset */ n = samples[sample].len - offs; /* * Nr of bytes left */ if (l > n) l = n; if (l > sizeof (rec->data.data8)) l = sizeof (rec->data.data8); if (l <= 0) return RET_ERROR (EINVAL); /* * Was there a bug? */ offs += sample_ptrs[sample]; /* * Begin offsess + offset to DRAM */ for (n = 0; n < l; n++) rec->data.data8[n] = gus_peek (offs++); rec->parm1 = n; /* * Nr of bytes copied */ } return 0; break; case PM_WRITE_PATCH: /* * Writes a block of wave data to the DRAM */ { int sample = rec->parm1; int n; long offs = rec->parm2; int l = rec->parm3; if (sample < 0 || sample >= free_sample) return RET_ERROR (EINVAL); if (offs < 0 || offs >= samples[sample].len) return RET_ERROR (EINVAL); /* * Invalid offset */ n = samples[sample].len - offs; /* * Nr of bytes left */ if (l > n) l = n; if (l > sizeof (rec->data.data8)) l = sizeof (rec->data.data8); if (l <= 0) return RET_ERROR (EINVAL); /* * Was there a bug? */ offs += sample_ptrs[sample]; /* * Begin offsess + offset to DRAM */ for (n = 0; n < l; n++) gus_poke (offs++, rec->data.data8[n]); rec->parm1 = n; /* * Nr of bytes copied */ } return 0; break; default: return RET_ERROR (EINVAL); } } static struct synth_operations guswave_operations = { &gus_info, #ifdef FUTURE_VERSION 0, #endif SYNTH_TYPE_SAMPLE, SAMPLE_TYPE_GUS, guswave_open, guswave_close, guswave_ioctl, guswave_kill_note, guswave_start_note, guswave_set_instr, guswave_reset, guswave_hw_control, guswave_load_patch, guswave_aftertouch, guswave_controller, guswave_panning, guswave_volume_method, guswave_patchmgr, #ifdef FUTURE_VERSION guswave_bender #endif }; static void set_input_volumes (void) { unsigned long flags; unsigned char mask = 0xff & ~0x06; /* Just line out enabled */ DISABLE_INTR (flags); /* * Enable channels having vol > 10% * Note! bit 0x01 means line in DISABLED while 0x04 means * mic in ENABLED. */ if (gus_line_vol > 10) mask &= ~0x01; if (gus_mic_vol > 10) mask |= 0x04; if (recording_active) { /* * Disable channel, if not selected for recording */ if (!(gus_recmask & SOUND_MASK_LINE)) mask |= 0x01; if (!(gus_recmask & SOUND_MASK_MIC)) mask &= ~0x04; } mix_image &= ~0x07; mix_image |= mask & 0x07; OUTB (mix_image, u_Mixer); RESTORE_INTR (flags); } int gus_default_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg) { #define MIX_DEVS (SOUND_MASK_MIC|SOUND_MASK_LINE| \ SOUND_MASK_SYNTH|SOUND_MASK_PCM) if (((cmd >> 8) & 0xff) == 'M') { if (cmd & IOC_IN) switch (cmd & 0xff) { case SOUND_MIXER_RECSRC: gus_recmask = IOCTL_IN (arg) & MIX_DEVS; if (!(gus_recmask & (SOUND_MASK_MIC | SOUND_MASK_LINE))) gus_recmask = SOUND_MASK_MIC; /* Note! Input volumes are updated during next open for recording */ return IOCTL_OUT (arg, gus_recmask); break; case SOUND_MIXER_MIC: { int vol = IOCTL_IN (arg) & 0xff; if (vol < 0) vol = 0; if (vol > 100) vol = 100; gus_mic_vol = vol; set_input_volumes (); return IOCTL_OUT (arg, vol | (vol << 8)); } break; case SOUND_MIXER_LINE: { int vol = IOCTL_IN (arg) & 0xff; if (vol < 0) vol = 0; if (vol > 100) vol = 100; gus_line_vol = vol; set_input_volumes (); return IOCTL_OUT (arg, vol | (vol << 8)); } break; case SOUND_MIXER_PCM: gus_pcm_volume = IOCTL_IN (arg) & 0xff; if (gus_pcm_volume < 0) gus_pcm_volume = 0; if (gus_pcm_volume > 100) gus_pcm_volume = 100; gus_sampling_update_volume (); return IOCTL_OUT (arg, gus_pcm_volume | (gus_pcm_volume << 8)); break; case SOUND_MIXER_SYNTH: { int voice; gus_wave_volume = IOCTL_IN (arg) & 0xff; if (gus_wave_volume < 0) gus_wave_volume = 0; if (gus_wave_volume > 100) gus_wave_volume = 100; if (active_device == GUS_DEV_WAVE) for (voice = 0; voice < nr_voices; voice++) dynamic_volume_change (voice); /* * Apply the new * volume */ return IOCTL_OUT (arg, gus_wave_volume | (gus_wave_volume << 8)); } break; default: return RET_ERROR (EINVAL); } else switch (cmd & 0xff) /* * Return parameters */ { case SOUND_MIXER_RECSRC: return IOCTL_OUT (arg, gus_recmask); break; case SOUND_MIXER_DEVMASK: return IOCTL_OUT (arg, MIX_DEVS); break; case SOUND_MIXER_STEREODEVS: return IOCTL_OUT (arg, 0); break; case SOUND_MIXER_RECMASK: return IOCTL_OUT (arg, SOUND_MASK_MIC | SOUND_MASK_LINE); break; case SOUND_MIXER_CAPS: return IOCTL_OUT (arg, 0); break; case SOUND_MIXER_MIC: return IOCTL_OUT (arg, gus_mic_vol | (gus_mic_vol << 8)); break; case SOUND_MIXER_LINE: return IOCTL_OUT (arg, gus_line_vol | (gus_line_vol << 8)); break; case SOUND_MIXER_PCM: return IOCTL_OUT (arg, gus_pcm_volume | (gus_pcm_volume << 8)); break; case SOUND_MIXER_SYNTH: return IOCTL_OUT (arg, gus_wave_volume | (gus_wave_volume << 8)); break; default: return RET_ERROR (EINVAL); } } else return RET_ERROR (EINVAL); } static struct mixer_operations gus_mixer_operations = { gus_default_mixer_ioctl }; static long gus_default_mixer_init (long mem_start) { if (num_mixers < MAX_MIXER_DEV) /* * Don't install if there is another * mixer */ mixer_devs[num_mixers++] = &gus_mixer_operations; return mem_start; } long gus_wave_init (long mem_start, int irq, int dma) { unsigned long flags; unsigned char val; char *model_num = "2.4"; int gus_type = 0x24; /* 2.4 */ int mixer_type = 0; /* * Try to identify the GUS model. * * Versions < 3.6 don't have the digital ASIC. Try to probe it first. */ DISABLE_INTR (flags); OUTB (0x20, gus_base + 0x0f); val = INB (gus_base + 0x0f); RESTORE_INTR (flags); if (val != 0xff && (val & 0x06)) /* Should be 0x02? */ { /* * It has the digital ASIC so the card is at least v3.4. * Next try to detect the true model. */ val = INB (u_MixSelect); /* * Value 255 means pre-3.7 which don't have mixer. * Values 5 thru 9 mean v3.7 which has a ICS2101 mixer. * 10 and above is GUS MAX which has the CS4231 codec/mixer. * * Sorry. No GUS max support yet but it should be available * soon after the SDK for GUS MAX is available. */ if (val == 255 || val < 5) { model_num = "3.4"; gus_type = 0x34; } else if (val < 10) { model_num = "3.7"; gus_type = 0x37; mixer_type = ICS2101; } else { model_num = "MAX"; gus_type = 0x40; mixer_type = CS4231; } } else { /* * ASIC not detected so the card must be 2.2 or 2.4. * There could still be the 16-bit/mixer daughter card. * It has the same codec/mixer than MAX. * At this time there is no support for it but it will appear soon. */ } #ifdef __FreeBSD__ printk ("snd4: ", model_num, (int) gus_mem_size / 1024); #else printk (" ", model_num, (int) gus_mem_size / 1024); #endif #ifndef SCO sprintf (gus_info.name, "Gravis UltraSound %s (%dk)", model_num, (int) gus_mem_size / 1024); #endif if (irq < 0 || irq > 15) { printk ("ERROR! Invalid IRQ#%d. GUS Disabled", irq); return mem_start; } if (dma < 0 || dma > 7) { printk ("ERROR! Invalid DMA#%d. GUS Disabled", dma); return mem_start; } gus_irq = irq; gus_dma = dma; if (num_synths >= MAX_SYNTH_DEV) printk ("GUS Error: Too many synthesizers\n"); else synth_devs[num_synths++] = &guswave_operations; PERMANENT_MALLOC (struct patch_info *, samples, (MAX_SAMPLE + 1) * sizeof (*samples), mem_start); reset_sample_memory (); gus_initialize (); if (num_dspdevs < MAX_DSP_DEV) { dsp_devs[gus_devnum = num_dspdevs++] = &gus_sampling_operations; sound_dsp_dmachan[gus_devnum] = dma; sound_buffcounts[gus_devnum] = DSP_BUFFCOUNT; sound_buffsizes[gus_devnum] = DSP_BUFFSIZE; sound_dma_automode[gus_devnum] = 0; } else printk ("GUS: Too many PCM devices available\n"); /* * Mixer dependent initialization. */ switch (mixer_type) { case ICS2101: gus_line_vol=gus_mic_vol=gus_wave_volume = gus_pcm_volume = 100; return ics2101_mixer_init (mem_start); case CS4231: /* Available soon */ default: return gus_default_mixer_init (mem_start); } return mem_start; } static void do_loop_irq (int voice) { unsigned char tmp; int mode, parm; unsigned long flags; DISABLE_INTR (flags); gus_select_voice (voice); tmp = gus_read8 (0x00); tmp &= ~0x20; /* * Disable wave IRQ for this_one voice */ gus_write8 (0x00, tmp); mode = voices[voice].loop_irq_mode; voices[voice].loop_irq_mode = 0; parm = voices[voice].loop_irq_parm; switch (mode) { case LMODE_FINISH: /* * Final loop finished, shoot volume down */ if ((gus_read16 (0x09) >> 4) < 100) /* * Get current volume */ { gus_voice_off (); gus_rampoff (); gus_voice_init (voice); break; } gus_ramp_range (65, 4065); gus_ramp_rate (0, 63); /* * Fastest possible rate */ gus_rampon (0x20 | 0x40); /* * Ramp down, once, irq */ voices[voice].volume_irq_mode = VMODE_HALT; break; case LMODE_PCM_STOP: pcm_active = 0; /* * Requires extensive processing */ case LMODE_PCM: { int orig_qlen = pcm_qlen; pcm_qlen--; pcm_head = (pcm_head + 1) % pcm_nblk; if (pcm_qlen) { play_next_pcm_block (); } else { /* * Out of data. Just stop the voice */ gus_voice_off (); gus_rampoff (); pcm_active = 0; } if (orig_qlen == pcm_nblk) { DMAbuf_outputintr (gus_devnum, 0); } } break; default:; } RESTORE_INTR (flags); } static void do_volume_irq (int voice) { unsigned char tmp; int mode, parm; unsigned long flags; DISABLE_INTR (flags); gus_select_voice (voice); tmp = gus_read8 (0x0d); tmp &= ~0x20; /* * Disable volume ramp IRQ */ gus_write8 (0x0d, tmp); mode = voices[voice].volume_irq_mode; voices[voice].volume_irq_mode = 0; parm = voices[voice].volume_irq_parm; switch (mode) { case VMODE_HALT: /* * Decay phase finished */ RESTORE_INTR (flags); gus_voice_init (voice); break; case VMODE_ENVELOPE: gus_rampoff (); RESTORE_INTR (flags); step_envelope (voice); break; case VMODE_START_NOTE: RESTORE_INTR (flags); guswave_start_note2 (voices[voice].dev_pending, voice, voices[voice].note_pending, voices[voice].volume_pending); if (voices[voice].kill_pending) guswave_kill_note (voices[voice].dev_pending, voice, 0); if (voices[voice].sample_pending >= 0) { guswave_set_instr (voices[voice].dev_pending, voice, voices[voice].sample_pending); voices[voice].sample_pending = -1; } break; default:; } } void gus_voice_irq (void) { unsigned long wave_ignore = 0, volume_ignore = 0; unsigned long voice_bit; unsigned char src, voice; while (1) { src = gus_read8 (0x0f); /* * Get source info */ voice = src & 0x1f; src &= 0xc0; if (src == (0x80 | 0x40)) return; /* * No interrupt */ voice_bit = 1 << voice; if (!(src & 0x80)) /* * Wave IRQ pending */ if (!(wave_ignore & voice_bit) && voice < nr_voices) /* * Not done * yet */ { wave_ignore |= voice_bit; do_loop_irq (voice); } if (!(src & 0x40)) /* * Volume IRQ pending */ if (!(volume_ignore & voice_bit) && voice < nr_voices) /* * Not done * yet */ { volume_ignore |= voice_bit; do_volume_irq (voice); } } } void guswave_dma_irq (void) { unsigned char status; status = gus_look8 (0x41); /* * Get DMA IRQ Status */ if (status & 0x40) /* * DMA Irq pending */ switch (active_device) { case GUS_DEV_WAVE: if (SOMEONE_WAITING (dram_sleeper, dram_sleep_flag)) WAKE_UP (dram_sleeper, dram_sleep_flag); break; case GUS_DEV_PCM_CONTINUE: gus_transfer_output_block (pcm_current_dev, pcm_current_buf, pcm_current_count, pcm_current_intrflag, 1); break; case GUS_DEV_PCM_DONE: if (pcm_qlen < pcm_nblk) { DMAbuf_outputintr (gus_devnum, pcm_qlen == 0); } break; default:; } status = gus_look8 (0x49); /* * Get Sampling IRQ Status */ if (status & 0x40) /* * Sampling Irq pending */ { DMAbuf_inputintr (gus_devnum); } } #endif