1e25d964d2
with individual devices for each type of sound card: opl, sb, sbxvi, sbmidi, pas, mpu, gus, gusxvi, gusmax, mss, uart EXCLUDE_* options are no longer required to be included in the config file. They are automatically determined by local.h depending on the devices included. Move #includes in local.h to os.h so files are included in the proper order to avoid warnings. soundcard.c now has additional code to reflect the device driver routines needed. Define new EXCLUDE_SB16MIDI for use in sb16_midi.c and dev_table.h. #ifndef EXCLUDE_SEQUENCER or EXCLUDE_AUDIO have been added to soundcard.c and sound_switch.c where appropriate. Probe outputs changed to reflect new device names. Readme.freebsd not needed. Update sound.doc with new config instructions. Reviewed by: wollman
3260 lines
72 KiB
C
3260 lines
72 KiB
C
/*
|
|
* sound/gus_wave.c
|
|
*
|
|
* Driver for the Gravis UltraSound wave table synth.
|
|
*
|
|
* Copyright by Hannu Savolainen 1993, 1994
|
|
*
|
|
* 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.
|
|
*
|
|
* $Id: gus_wave.c,v 1.11 1995/01/04 20:07:27 pst Exp $
|
|
*/
|
|
|
|
#include "sound_config.h"
|
|
#include <machine/ultrasound.h>
|
|
#include "gus_hw.h"
|
|
|
|
#undef OUTB
|
|
#define OUTB(val, port) outb(port, val)
|
|
|
|
#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;
|
|
|
|
};
|
|
|
|
static struct voice_alloc_info *voice_alloc;
|
|
|
|
extern int gus_base;
|
|
extern int gus_irq, gus_dma;
|
|
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_recmask = SOUND_MASK_MIC;
|
|
static int recording_active = 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;
|
|
|
|
/*
|
|
* 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_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, 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;
|
|
|
|
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 /* 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] = -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)
|
|
{ /* Writes a byte to the DRAM */
|
|
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)
|
|
{ /* Reads a byte from the DRAM */
|
|
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)
|
|
{ /* Writes to an indirect register (8 bit) */
|
|
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)
|
|
{ /* Reads from an indirect register (8 bit). Offset 0x80. */
|
|
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)
|
|
{ /* Reads from an indirect register (8 bit). No additional offset. */
|
|
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)
|
|
{ /* Writes to an indirect register (16 bit) */
|
|
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)
|
|
{ /* Reads from an indirect register (16 bit). Offset 0x80. */
|
|
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)
|
|
{ /* Writes an 24 bit memory address */
|
|
unsigned long hold_address;
|
|
unsigned long flags;
|
|
|
|
DISABLE_INTR (flags);
|
|
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));
|
|
RESTORE_INTR (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 (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;
|
|
|
|
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,
|
|
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 */
|
|
voice_alloc->map[voice] = 0;
|
|
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 phase begins. Continue envelope after receiving note off.
|
|
*/
|
|
}
|
|
|
|
if (voices[voice].env_phase >= 5)
|
|
{ /* Envelope finished. Shoot the voice down */
|
|
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 the envelope on the next step */
|
|
return;
|
|
}
|
|
|
|
if (vol > prev_vol)
|
|
{
|
|
if (vol >= (4096 - 64))
|
|
vol = 4096 - 65;
|
|
gus_ramp_range (0, vol);
|
|
gus_rampon (0x20); /* Increasing volume, with IRQ */
|
|
}
|
|
else
|
|
{
|
|
if (vol <= 64)
|
|
vol = 65;
|
|
gus_ramp_range (vol, 4030);
|
|
gus_rampon (0x60); /* Decreasing volume, with 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 */
|
|
voice_alloc->map[voice] = 0;
|
|
RESTORE_INTR (flags);
|
|
return;
|
|
}
|
|
|
|
is16bits = (samples[instr_no].mode & WAVE_16_BITS) ? 1 : 0; /* 8 or 16 bits */
|
|
|
|
if (voices[voice].mode & WAVE_ENVELOPES)
|
|
{
|
|
start_release (voice, flags);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Ramp the volume down but not too quickly.
|
|
*/
|
|
if ((int) (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, with 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;
|
|
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
|
|
guswave_kill_note (int dev, int voice, int note, int velocity)
|
|
{
|
|
unsigned long flags;
|
|
|
|
DISABLE_INTR (flags);
|
|
voice_alloc->map[voice] = 0xffff;
|
|
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; /* Procedure 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, dow, 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) /* Close enough to target. */
|
|
{
|
|
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); /* Get voice status */
|
|
RESTORE_INTR (flags);
|
|
|
|
if (status & 0x03)
|
|
return; /* Voice was not running */
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
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;
|
|
case CTL_EXPRESSION:
|
|
value /= 128;
|
|
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;
|
|
|
|
case CTL_PAN:
|
|
voices[voice].panning = (value * 2) - 128;
|
|
break;
|
|
|
|
case CTL_MAIN_VOLUME:
|
|
value = (value * 100) / 16383;
|
|
|
|
case CTRL_MAIN_VOLUME:
|
|
voices[voice].main_vol = value;
|
|
if (voices[voice].volume_irq_mode != VMODE_START_NOTE)
|
|
dynamic_volume_change (voice);
|
|
break;
|
|
|
|
default:
|
|
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;
|
|
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; /* Try 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 ();
|
|
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); /* 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;
|
|
|
|
if (samples[sample].mode & WAVE_BIDIR_LOOP)
|
|
mode |= 0x10;
|
|
|
|
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 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) || (int) ((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)) < 0)
|
|
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; /* Header size */
|
|
|
|
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; /* 32 byte alignment */
|
|
|
|
#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 = /* Aling to 256K */
|
|
((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 completely transferred yet */
|
|
{
|
|
blk_size = audio_devs[gus_devnum]->buffsize;
|
|
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))
|
|
{ /* 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 (audio_devs[gus_devnum]->dmap->raw_buf[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,
|
|
audio_devs[gus_devnum]->dmap->raw_buf_phys[0],
|
|
blk_size, DMA_MODE_WRITE);
|
|
|
|
/*
|
|
* Set the DRAM address for the wave data
|
|
*/
|
|
|
|
address = target;
|
|
|
|
if (audio_devs[gus_devnum]->dmachan > 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 (audio_devs[gus_devnum]->dmachan > 3)
|
|
dma_command |= 0x04; /* 16 bit DMA _channel_ */
|
|
|
|
gus_write8 (0x41, dma_command); /* Lets bo 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; /* Don't allow interrupts */
|
|
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; /* Don't allow interrupts */
|
|
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 software voice level */
|
|
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; /* NJET-NJET */
|
|
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; /* Don't allow interrupts */
|
|
gus_ramp_mode (p1);
|
|
RESTORE_INTR (flags);
|
|
break;
|
|
|
|
case _GUS_RAMPON:
|
|
if (voices[voice].mode & WAVE_ENVELOPES)
|
|
break; /* EI-EI */
|
|
DISABLE_INTR (flags);
|
|
gus_select_voice (voice);
|
|
p1 &= ~0x20; /* Don't allow interrupts */
|
|
gus_rampon (p1);
|
|
RESTORE_INTR (flags);
|
|
break;
|
|
|
|
case _GUS_RAMPOFF:
|
|
if (voices[voice].mode & WAVE_ENVELOPES)
|
|
break; /* NEJ-NEJ */
|
|
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_SETFMT:
|
|
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 POSSIBLE */
|
|
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;
|
|
dma_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 fragment of the DRAM buffer */
|
|
{
|
|
mode[chn] |= 0x08; /* Enable loop */
|
|
ramp_mode[chn] = 0x03;/* Disable rollover bit */
|
|
}
|
|
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) /* Playback not already active */
|
|
{
|
|
/*
|
|
* 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 ();
|
|
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 */
|
|
|
|
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 looping */
|
|
|
|
if (pcm_datasize[this_one] != pcm_bsize)
|
|
{
|
|
/*
|
|
* Incompletely filled block. Possibly the last one.
|
|
*/
|
|
if (chn == 0)
|
|
{
|
|
mode[chn] &= ~0x08; /* Disable looping */
|
|
mode[chn] |= 0x20;/* Enable IRQ at the end */
|
|
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 looping */
|
|
}
|
|
}
|
|
|
|
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 (audio_devs[dev]->dmachan > 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 (audio_devs[dev]->dmachan > 3)
|
|
dma_command |= 0x04; /* 16 bit DMA channel */
|
|
|
|
gus_write8 (0x41, dma_command); /* Kickstart */
|
|
|
|
if (chn == (gus_sampling_channels - 1)) /* Last channel */
|
|
{
|
|
/*
|
|
* Last (right or mono) channel data
|
|
*/
|
|
dma_active = 1; /* DMA started. There is a unacknowledged buffer */
|
|
active_device = GUS_DEV_PCM_DONE;
|
|
if (!pcm_active && (pcm_qlen > 0 || 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 enabled, invert MSB */
|
|
|
|
if (audio_devs[dev]->dmachan > 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 rate */
|
|
|
|
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_local_qlen (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,
|
|
AFMT_U8 | AFMT_S16_LE,
|
|
NULL,
|
|
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_local_qlen,
|
|
gus_copy_from_user
|
|
};
|
|
|
|
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);
|
|
}
|
|
|
|
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 location */
|
|
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; /* Num 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 offset + 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 offset + 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 int
|
|
guswave_alloc (int dev, int chn, int note, struct voice_alloc_info *alloc)
|
|
{
|
|
int i, p;
|
|
|
|
p = alloc->ptr;
|
|
/*
|
|
* First look for a completely stopped voice
|
|
*/
|
|
|
|
for (i = 0; i < alloc->max_voice; i++)
|
|
{
|
|
if (alloc->map[p] == 0)
|
|
{
|
|
alloc->ptr = p;
|
|
return p;
|
|
}
|
|
p = (p + 1) % alloc->max_voice;
|
|
}
|
|
|
|
/*
|
|
* Then look for a releasing voice
|
|
*/
|
|
|
|
for (i = 0; i < alloc->max_voice; i++)
|
|
{
|
|
if (alloc->map[p] == 0xffff)
|
|
{
|
|
alloc->ptr = p;
|
|
return p;
|
|
}
|
|
p = (p + 1) % alloc->max_voice;
|
|
}
|
|
printk ("GUS: Out of free voices\n");
|
|
|
|
alloc->ptr = p;
|
|
return p;
|
|
}
|
|
|
|
static struct synth_operations guswave_operations =
|
|
{
|
|
&gus_info,
|
|
0,
|
|
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,
|
|
guswave_bender,
|
|
guswave_alloc
|
|
};
|
|
|
|
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 vol */
|
|
|
|
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;
|
|
#ifndef EXCLUDE_GUSMAX
|
|
{
|
|
unsigned char max_config = 0x40; /* Codec enable */
|
|
|
|
if (dma > 3)
|
|
max_config |= 0x30; /* 16 bit playback and capture DMAs */
|
|
|
|
max_config |= (gus_base >> 4) & 0x0f; /* Extract the X from 2X0 */
|
|
|
|
OUTB (max_config, gus_base + 0x106); /* UltraMax control */
|
|
}
|
|
|
|
if (ad1848_detect (gus_base + 0x10c))
|
|
{
|
|
gus_mic_vol = gus_line_vol = gus_pcm_volume = 100;
|
|
gus_wave_volume = 90;
|
|
have_gus_max = 1;
|
|
ad1848_init ("gusmax0: <GUS MAX>", gus_base + 0x10c,
|
|
-irq,
|
|
dma,
|
|
dma);
|
|
}
|
|
else
|
|
printk ("[Where's the CS4231?]");
|
|
#endif
|
|
}
|
|
}
|
|
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: <Gravis UltraSound %s (%dk)>", model_num, (int) gus_mem_size / 1024);
|
|
#else /* __FreeBSD__ */
|
|
printk (" <Gravis UltraSound %s (%dk)>", model_num, (int) gus_mem_size / 1024);
|
|
#endif /* __FreeBSD__ */
|
|
|
|
#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
|
|
{
|
|
voice_alloc = &guswave_operations.alloc;
|
|
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_audiodevs < MAX_AUDIO_DEV)
|
|
{
|
|
audio_devs[gus_devnum = num_audiodevs++] = &gus_sampling_operations;
|
|
audio_devs[gus_devnum]->dmachan = dma;
|
|
#ifndef NO_AUTODMA
|
|
audio_devs[gus_devnum]->buffcount = 1;
|
|
#else
|
|
audio_devs[gus_devnum]->flags &= ~DMA_AUTOMODE;
|
|
audio_devs[gus_devnum]->buffcount = DSP_BUFFCOUNT;
|
|
#endif
|
|
audio_devs[gus_devnum]->buffsize = DSP_BUFFSIZE;
|
|
}
|
|
else
|
|
printk ("GUS: Too many PCM devices available\n");
|
|
|
|
/*
|
|
* Mixer dependent initialization.
|
|
*/
|
|
|
|
switch (mixer_type)
|
|
{
|
|
case ICS2101:
|
|
gus_mic_vol = gus_line_vol = gus_pcm_volume = 100;
|
|
gus_wave_volume = 90;
|
|
return ics2101_mixer_init (mem_start);
|
|
|
|
case CS4231:
|
|
/* Initialized elsewhere (ad1848.c) */
|
|
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 ((int) (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; /* Signal to the play_next_pcm_block routine */
|
|
case LMODE_PCM:
|
|
{
|
|
int orig_qlen = pcm_qlen;
|
|
int flag; /* 0 or 2 */
|
|
|
|
pcm_qlen--;
|
|
pcm_head = (pcm_head + 1) % pcm_nblk;
|
|
if (pcm_qlen)
|
|
{
|
|
play_next_pcm_block ();
|
|
}
|
|
else
|
|
{ /* Underrun. Just stop the voice */
|
|
gus_voice_off ();
|
|
gus_rampoff ();
|
|
pcm_active = 0;
|
|
}
|
|
|
|
/*
|
|
* If the queue was full before this interrupt, the DMA transfer was
|
|
* suspended. Let it continue now.
|
|
*/
|
|
if (dma_active)
|
|
{
|
|
if (pcm_qlen == 0)
|
|
flag = 1; /* Underflow */
|
|
else
|
|
flag = 0;
|
|
dma_active = 0;
|
|
}
|
|
else
|
|
flag = 2; /* Just notify the dmabuf.c */
|
|
DMAbuf_outputintr (gus_devnum, flag);
|
|
}
|
|
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,
|
|
voices[voice].note_pending, 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) && (int) 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) && (int) 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 interrupt 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: /* Left channel data transferred */
|
|
gus_transfer_output_block (pcm_current_dev, pcm_current_buf,
|
|
pcm_current_count,
|
|
pcm_current_intrflag, 1);
|
|
break;
|
|
|
|
case GUS_DEV_PCM_DONE: /* Right or mono channel data transferred */
|
|
if (pcm_qlen < pcm_nblk)
|
|
{
|
|
int flag = (1 - dma_active) * 2; /* 0 or 2 */
|
|
|
|
if (pcm_qlen == 0)
|
|
flag = 1; /* Underrun */
|
|
dma_active = 0;
|
|
DMAbuf_outputintr (gus_devnum, flag);
|
|
}
|
|
break;
|
|
|
|
default:;
|
|
}
|
|
|
|
status = gus_look8 (0x49); /*
|
|
* Get Sampling IRQ Status
|
|
*/
|
|
if (status & 0x40) /*
|
|
* Sampling Irq pending
|
|
*/
|
|
{
|
|
DMAbuf_inputintr (gus_devnum);
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|