📄 gus_wave.c
字号:
/* * sound/gus_wave.c * * Driver for the Gravis UltraSound wave table synth. * * * Copyright (C) by Hannu Savolainen 1993-1997 * * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) * Version 2 (June 1991). See the "COPYING" file distributed with this software * for more info. * * * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) * Frank van de Pol : Fixed GUS MAX interrupt handling. Enabled simultanious * usage of CS4231A codec, GUS wave and MIDI for GUS MAX. * Bartlomiej Zolnierkiewicz : added some __init/__exit */ #include <linux/init.h> #include <linux/config.h>#define GUSPNP_AUTODETECT#include "sound_config.h"#include <linux/ultrasound.h>#include "gus.h"#include "gus_hw.h"#define GUS_BANK_SIZE (((iw_mode) ? 256*1024*1024 : 256*1024))#define MAX_SAMPLE 150#define MAX_PATCH 256#define NOT_SAMPLE 0xffffstruct voice_info{ unsigned long orig_freq; unsigned long current_freq; unsigned long mode; int fixed_pitch; 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;};static struct voice_alloc_info *voice_alloc;static struct address_info *gus_hw_config;extern int gus_base;extern int gus_irq, gus_dma;extern int gus_pnp_flag;extern int gus_no_wave_dma;static int gus_dma2 = -1;static int dual_dma_mode = 0;static long gus_mem_size = 0;static long free_mem_ptr = 0;static int gus_busy = 0;static int gus_no_dma = 0;static int nr_voices = 0;static int gus_devnum = 0;static int volume_base, volume_scale, volume_method;static int gus_recmask = SOUND_MASK_MIC;static int recording_active = 0;static int only_read_access = 0;static int only_8_bits = 0;int iw_mode = 0;int gus_wave_volume = 60;int gus_pcm_volume = 80;int have_gus_max = 0;static int gus_line_vol = 100, gus_mic_vol = 0;static unsigned char mix_image = 0x00;int gus_timer_enabled = 0;/* * 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 done ch. 1/2 */static int gus_audio_speed;static int gus_audio_channels;static int gus_audio_bits;static int gus_audio_bsize;static char bounce_buf[8 * 1024]; /* Must match value set to max_fragment */static DECLARE_WAIT_QUEUE_HEAD(dram_sleeper);/* * Variables and buffers for PCM output */#define MAX_PCM_BUFFERS (128*MAX_REALTIME_FACTOR) /* Don't change */static int pcm_bsize, pcm_nblk, pcm_banksize;static int pcm_datasize[MAX_PCM_BUFFERS];static volatile int pcm_head, pcm_tail, pcm_qlen;static volatile int pcm_active;static volatile int dma_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;extern int *gus_osp;static 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 = NULL;static long sample_ptrs[MAX_SAMPLE + 1];static int sample_map[32];static int free_sample;static int mixer_type = 0;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);static void gus_tmr_install(int io_base);#define INSTANT_RAMP -1 /* Instant change. No 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 a silent sample to the beginning */ gus_poke(1, 0); free_mem_ptr = 2; free_sample = 0; for (i = 0; i < MAX_PATCH; i++) patch_table[i] = NOT_SAMPLE;}void gus_delay(void){ int i; for (i = 0; i < 7; i++) inb(u_DRAMIO);}static void gus_poke(long addr, unsigned char data){ /* Writes a byte to the DRAM */ unsigned long flags; save_flags(flags); cli(); 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_flags(flags);}static unsigned char gus_peek(long addr){ /* Reads a byte from the DRAM */ unsigned long flags; unsigned char tmp; save_flags(flags); cli(); 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_flags(flags); return tmp;}void gus_write8(int reg, unsigned int data){ /* Writes to an indirect register (8 bit) */ unsigned long flags; save_flags(flags); cli(); outb((reg), u_Command); outb(((unsigned char) (data & 0xff)), u_DataHi); restore_flags(flags);}static unsigned char gus_read8(int reg){ /* Reads from an indirect register (8 bit). Offset 0x80. */ unsigned long flags; unsigned char val; save_flags(flags); cli(); outb((reg | 0x80), u_Command); val = inb(u_DataHi); restore_flags(flags); return val;}static unsigned char gus_look8(int reg){ /* Reads from an indirect register (8 bit). No additional offset. */ unsigned long flags; unsigned char val; save_flags(flags); cli(); outb((reg), u_Command); val = inb(u_DataHi); restore_flags(flags); return val;}static void gus_write16(int reg, unsigned int data){ /* Writes to an indirect register (16 bit) */ unsigned long flags; save_flags(flags); cli(); outb((reg), u_Command); outb(((unsigned char) (data & 0xff)), u_DataLo); outb(((unsigned char) ((data >> 8) & 0xff)), u_DataHi); restore_flags(flags);}static unsigned short gus_read16(int reg){ /* Reads from an indirect register (16 bit). Offset 0x80. */ unsigned long flags; unsigned char hi, lo; save_flags(flags); cli(); outb((reg | 0x80), u_Command); lo = inb(u_DataLo); hi = inb(u_DataHi); restore_flags(flags); return ((hi << 8) & 0xff00) | lo;}static unsigned short gus_look16(int reg){ /* Reads from an indirect register (16 bit). No additional offset. */ unsigned long flags; unsigned char hi, lo; save_flags(flags); cli(); outb((reg), u_Command); lo = inb(u_DataLo); hi = inb(u_DataHi); restore_flags(flags); return ((hi << 8) & 0xff00) | lo;}static void gus_write_addr(int reg, unsigned long address, int frac, int is16bit){ /* Writes an 24 bit memory address */ unsigned long hold_address; unsigned long flags; save_flags(flags); cli(); if (is16bit) { if (iw_mode) { /* Interwave spesific address translations */ address >>= 1; } else { /* * 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) + (frac << 5)); /* Could writing twice fix problems with GUS_VOICE_POS()? Let's try. */ gus_delay(); gus_write16(reg, (unsigned short) ((address >> 7) & 0xffff)); gus_write16(reg + 1, (unsigned short) ((address << 9) & 0xffff) + (frac << 5)); restore_flags(flags);}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 (iw_mode) nvoices = 32; if (nvoices < 14) nvoices = 14; if (nvoices > 32) nvoices = 32; voice_alloc->max_voice = 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 touch last two bits */ 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; /* Interwave plays at 44100 Hz with any number of voices */ if (iw_mode) fc = (unsigned short) (((freq << 9) + (44100 >> 1)) / 44100); else 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)); /* Leave the last 2 bits alone */ 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, 0, samples[sample_no].mode & WAVE_16_BITS); } }}static void gus_voice_init(int voice){ unsigned long flags; save_flags(flags); cli(); gus_select_voice(voice); gus_voice_volume(0); gus_voice_off(); gus_write_addr(0x0a, 0, 0, 0); /* Set current position to 0 */ gus_write8(0x00, 0x03); /* Voice off */ gus_write8(0x0d, 0x03); /* Ramping off */ voice_alloc->map[voice] = 0; voice_alloc->alloc_times[voice] = 0; restore_flags(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; voices[voice].fixed_pitch = 0;}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) { save_flags(flags); cli(); gus_select_voice(voice); gus_rampoff(); restore_flags(flags); return; /* * Sustain phase begins. Continue envelope after receiving note off. */ } if (voices[voice].env_phase >= 5) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -