Commit e5723b41 authored by Takashi Iwai's avatar Takashi Iwai Committed by Jaroslav Kysela

[ALSA] Remove sequencer instrument layer

Remove sequencer instrument layer from the tree.
This mechanism hasn't been used much with the actual devices.  The only
reasonable user was OPL3 loader, and now it was rewritten to use hwdep
instead.  So, let's remove the rest of rotten codes.
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
Signed-off-by: default avatarJaroslav Kysela <perex@perex.cz>
parent 05c1afe7
/*
* Advanced Linux Sound Architecture
*
* FM (OPL2/3) Instrument Format
* Copyright (c) 2000 Uros Bizjak <uros@kss-loka.si>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef __SOUND_AINSTR_FM_H
#define __SOUND_AINSTR_FM_H
#ifndef __KERNEL__
#include <asm/types.h>
#include <asm/byteorder.h>
#endif
/*
* share types (share ID 1)
*/
#define FM_SHARE_FILE 0
/*
* FM operator
*/
struct fm_operator {
unsigned char am_vib;
unsigned char ksl_level;
unsigned char attack_decay;
unsigned char sustain_release;
unsigned char wave_select;
};
/*
* Instrument
*/
#define FM_PATCH_OPL2 0x01 /* OPL2 2 operators FM instrument */
#define FM_PATCH_OPL3 0x02 /* OPL3 4 operators FM instrument */
struct fm_instrument {
unsigned int share_id[4]; /* share id - zero = no sharing */
unsigned char type; /* instrument type */
struct fm_operator op[4];
unsigned char feedback_connection[2];
unsigned char echo_delay;
unsigned char echo_atten;
unsigned char chorus_spread;
unsigned char trnsps;
unsigned char fix_dur;
unsigned char modes;
unsigned char fix_key;
};
/*
*
* Kernel <-> user space
* Hardware (CPU) independent section
*
* * = zero or more
* + = one or more
*
* fm_xinstrument FM_STRU_INSTR
*
*/
#define FM_STRU_INSTR __cpu_to_be32(('I'<<24)|('N'<<16)|('S'<<8)|'T')
/*
* FM operator
*/
struct fm_xoperator {
__u8 am_vib;
__u8 ksl_level;
__u8 attack_decay;
__u8 sustain_release;
__u8 wave_select;
};
/*
* Instrument
*/
struct fm_xinstrument {
__u32 stype; /* structure type */
__u32 share_id[4]; /* share id - zero = no sharing */
__u8 type; /* instrument type */
struct fm_xoperator op[4]; /* fm operators */
__u8 feedback_connection[2];
__u8 echo_delay;
__u8 echo_atten;
__u8 chorus_spread;
__u8 trnsps;
__u8 fix_dur;
__u8 modes;
__u8 fix_key;
};
#ifdef __KERNEL__
#include "seq_instr.h"
int snd_seq_fm_init(struct snd_seq_kinstr_ops * ops,
struct snd_seq_kinstr_ops * next);
#endif
/* typedefs for compatibility to user-space */
typedef struct fm_xoperator fm_xoperator_t;
typedef struct fm_xinstrument fm_xinstrument_t;
#endif /* __SOUND_AINSTR_FM_H */
/*
* Advanced Linux Sound Architecture
*
* GF1 (GUS) Patch Instrument Format
* Copyright (c) 1994-99 by Jaroslav Kysela <perex@perex.cz>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef __SOUND_AINSTR_GF1_H
#define __SOUND_AINSTR_GF1_H
#ifndef __KERNEL__
#include <asm/types.h>
#include <asm/byteorder.h>
#endif
/*
* share types (share ID 1)
*/
#define GF1_SHARE_FILE 0
/*
* wave formats
*/
#define GF1_WAVE_16BIT 0x0001 /* 16-bit wave */
#define GF1_WAVE_UNSIGNED 0x0002 /* unsigned wave */
#define GF1_WAVE_INVERT 0x0002 /* same as unsigned wave */
#define GF1_WAVE_BACKWARD 0x0004 /* backward mode (maybe used for reverb or ping-ping loop) */
#define GF1_WAVE_LOOP 0x0008 /* loop mode */
#define GF1_WAVE_BIDIR 0x0010 /* bidirectional mode */
#define GF1_WAVE_STEREO 0x0100 /* stereo mode */
#define GF1_WAVE_ULAW 0x0200 /* uLaw compression mode */
/*
* Wavetable definitions
*/
struct gf1_wave {
unsigned int share_id[4]; /* share id - zero = no sharing */
unsigned int format; /* wave format */
struct {
unsigned int number; /* some other ID for this instrument */
unsigned int memory; /* begin of waveform in onboard memory */
unsigned char *ptr; /* pointer to waveform in system memory */
} address;
unsigned int size; /* size of waveform in samples */
unsigned int start; /* start offset in samples * 16 (lowest 4 bits - fraction) */
unsigned int loop_start; /* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */
unsigned int loop_end; /* loop start offset in samples * 16 (lowest 4 bits - fraction) */
unsigned short loop_repeat; /* loop repeat - 0 = forever */
unsigned char flags; /* GF1 patch flags */
unsigned char pad;
unsigned int sample_rate; /* sample rate in Hz */
unsigned int low_frequency; /* low frequency range */
unsigned int high_frequency; /* high frequency range */
unsigned int root_frequency; /* root frequency range */
signed short tune;
unsigned char balance;
unsigned char envelope_rate[6];
unsigned char envelope_offset[6];
unsigned char tremolo_sweep;
unsigned char tremolo_rate;
unsigned char tremolo_depth;
unsigned char vibrato_sweep;
unsigned char vibrato_rate;
unsigned char vibrato_depth;
unsigned short scale_frequency;
unsigned short scale_factor; /* 0-2048 or 0-2 */
struct gf1_wave *next;
};
/*
* Instrument
*/
#define IWFFFF_EXCLUDE_NONE 0x0000 /* exclusion mode - none */
#define IWFFFF_EXCLUDE_SINGLE 0x0001 /* exclude single - single note from the instrument group */
#define IWFFFF_EXCLUDE_MULTIPLE 0x0002 /* exclude multiple - stop only same note from this instrument */
#define IWFFFF_EFFECT_NONE 0
#define IWFFFF_EFFECT_REVERB 1
#define IWFFFF_EFFECT_CHORUS 2
#define IWFFFF_EFFECT_ECHO 3
struct gf1_instrument {
unsigned short exclusion;
unsigned short exclusion_group; /* 0 - none, 1-65535 */
unsigned char effect1; /* effect 1 */
unsigned char effect1_depth; /* 0-127 */
unsigned char effect2; /* effect 2 */
unsigned char effect2_depth; /* 0-127 */
struct gf1_wave *wave; /* first waveform */
};
/*
*
* Kernel <-> user space
* Hardware (CPU) independent section
*
* * = zero or more
* + = one or more
*
* gf1_xinstrument IWFFFF_STRU_INSTR
* +gf1_xwave IWFFFF_STRU_WAVE
*
*/
#define GF1_STRU_WAVE __cpu_to_be32(('W'<<24)|('A'<<16)|('V'<<8)|'E')
#define GF1_STRU_INSTR __cpu_to_be32(('I'<<24)|('N'<<16)|('S'<<8)|'T')
/*
* Wavetable definitions
*/
struct gf1_xwave {
__u32 stype; /* structure type */
__u32 share_id[4]; /* share id - zero = no sharing */
__u32 format; /* wave format */
__u32 size; /* size of waveform in samples */
__u32 start; /* start offset in samples * 16 (lowest 4 bits - fraction) */
__u32 loop_start; /* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */
__u32 loop_end; /* loop start offset in samples * 16 (lowest 4 bits - fraction) */
__u16 loop_repeat; /* loop repeat - 0 = forever */
__u8 flags; /* GF1 patch flags */
__u8 pad;
__u32 sample_rate; /* sample rate in Hz */
__u32 low_frequency; /* low frequency range */
__u32 high_frequency; /* high frequency range */
__u32 root_frequency; /* root frequency range */
__s16 tune;
__u8 balance;
__u8 envelope_rate[6];
__u8 envelope_offset[6];
__u8 tremolo_sweep;
__u8 tremolo_rate;
__u8 tremolo_depth;
__u8 vibrato_sweep;
__u8 vibrato_rate;
__u8 vibrato_depth;
__u16 scale_frequency;
__u16 scale_factor; /* 0-2048 or 0-2 */
};
/*
* Instrument
*/
struct gf1_xinstrument {
__u32 stype;
__u16 exclusion;
__u16 exclusion_group; /* 0 - none, 1-65535 */
__u8 effect1; /* effect 1 */
__u8 effect1_depth; /* 0-127 */
__u8 effect2; /* effect 2 */
__u8 effect2_depth; /* 0-127 */
};
/*
* Instrument info
*/
#define GF1_INFO_ENVELOPE (1<<0)
#define GF1_INFO_TREMOLO (1<<1)
#define GF1_INFO_VIBRATO (1<<2)
struct gf1_info {
unsigned char flags; /* supported wave flags */
unsigned char pad[3];
unsigned int features; /* supported features */
unsigned int max8_len; /* maximum 8-bit wave length */
unsigned int max16_len; /* maximum 16-bit wave length */
};
#ifdef __KERNEL__
#include "seq_instr.h"
struct snd_gf1_ops {
void *private_data;
int (*info)(void *private_data, struct gf1_info *info);
int (*put_sample)(void *private_data, struct gf1_wave *wave,
char __user *data, long len, int atomic);
int (*get_sample)(void *private_data, struct gf1_wave *wave,
char __user *data, long len, int atomic);
int (*remove_sample)(void *private_data, struct gf1_wave *wave,
int atomic);
void (*notify)(void *private_data, struct snd_seq_kinstr *instr, int what);
struct snd_seq_kinstr_ops kops;
};
int snd_seq_gf1_init(struct snd_gf1_ops *ops,
void *private_data,
struct snd_seq_kinstr_ops *next);
#endif
/* typedefs for compatibility to user-space */
typedef struct gf1_xwave gf1_xwave_t;
typedef struct gf1_xinstrument gf1_xinstrument_t;
#endif /* __SOUND_AINSTR_GF1_H */
/*
* Advanced Linux Sound Architecture
*
* InterWave FFFF Instrument Format
* Copyright (c) 1994-99 by Jaroslav Kysela <perex@perex.cz>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef __SOUND_AINSTR_IW_H
#define __SOUND_AINSTR_IW_H
#ifndef __KERNEL__
#include <asm/types.h>
#include <asm/byteorder.h>
#endif
/*
* share types (share ID 1)
*/
#define IWFFFF_SHARE_FILE 0
/*
* wave formats
*/
#define IWFFFF_WAVE_16BIT 0x0001 /* 16-bit wave */
#define IWFFFF_WAVE_UNSIGNED 0x0002 /* unsigned wave */
#define IWFFFF_WAVE_INVERT 0x0002 /* same as unsigned wave */
#define IWFFFF_WAVE_BACKWARD 0x0004 /* backward mode (maybe used for reverb or ping-ping loop) */
#define IWFFFF_WAVE_LOOP 0x0008 /* loop mode */
#define IWFFFF_WAVE_BIDIR 0x0010 /* bidirectional mode */
#define IWFFFF_WAVE_ULAW 0x0020 /* uLaw compressed wave */
#define IWFFFF_WAVE_RAM 0x0040 /* wave is _preloaded_ in RAM (it is used for ROM simulation) */
#define IWFFFF_WAVE_ROM 0x0080 /* wave is in ROM */
#define IWFFFF_WAVE_STEREO 0x0100 /* wave is stereo */
/*
* Wavetable definitions
*/
struct iwffff_wave {
unsigned int share_id[4]; /* share id - zero = no sharing */
unsigned int format; /* wave format */
struct {
unsigned int number; /* some other ID for this wave */
unsigned int memory; /* begin of waveform in onboard memory */
unsigned char *ptr; /* pointer to waveform in system memory */
} address;
unsigned int size; /* size of waveform in samples */
unsigned int start; /* start offset in samples * 16 (lowest 4 bits - fraction) */
unsigned int loop_start; /* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */
unsigned int loop_end; /* loop start offset in samples * 16 (lowest 4 bits - fraction) */
unsigned short loop_repeat; /* loop repeat - 0 = forever */
unsigned int sample_ratio; /* sample ratio (44100 * 1024 / rate) */
unsigned char attenuation; /* 0 - 127 (no corresponding midi controller) */
unsigned char low_note; /* lower frequency range for this waveform */
unsigned char high_note; /* higher frequency range for this waveform */
unsigned char pad;
struct iwffff_wave *next;
};
/*
* Layer
*/
#define IWFFFF_LFO_SHAPE_TRIANGLE 0
#define IWFFFF_LFO_SHAPE_POSTRIANGLE 1
struct iwffff_lfo {
unsigned short freq; /* (0-2047) 0.01Hz - 21.5Hz */
signed short depth; /* volume +- (0-255) 0.48675dB/step */
signed short sweep; /* 0 - 950 deciseconds */
unsigned char shape; /* see to IWFFFF_LFO_SHAPE_XXXX */
unsigned char delay; /* 0 - 255 deciseconds */
};
#define IWFFFF_ENV_FLAG_RETRIGGER 0x0001 /* flag - retrigger */
#define IWFFFF_ENV_MODE_ONE_SHOT 0x0001 /* mode - one shot */
#define IWFFFF_ENV_MODE_SUSTAIN 0x0002 /* mode - sustain */
#define IWFFFF_ENV_MODE_NO_SUSTAIN 0x0003 /* mode - no sustain */
#define IWFFFF_ENV_INDEX_VELOCITY 0x0001 /* index - velocity */
#define IWFFFF_ENV_INDEX_FREQUENCY 0x0002 /* index - frequency */
struct iwffff_env_point {
unsigned short offset;
unsigned short rate;
};
struct iwffff_env_record {
unsigned short nattack;
unsigned short nrelease;
unsigned short sustain_offset;
unsigned short sustain_rate;
unsigned short release_rate;
unsigned char hirange;
unsigned char pad;
struct iwffff_env_record *next;
/* points are stored here */
/* count of points = nattack + nrelease */
};
struct iwffff_env {
unsigned char flags;
unsigned char mode;
unsigned char index;
unsigned char pad;
struct iwffff_env_record *record;
};
#define IWFFFF_LAYER_FLAG_RETRIGGER 0x0001 /* retrigger */
#define IWFFFF_LAYER_VELOCITY_TIME 0x0000 /* velocity mode = time */
#define IWFFFF_LAYER_VELOCITY_RATE 0x0001 /* velocity mode = rate */
#define IWFFFF_LAYER_EVENT_KUP 0x0000 /* layer event - key up */
#define IWFFFF_LAYER_EVENT_KDOWN 0x0001 /* layer event - key down */
#define IWFFFF_LAYER_EVENT_RETRIG 0x0002 /* layer event - retrigger */
#define IWFFFF_LAYER_EVENT_LEGATO 0x0003 /* layer event - legato */
struct iwffff_layer {
unsigned char flags;
unsigned char velocity_mode;
unsigned char layer_event;
unsigned char low_range; /* range for layer based */
unsigned char high_range; /* on either velocity or frequency */
unsigned char pan; /* pan offset from CC1 (0 left - 127 right) */
unsigned char pan_freq_scale; /* position based on frequency (0-127) */
unsigned char attenuation; /* 0-127 (no corresponding midi controller) */
struct iwffff_lfo tremolo; /* tremolo effect */
struct iwffff_lfo vibrato; /* vibrato effect */
unsigned short freq_scale; /* 0-2048, 1024 is equal to semitone scaling */
unsigned char freq_center; /* center for keyboard frequency scaling */
unsigned char pad;
struct iwffff_env penv; /* pitch envelope */
struct iwffff_env venv; /* volume envelope */
struct iwffff_wave *wave;
struct iwffff_layer *next;
};
/*
* Instrument
*/
#define IWFFFF_EXCLUDE_NONE 0x0000 /* exclusion mode - none */
#define IWFFFF_EXCLUDE_SINGLE 0x0001 /* exclude single - single note from the instrument group */
#define IWFFFF_EXCLUDE_MULTIPLE 0x0002 /* exclude multiple - stop only same note from this instrument */
#define IWFFFF_LAYER_NONE 0x0000 /* not layered */
#define IWFFFF_LAYER_ON 0x0001 /* layered */
#define IWFFFF_LAYER_VELOCITY 0x0002 /* layered by velocity */
#define IWFFFF_LAYER_FREQUENCY 0x0003 /* layered by frequency */
#define IWFFFF_EFFECT_NONE 0
#define IWFFFF_EFFECT_REVERB 1
#define IWFFFF_EFFECT_CHORUS 2
#define IWFFFF_EFFECT_ECHO 3
struct iwffff_instrument {
unsigned short exclusion;
unsigned short layer_type;
unsigned short exclusion_group; /* 0 - none, 1-65535 */
unsigned char effect1; /* effect 1 */
unsigned char effect1_depth; /* 0-127 */
unsigned char effect2; /* effect 2 */
unsigned char effect2_depth; /* 0-127 */
struct iwffff_layer *layer; /* first layer */
};
/*
*
* Kernel <-> user space
* Hardware (CPU) independent section
*
* * = zero or more
* + = one or more
*
* iwffff_xinstrument IWFFFF_STRU_INSTR
* +iwffff_xlayer IWFFFF_STRU_LAYER
* *iwffff_xenv_record IWFFFF_STRU_ENV_RECT (tremolo)
* *iwffff_xenv_record IWFFFF_STRU_EVN_RECT (vibrato)
* +iwffff_xwave IWFFFF_STRU_WAVE
*
*/
#define IWFFFF_STRU_WAVE __cpu_to_be32(('W'<<24)|('A'<<16)|('V'<<8)|'E')
#define IWFFFF_STRU_ENV_RECP __cpu_to_be32(('E'<<24)|('N'<<16)|('R'<<8)|'P')
#define IWFFFF_STRU_ENV_RECV __cpu_to_be32(('E'<<24)|('N'<<16)|('R'<<8)|'V')
#define IWFFFF_STRU_LAYER __cpu_to_be32(('L'<<24)|('A'<<16)|('Y'<<8)|'R')
#define IWFFFF_STRU_INSTR __cpu_to_be32(('I'<<24)|('N'<<16)|('S'<<8)|'T')
/*
* Wavetable definitions
*/
struct iwffff_xwave {
__u32 stype; /* structure type */
__u32 share_id[4]; /* share id - zero = no sharing */
__u32 format; /* wave format */
__u32 offset; /* offset to ROM (address) */
__u32 size; /* size of waveform in samples */
__u32 start; /* start offset in samples * 16 (lowest 4 bits - fraction) */
__u32 loop_start; /* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */
__u32 loop_end; /* loop start offset in samples * 16 (lowest 4 bits - fraction) */
__u16 loop_repeat; /* loop repeat - 0 = forever */
__u32 sample_ratio; /* sample ratio (44100 * 1024 / rate) */
__u8 attenuation; /* 0 - 127 (no corresponding midi controller) */
__u8 low_note; /* lower frequency range for this waveform */
__u8 high_note; /* higher frequency range for this waveform */
__u8 pad;
};
/*
* Layer
*/
struct iwffff_xlfo {
__u16 freq; /* (0-2047) 0.01Hz - 21.5Hz */
__s16 depth; /* volume +- (0-255) 0.48675dB/step */
__s16 sweep; /* 0 - 950 deciseconds */
__u8 shape; /* see to ULTRA_IW_LFO_SHAPE_XXXX */
__u8 delay; /* 0 - 255 deciseconds */
};
struct iwffff_xenv_point {
__u16 offset;
__u16 rate;
};
struct iwffff_xenv_record {
__u32 stype;
__u16 nattack;
__u16 nrelease;
__u16 sustain_offset;
__u16 sustain_rate;
__u16 release_rate;
__u8 hirange;
__u8 pad;
/* points are stored here.. */
/* count of points = nattack + nrelease */
};
struct iwffff_xenv {
__u8 flags;
__u8 mode;
__u8 index;
__u8 pad;
};
struct iwffff_xlayer {
__u32 stype;
__u8 flags;
__u8 velocity_mode;
__u8 layer_event;
__u8 low_range; /* range for layer based */
__u8 high_range; /* on either velocity or frequency */
__u8 pan; /* pan offset from CC1 (0 left - 127 right) */
__u8 pan_freq_scale; /* position based on frequency (0-127) */
__u8 attenuation; /* 0-127 (no corresponding midi controller) */
struct iwffff_xlfo tremolo; /* tremolo effect */
struct iwffff_xlfo vibrato; /* vibrato effect */
__u16 freq_scale; /* 0-2048, 1024 is equal to semitone scaling */
__u8 freq_center; /* center for keyboard frequency scaling */
__u8 pad;
struct iwffff_xenv penv; /* pitch envelope */
struct iwffff_xenv venv; /* volume envelope */
};
/*
* Instrument
*/
struct iwffff_xinstrument {
__u32 stype;
__u16 exclusion;
__u16 layer_type;
__u16 exclusion_group; /* 0 - none, 1-65535 */
__u8 effect1; /* effect 1 */
__u8 effect1_depth; /* 0-127 */
__u8 effect2; /* effect 2 */
__u8 effect2_depth; /* 0-127 */
};
/*
* ROM support
* InterWave ROMs are Little-Endian (x86)
*/
#define IWFFFF_ROM_HDR_SIZE 512
struct iwffff_rom_header {
__u8 iwave[8];
__u8 revision;
__u8 series_number;
__u8 series_name[16];
__u8 date[10];
__u16 vendor_revision_major;
__u16 vendor_revision_minor;
__u32 rom_size;
__u8 copyright[128];
__u8 vendor_name[64];
__u8 description[128];
};
/*
* Instrument info
*/
#define IWFFFF_INFO_LFO_VIBRATO (1<<0)
#define IWFFFF_INFO_LFO_VIBRATO_SHAPE (1<<1)
#define IWFFFF_INFO_LFO_TREMOLO (1<<2)
#define IWFFFF_INFO_LFO_TREMOLO_SHAPE (1<<3)
struct iwffff_info {
unsigned int format; /* supported format bits */
unsigned int effects; /* supported effects (1 << IWFFFF_EFFECT*) */
unsigned int lfos; /* LFO effects */
unsigned int max8_len; /* maximum 8-bit wave length */
unsigned int max16_len; /* maximum 16-bit wave length */
};
#ifdef __KERNEL__
#include "seq_instr.h"
struct snd_iwffff_ops {
void *private_data;
int (*info)(void *private_data, struct iwffff_info *info);
int (*put_sample)(void *private_data, struct iwffff_wave *wave,
char __user *data, long len, int atomic);
int (*get_sample)(void *private_data, struct iwffff_wave *wave,
char __user *data, long len, int atomic);
int (*remove_sample)(void *private_data, struct iwffff_wave *wave,
int atomic);
void (*notify)(void *private_data, struct snd_seq_kinstr *instr, int what);
struct snd_seq_kinstr_ops kops;
};
int snd_seq_iwffff_init(struct snd_iwffff_ops *ops,
void *private_data,
struct snd_seq_kinstr_ops *next);
#endif
/* typedefs for compatibility to user-space */
typedef struct iwffff_xwave iwffff_xwave_t;
typedef struct iwffff_xlfo iwffff_xlfo_t;
typedef struct iwffff_xenv_point iwffff_xenv_point_t;
typedef struct iwffff_xenv_record iwffff_xenv_record_t;
typedef struct iwffff_xenv iwffff_xenv_t;
typedef struct iwffff_xlayer iwffff_xlayer_t;
typedef struct iwffff_xinstrument iwffff_xinstrument_t;
typedef struct iwffff_rom_header iwffff_rom_header_t;
typedef struct iwffff_info iwffff_info_t;
#endif /* __SOUND_AINSTR_IW_H */
/*
* Advanced Linux Sound Architecture
*
* Simple (MOD player) Instrument Format
* Copyright (c) 1994-99 by Jaroslav Kysela <perex@perex.cz>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef __SOUND_AINSTR_SIMPLE_H
#define __SOUND_AINSTR_SIMPLE_H
#ifndef __KERNEL__
#include <asm/types.h>
#include <asm/byteorder.h>
#endif
/*
* share types (share ID 1)
*/
#define SIMPLE_SHARE_FILE 0
/*
* wave formats
*/
#define SIMPLE_WAVE_16BIT 0x0001 /* 16-bit wave */
#define SIMPLE_WAVE_UNSIGNED 0x0002 /* unsigned wave */
#define SIMPLE_WAVE_INVERT 0x0002 /* same as unsigned wave */
#define SIMPLE_WAVE_BACKWARD 0x0004 /* backward mode (maybe used for reverb or ping-ping loop) */
#define SIMPLE_WAVE_LOOP 0x0008 /* loop mode */
#define SIMPLE_WAVE_BIDIR 0x0010 /* bidirectional mode */
#define SIMPLE_WAVE_STEREO 0x0100 /* stereo wave */
#define SIMPLE_WAVE_ULAW 0x0200 /* uLaw compression mode */
/*
* instrument effects
*/
#define SIMPLE_EFFECT_NONE 0
#define SIMPLE_EFFECT_REVERB 1
#define SIMPLE_EFFECT_CHORUS 2
#define SIMPLE_EFFECT_ECHO 3
/*
* instrument info
*/
struct simple_instrument_info {
unsigned int format; /* supported format bits */
unsigned int effects; /* supported effects (1 << SIMPLE_EFFECT_*) */
unsigned int max8_len; /* maximum 8-bit wave length */
unsigned int max16_len; /* maximum 16-bit wave length */
};
/*
* Instrument
*/
struct simple_instrument {
unsigned int share_id[4]; /* share id - zero = no sharing */
unsigned int format; /* wave format */
struct {
unsigned int number; /* some other ID for this instrument */
unsigned int memory; /* begin of waveform in onboard memory */
unsigned char *ptr; /* pointer to waveform in system memory */
} address;
unsigned int size; /* size of waveform in samples */
unsigned int start; /* start offset in samples * 16 (lowest 4 bits - fraction) */
unsigned int loop_start; /* loop start offset in samples * 16 (lowest 4 bits - fraction) */
unsigned int loop_end; /* loop end offset in samples * 16 (lowest 4 bits - fraction) */
unsigned short loop_repeat; /* loop repeat - 0 = forever */
unsigned char effect1; /* effect 1 */
unsigned char effect1_depth; /* 0-127 */
unsigned char effect2; /* effect 2 */
unsigned char effect2_depth; /* 0-127 */
};
/*
*
* Kernel <-> user space
* Hardware (CPU) independent section
*
* * = zero or more
* + = one or more
*
* simple_xinstrument SIMPLE_STRU_INSTR
*
*/
#define SIMPLE_STRU_INSTR __cpu_to_be32(('I'<<24)|('N'<<16)|('S'<<8)|'T')
/*
* Instrument
*/
struct simple_xinstrument {
__u32 stype;
__u32 share_id[4]; /* share id - zero = no sharing */
__u32 format; /* wave format */
__u32 size; /* size of waveform in samples */
__u32 start; /* start offset in samples * 16 (lowest 4 bits - fraction) */
__u32 loop_start; /* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */
__u32 loop_end; /* loop start offset in samples * 16 (lowest 4 bits - fraction) */
__u16 loop_repeat; /* loop repeat - 0 = forever */
__u8 effect1; /* effect 1 */
__u8 effect1_depth; /* 0-127 */
__u8 effect2; /* effect 2 */
__u8 effect2_depth; /* 0-127 */
};
#ifdef __KERNEL__
#include "seq_instr.h"
struct snd_simple_ops {
void *private_data;
int (*info)(void *private_data, struct simple_instrument_info *info);
int (*put_sample)(void *private_data, struct simple_instrument *instr,
char __user *data, long len, int atomic);
int (*get_sample)(void *private_data, struct simple_instrument *instr,
char __user *data, long len, int atomic);
int (*remove_sample)(void *private_data, struct simple_instrument *instr,
int atomic);
void (*notify)(void *private_data, struct snd_seq_kinstr *instr, int what);
struct snd_seq_kinstr_ops kops;
};
int snd_seq_simple_init(struct snd_simple_ops *ops,
void *private_data,
struct snd_seq_kinstr_ops *next);
#endif
/* typedefs for compatibility to user-space */
typedef struct simple_xinstrument simple_xinstrument_t;
#endif /* __SOUND_AINSTR_SIMPLE_H */
...@@ -110,18 +110,7 @@ ...@@ -110,18 +110,7 @@
#define SNDRV_SEQ_EVENT_PORT_SUBSCRIBED 66 /* ports connected */ #define SNDRV_SEQ_EVENT_PORT_SUBSCRIBED 66 /* ports connected */
#define SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED 67 /* ports disconnected */ #define SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED 67 /* ports disconnected */
/** synthesizer events /* 70-89: synthesizer events - obsoleted */
* event data type = snd_seq_eve_sample_control
*/
#define SNDRV_SEQ_EVENT_SAMPLE 70 /* sample select */
#define SNDRV_SEQ_EVENT_SAMPLE_CLUSTER 71 /* sample cluster select */
#define SNDRV_SEQ_EVENT_SAMPLE_START 72 /* voice start */
#define SNDRV_SEQ_EVENT_SAMPLE_STOP 73 /* voice stop */
#define SNDRV_SEQ_EVENT_SAMPLE_FREQ 74 /* playback frequency */
#define SNDRV_SEQ_EVENT_SAMPLE_VOLUME 75 /* volume and balance */
#define SNDRV_SEQ_EVENT_SAMPLE_LOOP 76 /* sample loop */
#define SNDRV_SEQ_EVENT_SAMPLE_POSITION 77 /* sample position */
#define SNDRV_SEQ_EVENT_SAMPLE_PRIVATE1 78 /* private (hardware dependent) event */
/** user-defined events with fixed length /** user-defined events with fixed length
* event data type = any * event data type = any
...@@ -137,28 +126,7 @@ ...@@ -137,28 +126,7 @@
#define SNDRV_SEQ_EVENT_USR8 98 #define SNDRV_SEQ_EVENT_USR8 98
#define SNDRV_SEQ_EVENT_USR9 99 #define SNDRV_SEQ_EVENT_USR9 99
/** instrument layer /* 100-118: instrument layer - obsoleted */
* variable length data can be passed directly to the driver
*/
#define SNDRV_SEQ_EVENT_INSTR_BEGIN 100 /* begin of instrument management */
#define SNDRV_SEQ_EVENT_INSTR_END 101 /* end of instrument management */
#define SNDRV_SEQ_EVENT_INSTR_INFO 102 /* instrument interface info */
#define SNDRV_SEQ_EVENT_INSTR_INFO_RESULT 103 /* result */
#define SNDRV_SEQ_EVENT_INSTR_FINFO 104 /* get format info */
#define SNDRV_SEQ_EVENT_INSTR_FINFO_RESULT 105 /* get format info */
#define SNDRV_SEQ_EVENT_INSTR_RESET 106 /* reset instrument memory */
#define SNDRV_SEQ_EVENT_INSTR_STATUS 107 /* instrument interface status */
#define SNDRV_SEQ_EVENT_INSTR_STATUS_RESULT 108 /* result */
#define SNDRV_SEQ_EVENT_INSTR_PUT 109 /* put instrument to port */
#define SNDRV_SEQ_EVENT_INSTR_GET 110 /* get instrument from port */
#define SNDRV_SEQ_EVENT_INSTR_GET_RESULT 111 /* result */
#define SNDRV_SEQ_EVENT_INSTR_FREE 112 /* free instrument(s) */
#define SNDRV_SEQ_EVENT_INSTR_LIST 113 /* instrument list */
#define SNDRV_SEQ_EVENT_INSTR_LIST_RESULT 114 /* result */
#define SNDRV_SEQ_EVENT_INSTR_CLUSTER 115 /* cluster parameters */
#define SNDRV_SEQ_EVENT_INSTR_CLUSTER_GET 116 /* get cluster parameters */
#define SNDRV_SEQ_EVENT_INSTR_CLUSTER_RESULT 117 /* result */
#define SNDRV_SEQ_EVENT_INSTR_CHANGE 118 /* instrument change */
/* 119-129: reserved */ /* 119-129: reserved */
/* 130-139: variable length events /* 130-139: variable length events
...@@ -258,78 +226,6 @@ struct snd_seq_ev_ext { ...@@ -258,78 +226,6 @@ struct snd_seq_ev_ext {
void *ptr; /* pointer to data (note: maybe 64-bit) */ void *ptr; /* pointer to data (note: maybe 64-bit) */
} __attribute__((packed)); } __attribute__((packed));
/* Instrument cluster type */
typedef unsigned int snd_seq_instr_cluster_t;
/* Instrument type */
struct snd_seq_instr {
snd_seq_instr_cluster_t cluster;
unsigned int std; /* the upper byte means a private instrument (owner - client #) */
unsigned short bank;
unsigned short prg;
};
/* sample number */
struct snd_seq_ev_sample {
unsigned int std;
unsigned short bank;
unsigned short prg;
};
/* sample cluster */
struct snd_seq_ev_cluster {
snd_seq_instr_cluster_t cluster;
};
/* sample position */
typedef unsigned int snd_seq_position_t; /* playback position (in samples) * 16 */
/* sample stop mode */
enum {
SAMPLE_STOP_IMMEDIATELY = 0, /* terminate playing immediately */
SAMPLE_STOP_VENVELOPE = 1, /* finish volume envelope */
SAMPLE_STOP_LOOP = 2 /* terminate loop and finish wave */
};
/* sample frequency */
typedef int snd_seq_frequency_t; /* playback frequency in HZ * 16 */
/* sample volume control; if any value is set to -1 == do not change */
struct snd_seq_ev_volume {
signed short volume; /* range: 0-16383 */
signed short lr; /* left-right balance; range: 0-16383 */
signed short fr; /* front-rear balance; range: 0-16383 */
signed short du; /* down-up balance; range: 0-16383 */
};
/* simple loop redefinition */
struct snd_seq_ev_loop {
unsigned int start; /* loop start (in samples) * 16 */
unsigned int end; /* loop end (in samples) * 16 */
};
struct snd_seq_ev_sample_control {
unsigned char channel;
unsigned char unused1, unused2, unused3; /* pad */
union {
struct snd_seq_ev_sample sample;
struct snd_seq_ev_cluster cluster;
snd_seq_position_t position;
int stop_mode;
snd_seq_frequency_t frequency;
struct snd_seq_ev_volume volume;
struct snd_seq_ev_loop loop;
unsigned char raw8[8];
} param;
};
/* INSTR_BEGIN event */
struct snd_seq_ev_instr_begin {
int timeout; /* zero = forever, otherwise timeout in ms */
};
struct snd_seq_result { struct snd_seq_result {
int event; /* processed event type */ int event; /* processed event type */
int result; int result;
...@@ -399,8 +295,6 @@ struct snd_seq_event { ...@@ -399,8 +295,6 @@ struct snd_seq_event {
struct snd_seq_addr addr; struct snd_seq_addr addr;
struct snd_seq_connect connect; struct snd_seq_connect connect;
struct snd_seq_result result; struct snd_seq_result result;
struct snd_seq_ev_instr_begin instr_begin;
struct snd_seq_ev_sample_control sample;
struct snd_seq_ev_quote quote; struct snd_seq_ev_quote quote;
} data; } data;
}; };
...@@ -441,8 +335,6 @@ struct snd_seq_event_bounce { ...@@ -441,8 +335,6 @@ struct snd_seq_event_bounce {
#define snd_seq_ev_is_user_type(ev) ((ev)->type >= 90 && (ev)->type < 99) #define snd_seq_ev_is_user_type(ev) ((ev)->type >= 90 && (ev)->type < 99)
/* fixed length events: 0-99 */ /* fixed length events: 0-99 */
#define snd_seq_ev_is_fixed_type(ev) ((ev)->type < 100) #define snd_seq_ev_is_fixed_type(ev) ((ev)->type < 100)
/* instrument layer events: 100-129 */
#define snd_seq_ev_is_instr_type(ev) ((ev)->type >= 100 && (ev)->type < 130)
/* variable length events: 130-139 */ /* variable length events: 130-139 */
#define snd_seq_ev_is_variable_type(ev) ((ev)->type >= 130 && (ev)->type < 140) #define snd_seq_ev_is_variable_type(ev) ((ev)->type >= 130 && (ev)->type < 140)
/* reserved for kernel */ /* reserved for kernel */
...@@ -737,136 +629,6 @@ struct snd_seq_query_subs { ...@@ -737,136 +629,6 @@ struct snd_seq_query_subs {
}; };
/*
* Instrument abstraction layer
* - based on events
*/
/* instrument types */
#define SNDRV_SEQ_INSTR_ATYPE_DATA 0 /* instrument data */
#define SNDRV_SEQ_INSTR_ATYPE_ALIAS 1 /* instrument alias */
/* instrument ASCII identifiers */
#define SNDRV_SEQ_INSTR_ID_DLS1 "DLS1"
#define SNDRV_SEQ_INSTR_ID_DLS2 "DLS2"
#define SNDRV_SEQ_INSTR_ID_SIMPLE "Simple Wave"
#define SNDRV_SEQ_INSTR_ID_SOUNDFONT "SoundFont"
#define SNDRV_SEQ_INSTR_ID_GUS_PATCH "GUS Patch"
#define SNDRV_SEQ_INSTR_ID_INTERWAVE "InterWave FFFF"
#define SNDRV_SEQ_INSTR_ID_OPL2_3 "OPL2/3 FM"
#define SNDRV_SEQ_INSTR_ID_OPL4 "OPL4"
/* instrument types */
#define SNDRV_SEQ_INSTR_TYPE0_DLS1 (1<<0) /* MIDI DLS v1 */
#define SNDRV_SEQ_INSTR_TYPE0_DLS2 (1<<1) /* MIDI DLS v2 */
#define SNDRV_SEQ_INSTR_TYPE1_SIMPLE (1<<0) /* Simple Wave */
#define SNDRV_SEQ_INSTR_TYPE1_SOUNDFONT (1<<1) /* EMU SoundFont */
#define SNDRV_SEQ_INSTR_TYPE1_GUS_PATCH (1<<2) /* Gravis UltraSound Patch */
#define SNDRV_SEQ_INSTR_TYPE1_INTERWAVE (1<<3) /* InterWave FFFF */
#define SNDRV_SEQ_INSTR_TYPE2_OPL2_3 (1<<0) /* Yamaha OPL2/3 FM */
#define SNDRV_SEQ_INSTR_TYPE2_OPL4 (1<<1) /* Yamaha OPL4 */
/* put commands */
#define SNDRV_SEQ_INSTR_PUT_CMD_CREATE 0
#define SNDRV_SEQ_INSTR_PUT_CMD_REPLACE 1
#define SNDRV_SEQ_INSTR_PUT_CMD_MODIFY 2
#define SNDRV_SEQ_INSTR_PUT_CMD_ADD 3
#define SNDRV_SEQ_INSTR_PUT_CMD_REMOVE 4
/* get commands */
#define SNDRV_SEQ_INSTR_GET_CMD_FULL 0
#define SNDRV_SEQ_INSTR_GET_CMD_PARTIAL 1
/* query flags */
#define SNDRV_SEQ_INSTR_QUERY_FOLLOW_ALIAS (1<<0)
/* free commands */
#define SNDRV_SEQ_INSTR_FREE_CMD_ALL 0
#define SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE 1
#define SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER 2
#define SNDRV_SEQ_INSTR_FREE_CMD_SINGLE 3
/* size of ROM/RAM */
typedef unsigned int snd_seq_instr_size_t;
/* INSTR_INFO */
struct snd_seq_instr_info {
int result; /* operation result */
unsigned int formats[8]; /* bitmap of supported formats */
int ram_count; /* count of RAM banks */
snd_seq_instr_size_t ram_sizes[16]; /* size of RAM banks */
int rom_count; /* count of ROM banks */
snd_seq_instr_size_t rom_sizes[8]; /* size of ROM banks */
char reserved[128];
};
/* INSTR_STATUS */
struct snd_seq_instr_status {
int result; /* operation result */
snd_seq_instr_size_t free_ram[16]; /* free RAM in banks */
int instrument_count; /* count of downloaded instruments */
char reserved[128];
};
/* INSTR_FORMAT_INFO */
struct snd_seq_instr_format_info {
char format[16]; /* format identifier - SNDRV_SEQ_INSTR_ID_* */
unsigned int len; /* max data length (without this structure) */
};
struct snd_seq_instr_format_info_result {
int result; /* operation result */
char format[16]; /* format identifier */
unsigned int len; /* filled data length (without this structure) */
};
/* instrument data */
struct snd_seq_instr_data {
char name[32]; /* instrument name */
char reserved[16]; /* for the future use */
int type; /* instrument type */
union {
char format[16]; /* format identifier */
struct snd_seq_instr alias;
} data;
};
/* INSTR_PUT/GET, data are stored in one block (extended), header + data */
struct snd_seq_instr_header {
union {
struct snd_seq_instr instr;
snd_seq_instr_cluster_t cluster;
} id; /* instrument identifier */
unsigned int cmd; /* get/put/free command */
unsigned int flags; /* query flags (only for get) */
unsigned int len; /* real instrument data length (without header) */
int result; /* operation result */
char reserved[16]; /* for the future */
struct snd_seq_instr_data data; /* instrument data (for put/get result) */
};
/* INSTR_CLUSTER_SET */
struct snd_seq_instr_cluster_set {
snd_seq_instr_cluster_t cluster; /* cluster identifier */
char name[32]; /* cluster name */
int priority; /* cluster priority */
char reserved[64]; /* for the future use */
};
/* INSTR_CLUSTER_GET */
struct snd_seq_instr_cluster_get {
snd_seq_instr_cluster_t cluster; /* cluster identifier */
char name[32]; /* cluster name */
int priority; /* cluster priority */
char reserved[64]; /* for the future use */
};
/* /*
* IOCTL commands * IOCTL commands
*/ */
......
...@@ -27,13 +27,8 @@ ...@@ -27,13 +27,8 @@
#include "timer.h" #include "timer.h"
#include "seq_midi_emul.h" #include "seq_midi_emul.h"
#include "seq_device.h" #include "seq_device.h"
#include "ainstr_iw.h"
#include "ainstr_gf1.h"
#include "ainstr_simple.h"
#include <asm/io.h> #include <asm/io.h>
#define SNDRV_SEQ_DEV_ID_GUS "gus-synth"
/* IO ports */ /* IO ports */
#define GUSP(gus, x) ((gus)->gf1.port + SNDRV_g_u_s_##x) #define GUSP(gus, x) ((gus)->gf1.port + SNDRV_g_u_s_##x)
...@@ -234,16 +229,6 @@ struct snd_gus_port { ...@@ -234,16 +229,6 @@ struct snd_gus_port {
struct snd_gus_voice; struct snd_gus_voice;
struct snd_gus_sample_ops {
void (*sample_start)(struct snd_gus_card *gus, struct snd_gus_voice *voice, snd_seq_position_t position);
void (*sample_stop)(struct snd_gus_card *gus, struct snd_gus_voice *voice, int mode);
void (*sample_freq)(struct snd_gus_card *gus, struct snd_gus_voice *voice, snd_seq_frequency_t freq);
void (*sample_volume)(struct snd_gus_card *gus, struct snd_gus_voice *voice, struct snd_seq_ev_volume *volume);
void (*sample_loop)(struct snd_gus_card *card, struct snd_gus_voice *voice, struct snd_seq_ev_loop *loop);
void (*sample_pos)(struct snd_gus_card *card, struct snd_gus_voice *voice, snd_seq_position_t position);
void (*sample_private1)(struct snd_gus_card *card, struct snd_gus_voice *voice, unsigned char *data);
};
#define SNDRV_GF1_VOICE_TYPE_PCM 0 #define SNDRV_GF1_VOICE_TYPE_PCM 0
#define SNDRV_GF1_VOICE_TYPE_SYNTH 1 #define SNDRV_GF1_VOICE_TYPE_SYNTH 1
#define SNDRV_GF1_VOICE_TYPE_MIDI 2 #define SNDRV_GF1_VOICE_TYPE_MIDI 2
...@@ -284,12 +269,8 @@ struct snd_gus_voice { ...@@ -284,12 +269,8 @@ struct snd_gus_voice {
struct snd_gus_sample_ops *sample_ops; struct snd_gus_sample_ops *sample_ops;
struct snd_seq_instr instr;
/* running status / registers */ /* running status / registers */
struct snd_seq_ev_volume sample_volume;
unsigned short fc_register; unsigned short fc_register;
unsigned short fc_lfo; unsigned short fc_lfo;
unsigned short gf1_volume; unsigned short gf1_volume;
...@@ -382,10 +363,6 @@ struct snd_gf1 { ...@@ -382,10 +363,6 @@ struct snd_gf1 {
int seq_client; int seq_client;
struct snd_gus_port seq_ports[4]; struct snd_gus_port seq_ports[4];
struct snd_seq_kinstr_list *ilist;
struct snd_iwffff_ops iwffff_ops;
struct snd_gf1_ops gf1_ops;
struct snd_simple_ops simple_ops;
/* timer */ /* timer */
...@@ -458,8 +435,6 @@ struct snd_gus_card { ...@@ -458,8 +435,6 @@ struct snd_gus_card {
struct snd_rawmidi_substream *midi_substream_output; struct snd_rawmidi_substream *midi_substream_output;
struct snd_rawmidi_substream *midi_substream_input; struct snd_rawmidi_substream *midi_substream_input;
struct snd_seq_device *seq_dev;
spinlock_t reg_lock; spinlock_t reg_lock;
spinlock_t voice_alloc; spinlock_t voice_alloc;
spinlock_t active_voice_lock; spinlock_t active_voice_lock;
...@@ -647,48 +622,10 @@ void snd_gus_irq_profile_init(struct snd_gus_card *gus); ...@@ -647,48 +622,10 @@ void snd_gus_irq_profile_init(struct snd_gus_card *gus);
int snd_gf1_rawmidi_new(struct snd_gus_card * gus, int device, struct snd_rawmidi **rrawmidi); int snd_gf1_rawmidi_new(struct snd_gus_card * gus, int device, struct snd_rawmidi **rrawmidi);
#if 0
extern void snd_engine_instrument_register(unsigned short mode,
struct _SND_INSTRUMENT_VOICE_COMMANDS *voice_cmds,
struct _SND_INSTRUMENT_NOTE_COMMANDS *note_cmds,
struct _SND_INSTRUMENT_CHANNEL_COMMANDS *channel_cmds);
extern int snd_engine_instrument_register_ask(unsigned short mode);
#endif
/* gus_dram.c */ /* gus_dram.c */
int snd_gus_dram_write(struct snd_gus_card *gus, char __user *ptr, int snd_gus_dram_write(struct snd_gus_card *gus, char __user *ptr,
unsigned int addr, unsigned int size); unsigned int addr, unsigned int size);
int snd_gus_dram_read(struct snd_gus_card *gus, char __user *ptr, int snd_gus_dram_read(struct snd_gus_card *gus, char __user *ptr,
unsigned int addr, unsigned int size, int rom); unsigned int addr, unsigned int size, int rom);
#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
/* gus_sample.c */
void snd_gus_sample_event(struct snd_seq_event *ev, struct snd_gus_port *p);
/* gus_simple.c */
void snd_gf1_simple_init(struct snd_gus_voice *voice);
/* gus_instr.c */
int snd_gus_iwffff_put_sample(void *private_data, struct iwffff_wave *wave,
char __user *data, long len, int atomic);
int snd_gus_iwffff_get_sample(void *private_data, struct iwffff_wave *wave,
char __user *data, long len, int atomic);
int snd_gus_iwffff_remove_sample(void *private_data, struct iwffff_wave *wave,
int atomic);
int snd_gus_gf1_put_sample(void *private_data, struct gf1_wave *wave,
char __user *data, long len, int atomic);
int snd_gus_gf1_get_sample(void *private_data, struct gf1_wave *wave,
char __user *data, long len, int atomic);
int snd_gus_gf1_remove_sample(void *private_data, struct gf1_wave *wave,
int atomic);
int snd_gus_simple_put_sample(void *private_data, struct simple_instrument *instr,
char __user *data, long len, int atomic);
int snd_gus_simple_get_sample(void *private_data, struct simple_instrument *instr,
char __user *data, long len, int atomic);
int snd_gus_simple_remove_sample(void *private_data, struct simple_instrument *instr,
int atomic);
#endif /* CONFIG_SND_SEQUENCER */
#endif /* __SOUND_GUS_H */ #endif /* __SOUND_GUS_H */
#ifndef __SOUND_SEQ_INSTR_H
#define __SOUND_SEQ_INSTR_H
/*
* Main kernel header file for the ALSA sequencer
* Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "seq_kernel.h"
/* Instrument cluster */
struct snd_seq_kcluster {
snd_seq_instr_cluster_t cluster;
char name[32];
int priority;
struct snd_seq_kcluster *next;
};
/* return pointer to private data */
#define KINSTR_DATA(kinstr) (void *)(((char *)kinstr) + sizeof(struct snd_seq_kinstr))
/* Instrument structure */
struct snd_seq_kinstr {
struct snd_seq_instr instr;
char name[32];
int type; /* instrument type */
int use; /* use count */
int busy; /* not useable */
int add_len; /* additional length */
struct snd_seq_kinstr_ops *ops; /* operations */
struct snd_seq_kinstr *next;
};
#define SNDRV_SEQ_INSTR_HASH_SIZE 32
/* Instrument flags */
#define SNDRV_SEQ_INSTR_FLG_DIRECT (1<<0) /* accept only direct events */
/* List of all instruments */
struct snd_seq_kinstr_list {
struct snd_seq_kinstr *hash[SNDRV_SEQ_INSTR_HASH_SIZE];
int count; /* count of all instruments */
struct snd_seq_kcluster *chash[SNDRV_SEQ_INSTR_HASH_SIZE];
int ccount; /* count of all clusters */
int owner; /* current owner of the instrument list */
unsigned int flags;
spinlock_t lock;
spinlock_t ops_lock;
struct mutex ops_mutex;
unsigned long ops_flags;
};
#define SNDRV_SEQ_INSTR_NOTIFY_REMOVE 0
#define SNDRV_SEQ_INSTR_NOTIFY_CHANGE 1
struct snd_seq_kinstr_ops {
void *private_data;
long add_len; /* additional length */
char *instr_type;
int (*info)(void *private_data, char *info_data, long len);
int (*put)(void *private_data, struct snd_seq_kinstr *kinstr,
char __user *instr_data, long len, int atomic, int cmd);
int (*get)(void *private_data, struct snd_seq_kinstr *kinstr,
char __user *instr_data, long len, int atomic, int cmd);
int (*get_size)(void *private_data, struct snd_seq_kinstr *kinstr, long *size);
int (*remove)(void *private_data, struct snd_seq_kinstr *kinstr, int atomic);
void (*notify)(void *private_data, struct snd_seq_kinstr *kinstr, int what);
struct snd_seq_kinstr_ops *next;
};
/* instrument operations */
struct snd_seq_kinstr_list *snd_seq_instr_list_new(void);
void snd_seq_instr_list_free(struct snd_seq_kinstr_list **list);
int snd_seq_instr_list_free_cond(struct snd_seq_kinstr_list *list,
struct snd_seq_instr_header *ifree,
int client,
int atomic);
struct snd_seq_kinstr *snd_seq_instr_find(struct snd_seq_kinstr_list *list,
struct snd_seq_instr *instr,
int exact,
int follow_alias);
void snd_seq_instr_free_use(struct snd_seq_kinstr_list *list,
struct snd_seq_kinstr *instr);
int snd_seq_instr_event(struct snd_seq_kinstr_ops *ops,
struct snd_seq_kinstr_list *list,
struct snd_seq_event *ev,
int client,
int atomic,
int hop);
#endif /* __SOUND_SEQ_INSTR_H */
...@@ -26,19 +26,12 @@ ...@@ -26,19 +26,12 @@
#include "pcm.h" #include "pcm.h"
#include "mpu401.h" #include "mpu401.h"
#include "ac97_codec.h" #include "ac97_codec.h"
#include "seq_midi_emul.h"
#include "seq_device.h"
#include "util_mem.h" #include "util_mem.h"
//#include "ainstr_iw.h"
//#include "ainstr_gf1.h"
#include "ainstr_simple.h"
#define TRIDENT_DEVICE_ID_DX ((PCI_VENDOR_ID_TRIDENT<<16)|PCI_DEVICE_ID_TRIDENT_4DWAVE_DX) #define TRIDENT_DEVICE_ID_DX ((PCI_VENDOR_ID_TRIDENT<<16)|PCI_DEVICE_ID_TRIDENT_4DWAVE_DX)
#define TRIDENT_DEVICE_ID_NX ((PCI_VENDOR_ID_TRIDENT<<16)|PCI_DEVICE_ID_TRIDENT_4DWAVE_NX) #define TRIDENT_DEVICE_ID_NX ((PCI_VENDOR_ID_TRIDENT<<16)|PCI_DEVICE_ID_TRIDENT_4DWAVE_NX)
#define TRIDENT_DEVICE_ID_SI7018 ((PCI_VENDOR_ID_SI<<16)|PCI_DEVICE_ID_SI_7018) #define TRIDENT_DEVICE_ID_SI7018 ((PCI_VENDOR_ID_SI<<16)|PCI_DEVICE_ID_SI_7018)
#define SNDRV_SEQ_DEV_ID_TRIDENT "trident-synth"
#define SNDRV_TRIDENT_VOICE_TYPE_PCM 0 #define SNDRV_TRIDENT_VOICE_TYPE_PCM 0
#define SNDRV_TRIDENT_VOICE_TYPE_SYNTH 1 #define SNDRV_TRIDENT_VOICE_TYPE_SYNTH 1
#define SNDRV_TRIDENT_VOICE_TYPE_MIDI 2 #define SNDRV_TRIDENT_VOICE_TYPE_MIDI 2
...@@ -257,16 +250,6 @@ struct snd_trident; ...@@ -257,16 +250,6 @@ struct snd_trident;
struct snd_trident_voice; struct snd_trident_voice;
struct snd_trident_pcm_mixer; struct snd_trident_pcm_mixer;
struct snd_trident_sample_ops {
void (*sample_start)(struct snd_trident *gus, struct snd_trident_voice *voice, snd_seq_position_t position);
void (*sample_stop)(struct snd_trident *gus, struct snd_trident_voice *voice, int mode);
void (*sample_freq)(struct snd_trident *gus, struct snd_trident_voice *voice, snd_seq_frequency_t freq);
void (*sample_volume)(struct snd_trident *gus, struct snd_trident_voice *voice, struct snd_seq_ev_volume *volume);
void (*sample_loop)(struct snd_trident *card, struct snd_trident_voice *voice, struct snd_seq_ev_loop *loop);
void (*sample_pos)(struct snd_trident *card, struct snd_trident_voice *voice, snd_seq_position_t position);
void (*sample_private1)(struct snd_trident *card, struct snd_trident_voice *voice, unsigned char *data);
};
struct snd_trident_port { struct snd_trident_port {
struct snd_midi_channel_set * chset; struct snd_midi_channel_set * chset;
struct snd_trident * trident; struct snd_trident * trident;
...@@ -300,7 +283,6 @@ struct snd_trident_voice { ...@@ -300,7 +283,6 @@ struct snd_trident_voice {
unsigned char port; unsigned char port;
unsigned char index; unsigned char index;
struct snd_seq_instr instr;
struct snd_trident_sample_ops *sample_ops; struct snd_trident_sample_ops *sample_ops;
/* channel parameters */ /* channel parameters */
...@@ -354,9 +336,6 @@ struct snd_4dwave { ...@@ -354,9 +336,6 @@ struct snd_4dwave {
int seq_client; int seq_client;
struct snd_trident_port seq_ports[4]; struct snd_trident_port seq_ports[4];
struct snd_simple_ops simple_ops;
struct snd_seq_kinstr_list *ilist;
struct snd_trident_voice voices[64]; struct snd_trident_voice voices[64];
int ChanSynthCount; /* number of allocated synth channels */ int ChanSynthCount; /* number of allocated synth channels */
...@@ -416,7 +395,6 @@ struct snd_trident { ...@@ -416,7 +395,6 @@ struct snd_trident {
struct snd_pcm *foldback; /* Foldback PCM */ struct snd_pcm *foldback; /* Foldback PCM */
struct snd_pcm *spdif; /* SPDIF PCM */ struct snd_pcm *spdif; /* SPDIF PCM */
struct snd_rawmidi *rmidi; struct snd_rawmidi *rmidi;
struct snd_seq_device *seq_dev;
struct snd_ac97_bus *ac97_bus; struct snd_ac97_bus *ac97_bus;
struct snd_ac97 *ac97; struct snd_ac97 *ac97;
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
# Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz> # Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>
# #
obj-$(CONFIG_SND) += instr/
ifeq ($(CONFIG_SND_SEQUENCER_OSS),y) ifeq ($(CONFIG_SND_SEQUENCER_OSS),y)
obj-$(CONFIG_SND_SEQUENCER) += oss/ obj-$(CONFIG_SND_SEQUENCER) += oss/
endif endif
...@@ -15,7 +14,6 @@ snd-seq-objs := seq.o seq_lock.o seq_clientmgr.o seq_memory.o seq_queue.o \ ...@@ -15,7 +14,6 @@ snd-seq-objs := seq.o seq_lock.o seq_clientmgr.o seq_memory.o seq_queue.o \
snd-seq-midi-objs := seq_midi.o snd-seq-midi-objs := seq_midi.o
snd-seq-midi-emul-objs := seq_midi_emul.o snd-seq-midi-emul-objs := seq_midi_emul.o
snd-seq-midi-event-objs := seq_midi_event.o snd-seq-midi-event-objs := seq_midi_event.o
snd-seq-instr-objs := seq_instr.o
snd-seq-dummy-objs := seq_dummy.o snd-seq-dummy-objs := seq_dummy.o
snd-seq-virmidi-objs := seq_virmidi.o snd-seq-virmidi-objs := seq_virmidi.o
...@@ -36,9 +34,7 @@ obj-$(CONFIG_SND_SEQ_DUMMY) += snd-seq-dummy.o ...@@ -36,9 +34,7 @@ obj-$(CONFIG_SND_SEQ_DUMMY) += snd-seq-dummy.o
# Toplevel Module Dependency # Toplevel Module Dependency
obj-$(CONFIG_SND_VIRMIDI) += snd-seq-virmidi.o snd-seq-midi-event.o obj-$(CONFIG_SND_VIRMIDI) += snd-seq-virmidi.o snd-seq-midi-event.o
obj-$(call sequencer,$(CONFIG_SND_RAWMIDI)) += snd-seq-midi.o snd-seq-midi-event.o obj-$(call sequencer,$(CONFIG_SND_RAWMIDI)) += snd-seq-midi.o snd-seq-midi-event.o
obj-$(call sequencer,$(CONFIG_SND_OPL3_LIB)) += snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o obj-$(call sequencer,$(CONFIG_SND_OPL3_LIB)) += snd-seq-midi-event.o snd-seq-midi-emul.o
obj-$(call sequencer,$(CONFIG_SND_OPL4_LIB)) += snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o obj-$(call sequencer,$(CONFIG_SND_OPL4_LIB)) += snd-seq-midi-event.o snd-seq-midi-emul.o
obj-$(call sequencer,$(CONFIG_SND_GUS_SYNTH)) += snd-seq-midi-emul.o snd-seq-instr.o
obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-seq-midi-emul.o snd-seq-virmidi.o obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-seq-midi-emul.o snd-seq-virmidi.o
obj-$(call sequencer,$(CONFIG_SND_EMU10K1)) += snd-seq-midi-emul.o snd-seq-virmidi.o obj-$(call sequencer,$(CONFIG_SND_EMU10K1)) += snd-seq-midi-emul.o snd-seq-virmidi.o
obj-$(call sequencer,$(CONFIG_SND_TRIDENT)) += snd-seq-midi-emul.o snd-seq-instr.o
#
# Makefile for ALSA
# Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>
#
snd-ainstr-fm-objs := ainstr_fm.o
snd-ainstr-simple-objs := ainstr_simple.o
snd-ainstr-gf1-objs := ainstr_gf1.o
snd-ainstr-iw-objs := ainstr_iw.o
#
# this function returns:
# "m" - CONFIG_SND_SEQUENCER is m
# <empty string> - CONFIG_SND_SEQUENCER is undefined
# otherwise parameter #1 value
#
sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
# Toplevel Module Dependency
obj-$(call sequencer,$(CONFIG_SND_OPL3_LIB)) += snd-ainstr-fm.o
obj-$(call sequencer,$(CONFIG_SND_OPL4_LIB)) += snd-ainstr-fm.o
obj-$(call sequencer,$(CONFIG_SND_GUS_SYNTH)) += snd-ainstr-gf1.o snd-ainstr-simple.o snd-ainstr-iw.o
obj-$(call sequencer,$(CONFIG_SND_TRIDENT)) += snd-ainstr-simple.o
/*
* FM (OPL2/3) Instrument routines
* Copyright (c) 2000 Uros Bizjak <uros@kss-loka.si>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <sound/driver.h>
#include <linux/init.h>
#include <sound/core.h>
#include <sound/ainstr_fm.h>
#include <sound/initval.h>
#include <asm/uaccess.h>
MODULE_AUTHOR("Uros Bizjak <uros@kss-loka.si>");
MODULE_DESCRIPTION("Advanced Linux Sound Architecture FM Instrument support.");
MODULE_LICENSE("GPL");
static int snd_seq_fm_put(void *private_data, struct snd_seq_kinstr *instr,
char __user *instr_data, long len, int atomic, int cmd)
{
struct fm_instrument *ip;
struct fm_xinstrument ix;
int idx;
if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE)
return -EINVAL;
/* copy instrument data */
if (len < (long)sizeof(ix))
return -EINVAL;
if (copy_from_user(&ix, instr_data, sizeof(ix)))
return -EFAULT;
if (ix.stype != FM_STRU_INSTR)
return -EINVAL;
ip = (struct fm_instrument *)KINSTR_DATA(instr);
ip->share_id[0] = le32_to_cpu(ix.share_id[0]);
ip->share_id[1] = le32_to_cpu(ix.share_id[1]);
ip->share_id[2] = le32_to_cpu(ix.share_id[2]);
ip->share_id[3] = le32_to_cpu(ix.share_id[3]);
ip->type = ix.type;
for (idx = 0; idx < 4; idx++) {
ip->op[idx].am_vib = ix.op[idx].am_vib;
ip->op[idx].ksl_level = ix.op[idx].ksl_level;
ip->op[idx].attack_decay = ix.op[idx].attack_decay;
ip->op[idx].sustain_release = ix.op[idx].sustain_release;
ip->op[idx].wave_select = ix.op[idx].wave_select;
}
for (idx = 0; idx < 2; idx++) {
ip->feedback_connection[idx] = ix.feedback_connection[idx];
}
ip->echo_delay = ix.echo_delay;
ip->echo_atten = ix.echo_atten;
ip->chorus_spread = ix.chorus_spread;
ip->trnsps = ix.trnsps;
ip->fix_dur = ix.fix_dur;
ip->modes = ix.modes;
ip->fix_key = ix.fix_key;
return 0;
}
static int snd_seq_fm_get(void *private_data, struct snd_seq_kinstr *instr,
char __user *instr_data, long len, int atomic,
int cmd)
{
struct fm_instrument *ip;
struct fm_xinstrument ix;
int idx;
if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL)
return -EINVAL;
if (len < (long)sizeof(ix))
return -ENOMEM;
memset(&ix, 0, sizeof(ix));
ip = (struct fm_instrument *)KINSTR_DATA(instr);
ix.stype = FM_STRU_INSTR;
ix.share_id[0] = cpu_to_le32(ip->share_id[0]);
ix.share_id[1] = cpu_to_le32(ip->share_id[1]);
ix.share_id[2] = cpu_to_le32(ip->share_id[2]);
ix.share_id[3] = cpu_to_le32(ip->share_id[3]);
ix.type = ip->type;
for (idx = 0; idx < 4; idx++) {
ix.op[idx].am_vib = ip->op[idx].am_vib;
ix.op[idx].ksl_level = ip->op[idx].ksl_level;
ix.op[idx].attack_decay = ip->op[idx].attack_decay;
ix.op[idx].sustain_release = ip->op[idx].sustain_release;
ix.op[idx].wave_select = ip->op[idx].wave_select;
}
for (idx = 0; idx < 2; idx++) {
ix.feedback_connection[idx] = ip->feedback_connection[idx];
}
if (copy_to_user(instr_data, &ix, sizeof(ix)))
return -EFAULT;
ix.echo_delay = ip->echo_delay;
ix.echo_atten = ip->echo_atten;
ix.chorus_spread = ip->chorus_spread;
ix.trnsps = ip->trnsps;
ix.fix_dur = ip->fix_dur;
ix.modes = ip->modes;
ix.fix_key = ip->fix_key;
return 0;
}
static int snd_seq_fm_get_size(void *private_data, struct snd_seq_kinstr *instr,
long *size)
{
*size = sizeof(struct fm_xinstrument);
return 0;
}
int snd_seq_fm_init(struct snd_seq_kinstr_ops *ops,
struct snd_seq_kinstr_ops *next)
{
memset(ops, 0, sizeof(*ops));
// ops->private_data = private_data;
ops->add_len = sizeof(struct fm_instrument);
ops->instr_type = SNDRV_SEQ_INSTR_ID_OPL2_3;
ops->put = snd_seq_fm_put;
ops->get = snd_seq_fm_get;
ops->get_size = snd_seq_fm_get_size;
// ops->remove = snd_seq_fm_remove;
// ops->notify = snd_seq_fm_notify;
ops->next = next;
return 0;
}
/*
* Init part
*/
static int __init alsa_ainstr_fm_init(void)
{
return 0;
}
static void __exit alsa_ainstr_fm_exit(void)
{
}
module_init(alsa_ainstr_fm_init)
module_exit(alsa_ainstr_fm_exit)
EXPORT_SYMBOL(snd_seq_fm_init);
/*
* GF1 (GUS) Patch - Instrument routines
* Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <sound/driver.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/ainstr_gf1.h>
#include <sound/initval.h>
#include <asm/uaccess.h>
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Advanced Linux Sound Architecture GF1 (GUS) Patch support.");
MODULE_LICENSE("GPL");
static unsigned int snd_seq_gf1_size(unsigned int size, unsigned int format)
{
unsigned int result = size;
if (format & GF1_WAVE_16BIT)
result <<= 1;
if (format & GF1_WAVE_STEREO)
result <<= 1;
return format;
}
static int snd_seq_gf1_copy_wave_from_stream(struct snd_gf1_ops *ops,
struct gf1_instrument *ip,
char __user **data,
long *len,
int atomic)
{
struct gf1_wave *wp, *prev;
struct gf1_xwave xp;
int err;
gfp_t gfp_mask;
unsigned int real_size;
gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
if (*len < (long)sizeof(xp))
return -EINVAL;
if (copy_from_user(&xp, *data, sizeof(xp)))
return -EFAULT;
*data += sizeof(xp);
*len -= sizeof(xp);
wp = kzalloc(sizeof(*wp), gfp_mask);
if (wp == NULL)
return -ENOMEM;
wp->share_id[0] = le32_to_cpu(xp.share_id[0]);
wp->share_id[1] = le32_to_cpu(xp.share_id[1]);
wp->share_id[2] = le32_to_cpu(xp.share_id[2]);
wp->share_id[3] = le32_to_cpu(xp.share_id[3]);
wp->format = le32_to_cpu(xp.format);
wp->size = le32_to_cpu(xp.size);
wp->start = le32_to_cpu(xp.start);
wp->loop_start = le32_to_cpu(xp.loop_start);
wp->loop_end = le32_to_cpu(xp.loop_end);
wp->loop_repeat = le16_to_cpu(xp.loop_repeat);
wp->flags = xp.flags;
wp->sample_rate = le32_to_cpu(xp.sample_rate);
wp->low_frequency = le32_to_cpu(xp.low_frequency);
wp->high_frequency = le32_to_cpu(xp.high_frequency);
wp->root_frequency = le32_to_cpu(xp.root_frequency);
wp->tune = le16_to_cpu(xp.tune);
wp->balance = xp.balance;
memcpy(wp->envelope_rate, xp.envelope_rate, 6);
memcpy(wp->envelope_offset, xp.envelope_offset, 6);
wp->tremolo_sweep = xp.tremolo_sweep;
wp->tremolo_rate = xp.tremolo_rate;
wp->tremolo_depth = xp.tremolo_depth;
wp->vibrato_sweep = xp.vibrato_sweep;
wp->vibrato_rate = xp.vibrato_rate;
wp->vibrato_depth = xp.vibrato_depth;
wp->scale_frequency = le16_to_cpu(xp.scale_frequency);
wp->scale_factor = le16_to_cpu(xp.scale_factor);
real_size = snd_seq_gf1_size(wp->size, wp->format);
if ((long)real_size > *len) {
kfree(wp);
return -ENOMEM;
}
if (ops->put_sample) {
err = ops->put_sample(ops->private_data, wp,
*data, real_size, atomic);
if (err < 0) {
kfree(wp);
return err;
}
}
*data += real_size;
*len -= real_size;
prev = ip->wave;
if (prev) {
while (prev->next) prev = prev->next;
prev->next = wp;
} else {
ip->wave = wp;
}
return 0;
}
static void snd_seq_gf1_wave_free(struct snd_gf1_ops *ops,
struct gf1_wave *wave,
int atomic)
{
if (ops->remove_sample)
ops->remove_sample(ops->private_data, wave, atomic);
kfree(wave);
}
static void snd_seq_gf1_instr_free(struct snd_gf1_ops *ops,
struct gf1_instrument *ip,
int atomic)
{
struct gf1_wave *wave;
while ((wave = ip->wave) != NULL) {
ip->wave = wave->next;
snd_seq_gf1_wave_free(ops, wave, atomic);
}
}
static int snd_seq_gf1_put(void *private_data, struct snd_seq_kinstr *instr,
char __user *instr_data, long len, int atomic,
int cmd)
{
struct snd_gf1_ops *ops = private_data;
struct gf1_instrument *ip;
struct gf1_xinstrument ix;
int err;
gfp_t gfp_mask;
if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE)
return -EINVAL;
gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
/* copy instrument data */
if (len < (long)sizeof(ix))
return -EINVAL;
if (copy_from_user(&ix, instr_data, sizeof(ix)))
return -EFAULT;
if (ix.stype != GF1_STRU_INSTR)
return -EINVAL;
instr_data += sizeof(ix);
len -= sizeof(ix);
ip = (struct gf1_instrument *)KINSTR_DATA(instr);
ip->exclusion = le16_to_cpu(ix.exclusion);
ip->exclusion_group = le16_to_cpu(ix.exclusion_group);
ip->effect1 = ix.effect1;
ip->effect1_depth = ix.effect1_depth;
ip->effect2 = ix.effect2;
ip->effect2_depth = ix.effect2_depth;
/* copy layers */
while (len > (long)sizeof(__u32)) {
__u32 stype;
if (copy_from_user(&stype, instr_data, sizeof(stype)))
return -EFAULT;
if (stype != GF1_STRU_WAVE) {
snd_seq_gf1_instr_free(ops, ip, atomic);
return -EINVAL;
}
err = snd_seq_gf1_copy_wave_from_stream(ops,
ip,
&instr_data,
&len,
atomic);
if (err < 0) {
snd_seq_gf1_instr_free(ops, ip, atomic);
return err;
}
}
return 0;
}
static int snd_seq_gf1_copy_wave_to_stream(struct snd_gf1_ops *ops,
struct gf1_instrument *ip,
char __user **data,
long *len,
int atomic)
{
struct gf1_wave *wp;
struct gf1_xwave xp;
int err;
unsigned int real_size;
for (wp = ip->wave; wp; wp = wp->next) {
if (*len < (long)sizeof(xp))
return -ENOMEM;
memset(&xp, 0, sizeof(xp));
xp.stype = GF1_STRU_WAVE;
xp.share_id[0] = cpu_to_le32(wp->share_id[0]);
xp.share_id[1] = cpu_to_le32(wp->share_id[1]);
xp.share_id[2] = cpu_to_le32(wp->share_id[2]);
xp.share_id[3] = cpu_to_le32(wp->share_id[3]);
xp.format = cpu_to_le32(wp->format);
xp.size = cpu_to_le32(wp->size);
xp.start = cpu_to_le32(wp->start);
xp.loop_start = cpu_to_le32(wp->loop_start);
xp.loop_end = cpu_to_le32(wp->loop_end);
xp.loop_repeat = cpu_to_le32(wp->loop_repeat);
xp.flags = wp->flags;
xp.sample_rate = cpu_to_le32(wp->sample_rate);
xp.low_frequency = cpu_to_le32(wp->low_frequency);
xp.high_frequency = cpu_to_le32(wp->high_frequency);
xp.root_frequency = cpu_to_le32(wp->root_frequency);
xp.tune = cpu_to_le16(wp->tune);
xp.balance = wp->balance;
memcpy(xp.envelope_rate, wp->envelope_rate, 6);
memcpy(xp.envelope_offset, wp->envelope_offset, 6);
xp.tremolo_sweep = wp->tremolo_sweep;
xp.tremolo_rate = wp->tremolo_rate;
xp.tremolo_depth = wp->tremolo_depth;
xp.vibrato_sweep = wp->vibrato_sweep;
xp.vibrato_rate = wp->vibrato_rate;
xp.vibrato_depth = wp->vibrato_depth;
xp.scale_frequency = cpu_to_le16(wp->scale_frequency);
xp.scale_factor = cpu_to_le16(wp->scale_factor);
if (copy_to_user(*data, &xp, sizeof(xp)))
return -EFAULT;
*data += sizeof(xp);
*len -= sizeof(xp);
real_size = snd_seq_gf1_size(wp->size, wp->format);
if (*len < (long)real_size)
return -ENOMEM;
if (ops->get_sample) {
err = ops->get_sample(ops->private_data, wp,
*data, real_size, atomic);
if (err < 0)
return err;
}
*data += wp->size;
*len -= wp->size;
}
return 0;
}
static int snd_seq_gf1_get(void *private_data, struct snd_seq_kinstr *instr,
char __user *instr_data, long len, int atomic,
int cmd)
{
struct snd_gf1_ops *ops = private_data;
struct gf1_instrument *ip;
struct gf1_xinstrument ix;
if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL)
return -EINVAL;
if (len < (long)sizeof(ix))
return -ENOMEM;
memset(&ix, 0, sizeof(ix));
ip = (struct gf1_instrument *)KINSTR_DATA(instr);
ix.stype = GF1_STRU_INSTR;
ix.exclusion = cpu_to_le16(ip->exclusion);
ix.exclusion_group = cpu_to_le16(ip->exclusion_group);
ix.effect1 = cpu_to_le16(ip->effect1);
ix.effect1_depth = cpu_to_le16(ip->effect1_depth);
ix.effect2 = ip->effect2;
ix.effect2_depth = ip->effect2_depth;
if (copy_to_user(instr_data, &ix, sizeof(ix)))
return -EFAULT;
instr_data += sizeof(ix);
len -= sizeof(ix);
return snd_seq_gf1_copy_wave_to_stream(ops,
ip,
&instr_data,
&len,
atomic);
}
static int snd_seq_gf1_get_size(void *private_data, struct snd_seq_kinstr *instr,
long *size)
{
long result;
struct gf1_instrument *ip;
struct gf1_wave *wp;
*size = 0;
ip = (struct gf1_instrument *)KINSTR_DATA(instr);
result = sizeof(struct gf1_xinstrument);
for (wp = ip->wave; wp; wp = wp->next) {
result += sizeof(struct gf1_xwave);
result += wp->size;
}
*size = result;
return 0;
}
static int snd_seq_gf1_remove(void *private_data,
struct snd_seq_kinstr *instr,
int atomic)
{
struct snd_gf1_ops *ops = private_data;
struct gf1_instrument *ip;
ip = (struct gf1_instrument *)KINSTR_DATA(instr);
snd_seq_gf1_instr_free(ops, ip, atomic);
return 0;
}
static void snd_seq_gf1_notify(void *private_data,
struct snd_seq_kinstr *instr,
int what)
{
struct snd_gf1_ops *ops = private_data;
if (ops->notify)
ops->notify(ops->private_data, instr, what);
}
int snd_seq_gf1_init(struct snd_gf1_ops *ops,
void *private_data,
struct snd_seq_kinstr_ops *next)
{
memset(ops, 0, sizeof(*ops));
ops->private_data = private_data;
ops->kops.private_data = ops;
ops->kops.add_len = sizeof(struct gf1_instrument);
ops->kops.instr_type = SNDRV_SEQ_INSTR_ID_GUS_PATCH;
ops->kops.put = snd_seq_gf1_put;
ops->kops.get = snd_seq_gf1_get;
ops->kops.get_size = snd_seq_gf1_get_size;
ops->kops.remove = snd_seq_gf1_remove;
ops->kops.notify = snd_seq_gf1_notify;
ops->kops.next = next;
return 0;
}
/*
* Init part
*/
static int __init alsa_ainstr_gf1_init(void)
{
return 0;
}
static void __exit alsa_ainstr_gf1_exit(void)
{
}
module_init(alsa_ainstr_gf1_init)
module_exit(alsa_ainstr_gf1_exit)
EXPORT_SYMBOL(snd_seq_gf1_init);
/*
* IWFFFF - AMD InterWave (tm) - Instrument routines
* Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <sound/driver.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/ainstr_iw.h>
#include <sound/initval.h>
#include <asm/uaccess.h>
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Advanced Linux Sound Architecture IWFFFF support.");
MODULE_LICENSE("GPL");
static unsigned int snd_seq_iwffff_size(unsigned int size, unsigned int format)
{
unsigned int result = size;
if (format & IWFFFF_WAVE_16BIT)
result <<= 1;
if (format & IWFFFF_WAVE_STEREO)
result <<= 1;
return result;
}
static void snd_seq_iwffff_copy_lfo_from_stream(struct iwffff_lfo *fp,
struct iwffff_xlfo *fx)
{
fp->freq = le16_to_cpu(fx->freq);
fp->depth = le16_to_cpu(fx->depth);
fp->sweep = le16_to_cpu(fx->sweep);
fp->shape = fx->shape;
fp->delay = fx->delay;
}
static int snd_seq_iwffff_copy_env_from_stream(__u32 req_stype,
struct iwffff_layer *lp,
struct iwffff_env *ep,
struct iwffff_xenv *ex,
char __user **data,
long *len,
gfp_t gfp_mask)
{
__u32 stype;
struct iwffff_env_record *rp, *rp_last;
struct iwffff_xenv_record rx;
struct iwffff_env_point *pp;
struct iwffff_xenv_point px;
int points_size, idx;
ep->flags = ex->flags;
ep->mode = ex->mode;
ep->index = ex->index;
rp_last = NULL;
while (1) {
if (*len < (long)sizeof(__u32))
return -EINVAL;
if (copy_from_user(&stype, *data, sizeof(stype)))
return -EFAULT;
if (stype == IWFFFF_STRU_WAVE)
return 0;
if (req_stype != stype) {
if (stype == IWFFFF_STRU_ENV_RECP ||
stype == IWFFFF_STRU_ENV_RECV)
return 0;
}
if (*len < (long)sizeof(rx))
return -EINVAL;
if (copy_from_user(&rx, *data, sizeof(rx)))
return -EFAULT;
*data += sizeof(rx);
*len -= sizeof(rx);
points_size = (le16_to_cpu(rx.nattack) + le16_to_cpu(rx.nrelease)) * 2 * sizeof(__u16);
if (points_size > *len)
return -EINVAL;
rp = kzalloc(sizeof(*rp) + points_size, gfp_mask);
if (rp == NULL)
return -ENOMEM;
rp->nattack = le16_to_cpu(rx.nattack);
rp->nrelease = le16_to_cpu(rx.nrelease);
rp->sustain_offset = le16_to_cpu(rx.sustain_offset);
rp->sustain_rate = le16_to_cpu(rx.sustain_rate);
rp->release_rate = le16_to_cpu(rx.release_rate);
rp->hirange = rx.hirange;
pp = (struct iwffff_env_point *)(rp + 1);
for (idx = 0; idx < rp->nattack + rp->nrelease; idx++) {
if (copy_from_user(&px, *data, sizeof(px)))
return -EFAULT;
*data += sizeof(px);
*len -= sizeof(px);
pp->offset = le16_to_cpu(px.offset);
pp->rate = le16_to_cpu(px.rate);
}
if (ep->record == NULL) {
ep->record = rp;
} else {
rp_last = rp;
}
rp_last = rp;
}
return 0;
}
static int snd_seq_iwffff_copy_wave_from_stream(struct snd_iwffff_ops *ops,
struct iwffff_layer *lp,
char __user **data,
long *len,
int atomic)
{
struct iwffff_wave *wp, *prev;
struct iwffff_xwave xp;
int err;
gfp_t gfp_mask;
unsigned int real_size;
gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
if (*len < (long)sizeof(xp))
return -EINVAL;
if (copy_from_user(&xp, *data, sizeof(xp)))
return -EFAULT;
*data += sizeof(xp);
*len -= sizeof(xp);
wp = kzalloc(sizeof(*wp), gfp_mask);
if (wp == NULL)
return -ENOMEM;
wp->share_id[0] = le32_to_cpu(xp.share_id[0]);
wp->share_id[1] = le32_to_cpu(xp.share_id[1]);
wp->share_id[2] = le32_to_cpu(xp.share_id[2]);
wp->share_id[3] = le32_to_cpu(xp.share_id[3]);
wp->format = le32_to_cpu(xp.format);
wp->address.memory = le32_to_cpu(xp.offset);
wp->size = le32_to_cpu(xp.size);
wp->start = le32_to_cpu(xp.start);
wp->loop_start = le32_to_cpu(xp.loop_start);
wp->loop_end = le32_to_cpu(xp.loop_end);
wp->loop_repeat = le16_to_cpu(xp.loop_repeat);
wp->sample_ratio = le32_to_cpu(xp.sample_ratio);
wp->attenuation = xp.attenuation;
wp->low_note = xp.low_note;
wp->high_note = xp.high_note;
real_size = snd_seq_iwffff_size(wp->size, wp->format);
if (!(wp->format & IWFFFF_WAVE_ROM)) {
if ((long)real_size > *len) {
kfree(wp);
return -ENOMEM;
}
}
if (ops->put_sample) {
err = ops->put_sample(ops->private_data, wp,
*data, real_size, atomic);
if (err < 0) {
kfree(wp);
return err;
}
}
if (!(wp->format & IWFFFF_WAVE_ROM)) {
*data += real_size;
*len -= real_size;
}
prev = lp->wave;
if (prev) {
while (prev->next) prev = prev->next;
prev->next = wp;
} else {
lp->wave = wp;
}
return 0;
}
static void snd_seq_iwffff_env_free(struct snd_iwffff_ops *ops,
struct iwffff_env *env,
int atomic)
{
struct iwffff_env_record *rec;
while ((rec = env->record) != NULL) {
env->record = rec->next;
kfree(rec);
}
}
static void snd_seq_iwffff_wave_free(struct snd_iwffff_ops *ops,
struct iwffff_wave *wave,
int atomic)
{
if (ops->remove_sample)
ops->remove_sample(ops->private_data, wave, atomic);
kfree(wave);
}
static void snd_seq_iwffff_instr_free(struct snd_iwffff_ops *ops,
struct iwffff_instrument *ip,
int atomic)
{
struct iwffff_layer *layer;
struct iwffff_wave *wave;
while ((layer = ip->layer) != NULL) {
ip->layer = layer->next;
snd_seq_iwffff_env_free(ops, &layer->penv, atomic);
snd_seq_iwffff_env_free(ops, &layer->venv, atomic);
while ((wave = layer->wave) != NULL) {
layer->wave = wave->next;
snd_seq_iwffff_wave_free(ops, wave, atomic);
}
kfree(layer);
}
}
static int snd_seq_iwffff_put(void *private_data, struct snd_seq_kinstr *instr,
char __user *instr_data, long len, int atomic,
int cmd)
{
struct snd_iwffff_ops *ops = private_data;
struct iwffff_instrument *ip;
struct iwffff_xinstrument ix;
struct iwffff_layer *lp, *prev_lp;
struct iwffff_xlayer lx;
int err;
gfp_t gfp_mask;
if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE)
return -EINVAL;
gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
/* copy instrument data */
if (len < (long)sizeof(ix))
return -EINVAL;
if (copy_from_user(&ix, instr_data, sizeof(ix)))
return -EFAULT;
if (ix.stype != IWFFFF_STRU_INSTR)
return -EINVAL;
instr_data += sizeof(ix);
len -= sizeof(ix);
ip = (struct iwffff_instrument *)KINSTR_DATA(instr);
ip->exclusion = le16_to_cpu(ix.exclusion);
ip->layer_type = le16_to_cpu(ix.layer_type);
ip->exclusion_group = le16_to_cpu(ix.exclusion_group);
ip->effect1 = ix.effect1;
ip->effect1_depth = ix.effect1_depth;
ip->effect2 = ix.effect2;
ip->effect2_depth = ix.effect2_depth;
/* copy layers */
prev_lp = NULL;
while (len > 0) {
if (len < (long)sizeof(struct iwffff_xlayer)) {
snd_seq_iwffff_instr_free(ops, ip, atomic);
return -EINVAL;
}
if (copy_from_user(&lx, instr_data, sizeof(lx)))
return -EFAULT;
instr_data += sizeof(lx);
len -= sizeof(lx);
if (lx.stype != IWFFFF_STRU_LAYER) {
snd_seq_iwffff_instr_free(ops, ip, atomic);
return -EINVAL;
}
lp = kzalloc(sizeof(*lp), gfp_mask);
if (lp == NULL) {
snd_seq_iwffff_instr_free(ops, ip, atomic);
return -ENOMEM;
}
if (prev_lp) {
prev_lp->next = lp;
} else {
ip->layer = lp;
}
prev_lp = lp;
lp->flags = lx.flags;
lp->velocity_mode = lx.velocity_mode;
lp->layer_event = lx.layer_event;
lp->low_range = lx.low_range;
lp->high_range = lx.high_range;
lp->pan = lx.pan;
lp->pan_freq_scale = lx.pan_freq_scale;
lp->attenuation = lx.attenuation;
snd_seq_iwffff_copy_lfo_from_stream(&lp->tremolo, &lx.tremolo);
snd_seq_iwffff_copy_lfo_from_stream(&lp->vibrato, &lx.vibrato);
lp->freq_scale = le16_to_cpu(lx.freq_scale);
lp->freq_center = lx.freq_center;
err = snd_seq_iwffff_copy_env_from_stream(IWFFFF_STRU_ENV_RECP,
lp,
&lp->penv, &lx.penv,
&instr_data, &len,
gfp_mask);
if (err < 0) {
snd_seq_iwffff_instr_free(ops, ip, atomic);
return err;
}
err = snd_seq_iwffff_copy_env_from_stream(IWFFFF_STRU_ENV_RECV,
lp,
&lp->venv, &lx.venv,
&instr_data, &len,
gfp_mask);
if (err < 0) {
snd_seq_iwffff_instr_free(ops, ip, atomic);
return err;
}
while (len > (long)sizeof(__u32)) {
__u32 stype;
if (copy_from_user(&stype, instr_data, sizeof(stype)))
return -EFAULT;
if (stype != IWFFFF_STRU_WAVE)
break;
err = snd_seq_iwffff_copy_wave_from_stream(ops,
lp,
&instr_data,
&len,
atomic);
if (err < 0) {
snd_seq_iwffff_instr_free(ops, ip, atomic);
return err;
}
}
}
return 0;
}
static void snd_seq_iwffff_copy_lfo_to_stream(struct iwffff_xlfo *fx,
struct iwffff_lfo *fp)
{
fx->freq = cpu_to_le16(fp->freq);
fx->depth = cpu_to_le16(fp->depth);
fx->sweep = cpu_to_le16(fp->sweep);
fp->shape = fx->shape;
fp->delay = fx->delay;
}
static int snd_seq_iwffff_copy_env_to_stream(__u32 req_stype,
struct iwffff_layer *lp,
struct iwffff_xenv *ex,
struct iwffff_env *ep,
char __user **data,
long *len)
{
struct iwffff_env_record *rp;
struct iwffff_xenv_record rx;
struct iwffff_env_point *pp;
struct iwffff_xenv_point px;
int points_size, idx;
ex->flags = ep->flags;
ex->mode = ep->mode;
ex->index = ep->index;
for (rp = ep->record; rp; rp = rp->next) {
if (*len < (long)sizeof(rx))
return -ENOMEM;
memset(&rx, 0, sizeof(rx));
rx.stype = req_stype;
rx.nattack = cpu_to_le16(rp->nattack);
rx.nrelease = cpu_to_le16(rp->nrelease);
rx.sustain_offset = cpu_to_le16(rp->sustain_offset);
rx.sustain_rate = cpu_to_le16(rp->sustain_rate);
rx.release_rate = cpu_to_le16(rp->release_rate);
rx.hirange = cpu_to_le16(rp->hirange);
if (copy_to_user(*data, &rx, sizeof(rx)))
return -EFAULT;
*data += sizeof(rx);
*len -= sizeof(rx);
points_size = (rp->nattack + rp->nrelease) * 2 * sizeof(__u16);
if (*len < points_size)
return -ENOMEM;
pp = (struct iwffff_env_point *)(rp + 1);
for (idx = 0; idx < rp->nattack + rp->nrelease; idx++) {
px.offset = cpu_to_le16(pp->offset);
px.rate = cpu_to_le16(pp->rate);
if (copy_to_user(*data, &px, sizeof(px)))
return -EFAULT;
*data += sizeof(px);
*len -= sizeof(px);
}
}
return 0;
}
static int snd_seq_iwffff_copy_wave_to_stream(struct snd_iwffff_ops *ops,
struct iwffff_layer *lp,
char __user **data,
long *len,
int atomic)
{
struct iwffff_wave *wp;
struct iwffff_xwave xp;
int err;
unsigned int real_size;
for (wp = lp->wave; wp; wp = wp->next) {
if (*len < (long)sizeof(xp))
return -ENOMEM;
memset(&xp, 0, sizeof(xp));
xp.stype = IWFFFF_STRU_WAVE;
xp.share_id[0] = cpu_to_le32(wp->share_id[0]);
xp.share_id[1] = cpu_to_le32(wp->share_id[1]);
xp.share_id[2] = cpu_to_le32(wp->share_id[2]);
xp.share_id[3] = cpu_to_le32(wp->share_id[3]);
xp.format = cpu_to_le32(wp->format);
if (wp->format & IWFFFF_WAVE_ROM)
xp.offset = cpu_to_le32(wp->address.memory);
xp.size = cpu_to_le32(wp->size);
xp.start = cpu_to_le32(wp->start);
xp.loop_start = cpu_to_le32(wp->loop_start);
xp.loop_end = cpu_to_le32(wp->loop_end);
xp.loop_repeat = cpu_to_le32(wp->loop_repeat);
xp.sample_ratio = cpu_to_le32(wp->sample_ratio);
xp.attenuation = wp->attenuation;
xp.low_note = wp->low_note;
xp.high_note = wp->high_note;
if (copy_to_user(*data, &xp, sizeof(xp)))
return -EFAULT;
*data += sizeof(xp);
*len -= sizeof(xp);
real_size = snd_seq_iwffff_size(wp->size, wp->format);
if (!(wp->format & IWFFFF_WAVE_ROM)) {
if (*len < (long)real_size)
return -ENOMEM;
}
if (ops->get_sample) {
err = ops->get_sample(ops->private_data, wp,
*data, real_size, atomic);
if (err < 0)
return err;
}
if (!(wp->format & IWFFFF_WAVE_ROM)) {
*data += real_size;
*len -= real_size;
}
}
return 0;
}
static int snd_seq_iwffff_get(void *private_data, struct snd_seq_kinstr *instr,
char __user *instr_data, long len, int atomic, int cmd)
{
struct snd_iwffff_ops *ops = private_data;
struct iwffff_instrument *ip;
struct iwffff_xinstrument ix;
struct iwffff_layer *lp;
struct iwffff_xlayer lx;
char __user *layer_instr_data;
int err;
if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL)
return -EINVAL;
if (len < (long)sizeof(ix))
return -ENOMEM;
memset(&ix, 0, sizeof(ix));
ip = (struct iwffff_instrument *)KINSTR_DATA(instr);
ix.stype = IWFFFF_STRU_INSTR;
ix.exclusion = cpu_to_le16(ip->exclusion);
ix.layer_type = cpu_to_le16(ip->layer_type);
ix.exclusion_group = cpu_to_le16(ip->exclusion_group);
ix.effect1 = cpu_to_le16(ip->effect1);
ix.effect1_depth = cpu_to_le16(ip->effect1_depth);
ix.effect2 = ip->effect2;
ix.effect2_depth = ip->effect2_depth;
if (copy_to_user(instr_data, &ix, sizeof(ix)))
return -EFAULT;
instr_data += sizeof(ix);
len -= sizeof(ix);
for (lp = ip->layer; lp; lp = lp->next) {
if (len < (long)sizeof(lx))
return -ENOMEM;
memset(&lx, 0, sizeof(lx));
lx.stype = IWFFFF_STRU_LAYER;
lx.flags = lp->flags;
lx.velocity_mode = lp->velocity_mode;
lx.layer_event = lp->layer_event;
lx.low_range = lp->low_range;
lx.high_range = lp->high_range;
lx.pan = lp->pan;
lx.pan_freq_scale = lp->pan_freq_scale;
lx.attenuation = lp->attenuation;
snd_seq_iwffff_copy_lfo_to_stream(&lx.tremolo, &lp->tremolo);
snd_seq_iwffff_copy_lfo_to_stream(&lx.vibrato, &lp->vibrato);
layer_instr_data = instr_data;
instr_data += sizeof(lx);
len -= sizeof(lx);
err = snd_seq_iwffff_copy_env_to_stream(IWFFFF_STRU_ENV_RECP,
lp,
&lx.penv, &lp->penv,
&instr_data, &len);
if (err < 0)
return err;
err = snd_seq_iwffff_copy_env_to_stream(IWFFFF_STRU_ENV_RECV,
lp,
&lx.venv, &lp->venv,
&instr_data, &len);
if (err < 0)
return err;
/* layer structure updating is now finished */
if (copy_to_user(layer_instr_data, &lx, sizeof(lx)))
return -EFAULT;
err = snd_seq_iwffff_copy_wave_to_stream(ops,
lp,
&instr_data,
&len,
atomic);
if (err < 0)
return err;
}
return 0;
}
static long snd_seq_iwffff_env_size_in_stream(struct iwffff_env *ep)
{
long result = 0;
struct iwffff_env_record *rp;
for (rp = ep->record; rp; rp = rp->next) {
result += sizeof(struct iwffff_xenv_record);
result += (rp->nattack + rp->nrelease) * 2 * sizeof(__u16);
}
return 0;
}
static long snd_seq_iwffff_wave_size_in_stream(struct iwffff_layer *lp)
{
long result = 0;
struct iwffff_wave *wp;
for (wp = lp->wave; wp; wp = wp->next) {
result += sizeof(struct iwffff_xwave);
if (!(wp->format & IWFFFF_WAVE_ROM))
result += wp->size;
}
return result;
}
static int snd_seq_iwffff_get_size(void *private_data, struct snd_seq_kinstr *instr,
long *size)
{
long result;
struct iwffff_instrument *ip;
struct iwffff_layer *lp;
*size = 0;
ip = (struct iwffff_instrument *)KINSTR_DATA(instr);
result = sizeof(struct iwffff_xinstrument);
for (lp = ip->layer; lp; lp = lp->next) {
result += sizeof(struct iwffff_xlayer);
result += snd_seq_iwffff_env_size_in_stream(&lp->penv);
result += snd_seq_iwffff_env_size_in_stream(&lp->venv);
result += snd_seq_iwffff_wave_size_in_stream(lp);
}
*size = result;
return 0;
}
static int snd_seq_iwffff_remove(void *private_data,
struct snd_seq_kinstr *instr,
int atomic)
{
struct snd_iwffff_ops *ops = private_data;
struct iwffff_instrument *ip;
ip = (struct iwffff_instrument *)KINSTR_DATA(instr);
snd_seq_iwffff_instr_free(ops, ip, atomic);
return 0;
}
static void snd_seq_iwffff_notify(void *private_data,
struct snd_seq_kinstr *instr,
int what)
{
struct snd_iwffff_ops *ops = private_data;
if (ops->notify)
ops->notify(ops->private_data, instr, what);
}
int snd_seq_iwffff_init(struct snd_iwffff_ops *ops,
void *private_data,
struct snd_seq_kinstr_ops *next)
{
memset(ops, 0, sizeof(*ops));
ops->private_data = private_data;
ops->kops.private_data = ops;
ops->kops.add_len = sizeof(struct iwffff_instrument);
ops->kops.instr_type = SNDRV_SEQ_INSTR_ID_INTERWAVE;
ops->kops.put = snd_seq_iwffff_put;
ops->kops.get = snd_seq_iwffff_get;
ops->kops.get_size = snd_seq_iwffff_get_size;
ops->kops.remove = snd_seq_iwffff_remove;
ops->kops.notify = snd_seq_iwffff_notify;
ops->kops.next = next;
return 0;
}
/*
* Init part
*/
static int __init alsa_ainstr_iw_init(void)
{
return 0;
}
static void __exit alsa_ainstr_iw_exit(void)
{
}
module_init(alsa_ainstr_iw_init)
module_exit(alsa_ainstr_iw_exit)
EXPORT_SYMBOL(snd_seq_iwffff_init);
/*
* Simple (MOD player) - Instrument routines
* Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <sound/driver.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/ainstr_simple.h>
#include <sound/initval.h>
#include <asm/uaccess.h>
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Advanced Linux Sound Architecture Simple Instrument support.");
MODULE_LICENSE("GPL");
static unsigned int snd_seq_simple_size(unsigned int size, unsigned int format)
{
unsigned int result = size;
if (format & SIMPLE_WAVE_16BIT)
result <<= 1;
if (format & SIMPLE_WAVE_STEREO)
result <<= 1;
return result;
}
static void snd_seq_simple_instr_free(struct snd_simple_ops *ops,
struct simple_instrument *ip,
int atomic)
{
if (ops->remove_sample)
ops->remove_sample(ops->private_data, ip, atomic);
}
static int snd_seq_simple_put(void *private_data, struct snd_seq_kinstr *instr,
char __user *instr_data, long len,
int atomic, int cmd)
{
struct snd_simple_ops *ops = private_data;
struct simple_instrument *ip;
struct simple_xinstrument ix;
int err;
gfp_t gfp_mask;
unsigned int real_size;
if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE)
return -EINVAL;
gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
/* copy instrument data */
if (len < (long)sizeof(ix))
return -EINVAL;
if (copy_from_user(&ix, instr_data, sizeof(ix)))
return -EFAULT;
if (ix.stype != SIMPLE_STRU_INSTR)
return -EINVAL;
instr_data += sizeof(ix);
len -= sizeof(ix);
ip = (struct simple_instrument *)KINSTR_DATA(instr);
ip->share_id[0] = le32_to_cpu(ix.share_id[0]);
ip->share_id[1] = le32_to_cpu(ix.share_id[1]);
ip->share_id[2] = le32_to_cpu(ix.share_id[2]);
ip->share_id[3] = le32_to_cpu(ix.share_id[3]);
ip->format = le32_to_cpu(ix.format);
ip->size = le32_to_cpu(ix.size);
ip->start = le32_to_cpu(ix.start);
ip->loop_start = le32_to_cpu(ix.loop_start);
ip->loop_end = le32_to_cpu(ix.loop_end);
ip->loop_repeat = le16_to_cpu(ix.loop_repeat);
ip->effect1 = ix.effect1;
ip->effect1_depth = ix.effect1_depth;
ip->effect2 = ix.effect2;
ip->effect2_depth = ix.effect2_depth;
real_size = snd_seq_simple_size(ip->size, ip->format);
if (len < (long)real_size)
return -EINVAL;
if (ops->put_sample) {
err = ops->put_sample(ops->private_data, ip,
instr_data, real_size, atomic);
if (err < 0)
return err;
}
return 0;
}
static int snd_seq_simple_get(void *private_data, struct snd_seq_kinstr *instr,
char __user *instr_data, long len,
int atomic, int cmd)
{
struct snd_simple_ops *ops = private_data;
struct simple_instrument *ip;
struct simple_xinstrument ix;
int err;
unsigned int real_size;
if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL)
return -EINVAL;
if (len < (long)sizeof(ix))
return -ENOMEM;
memset(&ix, 0, sizeof(ix));
ip = (struct simple_instrument *)KINSTR_DATA(instr);
ix.stype = SIMPLE_STRU_INSTR;
ix.share_id[0] = cpu_to_le32(ip->share_id[0]);
ix.share_id[1] = cpu_to_le32(ip->share_id[1]);
ix.share_id[2] = cpu_to_le32(ip->share_id[2]);
ix.share_id[3] = cpu_to_le32(ip->share_id[3]);
ix.format = cpu_to_le32(ip->format);
ix.size = cpu_to_le32(ip->size);
ix.start = cpu_to_le32(ip->start);
ix.loop_start = cpu_to_le32(ip->loop_start);
ix.loop_end = cpu_to_le32(ip->loop_end);
ix.loop_repeat = cpu_to_le32(ip->loop_repeat);
ix.effect1 = cpu_to_le16(ip->effect1);
ix.effect1_depth = cpu_to_le16(ip->effect1_depth);
ix.effect2 = ip->effect2;
ix.effect2_depth = ip->effect2_depth;
if (copy_to_user(instr_data, &ix, sizeof(ix)))
return -EFAULT;
instr_data += sizeof(ix);
len -= sizeof(ix);
real_size = snd_seq_simple_size(ip->size, ip->format);
if (len < (long)real_size)
return -ENOMEM;
if (ops->get_sample) {
err = ops->get_sample(ops->private_data, ip,
instr_data, real_size, atomic);
if (err < 0)
return err;
}
return 0;
}
static int snd_seq_simple_get_size(void *private_data, struct snd_seq_kinstr *instr,
long *size)
{
struct simple_instrument *ip;
ip = (struct simple_instrument *)KINSTR_DATA(instr);
*size = sizeof(struct simple_xinstrument) + snd_seq_simple_size(ip->size, ip->format);
return 0;
}
static int snd_seq_simple_remove(void *private_data,
struct snd_seq_kinstr *instr,
int atomic)
{
struct snd_simple_ops *ops = private_data;
struct simple_instrument *ip;
ip = (struct simple_instrument *)KINSTR_DATA(instr);
snd_seq_simple_instr_free(ops, ip, atomic);
return 0;
}
static void snd_seq_simple_notify(void *private_data,
struct snd_seq_kinstr *instr,
int what)
{
struct snd_simple_ops *ops = private_data;
if (ops->notify)
ops->notify(ops->private_data, instr, what);
}
int snd_seq_simple_init(struct snd_simple_ops *ops,
void *private_data,
struct snd_seq_kinstr_ops *next)
{
memset(ops, 0, sizeof(*ops));
ops->private_data = private_data;
ops->kops.private_data = ops;
ops->kops.add_len = sizeof(struct simple_instrument);
ops->kops.instr_type = SNDRV_SEQ_INSTR_ID_SIMPLE;
ops->kops.put = snd_seq_simple_put;
ops->kops.get = snd_seq_simple_get;
ops->kops.get_size = snd_seq_simple_get_size;
ops->kops.remove = snd_seq_simple_remove;
ops->kops.notify = snd_seq_simple_notify;
ops->kops.next = next;
return 0;
}
/*
* Init part
*/
static int __init alsa_ainstr_simple_init(void)
{
return 0;
}
static void __exit alsa_ainstr_simple_exit(void)
{
}
module_init(alsa_ainstr_simple_init)
module_exit(alsa_ainstr_simple_exit)
EXPORT_SYMBOL(snd_seq_simple_init);
...@@ -966,8 +966,7 @@ static int check_event_type_and_length(struct snd_seq_event *ev) ...@@ -966,8 +966,7 @@ static int check_event_type_and_length(struct snd_seq_event *ev)
return -EINVAL; return -EINVAL;
break; break;
case SNDRV_SEQ_EVENT_LENGTH_VARUSR: case SNDRV_SEQ_EVENT_LENGTH_VARUSR:
if (! snd_seq_ev_is_instr_type(ev) || if (! snd_seq_ev_is_direct(ev))
! snd_seq_ev_is_direct(ev))
return -EINVAL; return -EINVAL;
break; break;
} }
......
/*
* Generic Instrument routines for ALSA sequencer
* Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <sound/driver.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <sound/core.h>
#include "seq_clientmgr.h"
#include <sound/seq_instr.h>
#include <sound/initval.h>
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer instrument library.");
MODULE_LICENSE("GPL");
static void snd_instr_lock_ops(struct snd_seq_kinstr_list *list)
{
if (!(list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT)) {
spin_lock_irqsave(&list->ops_lock, list->ops_flags);
} else {
mutex_lock(&list->ops_mutex);
}
}
static void snd_instr_unlock_ops(struct snd_seq_kinstr_list *list)
{
if (!(list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT)) {
spin_unlock_irqrestore(&list->ops_lock, list->ops_flags);
} else {
mutex_unlock(&list->ops_mutex);
}
}
static struct snd_seq_kinstr *snd_seq_instr_new(int add_len, int atomic)
{
struct snd_seq_kinstr *instr;
instr = kzalloc(sizeof(struct snd_seq_kinstr) + add_len, atomic ? GFP_ATOMIC : GFP_KERNEL);
if (instr == NULL)
return NULL;
instr->add_len = add_len;
return instr;
}
static int snd_seq_instr_free(struct snd_seq_kinstr *instr, int atomic)
{
int result = 0;
if (instr == NULL)
return -EINVAL;
if (instr->ops && instr->ops->remove)
result = instr->ops->remove(instr->ops->private_data, instr, 1);
if (!result)
kfree(instr);
return result;
}
struct snd_seq_kinstr_list *snd_seq_instr_list_new(void)
{
struct snd_seq_kinstr_list *list;
list = kzalloc(sizeof(struct snd_seq_kinstr_list), GFP_KERNEL);
if (list == NULL)
return NULL;
spin_lock_init(&list->lock);
spin_lock_init(&list->ops_lock);
mutex_init(&list->ops_mutex);
list->owner = -1;
return list;
}
void snd_seq_instr_list_free(struct snd_seq_kinstr_list **list_ptr)
{
struct snd_seq_kinstr_list *list;
struct snd_seq_kinstr *instr;
struct snd_seq_kcluster *cluster;
int idx;
unsigned long flags;
if (list_ptr == NULL)
return;
list = *list_ptr;
*list_ptr = NULL;
if (list == NULL)
return;
for (idx = 0; idx < SNDRV_SEQ_INSTR_HASH_SIZE; idx++) {
while ((instr = list->hash[idx]) != NULL) {
list->hash[idx] = instr->next;
list->count--;
spin_lock_irqsave(&list->lock, flags);
while (instr->use) {
spin_unlock_irqrestore(&list->lock, flags);
schedule_timeout_uninterruptible(1);
spin_lock_irqsave(&list->lock, flags);
}
spin_unlock_irqrestore(&list->lock, flags);
if (snd_seq_instr_free(instr, 0)<0)
snd_printk(KERN_WARNING "instrument free problem\n");
}
while ((cluster = list->chash[idx]) != NULL) {
list->chash[idx] = cluster->next;
list->ccount--;
kfree(cluster);
}
}
kfree(list);
}
static int instr_free_compare(struct snd_seq_kinstr *instr,
struct snd_seq_instr_header *ifree,
unsigned int client)
{
switch (ifree->cmd) {
case SNDRV_SEQ_INSTR_FREE_CMD_ALL:
/* all, except private for other clients */
if ((instr->instr.std & 0xff000000) == 0)
return 0;
if (((instr->instr.std >> 24) & 0xff) == client)
return 0;
return 1;
case SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE:
/* all my private instruments */
if ((instr->instr.std & 0xff000000) == 0)
return 1;
if (((instr->instr.std >> 24) & 0xff) == client)
return 0;
return 1;
case SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER:
/* all my private instruments */
if ((instr->instr.std & 0xff000000) == 0) {
if (instr->instr.cluster == ifree->id.cluster)
return 0;
return 1;
}
if (((instr->instr.std >> 24) & 0xff) == client) {
if (instr->instr.cluster == ifree->id.cluster)
return 0;
}
return 1;
}
return 1;
}
int snd_seq_instr_list_free_cond(struct snd_seq_kinstr_list *list,
struct snd_seq_instr_header *ifree,
int client,
int atomic)
{
struct snd_seq_kinstr *instr, *prev, *next, *flist;
int idx;
unsigned long flags;
snd_instr_lock_ops(list);
for (idx = 0; idx < SNDRV_SEQ_INSTR_HASH_SIZE; idx++) {
spin_lock_irqsave(&list->lock, flags);
instr = list->hash[idx];
prev = flist = NULL;
while (instr) {
while (instr && instr_free_compare(instr, ifree, (unsigned int)client)) {
prev = instr;
instr = instr->next;
}
if (instr == NULL)
continue;
if (instr->ops && instr->ops->notify)
instr->ops->notify(instr->ops->private_data, instr, SNDRV_SEQ_INSTR_NOTIFY_REMOVE);
next = instr->next;
if (prev == NULL) {
list->hash[idx] = next;
} else {
prev->next = next;
}
list->count--;
instr->next = flist;
flist = instr;
instr = next;
}
spin_unlock_irqrestore(&list->lock, flags);
while (flist) {
instr = flist;
flist = instr->next;
while (instr->use) {
schedule_timeout_uninterruptible(1);
barrier();
}
if (snd_seq_instr_free(instr, atomic)<0)
snd_printk(KERN_WARNING "instrument free problem\n");
instr = next;
}
}
snd_instr_unlock_ops(list);
return 0;
}
static int compute_hash_instr_key(struct snd_seq_instr *instr)
{
int result;
result = instr->bank | (instr->prg << 16);
result += result >> 24;
result += result >> 16;
result += result >> 8;
return result & (SNDRV_SEQ_INSTR_HASH_SIZE-1);
}
#if 0
static int compute_hash_cluster_key(snd_seq_instr_cluster_t cluster)
{
int result;
result = cluster;
result += result >> 24;
result += result >> 16;
result += result >> 8;
return result & (SNDRV_SEQ_INSTR_HASH_SIZE-1);
}
#endif
static int compare_instr(struct snd_seq_instr *i1, struct snd_seq_instr *i2, int exact)
{
if (exact) {
if (i1->cluster != i2->cluster ||
i1->bank != i2->bank ||
i1->prg != i2->prg)
return 1;
if ((i1->std & 0xff000000) != (i2->std & 0xff000000))
return 1;
if (!(i1->std & i2->std))
return 1;
return 0;
} else {
unsigned int client_check;
if (i2->cluster && i1->cluster != i2->cluster)
return 1;
client_check = i2->std & 0xff000000;
if (client_check) {
if ((i1->std & 0xff000000) != client_check)
return 1;
} else {
if ((i1->std & i2->std) != i2->std)
return 1;
}
return i1->bank != i2->bank || i1->prg != i2->prg;
}
}
struct snd_seq_kinstr *snd_seq_instr_find(struct snd_seq_kinstr_list *list,
struct snd_seq_instr *instr,
int exact,
int follow_alias)
{
unsigned long flags;
int depth = 0;
struct snd_seq_kinstr *result;
if (list == NULL || instr == NULL)
return NULL;
spin_lock_irqsave(&list->lock, flags);
__again:
result = list->hash[compute_hash_instr_key(instr)];
while (result) {
if (!compare_instr(&result->instr, instr, exact)) {
if (follow_alias && (result->type == SNDRV_SEQ_INSTR_ATYPE_ALIAS)) {
instr = (struct snd_seq_instr *)KINSTR_DATA(result);
if (++depth > 10)
goto __not_found;
goto __again;
}
result->use++;
spin_unlock_irqrestore(&list->lock, flags);
return result;
}
result = result->next;
}
__not_found:
spin_unlock_irqrestore(&list->lock, flags);
return NULL;
}
void snd_seq_instr_free_use(struct snd_seq_kinstr_list *list,
struct snd_seq_kinstr *instr)
{
unsigned long flags;
if (list == NULL || instr == NULL)
return;
spin_lock_irqsave(&list->lock, flags);
if (instr->use <= 0) {
snd_printk(KERN_ERR "free_use: fatal!!! use = %i, name = '%s'\n", instr->use, instr->name);
} else {
instr->use--;
}
spin_unlock_irqrestore(&list->lock, flags);
}
static struct snd_seq_kinstr_ops *instr_ops(struct snd_seq_kinstr_ops *ops,
char *instr_type)
{
while (ops) {
if (!strcmp(ops->instr_type, instr_type))
return ops;
ops = ops->next;
}
return NULL;
}
static int instr_result(struct snd_seq_event *ev,
int type, int result,
int atomic)
{
struct snd_seq_event sev;
memset(&sev, 0, sizeof(sev));
sev.type = SNDRV_SEQ_EVENT_RESULT;
sev.flags = SNDRV_SEQ_TIME_STAMP_REAL | SNDRV_SEQ_EVENT_LENGTH_FIXED |
SNDRV_SEQ_PRIORITY_NORMAL;
sev.source = ev->dest;
sev.dest = ev->source;
sev.data.result.event = type;
sev.data.result.result = result;
#if 0
printk("instr result - type = %i, result = %i, queue = %i, source.client:port = %i:%i, dest.client:port = %i:%i\n",
type, result,
sev.queue,
sev.source.client, sev.source.port,
sev.dest.client, sev.dest.port);
#endif
return snd_seq_kernel_client_dispatch(sev.source.client, &sev, atomic, 0);
}
static int instr_begin(struct snd_seq_kinstr_ops *ops,
struct snd_seq_kinstr_list *list,
struct snd_seq_event *ev,
int atomic, int hop)
{
unsigned long flags;
spin_lock_irqsave(&list->lock, flags);
if (list->owner >= 0 && list->owner != ev->source.client) {
spin_unlock_irqrestore(&list->lock, flags);
return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_BEGIN, -EBUSY, atomic);
}
list->owner = ev->source.client;
spin_unlock_irqrestore(&list->lock, flags);
return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_BEGIN, 0, atomic);
}
static int instr_end(struct snd_seq_kinstr_ops *ops,
struct snd_seq_kinstr_list *list,
struct snd_seq_event *ev,
int atomic, int hop)
{
unsigned long flags;
/* TODO: timeout handling */
spin_lock_irqsave(&list->lock, flags);
if (list->owner == ev->source.client) {
list->owner = -1;
spin_unlock_irqrestore(&list->lock, flags);
return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_END, 0, atomic);
}
spin_unlock_irqrestore(&list->lock, flags);
return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_END, -EINVAL, atomic);
}
static int instr_info(struct snd_seq_kinstr_ops *ops,
struct snd_seq_kinstr_list *list,
struct snd_seq_event *ev,
int atomic, int hop)
{
return -ENXIO;
}
static int instr_format_info(struct snd_seq_kinstr_ops *ops,
struct snd_seq_kinstr_list *list,
struct snd_seq_event *ev,
int atomic, int hop)
{
return -ENXIO;
}
static int instr_reset(struct snd_seq_kinstr_ops *ops,
struct snd_seq_kinstr_list *list,
struct snd_seq_event *ev,
int atomic, int hop)
{
return -ENXIO;
}
static int instr_status(struct snd_seq_kinstr_ops *ops,
struct snd_seq_kinstr_list *list,
struct snd_seq_event *ev,
int atomic, int hop)
{
return -ENXIO;
}
static int instr_put(struct snd_seq_kinstr_ops *ops,
struct snd_seq_kinstr_list *list,
struct snd_seq_event *ev,
int atomic, int hop)
{
unsigned long flags;
struct snd_seq_instr_header put;
struct snd_seq_kinstr *instr;
int result = -EINVAL, len, key;
if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARUSR)
goto __return;
if (ev->data.ext.len < sizeof(struct snd_seq_instr_header))
goto __return;
if (copy_from_user(&put, (void __user *)ev->data.ext.ptr,
sizeof(struct snd_seq_instr_header))) {
result = -EFAULT;
goto __return;
}
snd_instr_lock_ops(list);
if (put.id.instr.std & 0xff000000) { /* private instrument */
put.id.instr.std &= 0x00ffffff;
put.id.instr.std |= (unsigned int)ev->source.client << 24;
}
if ((instr = snd_seq_instr_find(list, &put.id.instr, 1, 0))) {
snd_seq_instr_free_use(list, instr);
snd_instr_unlock_ops(list);
result = -EBUSY;
goto __return;
}
ops = instr_ops(ops, put.data.data.format);
if (ops == NULL) {
snd_instr_unlock_ops(list);
goto __return;
}
len = ops->add_len;
if (put.data.type == SNDRV_SEQ_INSTR_ATYPE_ALIAS)
len = sizeof(struct snd_seq_instr);
instr = snd_seq_instr_new(len, atomic);
if (instr == NULL) {
snd_instr_unlock_ops(list);
result = -ENOMEM;
goto __return;
}
instr->ops = ops;
instr->instr = put.id.instr;
strlcpy(instr->name, put.data.name, sizeof(instr->name));
instr->type = put.data.type;
if (instr->type == SNDRV_SEQ_INSTR_ATYPE_DATA) {
result = ops->put(ops->private_data,
instr,
(void __user *)ev->data.ext.ptr + sizeof(struct snd_seq_instr_header),
ev->data.ext.len - sizeof(struct snd_seq_instr_header),
atomic,
put.cmd);
if (result < 0) {
snd_seq_instr_free(instr, atomic);
snd_instr_unlock_ops(list);
goto __return;
}
}
key = compute_hash_instr_key(&instr->instr);
spin_lock_irqsave(&list->lock, flags);
instr->next = list->hash[key];
list->hash[key] = instr;
list->count++;
spin_unlock_irqrestore(&list->lock, flags);
snd_instr_unlock_ops(list);
result = 0;
__return:
instr_result(ev, SNDRV_SEQ_EVENT_INSTR_PUT, result, atomic);
return result;
}
static int instr_get(struct snd_seq_kinstr_ops *ops,
struct snd_seq_kinstr_list *list,
struct snd_seq_event *ev,
int atomic, int hop)
{
return -ENXIO;
}
static int instr_free(struct snd_seq_kinstr_ops *ops,
struct snd_seq_kinstr_list *list,
struct snd_seq_event *ev,
int atomic, int hop)
{
struct snd_seq_instr_header ifree;
struct snd_seq_kinstr *instr, *prev;
int result = -EINVAL;
unsigned long flags;
unsigned int hash;
if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARUSR)
goto __return;
if (ev->data.ext.len < sizeof(struct snd_seq_instr_header))
goto __return;
if (copy_from_user(&ifree, (void __user *)ev->data.ext.ptr,
sizeof(struct snd_seq_instr_header))) {
result = -EFAULT;
goto __return;
}
if (ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_ALL ||
ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE ||
ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER) {
result = snd_seq_instr_list_free_cond(list, &ifree, ev->dest.client, atomic);
goto __return;
}
if (ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_SINGLE) {
if (ifree.id.instr.std & 0xff000000) {
ifree.id.instr.std &= 0x00ffffff;
ifree.id.instr.std |= (unsigned int)ev->source.client << 24;
}
hash = compute_hash_instr_key(&ifree.id.instr);
snd_instr_lock_ops(list);
spin_lock_irqsave(&list->lock, flags);
instr = list->hash[hash];
prev = NULL;
while (instr) {
if (!compare_instr(&instr->instr, &ifree.id.instr, 1))
goto __free_single;
prev = instr;
instr = instr->next;
}
result = -ENOENT;
spin_unlock_irqrestore(&list->lock, flags);
snd_instr_unlock_ops(list);
goto __return;
__free_single:
if (prev) {
prev->next = instr->next;
} else {
list->hash[hash] = instr->next;
}
if (instr->ops && instr->ops->notify)
instr->ops->notify(instr->ops->private_data, instr,
SNDRV_SEQ_INSTR_NOTIFY_REMOVE);
while (instr->use) {
spin_unlock_irqrestore(&list->lock, flags);
schedule_timeout_uninterruptible(1);
spin_lock_irqsave(&list->lock, flags);
}
spin_unlock_irqrestore(&list->lock, flags);
result = snd_seq_instr_free(instr, atomic);
snd_instr_unlock_ops(list);
goto __return;
}
__return:
instr_result(ev, SNDRV_SEQ_EVENT_INSTR_FREE, result, atomic);
return result;
}
static int instr_list(struct snd_seq_kinstr_ops *ops,
struct snd_seq_kinstr_list *list,
struct snd_seq_event *ev,
int atomic, int hop)
{
return -ENXIO;
}
static int instr_cluster(struct snd_seq_kinstr_ops *ops,
struct snd_seq_kinstr_list *list,
struct snd_seq_event *ev,
int atomic, int hop)
{
return -ENXIO;
}
int snd_seq_instr_event(struct snd_seq_kinstr_ops *ops,
struct snd_seq_kinstr_list *list,
struct snd_seq_event *ev,
int client,
int atomic,
int hop)
{
int direct = 0;
snd_assert(ops != NULL && list != NULL && ev != NULL, return -EINVAL);
if (snd_seq_ev_is_direct(ev)) {
direct = 1;
switch (ev->type) {
case SNDRV_SEQ_EVENT_INSTR_BEGIN:
return instr_begin(ops, list, ev, atomic, hop);
case SNDRV_SEQ_EVENT_INSTR_END:
return instr_end(ops, list, ev, atomic, hop);
}
}
if ((list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT) && !direct)
return -EINVAL;
switch (ev->type) {
case SNDRV_SEQ_EVENT_INSTR_INFO:
return instr_info(ops, list, ev, atomic, hop);
case SNDRV_SEQ_EVENT_INSTR_FINFO:
return instr_format_info(ops, list, ev, atomic, hop);
case SNDRV_SEQ_EVENT_INSTR_RESET:
return instr_reset(ops, list, ev, atomic, hop);
case SNDRV_SEQ_EVENT_INSTR_STATUS:
return instr_status(ops, list, ev, atomic, hop);
case SNDRV_SEQ_EVENT_INSTR_PUT:
return instr_put(ops, list, ev, atomic, hop);
case SNDRV_SEQ_EVENT_INSTR_GET:
return instr_get(ops, list, ev, atomic, hop);
case SNDRV_SEQ_EVENT_INSTR_FREE:
return instr_free(ops, list, ev, atomic, hop);
case SNDRV_SEQ_EVENT_INSTR_LIST:
return instr_list(ops, list, ev, atomic, hop);
case SNDRV_SEQ_EVENT_INSTR_CLUSTER:
return instr_cluster(ops, list, ev, atomic, hop);
}
return -EINVAL;
}
/*
* Init part
*/
static int __init alsa_seq_instr_init(void)
{
return 0;
}
static void __exit alsa_seq_instr_exit(void)
{
}
module_init(alsa_seq_instr_init)
module_exit(alsa_seq_instr_exit)
EXPORT_SYMBOL(snd_seq_instr_list_new);
EXPORT_SYMBOL(snd_seq_instr_list_free);
EXPORT_SYMBOL(snd_seq_instr_list_free_cond);
EXPORT_SYMBOL(snd_seq_instr_find);
EXPORT_SYMBOL(snd_seq_instr_free_use);
EXPORT_SYMBOL(snd_seq_instr_event);
...@@ -229,13 +229,6 @@ snd_midi_process_event(struct snd_midi_op *ops, ...@@ -229,13 +229,6 @@ snd_midi_process_event(struct snd_midi_op *ops,
case SNDRV_SEQ_EVENT_PORT_START: case SNDRV_SEQ_EVENT_PORT_START:
case SNDRV_SEQ_EVENT_PORT_EXIT: case SNDRV_SEQ_EVENT_PORT_EXIT:
case SNDRV_SEQ_EVENT_PORT_CHANGE: case SNDRV_SEQ_EVENT_PORT_CHANGE:
case SNDRV_SEQ_EVENT_SAMPLE:
case SNDRV_SEQ_EVENT_SAMPLE_START:
case SNDRV_SEQ_EVENT_SAMPLE_STOP:
case SNDRV_SEQ_EVENT_SAMPLE_FREQ:
case SNDRV_SEQ_EVENT_SAMPLE_VOLUME:
case SNDRV_SEQ_EVENT_SAMPLE_LOOP:
case SNDRV_SEQ_EVENT_SAMPLE_POSITION:
case SNDRV_SEQ_EVENT_ECHO: case SNDRV_SEQ_EVENT_ECHO:
not_yet: not_yet:
default: default:
......
...@@ -9,7 +9,6 @@ snd-gus-lib-objs := gus_main.o \ ...@@ -9,7 +9,6 @@ snd-gus-lib-objs := gus_main.o \
gus_pcm.o gus_mixer.o \ gus_pcm.o gus_mixer.o \
gus_uart.o \ gus_uart.o \
gus_reset.o gus_reset.o
snd-gus-synth-objs := gus_synth.o gus_sample.o gus_simple.o gus_instr.o
snd-gusclassic-objs := gusclassic.o snd-gusclassic-objs := gusclassic.o
snd-gusextreme-objs := gusextreme.o snd-gusextreme-objs := gusextreme.o
...@@ -17,20 +16,9 @@ snd-gusmax-objs := gusmax.o ...@@ -17,20 +16,9 @@ snd-gusmax-objs := gusmax.o
snd-interwave-objs := interwave.o snd-interwave-objs := interwave.o
snd-interwave-stb-objs := interwave-stb.o snd-interwave-stb-objs := interwave-stb.o
#
# this function returns:
# "m" - CONFIG_SND_SEQUENCER is m
# <empty string> - CONFIG_SND_SEQUENCER is undefined
# otherwise parameter #1 value
#
sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
# Toplevel Module Dependency # Toplevel Module Dependency
obj-$(CONFIG_SND_GUSCLASSIC) += snd-gusclassic.o snd-gus-lib.o obj-$(CONFIG_SND_GUSCLASSIC) += snd-gusclassic.o snd-gus-lib.o
obj-$(CONFIG_SND_GUSMAX) += snd-gusmax.o snd-gus-lib.o obj-$(CONFIG_SND_GUSMAX) += snd-gusmax.o snd-gus-lib.o
obj-$(CONFIG_SND_GUSEXTREME) += snd-gusextreme.o snd-gus-lib.o obj-$(CONFIG_SND_GUSEXTREME) += snd-gusextreme.o snd-gus-lib.o
obj-$(CONFIG_SND_INTERWAVE) += snd-interwave.o snd-gus-lib.o obj-$(CONFIG_SND_INTERWAVE) += snd-interwave.o snd-gus-lib.o
obj-$(CONFIG_SND_INTERWAVE_STB) += snd-interwave-stb.o snd-gus-lib.o obj-$(CONFIG_SND_INTERWAVE_STB) += snd-interwave-stb.o snd-gus-lib.o
obj-$(call sequencer,$(CONFIG_SND_GUS_SYNTH)) += snd-gus-synth.o
obj-m := $(sort $(obj-m))
...@@ -104,12 +104,6 @@ static int snd_gus_free(struct snd_gus_card *gus) ...@@ -104,12 +104,6 @@ static int snd_gus_free(struct snd_gus_card *gus)
{ {
if (gus->gf1.res_port2 == NULL) if (gus->gf1.res_port2 == NULL)
goto __hw_end; goto __hw_end;
#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
if (gus->seq_dev) {
snd_device_free(gus->card, gus->seq_dev);
gus->seq_dev = NULL;
}
#endif
snd_gf1_stop(gus); snd_gf1_stop(gus);
snd_gus_init_dma_irq(gus, 0); snd_gus_init_dma_irq(gus, 0);
__hw_end: __hw_end:
...@@ -408,14 +402,6 @@ static int snd_gus_check_version(struct snd_gus_card * gus) ...@@ -408,14 +402,6 @@ static int snd_gus_check_version(struct snd_gus_card * gus)
return 0; return 0;
} }
#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
static void snd_gus_seq_dev_free(struct snd_seq_device *seq_dev)
{
struct snd_gus_card *gus = seq_dev->private_data;
gus->seq_dev = NULL;
}
#endif
int snd_gus_initialize(struct snd_gus_card *gus) int snd_gus_initialize(struct snd_gus_card *gus)
{ {
int err; int err;
...@@ -430,15 +416,6 @@ int snd_gus_initialize(struct snd_gus_card *gus) ...@@ -430,15 +416,6 @@ int snd_gus_initialize(struct snd_gus_card *gus)
} }
if ((err = snd_gus_init_dma_irq(gus, 1)) < 0) if ((err = snd_gus_init_dma_irq(gus, 1)) < 0)
return err; return err;
#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
if (snd_seq_device_new(gus->card, 1, SNDRV_SEQ_DEV_ID_GUS,
sizeof(struct snd_gus_card *), &gus->seq_dev) >= 0) {
strcpy(gus->seq_dev->name, "GUS");
*(struct snd_gus_card **)SNDRV_SEQ_DEVICE_ARGPTR(gus->seq_dev) = gus;
gus->seq_dev->private_data = gus;
gus->seq_dev->private_free = snd_gus_seq_dev_free;
}
#endif
snd_gf1_start(gus); snd_gf1_start(gus);
gus->initialized = 1; gus->initialized = 1;
return 0; return 0;
......
/*
* Routines for Gravis UltraSound soundcards - Sample support
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <sound/driver.h>
#include <linux/time.h>
#include <sound/core.h>
#include <sound/gus.h>
/*
*
*/
static void select_instrument(struct snd_gus_card * gus, struct snd_gus_voice * v)
{
struct snd_seq_kinstr *instr;
#if 0
printk("select instrument: cluster = %li, std = 0x%x, bank = %i, prg = %i\n",
v->instr.cluster,
v->instr.std,
v->instr.bank,
v->instr.prg);
#endif
instr = snd_seq_instr_find(gus->gf1.ilist, &v->instr, 0, 1);
if (instr != NULL) {
if (instr->ops) {
if (!strcmp(instr->ops->instr_type, SNDRV_SEQ_INSTR_ID_SIMPLE))
snd_gf1_simple_init(v);
}
snd_seq_instr_free_use(gus->gf1.ilist, instr);
}
}
/*
*
*/
static void event_sample(struct snd_seq_event *ev, struct snd_gus_port *p,
struct snd_gus_voice *v)
{
if (v->sample_ops && v->sample_ops->sample_stop)
v->sample_ops->sample_stop(p->gus, v, SAMPLE_STOP_IMMEDIATELY);
v->instr.std = ev->data.sample.param.sample.std;
if (v->instr.std & 0xff000000) { /* private instrument */
v->instr.std &= 0x00ffffff;
v->instr.std |= (unsigned int)ev->source.client << 24;
}
v->instr.bank = ev->data.sample.param.sample.bank;
v->instr.prg = ev->data.sample.param.sample.prg;
select_instrument(p->gus, v);
}
static void event_cluster(struct snd_seq_event *ev, struct snd_gus_port *p,
struct snd_gus_voice *v)
{
if (v->sample_ops && v->sample_ops->sample_stop)
v->sample_ops->sample_stop(p->gus, v, SAMPLE_STOP_IMMEDIATELY);
v->instr.cluster = ev->data.sample.param.cluster.cluster;
select_instrument(p->gus, v);
}
static void event_start(struct snd_seq_event *ev, struct snd_gus_port *p,
struct snd_gus_voice *v)
{
if (v->sample_ops && v->sample_ops->sample_start)
v->sample_ops->sample_start(p->gus, v, ev->data.sample.param.position);
}
static void event_stop(struct snd_seq_event *ev, struct snd_gus_port *p,
struct snd_gus_voice *v)
{
if (v->sample_ops && v->sample_ops->sample_stop)
v->sample_ops->sample_stop(p->gus, v, ev->data.sample.param.stop_mode);
}
static void event_freq(struct snd_seq_event *ev, struct snd_gus_port *p,
struct snd_gus_voice *v)
{
if (v->sample_ops && v->sample_ops->sample_freq)
v->sample_ops->sample_freq(p->gus, v, ev->data.sample.param.frequency);
}
static void event_volume(struct snd_seq_event *ev, struct snd_gus_port *p,
struct snd_gus_voice *v)
{
if (v->sample_ops && v->sample_ops->sample_volume)
v->sample_ops->sample_volume(p->gus, v, &ev->data.sample.param.volume);
}
static void event_loop(struct snd_seq_event *ev, struct snd_gus_port *p,
struct snd_gus_voice *v)
{
if (v->sample_ops && v->sample_ops->sample_loop)
v->sample_ops->sample_loop(p->gus, v, &ev->data.sample.param.loop);
}
static void event_position(struct snd_seq_event *ev, struct snd_gus_port *p,
struct snd_gus_voice *v)
{
if (v->sample_ops && v->sample_ops->sample_pos)
v->sample_ops->sample_pos(p->gus, v, ev->data.sample.param.position);
}
static void event_private1(struct snd_seq_event *ev, struct snd_gus_port *p,
struct snd_gus_voice *v)
{
if (v->sample_ops && v->sample_ops->sample_private1)
v->sample_ops->sample_private1(p->gus, v, (unsigned char *)&ev->data.sample.param.raw8);
}
typedef void (gus_sample_event_handler_t)(struct snd_seq_event *ev,
struct snd_gus_port *p,
struct snd_gus_voice *v);
static gus_sample_event_handler_t *gus_sample_event_handlers[9] = {
event_sample,
event_cluster,
event_start,
event_stop,
event_freq,
event_volume,
event_loop,
event_position,
event_private1
};
void snd_gus_sample_event(struct snd_seq_event *ev, struct snd_gus_port *p)
{
int idx, voice;
struct snd_gus_card *gus = p->gus;
struct snd_gus_voice *v;
unsigned long flags;
idx = ev->type - SNDRV_SEQ_EVENT_SAMPLE;
if (idx < 0 || idx > 8)
return;
for (voice = 0; voice < 32; voice++) {
v = &gus->gf1.voices[voice];
if (v->use && v->client == ev->source.client &&
v->port == ev->source.port &&
v->index == ev->data.sample.channel) {
spin_lock_irqsave(&gus->event_lock, flags);
gus_sample_event_handlers[idx](ev, p, v);
spin_unlock_irqrestore(&gus->event_lock, flags);
return;
}
}
}
/*
* Routines for Gravis UltraSound soundcards - Simple instrument handlers
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <sound/driver.h>
#include <linux/time.h>
#include <sound/core.h>
#include <sound/gus.h>
#include "gus_tables.h"
/*
*
*/
static void interrupt_wave(struct snd_gus_card *gus, struct snd_gus_voice *voice);
static void interrupt_volume(struct snd_gus_card *gus, struct snd_gus_voice *voice);
static void interrupt_effect(struct snd_gus_card *gus, struct snd_gus_voice *voice);
static void sample_start(struct snd_gus_card *gus, struct snd_gus_voice *voice, snd_seq_position_t position);
static void sample_stop(struct snd_gus_card *gus, struct snd_gus_voice *voice, int mode);
static void sample_freq(struct snd_gus_card *gus, struct snd_gus_voice *voice, snd_seq_frequency_t freq);
static void sample_volume(struct snd_gus_card *card, struct snd_gus_voice *voice, struct snd_seq_ev_volume *volume);
static void sample_loop(struct snd_gus_card *card, struct snd_gus_voice *voice, struct snd_seq_ev_loop *loop);
static void sample_pos(struct snd_gus_card *card, struct snd_gus_voice *voice, snd_seq_position_t position);
static void sample_private1(struct snd_gus_card *card, struct snd_gus_voice *voice, unsigned char *data);
static struct snd_gus_sample_ops sample_ops = {
sample_start,
sample_stop,
sample_freq,
sample_volume,
sample_loop,
sample_pos,
sample_private1
};
#if 0
static void note_stop(struct snd_gus_card *gus, struct snd_gus_voice *voice, int wait);
static void note_wait(struct snd_gus_card *gus, struct snd_gus_voice *voice);
static void note_off(struct snd_gus_card *gus, struct snd_gus_voice *voice);
static void note_volume(struct snd_gus_card *card, struct snd_gus_voice *voice);
static void note_pitchbend(struct snd_gus_card *card, struct snd_gus_voice *voice);
static void note_vibrato(struct snd_gus_card *card, struct snd_gus_voice *voice);
static void note_tremolo(struct snd_gus_card *card, struct snd_gus_voice *voice);
static struct snd_gus_note_handlers note_commands = {
note_stop,
note_wait,
note_off,
note_volume,
note_pitchbend,
note_vibrato,
note_tremolo
};
static void chn_trigger_down(struct snd_gus_card *card, ultra_channel_t *channel, ultra_instrument_t *instrument, unsigned char note, unsigned char velocity, unsigned char priority );
static void chn_trigger_up( ultra_card_t *card, ultra_note_t *note );
static void chn_control( ultra_card_t *card, ultra_channel_t *channel, unsigned short p1, unsigned short p2 );
static struct ULTRA_STRU_INSTRUMENT_CHANNEL_COMMANDS channel_commands = {
chn_trigger_down,
chn_trigger_up,
chn_control
};
#endif
static void do_volume_envelope(struct snd_gus_card *card, struct snd_gus_voice *voice);
static void do_pan_envelope(struct snd_gus_card *card, struct snd_gus_voice *voice);
/*
*
*/
static void interrupt_wave(struct snd_gus_card *gus, struct snd_gus_voice *voice)
{
spin_lock(&gus->event_lock);
snd_gf1_stop_voice(gus, voice->number);
spin_lock(&gus->reg_lock);
snd_gf1_select_voice(gus, voice->number);
snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, 0);
spin_unlock(&gus->reg_lock);
voice->flags &= ~SNDRV_GF1_VFLG_RUNNING;
spin_unlock(&gus->event_lock);
}
static void interrupt_volume(struct snd_gus_card *gus, struct snd_gus_voice *voice)
{
spin_lock(&gus->event_lock);
if (voice->flags & SNDRV_GF1_VFLG_RUNNING)
do_volume_envelope(gus, voice);
else
snd_gf1_stop_voice(gus, voice->number);
spin_unlock(&gus->event_lock);
}
static void interrupt_effect(struct snd_gus_card *gus, struct snd_gus_voice *voice)
{
spin_lock(&gus->event_lock);
if ((voice->flags & (SNDRV_GF1_VFLG_RUNNING|SNDRV_GF1_VFLG_EFFECT_TIMER1)) ==
(SNDRV_GF1_VFLG_RUNNING|SNDRV_GF1_VFLG_EFFECT_TIMER1))
do_pan_envelope(gus, voice);
spin_unlock(&gus->event_lock);
}
/*
*
*/
static void do_volume_envelope(struct snd_gus_card *gus, struct snd_gus_voice *voice)
{
unsigned short next, rate, old_volume;
int program_next_ramp;
unsigned long flags;
if (!gus->gf1.volume_ramp) {
spin_lock_irqsave(&gus->reg_lock, flags);
snd_gf1_select_voice(gus, voice->number);
snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, voice->gf1_volume);
/* printk("gf1_volume = 0x%x\n", voice->gf1_volume); */
spin_unlock_irqrestore(&gus->reg_lock, flags);
return;
}
program_next_ramp = 0;
rate = next = 0;
while (1) {
program_next_ramp = 0;
rate = next = 0;
switch (voice->venv_state) {
case VENV_BEFORE:
voice->venv_state = VENV_ATTACK;
voice->venv_value_next = 0;
spin_lock_irqsave(&gus->reg_lock, flags);
snd_gf1_select_voice(gus, voice->number);
snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, SNDRV_GF1_MIN_VOLUME);
spin_unlock_irqrestore(&gus->reg_lock, flags);
break;
case VENV_ATTACK:
voice->venv_state = VENV_SUSTAIN;
program_next_ramp++;
next = 255;
rate = gus->gf1.volume_ramp;
break;
case VENV_SUSTAIN:
voice->venv_state = VENV_RELEASE;
spin_lock_irqsave(&gus->reg_lock, flags);
snd_gf1_select_voice(gus, voice->number);
snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, ((int)voice->gf1_volume * (int)voice->venv_value_next) / 255);
spin_unlock_irqrestore(&gus->reg_lock, flags);
return;
case VENV_RELEASE:
voice->venv_state = VENV_DONE;
program_next_ramp++;
next = 0;
rate = gus->gf1.volume_ramp;
break;
case VENV_DONE:
snd_gf1_stop_voice(gus, voice->number);
voice->flags &= ~SNDRV_GF1_VFLG_RUNNING;
return;
case VENV_VOLUME:
program_next_ramp++;
next = voice->venv_value_next;
rate = gus->gf1.volume_ramp;
voice->venv_state = voice->venv_state_prev;
break;
}
voice->venv_value_next = next;
if (!program_next_ramp)
continue;
spin_lock_irqsave(&gus->reg_lock, flags);
snd_gf1_select_voice(gus, voice->number);
snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
old_volume = snd_gf1_read16(gus, SNDRV_GF1_VW_VOLUME) >> 8;
if (!rate) {
spin_unlock_irqrestore(&gus->reg_lock, flags);
continue;
}
next = (((int)voice->gf1_volume * (int)next) / 255) >> 8;
if (old_volume < SNDRV_GF1_MIN_OFFSET)
old_volume = SNDRV_GF1_MIN_OFFSET;
if (next < SNDRV_GF1_MIN_OFFSET)
next = SNDRV_GF1_MIN_OFFSET;
if (next > SNDRV_GF1_MAX_OFFSET)
next = SNDRV_GF1_MAX_OFFSET;
if (old_volume == next) {
spin_unlock_irqrestore(&gus->reg_lock, flags);
continue;
}
voice->volume_control &= ~0xc3;
voice->volume_control |= 0x20;
if (old_volume > next) {
snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, next);
snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, old_volume);
voice->volume_control |= 0x40;
} else {
snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, old_volume);
snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, next);
}
snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, rate);
snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, voice->volume_control);
if (!gus->gf1.enh_mode) {
snd_gf1_delay(gus);
snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, voice->volume_control);
}
spin_unlock_irqrestore(&gus->reg_lock, flags);
return;
}
}
static void do_pan_envelope(struct snd_gus_card *gus, struct snd_gus_voice *voice)
{
unsigned long flags;
unsigned char old_pan;
#if 0
snd_gf1_select_voice(gus, voice->number);
printk(" -%i- do_pan_envelope - flags = 0x%x (0x%x -> 0x%x)\n",
voice->number,
voice->flags,
voice->gf1_pan,
snd_gf1_i_read8(gus, SNDRV_GF1_VB_PAN) & 0x0f);
#endif
if (gus->gf1.enh_mode) {
voice->flags &= ~(SNDRV_GF1_VFLG_EFFECT_TIMER1|SNDRV_GF1_VFLG_PAN);
return;
}
if (!gus->gf1.smooth_pan) {
spin_lock_irqsave(&gus->reg_lock, flags);
snd_gf1_select_voice(gus, voice->number);
snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, voice->gf1_pan);
spin_unlock_irqrestore(&gus->reg_lock, flags);
return;
}
if (!(voice->flags & SNDRV_GF1_VFLG_PAN)) /* before */
voice->flags |= SNDRV_GF1_VFLG_EFFECT_TIMER1|SNDRV_GF1_VFLG_PAN;
spin_lock_irqsave(&gus->reg_lock, flags);
snd_gf1_select_voice(gus, voice->number);
old_pan = snd_gf1_read8(gus, SNDRV_GF1_VB_PAN) & 0x0f;
if (old_pan > voice->gf1_pan )
old_pan--;
if (old_pan < voice->gf1_pan)
old_pan++;
snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, old_pan);
spin_unlock_irqrestore(&gus->reg_lock, flags);
if (old_pan == voice->gf1_pan) /* the goal was reached */
voice->flags &= ~(SNDRV_GF1_VFLG_EFFECT_TIMER1|SNDRV_GF1_VFLG_PAN);
#if 0
snd_gf1_select_voice(gus, voice->number);
printk(" -%i- (1) do_pan_envelope - flags = 0x%x (0x%x -> 0x%x)\n",
voice->number,
voice->flags,
voice->gf1_pan,
snd_gf1_i_read8(gus, GF1_VB_PAN) & 0x0f);
#endif
}
static void set_enhanced_pan(struct snd_gus_card *gus, struct snd_gus_voice *voice, unsigned short pan)
{
unsigned long flags;
unsigned short vlo, vro;
vlo = SNDRV_GF1_ATTEN((SNDRV_GF1_ATTEN_TABLE_SIZE-1) - pan);
vro = SNDRV_GF1_ATTEN(pan);
if (pan != SNDRV_GF1_ATTEN_TABLE_SIZE - 1 && pan != 0) {
vlo >>= 1;
vro >>= 1;
}
vlo <<= 4;
vro <<= 4;
#if 0
printk("vlo = 0x%x (0x%x), vro = 0x%x (0x%x)\n",
vlo, snd_gf1_i_read16(gus, GF1_VW_OFFSET_LEFT),
vro, snd_gf1_i_read16(gus, GF1_VW_OFFSET_RIGHT));
#endif
spin_lock_irqsave(&gus->reg_lock, flags);
snd_gf1_select_voice(gus, voice->number);
snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_LEFT_FINAL, vlo);
snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_RIGHT_FINAL, vro);
spin_unlock_irqrestore(&gus->reg_lock, flags);
voice->vlo = vlo;
voice->vro = vro;
}
/*
*
*/
static void sample_start(struct snd_gus_card *gus, struct snd_gus_voice *voice, snd_seq_position_t position)
{
unsigned long flags;
unsigned int begin, addr, addr_end, addr_start;
int w_16;
struct simple_instrument *simple;
struct snd_seq_kinstr *instr;
instr = snd_seq_instr_find(gus->gf1.ilist, &voice->instr, 0, 1);
if (instr == NULL)
return;
voice->instr = instr->instr; /* copy ID to speedup aliases */
simple = KINSTR_DATA(instr);
begin = simple->address.memory << 4;
w_16 = simple->format & SIMPLE_WAVE_16BIT ? 0x04 : 0;
addr_start = simple->loop_start;
if (simple->format & SIMPLE_WAVE_LOOP) {
addr_end = simple->loop_end;
} else {
addr_end = (simple->size << 4) - (w_16 ? 40 : 24);
}
if (simple->format & SIMPLE_WAVE_BACKWARD) {
addr = simple->loop_end;
if (position < simple->loop_end)
addr -= position;
} else {
addr = position;
}
voice->control = 0x00;
voice->mode = 0x20; /* enable offset registers */
if (simple->format & SIMPLE_WAVE_16BIT)
voice->control |= 0x04;
if (simple->format & SIMPLE_WAVE_BACKWARD)
voice->control |= 0x40;
if (simple->format & SIMPLE_WAVE_LOOP) {
voice->control |= 0x08;
} else {
voice->control |= 0x20;
}
if (simple->format & SIMPLE_WAVE_BIDIR)
voice->control |= 0x10;
if (simple->format & SIMPLE_WAVE_ULAW)
voice->mode |= 0x40;
if (w_16) {
addr = ((addr << 1) & ~0x1f) | (addr & 0x0f);
addr_start = ((addr_start << 1) & ~0x1f) | (addr_start & 0x0f);
addr_end = ((addr_end << 1) & ~0x1f) | (addr_end & 0x0f);
}
addr += begin;
addr_start += begin;
addr_end += begin;
snd_gf1_stop_voice(gus, voice->number);
spin_lock_irqsave(&gus->reg_lock, flags);
snd_gf1_select_voice(gus, voice->number);
snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, voice->fc_register + voice->fc_lfo);
voice->venv_state = VENV_BEFORE;
voice->volume_control = 0x03;
snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, addr_start, w_16);
snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, addr_end, w_16);
snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, addr, w_16);
if (!gus->gf1.enh_mode) {
snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, voice->gf1_pan);
} else {
snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_LEFT, voice->vlo);
snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_LEFT_FINAL, voice->vlo);
snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_RIGHT, voice->vro);
snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_RIGHT_FINAL, voice->vro);
snd_gf1_write8(gus, SNDRV_GF1_VB_ACCUMULATOR, voice->effect_accumulator);
snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME, voice->gf1_effect_volume);
snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME_FINAL, voice->gf1_effect_volume);
}
spin_unlock_irqrestore(&gus->reg_lock, flags);
do_volume_envelope(gus, voice);
spin_lock_irqsave(&gus->reg_lock, flags);
snd_gf1_select_voice(gus, voice->number);
if (gus->gf1.enh_mode)
snd_gf1_write8(gus, SNDRV_GF1_VB_MODE, voice->mode);
snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice->control);
if (!gus->gf1.enh_mode) {
snd_gf1_delay(gus);
snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice->control );
}
spin_unlock_irqrestore(&gus->reg_lock, flags);
#if 0
snd_gf1_print_voice_registers(gus);
#endif
voice->flags |= SNDRV_GF1_VFLG_RUNNING;
snd_seq_instr_free_use(gus->gf1.ilist, instr);
}
static void sample_stop(struct snd_gus_card *gus, struct snd_gus_voice *voice, int mode)
{
unsigned char control;
unsigned long flags;
if (!(voice->flags & SNDRV_GF1_VFLG_RUNNING))
return;
switch (mode) {
default:
if (gus->gf1.volume_ramp > 0) {
if (voice->venv_state < VENV_RELEASE) {
voice->venv_state = VENV_RELEASE;
do_volume_envelope(gus, voice);
}
}
if (mode != SAMPLE_STOP_VENVELOPE) {
snd_gf1_stop_voice(gus, voice->number);
spin_lock_irqsave(&gus->reg_lock, flags);
snd_gf1_select_voice(gus, voice->number);
snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, SNDRV_GF1_MIN_VOLUME);
spin_unlock_irqrestore(&gus->reg_lock, flags);
voice->flags &= ~SNDRV_GF1_VFLG_RUNNING;
}
break;
case SAMPLE_STOP_LOOP: /* disable loop only */
spin_lock_irqsave(&gus->reg_lock, flags);
snd_gf1_select_voice(gus, voice->number);
control = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL);
control &= ~(0x83 | 0x04);
control |= 0x20;
snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, control);
spin_unlock_irqrestore(&gus->reg_lock, flags);
break;
}
}
static void sample_freq(struct snd_gus_card *gus, struct snd_gus_voice *voice, snd_seq_frequency_t freq)
{
unsigned long flags;
spin_lock_irqsave(&gus->reg_lock, flags);
voice->fc_register = snd_gf1_translate_freq(gus, freq);
snd_gf1_select_voice(gus, voice->number);
snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, voice->fc_register + voice->fc_lfo);
spin_unlock_irqrestore(&gus->reg_lock, flags);
}
static void sample_volume(struct snd_gus_card *gus, struct snd_gus_voice *voice, struct snd_seq_ev_volume *volume)
{
if (volume->volume >= 0) {
volume->volume &= 0x3fff;
voice->gf1_volume = snd_gf1_lvol_to_gvol_raw(volume->volume << 2) << 4;
voice->venv_state_prev = VENV_SUSTAIN;
voice->venv_state = VENV_VOLUME;
do_volume_envelope(gus, voice);
}
if (volume->lr >= 0) {
volume->lr &= 0x3fff;
if (!gus->gf1.enh_mode) {
voice->gf1_pan = (volume->lr >> 10) & 15;
if (!gus->gf1.full_range_pan) {
if (voice->gf1_pan == 0)
voice->gf1_pan++;
if (voice->gf1_pan == 15)
voice->gf1_pan--;
}
voice->flags &= ~SNDRV_GF1_VFLG_PAN; /* before */
do_pan_envelope(gus, voice);
} else {
set_enhanced_pan(gus, voice, volume->lr >> 7);
}
}
}
static void sample_loop(struct snd_gus_card *gus, struct snd_gus_voice *voice, struct snd_seq_ev_loop *loop)
{
unsigned long flags;
int w_16 = voice->control & 0x04;
unsigned int begin, addr_start, addr_end;
struct simple_instrument *simple;
struct snd_seq_kinstr *instr;
#if 0
printk("voice_loop: start = 0x%x, end = 0x%x\n", loop->start, loop->end);
#endif
instr = snd_seq_instr_find(gus->gf1.ilist, &voice->instr, 0, 1);
if (instr == NULL)
return;
voice->instr = instr->instr; /* copy ID to speedup aliases */
simple = KINSTR_DATA(instr);
begin = simple->address.memory;
addr_start = loop->start;
addr_end = loop->end;
addr_start = (((addr_start << 1) & ~0x1f) | (addr_start & 0x0f)) + begin;
addr_end = (((addr_end << 1) & ~0x1f) | (addr_end & 0x0f)) + begin;
spin_lock_irqsave(&gus->reg_lock, flags);
snd_gf1_select_voice(gus, voice->number);
snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, addr_start, w_16);
snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, addr_end, w_16);
spin_unlock_irqrestore(&gus->reg_lock, flags);
snd_seq_instr_free_use(gus->gf1.ilist, instr);
}
static void sample_pos(struct snd_gus_card *gus, struct snd_gus_voice *voice, snd_seq_position_t position)
{
unsigned long flags;
int w_16 = voice->control & 0x04;
unsigned int begin, addr;
struct simple_instrument *simple;
struct snd_seq_kinstr *instr;
#if 0
printk("voice_loop: start = 0x%x, end = 0x%x\n", loop->start, loop->end);
#endif
instr = snd_seq_instr_find(gus->gf1.ilist, &voice->instr, 0, 1);
if (instr == NULL)
return;
voice->instr = instr->instr; /* copy ID to speedup aliases */
simple = KINSTR_DATA(instr);
begin = simple->address.memory;
addr = (((position << 1) & ~0x1f) | (position & 0x0f)) + begin;
spin_lock_irqsave(&gus->reg_lock, flags);
snd_gf1_select_voice(gus, voice->number);
snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, addr, w_16);
spin_unlock_irqrestore(&gus->reg_lock, flags);
snd_seq_instr_free_use(gus->gf1.ilist, instr);
}
#if 0
static unsigned char get_effects_mask( ultra_card_t *card, int value )
{
if ( value > 7 ) return 0;
if ( card -> gf1.effects && card -> gf1.effects -> chip_type == ULTRA_EFFECT_CHIP_INTERWAVE )
return card -> gf1.effects -> chip.interwave.voice_output[ value ];
return 0;
}
#endif
static void sample_private1(struct snd_gus_card *card, struct snd_gus_voice *voice, unsigned char *data)
{
#if 0
unsigned long flags;
unsigned char uc;
switch ( *data ) {
case ULTRA_PRIV1_IW_EFFECT:
uc = get_effects_mask( card, ultra_get_byte( data, 4 ) );
uc |= get_effects_mask( card, ultra_get_byte( data, 4 ) >> 4 );
uc |= get_effects_mask( card, ultra_get_byte( data, 5 ) );
uc |= get_effects_mask( card, ultra_get_byte( data, 5 ) >> 4 );
voice -> data.simple.effect_accumulator = uc;
voice -> data.simple.effect_volume = ultra_translate_voice_volume( card, ultra_get_word( data, 2 ) ) << 4;
if ( !card -> gf1.enh_mode ) return;
if ( voice -> flags & VFLG_WAIT_FOR_START ) return;
if ( voice -> flags & VFLG_RUNNING )
{
CLI( &flags );
gf1_select_voice( card, voice -> number );
ultra_write8( card, GF1_VB_ACCUMULATOR, voice -> data.simple.effect_accumulator );
ultra_write16( card, GF1_VW_EFFECT_VOLUME_FINAL, voice -> data.simple.effect_volume );
STI( &flags );
}
break;
case ULTRA_PRIV1_IW_LFO:
ultra_lfo_command( card, voice -> number, data );
}
#endif
}
#if 0
/*
*
*/
static void note_stop( ultra_card_t *card, ultra_voice_t *voice, int wait )
{
}
static void note_wait( ultra_card_t *card, ultra_voice_t *voice )
{
}
static void note_off( ultra_card_t *card, ultra_voice_t *voice )
{
}
static void note_volume( ultra_card_t *card, ultra_voice_t *voice )
{
}
static void note_pitchbend( ultra_card_t *card, ultra_voice_t *voice )
{
}
static void note_vibrato( ultra_card_t *card, ultra_voice_t *voice )
{
}
static void note_tremolo( ultra_card_t *card, ultra_voice_t *voice )
{
}
/*
*
*/
static void chn_trigger_down( ultra_card_t *card, ultra_channel_t *channel, ultra_instrument_t *instrument, unsigned char note, unsigned char velocity, unsigned char priority )
{
}
static void chn_trigger_up( ultra_card_t *card, ultra_note_t *note )
{
}
static void chn_control( ultra_card_t *card, ultra_channel_t *channel, unsigned short p1, unsigned short p2 )
{
}
/*
*
*/
#endif
void snd_gf1_simple_init(struct snd_gus_voice *voice)
{
voice->handler_wave = interrupt_wave;
voice->handler_volume = interrupt_volume;
voice->handler_effect = interrupt_effect;
voice->volume_change = NULL;
voice->sample_ops = &sample_ops;
}
/*
* Routines for Gravis UltraSound soundcards - Synthesizer
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <sound/driver.h>
#include <linux/init.h>
#include <linux/time.h>
#include <sound/core.h>
#include <sound/gus.h>
#include <sound/seq_device.h>
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Routines for Gravis UltraSound soundcards - Synthesizer");
MODULE_LICENSE("GPL");
/*
*
*/
static void snd_gus_synth_free_voices(struct snd_gus_card * gus, int client, int port)
{
int idx;
struct snd_gus_voice * voice;
for (idx = 0; idx < 32; idx++) {
voice = &gus->gf1.voices[idx];
if (voice->use && voice->client == client && voice->port == port)
snd_gf1_free_voice(gus, voice);
}
}
static int snd_gus_synth_use(void *private_data, struct snd_seq_port_subscribe *info)
{
struct snd_gus_port * port = private_data;
struct snd_gus_card * gus = port->gus;
struct snd_gus_voice * voice;
unsigned int idx;
if (info->voices > 32)
return -EINVAL;
mutex_lock(&gus->register_mutex);
if (!snd_gus_use_inc(gus)) {
mutex_unlock(&gus->register_mutex);
return -EFAULT;
}
for (idx = 0; idx < info->voices; idx++) {
voice = snd_gf1_alloc_voice(gus, SNDRV_GF1_VOICE_TYPE_SYNTH, info->sender.client, info->sender.port);
if (voice == NULL) {
snd_gus_synth_free_voices(gus, info->sender.client, info->sender.port);
snd_gus_use_dec(gus);
mutex_unlock(&gus->register_mutex);
return -EBUSY;
}
voice->index = idx;
}
mutex_unlock(&gus->register_mutex);
return 0;
}
static int snd_gus_synth_unuse(void *private_data, struct snd_seq_port_subscribe *info)
{
struct snd_gus_port * port = private_data;
struct snd_gus_card * gus = port->gus;
mutex_lock(&gus->register_mutex);
snd_gus_synth_free_voices(gus, info->sender.client, info->sender.port);
snd_gus_use_dec(gus);
mutex_unlock(&gus->register_mutex);
return 0;
}
/*
*
*/
static void snd_gus_synth_free_private_instruments(struct snd_gus_port *p, int client)
{
struct snd_seq_instr_header ifree;
memset(&ifree, 0, sizeof(ifree));
ifree.cmd = SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE;
snd_seq_instr_list_free_cond(p->gus->gf1.ilist, &ifree, client, 0);
}
static int snd_gus_synth_event_input(struct snd_seq_event *ev, int direct,
void *private_data, int atomic, int hop)
{
struct snd_gus_port * p = private_data;
snd_assert(p != NULL, return -EINVAL);
if (ev->type >= SNDRV_SEQ_EVENT_SAMPLE &&
ev->type <= SNDRV_SEQ_EVENT_SAMPLE_PRIVATE1) {
snd_gus_sample_event(ev, p);
return 0;
}
if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM &&
ev->source.port == SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE) {
if (ev->type == SNDRV_SEQ_EVENT_CLIENT_EXIT) {
snd_gus_synth_free_private_instruments(p, ev->data.addr.client);
return 0;
}
}
if (direct) {
if (ev->type >= SNDRV_SEQ_EVENT_INSTR_BEGIN) {
snd_seq_instr_event(&p->gus->gf1.iwffff_ops.kops,
p->gus->gf1.ilist,
ev,
p->gus->gf1.seq_client,
atomic, hop);
return 0;
}
}
return 0;
}
static void snd_gus_synth_instr_notify(void *private_data,
struct snd_seq_kinstr *instr,
int what)
{
unsigned int idx;
struct snd_gus_card *gus = private_data;
struct snd_gus_voice *pvoice;
unsigned long flags;
spin_lock_irqsave(&gus->event_lock, flags);
for (idx = 0; idx < 32; idx++) {
pvoice = &gus->gf1.voices[idx];
if (pvoice->use && !memcmp(&pvoice->instr, &instr->instr, sizeof(pvoice->instr))) {
if (pvoice->sample_ops && pvoice->sample_ops->sample_stop) {
pvoice->sample_ops->sample_stop(gus, pvoice, SAMPLE_STOP_IMMEDIATELY);
} else {
snd_gf1_stop_voice(gus, pvoice->number);
pvoice->flags &= ~SNDRV_GF1_VFLG_RUNNING;
}
}
}
spin_unlock_irqrestore(&gus->event_lock, flags);
}
/*
*
*/
static void snd_gus_synth_free_port(void *private_data)
{
struct snd_gus_port * p = private_data;
if (p)
snd_midi_channel_free_set(p->chset);
}
static int snd_gus_synth_create_port(struct snd_gus_card * gus, int idx)
{
struct snd_gus_port * p;
struct snd_seq_port_callback callbacks;
char name[32];
int result;
p = &gus->gf1.seq_ports[idx];
p->chset = snd_midi_channel_alloc_set(16);
if (p->chset == NULL)
return -ENOMEM;
p->chset->private_data = p;
p->gus = gus;
p->client = gus->gf1.seq_client;
memset(&callbacks, 0, sizeof(callbacks));
callbacks.owner = THIS_MODULE;
callbacks.use = snd_gus_synth_use;
callbacks.unuse = snd_gus_synth_unuse;
callbacks.event_input = snd_gus_synth_event_input;
callbacks.private_free = snd_gus_synth_free_port;
callbacks.private_data = p;
sprintf(name, "%s port %i", gus->interwave ? "AMD InterWave" : "GF1", idx);
p->chset->port = snd_seq_event_port_attach(gus->gf1.seq_client,
&callbacks,
SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE,
SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE |
SNDRV_SEQ_PORT_TYPE_SYNTH |
SNDRV_SEQ_PORT_TYPE_HARDWARE |
SNDRV_SEQ_PORT_TYPE_SYNTHESIZER,
16, 0,
name);
if (p->chset->port < 0) {
result = p->chset->port;
snd_gus_synth_free_port(p);
return result;
}
p->port = p->chset->port;
return 0;
}
/*
*
*/
static int snd_gus_synth_new_device(struct snd_seq_device *dev)
{
struct snd_gus_card *gus;
int client, i;
struct snd_seq_port_subscribe sub;
struct snd_iwffff_ops *iwops;
struct snd_gf1_ops *gf1ops;
struct snd_simple_ops *simpleops;
gus = *(struct snd_gus_card **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
if (gus == NULL)
return -EINVAL;
mutex_init(&gus->register_mutex);
gus->gf1.seq_client = -1;
/* allocate new client */
client = gus->gf1.seq_client =
snd_seq_create_kernel_client(gus->card, 1, gus->interwave ?
"AMD InterWave" : "GF1");
if (client < 0)
return client;
for (i = 0; i < 4; i++)
snd_gus_synth_create_port(gus, i);
gus->gf1.ilist = snd_seq_instr_list_new();
if (gus->gf1.ilist == NULL) {
snd_seq_delete_kernel_client(client);
gus->gf1.seq_client = -1;
return -ENOMEM;
}
gus->gf1.ilist->flags = SNDRV_SEQ_INSTR_FLG_DIRECT;
simpleops = &gus->gf1.simple_ops;
snd_seq_simple_init(simpleops, gus, NULL);
simpleops->put_sample = snd_gus_simple_put_sample;
simpleops->get_sample = snd_gus_simple_get_sample;
simpleops->remove_sample = snd_gus_simple_remove_sample;
simpleops->notify = snd_gus_synth_instr_notify;
gf1ops = &gus->gf1.gf1_ops;
snd_seq_gf1_init(gf1ops, gus, &simpleops->kops);
gf1ops->put_sample = snd_gus_gf1_put_sample;
gf1ops->get_sample = snd_gus_gf1_get_sample;
gf1ops->remove_sample = snd_gus_gf1_remove_sample;
gf1ops->notify = snd_gus_synth_instr_notify;
iwops = &gus->gf1.iwffff_ops;
snd_seq_iwffff_init(iwops, gus, &gf1ops->kops);
iwops->put_sample = snd_gus_iwffff_put_sample;
iwops->get_sample = snd_gus_iwffff_get_sample;
iwops->remove_sample = snd_gus_iwffff_remove_sample;
iwops->notify = snd_gus_synth_instr_notify;
memset(&sub, 0, sizeof(sub));
sub.sender.client = SNDRV_SEQ_CLIENT_SYSTEM;
sub.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
sub.dest.client = client;
sub.dest.port = 0;
snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &sub);
return 0;
}
static int snd_gus_synth_delete_device(struct snd_seq_device *dev)
{
struct snd_gus_card *gus;
gus = *(struct snd_gus_card **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
if (gus == NULL)
return -EINVAL;
if (gus->gf1.seq_client >= 0) {
snd_seq_delete_kernel_client(gus->gf1.seq_client);
gus->gf1.seq_client = -1;
}
if (gus->gf1.ilist)
snd_seq_instr_list_free(&gus->gf1.ilist);
return 0;
}
static int __init alsa_gus_synth_init(void)
{
static struct snd_seq_dev_ops ops = {
snd_gus_synth_new_device,
snd_gus_synth_delete_device
};
return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_GUS, &ops,
sizeof(struct snd_gus_card *));
}
static void __exit alsa_gus_synth_exit(void)
{
snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_GUS);
}
module_init(alsa_gus_synth_init)
module_exit(alsa_gus_synth_exit)
...@@ -4,16 +4,6 @@ ...@@ -4,16 +4,6 @@
# #
snd-trident-objs := trident.o trident_main.o trident_memory.o snd-trident-objs := trident.o trident_main.o trident_memory.o
snd-trident-synth-objs := trident_synth.o
#
# this function returns:
# "m" - CONFIG_SND_SEQUENCER is m
# <empty string> - CONFIG_SND_SEQUENCER is undefined
# otherwise parameter #1 value
#
sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
# Toplevel Module Dependency # Toplevel Module Dependency
obj-$(CONFIG_SND_TRIDENT) += snd-trident.o obj-$(CONFIG_SND_TRIDENT) += snd-trident.o
obj-$(call sequencer,$(CONFIG_SND_TRIDENT)) += snd-trident-synth.o
...@@ -155,13 +155,6 @@ static int __devinit snd_trident_probe(struct pci_dev *pci, ...@@ -155,13 +155,6 @@ static int __devinit snd_trident_probe(struct pci_dev *pci,
return err; return err;
} }
#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
if ((err = snd_trident_attach_synthesizer(trident)) < 0) {
snd_card_free(card);
return err;
}
#endif
snd_trident_create_gameport(trident); snd_trident_create_gameport(trident);
if ((err = snd_card_register(card)) < 0) { if ((err = snd_card_register(card)) < 0) {
......
...@@ -3313,12 +3313,6 @@ static void snd_trident_proc_read(struct snd_info_entry *entry, ...@@ -3313,12 +3313,6 @@ static void snd_trident_proc_read(struct snd_info_entry *entry,
snd_iprintf(buffer, "Memory Free : %d\n", snd_util_mem_avail(trident->tlb.memhdr)); snd_iprintf(buffer, "Memory Free : %d\n", snd_util_mem_avail(trident->tlb.memhdr));
} }
} }
#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
snd_iprintf(buffer,"\nWavetable Synth\n");
snd_iprintf(buffer, "Memory Maximum : %d\n", trident->synth.max_size);
snd_iprintf(buffer, "Memory Used : %d\n", trident->synth.current_size);
snd_iprintf(buffer, "Memory Free : %d\n", (trident->synth.max_size-trident->synth.current_size));
#endif
} }
static void __devinit snd_trident_proc_init(struct snd_trident * trident) static void __devinit snd_trident_proc_init(struct snd_trident * trident)
...@@ -3815,28 +3809,6 @@ static irqreturn_t snd_trident_interrupt(int irq, void *dev_id) ...@@ -3815,28 +3809,6 @@ static irqreturn_t snd_trident_interrupt(int irq, void *dev_id)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
/*---------------------------------------------------------------------------
snd_trident_attach_synthesizer
Description: Attach synthesizer hooks
Paramters: trident - device specific private data for 4DWave card
Returns: None.
---------------------------------------------------------------------------*/
int snd_trident_attach_synthesizer(struct snd_trident *trident)
{
#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
if (snd_seq_device_new(trident->card, 1, SNDRV_SEQ_DEV_ID_TRIDENT,
sizeof(struct snd_trident *), &trident->seq_dev) >= 0) {
strcpy(trident->seq_dev->name, "4DWave");
*(struct snd_trident **)SNDRV_SEQ_DEVICE_ARGPTR(trident->seq_dev) = trident;
}
#endif
return 0;
}
struct snd_trident_voice *snd_trident_alloc_voice(struct snd_trident * trident, int type, int client, int port) struct snd_trident_voice *snd_trident_alloc_voice(struct snd_trident * trident, int type, int client, int port)
{ {
struct snd_trident_voice *pvoice; struct snd_trident_voice *pvoice;
......
/*
* Routines for Trident 4DWave NX/DX soundcards - Synthesizer
* Copyright (c) by Scott McNab <jedi@tartarus.uwa.edu.au>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <sound/driver.h>
#include <asm/io.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <sound/core.h>
#include <sound/trident.h>
#include <sound/seq_device.h>
MODULE_AUTHOR("Scott McNab <jedi@tartarus.uwa.edu.au>");
MODULE_DESCRIPTION("Routines for Trident 4DWave NX/DX soundcards - Synthesizer");
MODULE_LICENSE("GPL");
/* linear to log pan conversion table (4.2 channel attenuation format) */
static unsigned int pan_table[63] = {
7959, 7733, 7514, 7301, 7093, 6892, 6697, 6507,
6322, 6143, 5968, 5799, 5634, 5475, 5319, 5168,
5022, 4879, 4741, 4606, 4475, 4349, 4225, 4105,
3989, 3876, 3766, 3659, 3555, 3454, 3356, 3261,
3168, 3078, 2991, 2906, 2824, 2744, 2666, 2590,
2517, 2445, 2376, 2308, 2243, 2179, 2117, 2057,
1999, 1942, 1887, 1833, 1781, 1731, 1682, 1634,
1588, 1543, 1499, 1456, 1415, 1375, 1336
};
#define LOG_TABLE_SIZE 386
/* Linear half-attenuation to log conversion table in the format:
* {linear volume, logarithmic attenuation equivalent}, ...
*
* Provides conversion from a linear half-volume value in the range
* [0,8192] to a logarithmic attenuation value in the range 0 to 6.02dB.
* Halving the linear volume is equivalent to an additional 6dB of
* logarithmic attenuation. The algorithm used in log_from_linear()
* therefore uses this table as follows:
*
* - loop and for every time the volume is less than half the maximum
* volume (16384), add another 6dB and halve the maximum value used
* for this comparison.
* - when the volume is greater than half the maximum volume, take
* the difference of the volume to half volume (in the range [0,8192])
* and look up the log_table[] to find the nearest entry.
* - take the logarithic component of this entry and add it to the
* resulting attenuation.
*
* Thus this routine provides a linear->log conversion for a range of
* [0,16384] using only 386 table entries
*
* Note: although this table stores log attenuation in 8.8 format, values
* were only calculated for 6 bits fractional precision, since that is
* the most precision offered by the trident hardware.
*/
static unsigned short log_table[LOG_TABLE_SIZE*2] =
{
4, 0x0604, 19, 0x0600, 34, 0x05fc,
49, 0x05f8, 63, 0x05f4, 78, 0x05f0, 93, 0x05ec, 108, 0x05e8,
123, 0x05e4, 138, 0x05e0, 153, 0x05dc, 168, 0x05d8, 183, 0x05d4,
198, 0x05d0, 213, 0x05cc, 228, 0x05c8, 244, 0x05c4, 259, 0x05c0,
274, 0x05bc, 289, 0x05b8, 304, 0x05b4, 320, 0x05b0, 335, 0x05ac,
350, 0x05a8, 366, 0x05a4, 381, 0x05a0, 397, 0x059c, 412, 0x0598,
428, 0x0594, 443, 0x0590, 459, 0x058c, 474, 0x0588, 490, 0x0584,
506, 0x0580, 521, 0x057c, 537, 0x0578, 553, 0x0574, 568, 0x0570,
584, 0x056c, 600, 0x0568, 616, 0x0564, 632, 0x0560, 647, 0x055c,
663, 0x0558, 679, 0x0554, 695, 0x0550, 711, 0x054c, 727, 0x0548,
743, 0x0544, 759, 0x0540, 776, 0x053c, 792, 0x0538, 808, 0x0534,
824, 0x0530, 840, 0x052c, 857, 0x0528, 873, 0x0524, 889, 0x0520,
906, 0x051c, 922, 0x0518, 938, 0x0514, 955, 0x0510, 971, 0x050c,
988, 0x0508, 1004, 0x0504, 1021, 0x0500, 1037, 0x04fc, 1054, 0x04f8,
1071, 0x04f4, 1087, 0x04f0, 1104, 0x04ec, 1121, 0x04e8, 1138, 0x04e4,
1154, 0x04e0, 1171, 0x04dc, 1188, 0x04d8, 1205, 0x04d4, 1222, 0x04d0,
1239, 0x04cc, 1256, 0x04c8, 1273, 0x04c4, 1290, 0x04c0, 1307, 0x04bc,
1324, 0x04b8, 1341, 0x04b4, 1358, 0x04b0, 1376, 0x04ac, 1393, 0x04a8,
1410, 0x04a4, 1427, 0x04a0, 1445, 0x049c, 1462, 0x0498, 1479, 0x0494,
1497, 0x0490, 1514, 0x048c, 1532, 0x0488, 1549, 0x0484, 1567, 0x0480,
1584, 0x047c, 1602, 0x0478, 1620, 0x0474, 1637, 0x0470, 1655, 0x046c,
1673, 0x0468, 1690, 0x0464, 1708, 0x0460, 1726, 0x045c, 1744, 0x0458,
1762, 0x0454, 1780, 0x0450, 1798, 0x044c, 1816, 0x0448, 1834, 0x0444,
1852, 0x0440, 1870, 0x043c, 1888, 0x0438, 1906, 0x0434, 1924, 0x0430,
1943, 0x042c, 1961, 0x0428, 1979, 0x0424, 1997, 0x0420, 2016, 0x041c,
2034, 0x0418, 2053, 0x0414, 2071, 0x0410, 2089, 0x040c, 2108, 0x0408,
2127, 0x0404, 2145, 0x0400, 2164, 0x03fc, 2182, 0x03f8, 2201, 0x03f4,
2220, 0x03f0, 2239, 0x03ec, 2257, 0x03e8, 2276, 0x03e4, 2295, 0x03e0,
2314, 0x03dc, 2333, 0x03d8, 2352, 0x03d4, 2371, 0x03d0, 2390, 0x03cc,
2409, 0x03c8, 2428, 0x03c4, 2447, 0x03c0, 2466, 0x03bc, 2485, 0x03b8,
2505, 0x03b4, 2524, 0x03b0, 2543, 0x03ac, 2562, 0x03a8, 2582, 0x03a4,
2601, 0x03a0, 2621, 0x039c, 2640, 0x0398, 2660, 0x0394, 2679, 0x0390,
2699, 0x038c, 2718, 0x0388, 2738, 0x0384, 2758, 0x0380, 2777, 0x037c,
2797, 0x0378, 2817, 0x0374, 2837, 0x0370, 2857, 0x036c, 2876, 0x0368,
2896, 0x0364, 2916, 0x0360, 2936, 0x035c, 2956, 0x0358, 2976, 0x0354,
2997, 0x0350, 3017, 0x034c, 3037, 0x0348, 3057, 0x0344, 3077, 0x0340,
3098, 0x033c, 3118, 0x0338, 3138, 0x0334, 3159, 0x0330, 3179, 0x032c,
3200, 0x0328, 3220, 0x0324, 3241, 0x0320, 3261, 0x031c, 3282, 0x0318,
3303, 0x0314, 3323, 0x0310, 3344, 0x030c, 3365, 0x0308, 3386, 0x0304,
3406, 0x0300, 3427, 0x02fc, 3448, 0x02f8, 3469, 0x02f4, 3490, 0x02f0,
3511, 0x02ec, 3532, 0x02e8, 3553, 0x02e4, 3575, 0x02e0, 3596, 0x02dc,
3617, 0x02d8, 3638, 0x02d4, 3660, 0x02d0, 3681, 0x02cc, 3702, 0x02c8,
3724, 0x02c4, 3745, 0x02c0, 3767, 0x02bc, 3788, 0x02b8, 3810, 0x02b4,
3831, 0x02b0, 3853, 0x02ac, 3875, 0x02a8, 3896, 0x02a4, 3918, 0x02a0,
3940, 0x029c, 3962, 0x0298, 3984, 0x0294, 4006, 0x0290, 4028, 0x028c,
4050, 0x0288, 4072, 0x0284, 4094, 0x0280, 4116, 0x027c, 4138, 0x0278,
4160, 0x0274, 4182, 0x0270, 4205, 0x026c, 4227, 0x0268, 4249, 0x0264,
4272, 0x0260, 4294, 0x025c, 4317, 0x0258, 4339, 0x0254, 4362, 0x0250,
4384, 0x024c, 4407, 0x0248, 4430, 0x0244, 4453, 0x0240, 4475, 0x023c,
4498, 0x0238, 4521, 0x0234, 4544, 0x0230, 4567, 0x022c, 4590, 0x0228,
4613, 0x0224, 4636, 0x0220, 4659, 0x021c, 4682, 0x0218, 4705, 0x0214,
4728, 0x0210, 4752, 0x020c, 4775, 0x0208, 4798, 0x0204, 4822, 0x0200,
4845, 0x01fc, 4869, 0x01f8, 4892, 0x01f4, 4916, 0x01f0, 4939, 0x01ec,
4963, 0x01e8, 4987, 0x01e4, 5010, 0x01e0, 5034, 0x01dc, 5058, 0x01d8,
5082, 0x01d4, 5106, 0x01d0, 5130, 0x01cc, 5154, 0x01c8, 5178, 0x01c4,
5202, 0x01c0, 5226, 0x01bc, 5250, 0x01b8, 5274, 0x01b4, 5299, 0x01b0,
5323, 0x01ac, 5347, 0x01a8, 5372, 0x01a4, 5396, 0x01a0, 5420, 0x019c,
5445, 0x0198, 5469, 0x0194, 5494, 0x0190, 5519, 0x018c, 5543, 0x0188,
5568, 0x0184, 5593, 0x0180, 5618, 0x017c, 5643, 0x0178, 5668, 0x0174,
5692, 0x0170, 5717, 0x016c, 5743, 0x0168, 5768, 0x0164, 5793, 0x0160,
5818, 0x015c, 5843, 0x0158, 5868, 0x0154, 5894, 0x0150, 5919, 0x014c,
5945, 0x0148, 5970, 0x0144, 5995, 0x0140, 6021, 0x013c, 6047, 0x0138,
6072, 0x0134, 6098, 0x0130, 6124, 0x012c, 6149, 0x0128, 6175, 0x0124,
6201, 0x0120, 6227, 0x011c, 6253, 0x0118, 6279, 0x0114, 6305, 0x0110,
6331, 0x010c, 6357, 0x0108, 6384, 0x0104, 6410, 0x0100, 6436, 0x00fc,
6462, 0x00f8, 6489, 0x00f4, 6515, 0x00f0, 6542, 0x00ec, 6568, 0x00e8,
6595, 0x00e4, 6621, 0x00e0, 6648, 0x00dc, 6675, 0x00d8, 6702, 0x00d4,
6728, 0x00d0, 6755, 0x00cc, 6782, 0x00c8, 6809, 0x00c4, 6836, 0x00c0,
6863, 0x00bc, 6890, 0x00b8, 6917, 0x00b4, 6945, 0x00b0, 6972, 0x00ac,
6999, 0x00a8, 7027, 0x00a4, 7054, 0x00a0, 7081, 0x009c, 7109, 0x0098,
7136, 0x0094, 7164, 0x0090, 7192, 0x008c, 7219, 0x0088, 7247, 0x0084,
7275, 0x0080, 7303, 0x007c, 7331, 0x0078, 7359, 0x0074, 7387, 0x0070,
7415, 0x006c, 7443, 0x0068, 7471, 0x0064, 7499, 0x0060, 7527, 0x005c,
7556, 0x0058, 7584, 0x0054, 7613, 0x0050, 7641, 0x004c, 7669, 0x0048,
7698, 0x0044, 7727, 0x0040, 7755, 0x003c, 7784, 0x0038, 7813, 0x0034,
7842, 0x0030, 7870, 0x002c, 7899, 0x0028, 7928, 0x0024, 7957, 0x0020,
7986, 0x001c, 8016, 0x0018, 8045, 0x0014, 8074, 0x0010, 8103, 0x000c,
8133, 0x0008, 8162, 0x0004, 8192, 0x0000
};
static unsigned short lookup_volume_table( unsigned short value )
{
/* This code is an optimised version of:
* int i = 0;
* while( volume_table[i*2] < value )
* i++;
* return volume_table[i*2+1];
*/
unsigned short *ptr = log_table;
while( *ptr < value )
ptr += 2;
return *(ptr+1);
}
/* this function calculates a 8.8 fixed point logarithmic attenuation
* value from a linear volume value in the range 0 to 16384 */
static unsigned short log_from_linear( unsigned short value )
{
if (value >= 16384)
return 0x0000;
if (value) {
unsigned short result = 0;
int v, c;
for( c = 0, v = 8192; c < 14; c++, v >>= 1 ) {
if( value >= v ) {
result += lookup_volume_table( (value - v) << c );
return result;
}
result += 0x0605; /* 6.0205 (result of -20*log10(0.5)) */
}
}
return 0xffff;
}
/*
* Sample handling operations
*/
static void sample_start(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_position_t position);
static void sample_stop(struct snd_trident * trident, struct snd_trident_voice * voice, int mode);
static void sample_freq(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_frequency_t freq);
static void sample_volume(struct snd_trident * trident, struct snd_trident_voice * voice, struct snd_seq_ev_volume * volume);
static void sample_loop(struct snd_trident * trident, struct snd_trident_voice * voice, struct snd_seq_ev_loop * loop);
static void sample_pos(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_position_t position);
static void sample_private1(struct snd_trident * trident, struct snd_trident_voice * voice, unsigned char *data);
static struct snd_trident_sample_ops sample_ops =
{
sample_start,
sample_stop,
sample_freq,
sample_volume,
sample_loop,
sample_pos,
sample_private1
};
static void snd_trident_simple_init(struct snd_trident_voice * voice)
{
//voice->handler_wave = interrupt_wave;
//voice->handler_volume = interrupt_volume;
//voice->handler_effect = interrupt_effect;
//voice->volume_change = NULL;
voice->sample_ops = &sample_ops;
}
static void sample_start(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_position_t position)
{
struct simple_instrument *simple;
struct snd_seq_kinstr *instr;
unsigned long flags;
unsigned int loop_start, loop_end, sample_start, sample_end, start_offset;
unsigned int value;
unsigned int shift = 0;
instr = snd_seq_instr_find(trident->synth.ilist, &voice->instr, 0, 1);
if (instr == NULL)
return;
voice->instr = instr->instr; /* copy ID to speedup aliases */
simple = KINSTR_DATA(instr);
spin_lock_irqsave(&trident->reg_lock, flags);
if (trident->device == TRIDENT_DEVICE_ID_SI7018)
voice->GVSel = 1; /* route to Wave volume */
voice->CTRL = 0;
voice->Alpha = 0;
voice->FMS = 0;
loop_start = simple->loop_start >> 4;
loop_end = simple->loop_end >> 4;
sample_start = (simple->start + position) >> 4;
if( sample_start >= simple->size )
sample_start = simple->start >> 4;
sample_end = simple->size;
start_offset = position >> 4;
if (simple->format & SIMPLE_WAVE_16BIT) {
voice->CTRL |= 8;
shift++;
}
if (simple->format & SIMPLE_WAVE_STEREO) {
voice->CTRL |= 4;
shift++;
}
if (!(simple->format & SIMPLE_WAVE_UNSIGNED))
voice->CTRL |= 2;
voice->LBA = simple->address.memory;
if (simple->format & SIMPLE_WAVE_LOOP) {
voice->CTRL |= 1;
voice->LBA += loop_start << shift;
if( start_offset >= loop_start ) {
voice->CSO = start_offset - loop_start;
voice->negCSO = 0;
} else {
voice->CSO = loop_start - start_offset;
voice->negCSO = 1;
}
voice->ESO = loop_end - loop_start - 1;
} else {
voice->LBA += start_offset << shift;
voice->CSO = sample_start;
voice->ESO = sample_end - 1;
voice->negCSO = 0;
}
if (voice->flags & SNDRV_TRIDENT_VFLG_RUNNING) {
snd_trident_stop_voice(trident, voice->number);
voice->flags &= ~SNDRV_TRIDENT_VFLG_RUNNING;
}
/* set CSO sign */
value = inl(TRID_REG(trident, T4D_SIGN_CSO_A));
if( voice->negCSO ) {
value |= 1 << (voice->number&31);
} else {
value &= ~(1 << (voice->number&31));
}
outl(value,TRID_REG(trident, T4D_SIGN_CSO_A));
voice->Attribute = 0;
snd_trident_write_voice_regs(trident, voice);
snd_trident_start_voice(trident, voice->number);
voice->flags |= SNDRV_TRIDENT_VFLG_RUNNING;
spin_unlock_irqrestore(&trident->reg_lock, flags);
snd_seq_instr_free_use(trident->synth.ilist, instr);
}
static void sample_stop(struct snd_trident * trident, struct snd_trident_voice * voice, int mode)
{
unsigned long flags;
if (!(voice->flags & SNDRV_TRIDENT_VFLG_RUNNING))
return;
switch (mode) {
default:
spin_lock_irqsave(&trident->reg_lock, flags);
snd_trident_stop_voice(trident, voice->number);
voice->flags &= ~SNDRV_TRIDENT_VFLG_RUNNING;
spin_unlock_irqrestore(&trident->reg_lock, flags);
break;
case SAMPLE_STOP_LOOP: /* disable loop only */
voice->CTRL &= ~1;
spin_lock_irqsave(&trident->reg_lock, flags);
outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
outw((((voice->CTRL << 12) | (voice->EC & 0x0fff)) & 0xffff), CH_GVSEL_PAN_VOL_CTRL_EC);
spin_unlock_irqrestore(&trident->reg_lock, flags);
break;
}
}
static void sample_freq(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_frequency_t freq)
{
unsigned long flags;
freq >>= 4;
spin_lock_irqsave(&trident->reg_lock, flags);
if (freq == 44100)
voice->Delta = 0xeb3;
else if (freq == 8000)
voice->Delta = 0x2ab;
else if (freq == 48000)
voice->Delta = 0x1000;
else
voice->Delta = (((freq << 12) + freq) / 48000) & 0x0000ffff;
outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
if (trident->device == TRIDENT_DEVICE_ID_NX) {
outb((unsigned char) voice->Delta, TRID_REG(trident, CH_NX_DELTA_CSO + 3));
outb((unsigned char) (voice->Delta >> 8), TRID_REG(trident, CH_NX_DELTA_ESO + 3));
} else {
outw((unsigned short) voice->Delta, TRID_REG(trident, CH_DX_ESO_DELTA));
}
spin_unlock_irqrestore(&trident->reg_lock, flags);
}
static void sample_volume(struct snd_trident * trident, struct snd_trident_voice * voice, struct snd_seq_ev_volume * volume)
{
unsigned long flags;
unsigned short value;
spin_lock_irqsave(&trident->reg_lock, flags);
voice->GVSel = 0; /* use global music volume */
voice->FMC = 0x03; /* fixme: can we do something useful with FMC? */
if (volume->volume >= 0) {
volume->volume &= 0x3fff;
/* linear volume -> logarithmic attenuation conversion
* uses EC register for greater resolution (6.6 bits) than Vol register (5.3 bits)
* Vol register used when additional attenuation is required */
voice->RVol = 0;
voice->CVol = 0;
value = log_from_linear( volume->volume );
voice->Vol = 0;
voice->EC = (value & 0x3fff) >> 2;
if (value > 0x3fff) {
voice->EC |= 0xfc0;
if (value < 0x5f00 )
voice->Vol = ((value >> 8) - 0x3f) << 5;
else {
voice->Vol = 0x3ff;
voice->EC = 0xfff;
}
}
}
if (volume->lr >= 0) {
volume->lr &= 0x3fff;
/* approximate linear pan by attenuating channels */
if (volume->lr >= 0x2000) { /* attenuate left (pan right) */
value = 0x3fff - volume->lr;
for (voice->Pan = 0; voice->Pan < 63; voice->Pan++ )
if (value >= pan_table[voice->Pan] )
break;
} else { /* attenuate right (pan left) */
for (voice->Pan = 0; voice->Pan < 63; voice->Pan++ )
if ((unsigned int)volume->lr >= pan_table[voice->Pan] )
break;
voice->Pan |= 0x40;
}
}
outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
outl((voice->GVSel << 31) | ((voice->Pan & 0x0000007f) << 24) |
((voice->Vol & 0x000000ff) << 16) | ((voice->CTRL & 0x0000000f) << 12) |
(voice->EC & 0x00000fff), TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC));
value = ((voice->FMC & 0x03) << 14) | ((voice->RVol & 0x7f) << 7) | (voice->CVol & 0x7f);
outw(value, TRID_REG(trident, CH_DX_FMC_RVOL_CVOL));
spin_unlock_irqrestore(&trident->reg_lock, flags);
}
static void sample_loop(struct snd_trident * trident, struct snd_trident_voice * voice, struct snd_seq_ev_loop * loop)
{
unsigned long flags;
struct simple_instrument *simple;
struct snd_seq_kinstr *instr;
unsigned int loop_start, loop_end;
instr = snd_seq_instr_find(trident->synth.ilist, &voice->instr, 0, 1);
if (instr == NULL)
return;
voice->instr = instr->instr; /* copy ID to speedup aliases */
simple = KINSTR_DATA(instr);
loop_start = loop->start >> 4;
loop_end = loop->end >> 4;
spin_lock_irqsave(&trident->reg_lock, flags);
voice->LBA = simple->address.memory + loop_start;
voice->CSO = 0;
voice->ESO = loop_end - loop_start - 1;
outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
outb((voice->LBA >> 16), TRID_REG(trident, CH_LBA + 2));
outw((voice->LBA & 0xffff), TRID_REG(trident, CH_LBA));
if (trident->device == TRIDENT_DEVICE_ID_NX) {
outb((voice->ESO >> 16), TRID_REG(trident, CH_NX_DELTA_ESO + 2));
outw((voice->ESO & 0xffff), TRID_REG(trident, CH_NX_DELTA_ESO));
outb((voice->CSO >> 16), TRID_REG(trident, CH_NX_DELTA_CSO + 2));
outw((voice->CSO & 0xffff), TRID_REG(trident, CH_NX_DELTA_CSO));
} else {
outw((voice->ESO & 0xffff), TRID_REG(trident, CH_DX_ESO_DELTA + 2));
outw((voice->CSO & 0xffff), TRID_REG(trident, CH_DX_CSO_ALPHA_FMS + 2));
}
spin_unlock_irqrestore(&trident->reg_lock, flags);
snd_seq_instr_free_use(trident->synth.ilist, instr);
}
static void sample_pos(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_position_t position)
{
unsigned long flags;
struct simple_instrument *simple;
struct snd_seq_kinstr *instr;
unsigned int value;
instr = snd_seq_instr_find(trident->synth.ilist, &voice->instr, 0, 1);
if (instr == NULL)
return;
voice->instr = instr->instr; /* copy ID to speedup aliases */
simple = KINSTR_DATA(instr);
spin_lock_irqsave(&trident->reg_lock, flags);
if (simple->format & SIMPLE_WAVE_LOOP) {
if( position >= simple->loop_start ) {
voice->CSO = (position - simple->loop_start) >> 4;
voice->negCSO = 0;
} else {
voice->CSO = (simple->loop_start - position) >> 4;
voice->negCSO = 1;
}
} else {
voice->CSO = position >> 4;
voice->negCSO = 0;
}
/* set CSO sign */
value = inl(TRID_REG(trident, T4D_SIGN_CSO_A));
if( voice->negCSO ) {
value |= 1 << (voice->number&31);
} else {
value &= ~(1 << (voice->number&31));
}
outl(value,TRID_REG(trident, T4D_SIGN_CSO_A));
outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
if (trident->device == TRIDENT_DEVICE_ID_NX) {
outw((voice->CSO & 0xffff), TRID_REG(trident, CH_NX_DELTA_CSO));
outb((voice->CSO >> 16), TRID_REG(trident, CH_NX_DELTA_CSO + 2));
} else {
outw((voice->CSO & 0xffff), TRID_REG(trident, CH_DX_CSO_ALPHA_FMS) + 2);
}
spin_unlock_irqrestore(&trident->reg_lock, flags);
snd_seq_instr_free_use(trident->synth.ilist, instr);
}
static void sample_private1(struct snd_trident * trident, struct snd_trident_voice * voice, unsigned char *data)
{
}
/*
* Memory management / sample loading
*/
static int snd_trident_simple_put_sample(void *private_data,
struct simple_instrument * instr,
char __user *data, long len, int atomic)
{
struct snd_trident *trident = private_data;
int size = instr->size;
int shift = 0;
if (instr->format & SIMPLE_WAVE_BACKWARD ||
instr->format & SIMPLE_WAVE_BIDIR ||
instr->format & SIMPLE_WAVE_ULAW)
return -EINVAL; /* not supported */
if (instr->format & SIMPLE_WAVE_16BIT)
shift++;
if (instr->format & SIMPLE_WAVE_STEREO)
shift++;
size <<= shift;
if (trident->synth.current_size + size > trident->synth.max_size)
return -ENOMEM;
if (!access_ok(VERIFY_READ, data, size))
return -EFAULT;
if (trident->tlb.entries) {
struct snd_util_memblk *memblk;
memblk = snd_trident_synth_alloc(trident, size);
if (memblk == NULL)
return -ENOMEM;
if (snd_trident_synth_copy_from_user(trident, memblk, 0, data, size) ) {
snd_trident_synth_free(trident, memblk);
return -EFAULT;
}
instr->address.ptr = (unsigned char*)memblk;
instr->address.memory = memblk->offset;
} else {
struct snd_dma_buffer dmab;
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(trident->pci),
size, &dmab) < 0)
return -ENOMEM;
if (copy_from_user(dmab.area, data, size)) {
snd_dma_free_pages(&dmab);
return -EFAULT;
}
instr->address.ptr = dmab.area;
instr->address.memory = dmab.addr;
}
trident->synth.current_size += size;
return 0;
}
static int snd_trident_simple_get_sample(void *private_data,
struct simple_instrument * instr,
char __user *data, long len, int atomic)
{
//struct snd_trident *trident = private_data;
int size = instr->size;
int shift = 0;
if (instr->format & SIMPLE_WAVE_16BIT)
shift++;
if (instr->format & SIMPLE_WAVE_STEREO)
shift++;
size <<= shift;
if (!access_ok(VERIFY_WRITE, data, size))
return -EFAULT;
/* FIXME: not implemented yet */
return -EBUSY;
}
static int snd_trident_simple_remove_sample(void *private_data,
struct simple_instrument * instr,
int atomic)
{
struct snd_trident *trident = private_data;
int size = instr->size;
if (instr->format & SIMPLE_WAVE_16BIT)
size <<= 1;
if (instr->format & SIMPLE_WAVE_STEREO)
size <<= 1;
if (trident->tlb.entries) {
struct snd_util_memblk *memblk = (struct snd_util_memblk *)instr->address.ptr;
if (memblk)
snd_trident_synth_free(trident, memblk);
else
return -EFAULT;
} else {
struct snd_dma_buffer dmab;
dmab.dev.type = SNDRV_DMA_TYPE_DEV;
dmab.dev.dev = snd_dma_pci_data(trident->pci);
dmab.area = instr->address.ptr;
dmab.addr = instr->address.memory;
dmab.bytes = size;
snd_dma_free_pages(&dmab);
}
trident->synth.current_size -= size;
if (trident->synth.current_size < 0) /* shouldn't need this check... */
trident->synth.current_size = 0;
return 0;
}
static void select_instrument(struct snd_trident * trident, struct snd_trident_voice * v)
{
struct snd_seq_kinstr *instr;
instr = snd_seq_instr_find(trident->synth.ilist, &v->instr, 0, 1);
if (instr != NULL) {
if (instr->ops) {
if (!strcmp(instr->ops->instr_type, SNDRV_SEQ_INSTR_ID_SIMPLE))
snd_trident_simple_init(v);
}
snd_seq_instr_free_use(trident->synth.ilist, instr);
}
}
/*
*/
static void event_sample(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
{
if (v->sample_ops && v->sample_ops->sample_stop)
v->sample_ops->sample_stop(p->trident, v, SAMPLE_STOP_IMMEDIATELY);
v->instr.std = ev->data.sample.param.sample.std;
if (v->instr.std & 0xff000000) { /* private instrument */
v->instr.std &= 0x00ffffff;
v->instr.std |= (unsigned int)ev->source.client << 24;
}
v->instr.bank = ev->data.sample.param.sample.bank;
v->instr.prg = ev->data.sample.param.sample.prg;
select_instrument(p->trident, v);
}
static void event_cluster(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
{
if (v->sample_ops && v->sample_ops->sample_stop)
v->sample_ops->sample_stop(p->trident, v, SAMPLE_STOP_IMMEDIATELY);
v->instr.cluster = ev->data.sample.param.cluster.cluster;
select_instrument(p->trident, v);
}
static void event_start(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
{
if (v->sample_ops && v->sample_ops->sample_start)
v->sample_ops->sample_start(p->trident, v, ev->data.sample.param.position);
}
static void event_stop(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
{
if (v->sample_ops && v->sample_ops->sample_stop)
v->sample_ops->sample_stop(p->trident, v, ev->data.sample.param.stop_mode);
}
static void event_freq(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
{
if (v->sample_ops && v->sample_ops->sample_freq)
v->sample_ops->sample_freq(p->trident, v, ev->data.sample.param.frequency);
}
static void event_volume(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
{
if (v->sample_ops && v->sample_ops->sample_volume)
v->sample_ops->sample_volume(p->trident, v, &ev->data.sample.param.volume);
}
static void event_loop(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
{
if (v->sample_ops && v->sample_ops->sample_loop)
v->sample_ops->sample_loop(p->trident, v, &ev->data.sample.param.loop);
}
static void event_position(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
{
if (v->sample_ops && v->sample_ops->sample_pos)
v->sample_ops->sample_pos(p->trident, v, ev->data.sample.param.position);
}
static void event_private1(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
{
if (v->sample_ops && v->sample_ops->sample_private1)
v->sample_ops->sample_private1(p->trident, v, (unsigned char *) &ev->data.sample.param.raw8);
}
typedef void (trident_sample_event_handler_t) (struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v);
static trident_sample_event_handler_t *trident_sample_event_handlers[9] =
{
event_sample,
event_cluster,
event_start,
event_stop,
event_freq,
event_volume,
event_loop,
event_position,
event_private1
};
static void snd_trident_sample_event(struct snd_seq_event * ev, struct snd_trident_port * p)
{
int idx, voice;
struct snd_trident *trident = p->trident;
struct snd_trident_voice *v;
unsigned long flags;
idx = ev->type - SNDRV_SEQ_EVENT_SAMPLE;
if (idx < 0 || idx > 8)
return;
for (voice = 0; voice < 64; voice++) {
v = &trident->synth.voices[voice];
if (v->use && v->client == ev->source.client &&
v->port == ev->source.port &&
v->index == ev->data.sample.channel) {
spin_lock_irqsave(&trident->event_lock, flags);
trident_sample_event_handlers[idx] (ev, p, v);
spin_unlock_irqrestore(&trident->event_lock, flags);
return;
}
}
}
/*
*/
static void snd_trident_synth_free_voices(struct snd_trident * trident, int client, int port)
{
int idx;
struct snd_trident_voice *voice;
for (idx = 0; idx < 32; idx++) {
voice = &trident->synth.voices[idx];
if (voice->use && voice->client == client && voice->port == port)
snd_trident_free_voice(trident, voice);
}
}
static int snd_trident_synth_use(void *private_data, struct snd_seq_port_subscribe * info)
{
struct snd_trident_port *port = private_data;
struct snd_trident *trident = port->trident;
struct snd_trident_voice *voice;
unsigned int idx;
unsigned long flags;
if (info->voices > 32)
return -EINVAL;
spin_lock_irqsave(&trident->reg_lock, flags);
for (idx = 0; idx < info->voices; idx++) {
voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_SYNTH, info->sender.client, info->sender.port);
if (voice == NULL) {
snd_trident_synth_free_voices(trident, info->sender.client, info->sender.port);
spin_unlock_irqrestore(&trident->reg_lock, flags);
return -EBUSY;
}
voice->index = idx;
voice->Vol = 0x3ff;
voice->EC = 0x0fff;
}
#if 0
for (idx = 0; idx < info->midi_voices; idx++) {
port->midi_has_voices = 1;
voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_MIDI, info->sender.client, info->sender.port);
if (voice == NULL) {
snd_trident_synth_free_voices(trident, info->sender.client, info->sender.port);
spin_unlock_irqrestore(&trident->reg_lock, flags);
return -EBUSY;
}
voice->Vol = 0x3ff;
voice->EC = 0x0fff;
}
#endif
spin_unlock_irqrestore(&trident->reg_lock, flags);
return 0;
}
static int snd_trident_synth_unuse(void *private_data, struct snd_seq_port_subscribe * info)
{
struct snd_trident_port *port = private_data;
struct snd_trident *trident = port->trident;
unsigned long flags;
spin_lock_irqsave(&trident->reg_lock, flags);
snd_trident_synth_free_voices(trident, info->sender.client, info->sender.port);
spin_unlock_irqrestore(&trident->reg_lock, flags);
return 0;
}
/*
*/
static void snd_trident_synth_free_private_instruments(struct snd_trident_port * p, int client)
{
struct snd_seq_instr_header ifree;
memset(&ifree, 0, sizeof(ifree));
ifree.cmd = SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE;
snd_seq_instr_list_free_cond(p->trident->synth.ilist, &ifree, client, 0);
}
static int snd_trident_synth_event_input(struct snd_seq_event * ev, int direct, void *private_data, int atomic, int hop)
{
struct snd_trident_port *p = (struct snd_trident_port *) private_data;
if (p == NULL)
return -EINVAL;
if (ev->type >= SNDRV_SEQ_EVENT_SAMPLE &&
ev->type <= SNDRV_SEQ_EVENT_SAMPLE_PRIVATE1) {
snd_trident_sample_event(ev, p);
return 0;
}
if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM &&
ev->source.port == SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE) {
if (ev->type == SNDRV_SEQ_EVENT_CLIENT_EXIT) {
snd_trident_synth_free_private_instruments(p, ev->data.addr.client);
return 0;
}
}
if (direct) {
if (ev->type >= SNDRV_SEQ_EVENT_INSTR_BEGIN) {
snd_seq_instr_event(&p->trident->synth.simple_ops.kops,
p->trident->synth.ilist, ev,
p->trident->synth.seq_client, atomic, hop);
return 0;
}
}
return 0;
}
static void snd_trident_synth_instr_notify(void *private_data,
struct snd_seq_kinstr * instr,
int what)
{
int idx;
struct snd_trident *trident = private_data;
struct snd_trident_voice *pvoice;
unsigned long flags;
spin_lock_irqsave(&trident->event_lock, flags);
for (idx = 0; idx < 64; idx++) {
pvoice = &trident->synth.voices[idx];
if (pvoice->use && !memcmp(&pvoice->instr, &instr->instr, sizeof(pvoice->instr))) {
if (pvoice->sample_ops && pvoice->sample_ops->sample_stop) {
pvoice->sample_ops->sample_stop(trident, pvoice, SAMPLE_STOP_IMMEDIATELY);
} else {
snd_trident_stop_voice(trident, pvoice->number);
pvoice->flags &= ~SNDRV_TRIDENT_VFLG_RUNNING;
}
}
}
spin_unlock_irqrestore(&trident->event_lock, flags);
}
/*
*/
static void snd_trident_synth_free_port(void *private_data)
{
struct snd_trident_port *p = (struct snd_trident_port *) private_data;
if (p)
snd_midi_channel_free_set(p->chset);
}
static int snd_trident_synth_create_port(struct snd_trident * trident, int idx)
{
struct snd_trident_port *p;
struct snd_seq_port_callback callbacks;
char name[32];
char *str;
int result;
p = &trident->synth.seq_ports[idx];
p->chset = snd_midi_channel_alloc_set(16);
if (p->chset == NULL)
return -ENOMEM;
p->chset->private_data = p;
p->trident = trident;
p->client = trident->synth.seq_client;
memset(&callbacks, 0, sizeof(callbacks));
callbacks.owner = THIS_MODULE;
callbacks.use = snd_trident_synth_use;
callbacks.unuse = snd_trident_synth_unuse;
callbacks.event_input = snd_trident_synth_event_input;
callbacks.private_free = snd_trident_synth_free_port;
callbacks.private_data = p;
str = "???";
switch (trident->device) {
case TRIDENT_DEVICE_ID_DX: str = "Trident 4DWave-DX"; break;
case TRIDENT_DEVICE_ID_NX: str = "Trident 4DWave-NX"; break;
case TRIDENT_DEVICE_ID_SI7018: str = "SiS 7018"; break;
}
sprintf(name, "%s port %i", str, idx);
p->chset->port = snd_seq_event_port_attach(trident->synth.seq_client,
&callbacks,
SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE,
SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE |
SNDRV_SEQ_PORT_TYPE_SYNTH |
SNDRV_SEQ_PORT_TYPE_HARDWARE |
SNDRV_SEQ_PORT_TYPE_SYNTHESIZER,
16, 0,
name);
if (p->chset->port < 0) {
result = p->chset->port;
snd_trident_synth_free_port(p);
return result;
}
p->port = p->chset->port;
return 0;
}
/*
*/
static int snd_trident_synth_new_device(struct snd_seq_device *dev)
{
struct snd_trident *trident;
int client, i;
struct snd_seq_port_subscribe sub;
struct snd_simple_ops *simpleops;
char *str;
trident = *(struct snd_trident **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
if (trident == NULL)
return -EINVAL;
trident->synth.seq_client = -1;
/* allocate new client */
str = "???";
switch (trident->device) {
case TRIDENT_DEVICE_ID_DX: str = "Trident 4DWave-DX"; break;
case TRIDENT_DEVICE_ID_NX: str = "Trident 4DWave-NX"; break;
case TRIDENT_DEVICE_ID_SI7018: str = "SiS 7018"; break;
}
client = trident->synth.seq_client =
snd_seq_create_kernel_client(trident->card, 1, str);
if (client < 0)
return client;
for (i = 0; i < 4; i++)
snd_trident_synth_create_port(trident, i);
trident->synth.ilist = snd_seq_instr_list_new();
if (trident->synth.ilist == NULL) {
snd_seq_delete_kernel_client(client);
trident->synth.seq_client = -1;
return -ENOMEM;
}
trident->synth.ilist->flags = SNDRV_SEQ_INSTR_FLG_DIRECT;
simpleops = &trident->synth.simple_ops;
snd_seq_simple_init(simpleops, trident, NULL);
simpleops->put_sample = snd_trident_simple_put_sample;
simpleops->get_sample = snd_trident_simple_get_sample;
simpleops->remove_sample = snd_trident_simple_remove_sample;
simpleops->notify = snd_trident_synth_instr_notify;
memset(&sub, 0, sizeof(sub));
sub.sender.client = SNDRV_SEQ_CLIENT_SYSTEM;
sub.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
sub.dest.client = client;
sub.dest.port = 0;
snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &sub);
return 0;
}
static int snd_trident_synth_delete_device(struct snd_seq_device *dev)
{
struct snd_trident *trident;
trident = *(struct snd_trident **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
if (trident == NULL)
return -EINVAL;
if (trident->synth.seq_client >= 0) {
snd_seq_delete_kernel_client(trident->synth.seq_client);
trident->synth.seq_client = -1;
}
if (trident->synth.ilist)
snd_seq_instr_list_free(&trident->synth.ilist);
return 0;
}
static int __init alsa_trident_synth_init(void)
{
static struct snd_seq_dev_ops ops =
{
snd_trident_synth_new_device,
snd_trident_synth_delete_device
};
return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_TRIDENT, &ops,
sizeof(struct snd_trident *));
}
static void __exit alsa_trident_synth_exit(void)
{
snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_TRIDENT);
}
module_init(alsa_trident_synth_init)
module_exit(alsa_trident_synth_exit)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment