2005-04-17 02:20:36 +04:00
/*
* synth callback routines for Emu10k1
*
* Copyright ( C ) 2000 Takashi Iwai < tiwai @ suse . de >
*
* 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 "emu10k1_synth_local.h"
# include <sound/asoundef.h>
/* voice status */
enum {
V_FREE = 0 , V_OFF , V_RELEASED , V_PLAYING , V_END
} ;
/* Keeps track of what we are finding */
2005-11-17 16:50:13 +03:00
struct best_voice {
2005-04-17 02:20:36 +04:00
unsigned int time ;
int voice ;
2005-11-17 16:50:13 +03:00
} ;
2005-04-17 02:20:36 +04:00
/*
* prototypes
*/
2007-11-10 20:55:14 +03:00
static void lookup_voices ( struct snd_emux * emux , struct snd_emu10k1 * hw ,
2005-11-17 16:50:13 +03:00
struct best_voice * best , int active_only ) ;
2007-11-10 20:55:14 +03:00
static struct snd_emux_voice * get_voice ( struct snd_emux * emux ,
2005-11-17 16:50:13 +03:00
struct snd_emux_port * port ) ;
static int start_voice ( struct snd_emux_voice * vp ) ;
static void trigger_voice ( struct snd_emux_voice * vp ) ;
static void release_voice ( struct snd_emux_voice * vp ) ;
static void update_voice ( struct snd_emux_voice * vp , int update ) ;
static void terminate_voice ( struct snd_emux_voice * vp ) ;
static void free_voice ( struct snd_emux_voice * vp ) ;
static void set_fmmod ( struct snd_emu10k1 * hw , struct snd_emux_voice * vp ) ;
static void set_fm2frq2 ( struct snd_emu10k1 * hw , struct snd_emux_voice * vp ) ;
static void set_filterQ ( struct snd_emu10k1 * hw , struct snd_emux_voice * vp ) ;
2005-04-17 02:20:36 +04:00
/*
* Ensure a value is between two points
* macro evaluates its args more than once , so changed to upper - case .
*/
# define LIMITVALUE(x, a, b) do { if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b); } while (0)
# define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0)
/*
* set up operators
*/
2005-11-17 16:50:13 +03:00
static struct snd_emux_operators emu10k1_ops = {
2005-04-17 02:20:36 +04:00
. owner = THIS_MODULE ,
. get_voice = get_voice ,
. prepare = start_voice ,
. trigger = trigger_voice ,
. release = release_voice ,
. update = update_voice ,
. terminate = terminate_voice ,
. free_voice = free_voice ,
. sample_new = snd_emu10k1_sample_new ,
. sample_free = snd_emu10k1_sample_free ,
} ;
void
2007-11-10 20:55:14 +03:00
snd_emu10k1_ops_setup ( struct snd_emux * emux )
2005-04-17 02:20:36 +04:00
{
2007-11-10 20:55:14 +03:00
emux - > ops = emu10k1_ops ;
2005-04-17 02:20:36 +04:00
}
/*
* get more voice for pcm
*
* terminate most inactive voice and give it as a pcm voice .
*/
int
2005-11-17 16:50:13 +03:00
snd_emu10k1_synth_get_voice ( struct snd_emu10k1 * hw )
2005-04-17 02:20:36 +04:00
{
2005-11-17 16:50:13 +03:00
struct snd_emux * emu ;
struct snd_emux_voice * vp ;
struct best_voice best [ V_END ] ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
int i ;
emu = hw - > synth ;
spin_lock_irqsave ( & emu - > voice_lock , flags ) ;
lookup_voices ( emu , hw , best , 1 ) ; /* no OFF voices */
for ( i = 0 ; i < V_END ; i + + ) {
if ( best [ i ] . voice > = 0 ) {
int ch ;
vp = & emu - > voices [ best [ i ] . voice ] ;
if ( ( ch = vp - > ch ) < 0 ) {
2009-02-05 18:08:14 +03:00
/*
printk ( KERN_WARNING
" synth_get_voice: ch < 0 (%d) ?? " , i ) ;
*/
2005-04-17 02:20:36 +04:00
continue ;
}
vp - > emu - > num_voices - - ;
vp - > ch = - 1 ;
vp - > state = SNDRV_EMUX_ST_OFF ;
spin_unlock_irqrestore ( & emu - > voice_lock , flags ) ;
return ch ;
}
}
spin_unlock_irqrestore ( & emu - > voice_lock , flags ) ;
/* not found */
return - ENOMEM ;
}
/*
* turn off the voice ( not terminated )
*/
static void
2005-11-17 16:50:13 +03:00
release_voice ( struct snd_emux_voice * vp )
2005-04-17 02:20:36 +04:00
{
int dcysusv ;
2005-11-17 16:50:13 +03:00
struct snd_emu10k1 * hw ;
2005-04-17 02:20:36 +04:00
hw = vp - > hw ;
dcysusv = 0x8000 | ( unsigned char ) vp - > reg . parm . modrelease ;
snd_emu10k1_ptr_write ( hw , DCYSUSM , vp - > ch , dcysusv ) ;
dcysusv = 0x8000 | ( unsigned char ) vp - > reg . parm . volrelease | DCYSUSV_CHANNELENABLE_MASK ;
snd_emu10k1_ptr_write ( hw , DCYSUSV , vp - > ch , dcysusv ) ;
}
/*
* terminate the voice
*/
static void
2005-11-17 16:50:13 +03:00
terminate_voice ( struct snd_emux_voice * vp )
2005-04-17 02:20:36 +04:00
{
2005-11-17 16:50:13 +03:00
struct snd_emu10k1 * hw ;
2005-04-17 02:20:36 +04:00
2008-08-08 19:12:14 +04:00
if ( snd_BUG_ON ( ! vp ) )
return ;
2005-04-17 02:20:36 +04:00
hw = vp - > hw ;
snd_emu10k1_ptr_write ( hw , DCYSUSV , vp - > ch , 0x807f | DCYSUSV_CHANNELENABLE_MASK ) ;
if ( vp - > block ) {
2005-11-17 16:50:13 +03:00
struct snd_emu10k1_memblk * emem ;
emem = ( struct snd_emu10k1_memblk * ) vp - > block ;
2005-04-17 02:20:36 +04:00
if ( emem - > map_locked > 0 )
emem - > map_locked - - ;
}
}
/*
* release the voice to system
*/
static void
2005-11-17 16:50:13 +03:00
free_voice ( struct snd_emux_voice * vp )
2005-04-17 02:20:36 +04:00
{
2005-11-17 16:50:13 +03:00
struct snd_emu10k1 * hw ;
2005-04-17 02:20:36 +04:00
hw = vp - > hw ;
2007-11-10 20:55:14 +03:00
/* FIXME: emu10k1_synth is broken. */
/* This can get called with hw == 0 */
/* Problem apparent on plug, unplug then plug */
/* on the Audigy 2 ZS Notebook. */
if ( hw & & ( vp - > ch > = 0 ) ) {
2005-04-17 02:20:36 +04:00
snd_emu10k1_ptr_write ( hw , IFATN , vp - > ch , 0xff00 ) ;
snd_emu10k1_ptr_write ( hw , DCYSUSV , vp - > ch , 0x807f | DCYSUSV_CHANNELENABLE_MASK ) ;
// snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0);
snd_emu10k1_ptr_write ( hw , VTFT , vp - > ch , 0xffff ) ;
snd_emu10k1_ptr_write ( hw , CVCF , vp - > ch , 0xffff ) ;
snd_emu10k1_voice_free ( hw , & hw - > voices [ vp - > ch ] ) ;
vp - > emu - > num_voices - - ;
vp - > ch = - 1 ;
}
}
/*
* update registers
*/
static void
2005-11-17 16:50:13 +03:00
update_voice ( struct snd_emux_voice * vp , int update )
2005-04-17 02:20:36 +04:00
{
2005-11-17 16:50:13 +03:00
struct snd_emu10k1 * hw ;
2005-04-17 02:20:36 +04:00
hw = vp - > hw ;
if ( update & SNDRV_EMUX_UPDATE_VOLUME )
snd_emu10k1_ptr_write ( hw , IFATN_ATTENUATION , vp - > ch , vp - > avol ) ;
if ( update & SNDRV_EMUX_UPDATE_PITCH )
snd_emu10k1_ptr_write ( hw , IP , vp - > ch , vp - > apitch ) ;
if ( update & SNDRV_EMUX_UPDATE_PAN ) {
snd_emu10k1_ptr_write ( hw , PTRX_FXSENDAMOUNT_A , vp - > ch , vp - > apan ) ;
snd_emu10k1_ptr_write ( hw , PTRX_FXSENDAMOUNT_B , vp - > ch , vp - > aaux ) ;
}
if ( update & SNDRV_EMUX_UPDATE_FMMOD )
set_fmmod ( hw , vp ) ;
if ( update & SNDRV_EMUX_UPDATE_TREMFREQ )
snd_emu10k1_ptr_write ( hw , TREMFRQ , vp - > ch , vp - > reg . parm . tremfrq ) ;
if ( update & SNDRV_EMUX_UPDATE_FM2FRQ2 )
set_fm2frq2 ( hw , vp ) ;
if ( update & SNDRV_EMUX_UPDATE_Q )
set_filterQ ( hw , vp ) ;
}
/*
* look up voice table - get the best voice in order of preference
*/
/* spinlock held! */
static void
2005-11-17 16:50:13 +03:00
lookup_voices ( struct snd_emux * emu , struct snd_emu10k1 * hw ,
struct best_voice * best , int active_only )
2005-04-17 02:20:36 +04:00
{
2005-11-17 16:50:13 +03:00
struct snd_emux_voice * vp ;
struct best_voice * bp ;
2005-04-17 02:20:36 +04:00
int i ;
for ( i = 0 ; i < V_END ; i + + ) {
best [ i ] . time = ( unsigned int ) - 1 ; /* XXX MAX_?INT really */ ;
best [ i ] . voice = - 1 ;
}
/*
* Go through them all and get a best one to use .
* NOTE : could also look at volume and pick the quietest one .
*/
for ( i = 0 ; i < emu - > max_voices ; i + + ) {
int state , val ;
vp = & emu - > voices [ i ] ;
state = vp - > state ;
if ( state = = SNDRV_EMUX_ST_OFF ) {
if ( vp - > ch < 0 ) {
if ( active_only )
continue ;
bp = best + V_FREE ;
} else
bp = best + V_OFF ;
}
else if ( state = = SNDRV_EMUX_ST_RELEASED | |
state = = SNDRV_EMUX_ST_PENDING ) {
bp = best + V_RELEASED ;
2005-10-25 13:10:55 +04:00
# if 1
2005-04-17 02:20:36 +04:00
val = snd_emu10k1_ptr_read ( hw , CVCF_CURRENTVOL , vp - > ch ) ;
if ( ! val )
bp = best + V_OFF ;
# endif
}
else if ( state = = SNDRV_EMUX_ST_STANDBY )
continue ;
else if ( state & SNDRV_EMUX_ST_ON )
bp = best + V_PLAYING ;
else
continue ;
/* check if sample is finished playing (non-looping only) */
if ( bp ! = best + V_OFF & & bp ! = best + V_FREE & &
( vp - > reg . sample_mode & SNDRV_SFNT_SAMPLE_SINGLESHOT ) ) {
val = snd_emu10k1_ptr_read ( hw , CCCA_CURRADDR , vp - > ch ) ;
if ( val > = vp - > reg . loopstart )
bp = best + V_OFF ;
}
if ( vp - > time < bp - > time ) {
bp - > time = vp - > time ;
bp - > voice = i ;
}
}
}
/*
* get an empty voice
*
* emu - > voice_lock is already held .
*/
2005-11-17 16:50:13 +03:00
static struct snd_emux_voice *
get_voice ( struct snd_emux * emu , struct snd_emux_port * port )
2005-04-17 02:20:36 +04:00
{
2005-11-17 16:50:13 +03:00
struct snd_emu10k1 * hw ;
struct snd_emux_voice * vp ;
struct best_voice best [ V_END ] ;
2005-04-17 02:20:36 +04:00
int i ;
hw = emu - > hw ;
lookup_voices ( emu , hw , best , 0 ) ;
for ( i = 0 ; i < V_END ; i + + ) {
if ( best [ i ] . voice > = 0 ) {
vp = & emu - > voices [ best [ i ] . voice ] ;
if ( vp - > ch < 0 ) {
/* allocate a voice */
2005-11-17 16:50:13 +03:00
struct snd_emu10k1_voice * hwvoice ;
2005-04-17 02:20:36 +04:00
if ( snd_emu10k1_voice_alloc ( hw , EMU10K1_SYNTH , 1 , & hwvoice ) < 0 | | hwvoice = = NULL )
continue ;
vp - > ch = hwvoice - > number ;
emu - > num_voices + + ;
}
return vp ;
}
}
/* not found */
return NULL ;
}
/*
* prepare envelopes and LFOs
*/
static int
2005-11-17 16:50:13 +03:00
start_voice ( struct snd_emux_voice * vp )
2005-04-17 02:20:36 +04:00
{
unsigned int temp ;
int ch ;
unsigned int addr , mapped_offset ;
2005-11-17 16:50:13 +03:00
struct snd_midi_channel * chan ;
struct snd_emu10k1 * hw ;
struct snd_emu10k1_memblk * emem ;
2005-04-17 02:20:36 +04:00
hw = vp - > hw ;
ch = vp - > ch ;
2008-08-08 19:12:14 +04:00
if ( snd_BUG_ON ( ch < 0 ) )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
chan = vp - > chan ;
2005-11-17 16:50:13 +03:00
emem = ( struct snd_emu10k1_memblk * ) vp - > block ;
2005-04-17 02:20:36 +04:00
if ( emem = = NULL )
return - EINVAL ;
emem - > map_locked + + ;
if ( snd_emu10k1_memblk_map ( hw , emem ) < 0 ) {
2009-02-05 18:08:14 +03:00
/* printk(KERN_ERR "emu: cannot map!\n"); */
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
}
mapped_offset = snd_emu10k1_memblk_offset ( emem ) > > 1 ;
vp - > reg . start + = mapped_offset ;
vp - > reg . end + = mapped_offset ;
vp - > reg . loopstart + = mapped_offset ;
vp - > reg . loopend + = mapped_offset ;
/* set channel routing */
/* A = left(0), B = right(1), C = reverb(c), D = chorus(d) */
if ( hw - > audigy ) {
temp = FXBUS_MIDI_LEFT | ( FXBUS_MIDI_RIGHT < < 8 ) |
( FXBUS_MIDI_REVERB < < 16 ) | ( FXBUS_MIDI_CHORUS < < 24 ) ;
snd_emu10k1_ptr_write ( hw , A_FXRT1 , ch , temp ) ;
} else {
temp = ( FXBUS_MIDI_LEFT < < 16 ) | ( FXBUS_MIDI_RIGHT < < 20 ) |
( FXBUS_MIDI_REVERB < < 24 ) | ( FXBUS_MIDI_CHORUS < < 28 ) ;
snd_emu10k1_ptr_write ( hw , FXRT , ch , temp ) ;
}
/* channel to be silent and idle */
2005-10-25 13:10:55 +04:00
snd_emu10k1_ptr_write ( hw , DCYSUSV , ch , 0x0000 ) ;
2005-04-17 02:20:36 +04:00
snd_emu10k1_ptr_write ( hw , VTFT , ch , 0x0000FFFF ) ;
snd_emu10k1_ptr_write ( hw , CVCF , ch , 0x0000FFFF ) ;
snd_emu10k1_ptr_write ( hw , PTRX , ch , 0 ) ;
snd_emu10k1_ptr_write ( hw , CPF , ch , 0 ) ;
/* set pitch offset */
snd_emu10k1_ptr_write ( hw , IP , vp - > ch , vp - > apitch ) ;
/* set envelope parameters */
snd_emu10k1_ptr_write ( hw , ENVVAL , ch , vp - > reg . parm . moddelay ) ;
snd_emu10k1_ptr_write ( hw , ATKHLDM , ch , vp - > reg . parm . modatkhld ) ;
snd_emu10k1_ptr_write ( hw , DCYSUSM , ch , vp - > reg . parm . moddcysus ) ;
snd_emu10k1_ptr_write ( hw , ENVVOL , ch , vp - > reg . parm . voldelay ) ;
snd_emu10k1_ptr_write ( hw , ATKHLDV , ch , vp - > reg . parm . volatkhld ) ;
/* decay/sustain parameter for volume envelope is used
for triggerg the voice */
/* cutoff and volume */
temp = ( unsigned int ) vp - > acutoff < < 8 | ( unsigned char ) vp - > avol ;
snd_emu10k1_ptr_write ( hw , IFATN , vp - > ch , temp ) ;
/* modulation envelope heights */
snd_emu10k1_ptr_write ( hw , PEFE , ch , vp - > reg . parm . pefe ) ;
/* lfo1/2 delay */
snd_emu10k1_ptr_write ( hw , LFOVAL1 , ch , vp - > reg . parm . lfo1delay ) ;
snd_emu10k1_ptr_write ( hw , LFOVAL2 , ch , vp - > reg . parm . lfo2delay ) ;
/* lfo1 pitch & cutoff shift */
set_fmmod ( hw , vp ) ;
/* lfo1 volume & freq */
snd_emu10k1_ptr_write ( hw , TREMFRQ , vp - > ch , vp - > reg . parm . tremfrq ) ;
/* lfo2 pitch & freq */
set_fm2frq2 ( hw , vp ) ;
/* reverb and loop start (reverb 8bit, MSB) */
temp = vp - > reg . parm . reverb ;
temp + = ( int ) vp - > chan - > control [ MIDI_CTL_E1_REVERB_DEPTH ] * 9 / 10 ;
LIMITMAX ( temp , 255 ) ;
addr = vp - > reg . loopstart ;
snd_emu10k1_ptr_write ( hw , PSST , vp - > ch , ( temp < < 24 ) | addr ) ;
/* chorus & loop end (chorus 8bit, MSB) */
addr = vp - > reg . loopend ;
temp = vp - > reg . parm . chorus ;
temp + = ( int ) chan - > control [ MIDI_CTL_E3_CHORUS_DEPTH ] * 9 / 10 ;
LIMITMAX ( temp , 255 ) ;
temp = ( temp < < 24 ) | addr ;
snd_emu10k1_ptr_write ( hw , DSL , ch , temp ) ;
/* clear filter delay memory */
snd_emu10k1_ptr_write ( hw , Z1 , ch , 0 ) ;
snd_emu10k1_ptr_write ( hw , Z2 , ch , 0 ) ;
/* invalidate maps */
temp = ( hw - > silent_page . addr < < 1 ) | MAP_PTI_MASK ;
snd_emu10k1_ptr_write ( hw , MAPA , ch , temp ) ;
snd_emu10k1_ptr_write ( hw , MAPB , ch , temp ) ;
#if 0
/* cache */
{
unsigned int val , sample ;
val = 32 ;
if ( vp - > reg . sample_mode & SNDRV_SFNT_SAMPLE_8BITS )
sample = 0x80808080 ;
else {
sample = 0 ;
val * = 2 ;
}
/* cache */
snd_emu10k1_ptr_write ( hw , CCR , ch , 0x1c < < 16 ) ;
snd_emu10k1_ptr_write ( hw , CDE , ch , sample ) ;
snd_emu10k1_ptr_write ( hw , CDF , ch , sample ) ;
/* invalidate maps */
temp = ( ( unsigned int ) hw - > silent_page . addr < < 1 ) | MAP_PTI_MASK ;
snd_emu10k1_ptr_write ( hw , MAPA , ch , temp ) ;
snd_emu10k1_ptr_write ( hw , MAPB , ch , temp ) ;
/* fill cache */
val - = 4 ;
val < < = 25 ;
val | = 0x1c < < 16 ;
snd_emu10k1_ptr_write ( hw , CCR , ch , val ) ;
}
# endif
/* Q & current address (Q 4bit value, MSB) */
addr = vp - > reg . start ;
temp = vp - > reg . parm . filterQ ;
temp = ( temp < < 28 ) | addr ;
if ( vp - > apitch < 0xe400 )
temp | = CCCA_INTERPROM_0 ;
else {
unsigned int shift = ( vp - > apitch - 0xe000 ) > > 10 ;
temp | = shift < < 25 ;
}
if ( vp - > reg . sample_mode & SNDRV_SFNT_SAMPLE_8BITS )
temp | = CCCA_8BITSELECT ;
snd_emu10k1_ptr_write ( hw , CCCA , ch , temp ) ;
/* reset volume */
temp = ( unsigned int ) vp - > vtarget < < 16 ;
snd_emu10k1_ptr_write ( hw , VTFT , ch , temp | vp - > ftarget ) ;
snd_emu10k1_ptr_write ( hw , CVCF , ch , temp | 0xff00 ) ;
return 0 ;
}
/*
* Start envelope
*/
static void
2005-11-17 16:50:13 +03:00
trigger_voice ( struct snd_emux_voice * vp )
2005-04-17 02:20:36 +04:00
{
unsigned int temp , ptarget ;
2005-11-17 16:50:13 +03:00
struct snd_emu10k1 * hw ;
struct snd_emu10k1_memblk * emem ;
2005-04-17 02:20:36 +04:00
hw = vp - > hw ;
2005-11-17 16:50:13 +03:00
emem = ( struct snd_emu10k1_memblk * ) vp - > block ;
2005-04-17 02:20:36 +04:00
if ( ! emem | | emem - > mapped_page < 0 )
return ; /* not mapped */
#if 0
ptarget = ( unsigned int ) vp - > ptarget < < 16 ;
# else
ptarget = IP_TO_CP ( vp - > apitch ) ;
# endif
/* set pitch target and pan (volume) */
temp = ptarget | ( vp - > apan < < 8 ) | vp - > aaux ;
snd_emu10k1_ptr_write ( hw , PTRX , vp - > ch , temp ) ;
/* pitch target */
snd_emu10k1_ptr_write ( hw , CPF , vp - > ch , ptarget ) ;
/* trigger voice */
snd_emu10k1_ptr_write ( hw , DCYSUSV , vp - > ch , vp - > reg . parm . voldcysus | DCYSUSV_CHANNELENABLE_MASK ) ;
}
# define MOD_SENSE 18
/* set lfo1 modulation height and cutoff */
static void
2005-11-17 16:50:13 +03:00
set_fmmod ( struct snd_emu10k1 * hw , struct snd_emux_voice * vp )
2005-04-17 02:20:36 +04:00
{
unsigned short fmmod ;
short pitch ;
unsigned char cutoff ;
int modulation ;
pitch = ( char ) ( vp - > reg . parm . fmmod > > 8 ) ;
cutoff = ( vp - > reg . parm . fmmod & 0xff ) ;
modulation = vp - > chan - > gm_modulation + vp - > chan - > midi_pressure ;
pitch + = ( MOD_SENSE * modulation ) / 1200 ;
LIMITVALUE ( pitch , - 128 , 127 ) ;
fmmod = ( ( unsigned char ) pitch < < 8 ) | cutoff ;
snd_emu10k1_ptr_write ( hw , FMMOD , vp - > ch , fmmod ) ;
}
/* set lfo2 pitch & frequency */
static void
2005-11-17 16:50:13 +03:00
set_fm2frq2 ( struct snd_emu10k1 * hw , struct snd_emux_voice * vp )
2005-04-17 02:20:36 +04:00
{
unsigned short fm2frq2 ;
short pitch ;
unsigned char freq ;
int modulation ;
pitch = ( char ) ( vp - > reg . parm . fm2frq2 > > 8 ) ;
freq = vp - > reg . parm . fm2frq2 & 0xff ;
modulation = vp - > chan - > gm_modulation + vp - > chan - > midi_pressure ;
pitch + = ( MOD_SENSE * modulation ) / 1200 ;
LIMITVALUE ( pitch , - 128 , 127 ) ;
fm2frq2 = ( ( unsigned char ) pitch < < 8 ) | freq ;
snd_emu10k1_ptr_write ( hw , FM2FRQ2 , vp - > ch , fm2frq2 ) ;
}
/* set filterQ */
static void
2005-11-17 16:50:13 +03:00
set_filterQ ( struct snd_emu10k1 * hw , struct snd_emux_voice * vp )
2005-04-17 02:20:36 +04:00
{
unsigned int val ;
val = snd_emu10k1_ptr_read ( hw , CCCA , vp - > ch ) & ~ CCCA_RESONANCE ;
val | = ( vp - > reg . parm . filterQ < < 28 ) ;
snd_emu10k1_ptr_write ( hw , CCCA , vp - > ch , val ) ;
}