2005-04-17 02:20:36 +04:00
/*
* Patch transfer callback 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
*/
/*
* All the code for loading in a patch . There is very little that is
* chip specific here . Just the actual writing to the board .
*/
# include "emu10k1_synth_local.h"
/*
*/
# define BLANK_LOOP_START 4
# define BLANK_LOOP_END 8
# define BLANK_LOOP_SIZE 12
# define BLANK_HEAD_SIZE 32
/*
* allocate a sample block and copy data from userspace
*/
int
2005-11-17 16:50:13 +03:00
snd_emu10k1_sample_new ( struct snd_emux * rec , struct snd_sf_sample * sp ,
struct snd_util_memhdr * hdr ,
const void __user * data , long count )
2005-04-17 02:20:36 +04:00
{
int offset ;
int truesize , size , loopsize , blocksize ;
int loopend , sampleend ;
unsigned int start_addr ;
2005-11-17 16:50:13 +03:00
struct snd_emu10k1 * emu ;
2005-04-17 02:20:36 +04:00
emu = rec - > hw ;
snd_assert ( sp ! = NULL , return - EINVAL ) ;
snd_assert ( hdr ! = NULL , return - EINVAL ) ;
if ( sp - > v . size = = 0 ) {
snd_printd ( " emu: rom font for sample %d \n " , sp - > v . sample ) ;
return 0 ;
}
/* recalculate address offset */
sp - > v . end - = sp - > v . start ;
sp - > v . loopstart - = sp - > v . start ;
sp - > v . loopend - = sp - > v . start ;
sp - > v . start = 0 ;
/* some samples have invalid data. the addresses are corrected in voice info */
sampleend = sp - > v . end ;
if ( sampleend > sp - > v . size )
sampleend = sp - > v . size ;
loopend = sp - > v . loopend ;
if ( loopend > sampleend )
loopend = sampleend ;
/* be sure loop points start < end */
if ( sp - > v . loopstart > = sp - > v . loopend ) {
int tmp = sp - > v . loopstart ;
sp - > v . loopstart = sp - > v . loopend ;
sp - > v . loopend = tmp ;
}
/* compute true data size to be loaded */
truesize = sp - > v . size + BLANK_HEAD_SIZE ;
loopsize = 0 ;
#if 0 /* not supported */
if ( sp - > v . mode_flags & ( SNDRV_SFNT_SAMPLE_BIDIR_LOOP | SNDRV_SFNT_SAMPLE_REVERSE_LOOP ) )
loopsize = sp - > v . loopend - sp - > v . loopstart ;
truesize + = loopsize ;
# endif
if ( sp - > v . mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK )
truesize + = BLANK_LOOP_SIZE ;
/* try to allocate a memory block */
blocksize = truesize ;
if ( ! ( sp - > v . mode_flags & SNDRV_SFNT_SAMPLE_8BITS ) )
blocksize * = 2 ;
sp - > block = snd_emu10k1_synth_alloc ( emu , blocksize ) ;
if ( sp - > block = = NULL ) {
snd_printd ( " emu10k1: synth malloc failed (size=%d) \n " , blocksize ) ;
/* not ENOMEM (for compatibility with OSS) */
return - ENOSPC ;
}
/* set the total size */
sp - > v . truesize = blocksize ;
/* write blank samples at head */
offset = 0 ;
size = BLANK_HEAD_SIZE ;
if ( ! ( sp - > v . mode_flags & SNDRV_SFNT_SAMPLE_8BITS ) )
size * = 2 ;
snd_assert ( offset + size < = blocksize , return - EINVAL ) ;
snd_emu10k1_synth_bzero ( emu , sp - > block , offset , size ) ;
offset + = size ;
/* copy start->loopend */
size = loopend ;
if ( ! ( sp - > v . mode_flags & SNDRV_SFNT_SAMPLE_8BITS ) )
size * = 2 ;
snd_assert ( offset + size < = blocksize , return - EINVAL ) ;
if ( snd_emu10k1_synth_copy_from_user ( emu , sp - > block , offset , data , size ) ) {
snd_emu10k1_synth_free ( emu , sp - > block ) ;
sp - > block = NULL ;
return - EFAULT ;
}
offset + = size ;
data + = size ;
#if 0 /* not suppported yet */
/* handle reverse (or bidirectional) loop */
if ( sp - > v . mode_flags & ( SNDRV_SFNT_SAMPLE_BIDIR_LOOP | SNDRV_SFNT_SAMPLE_REVERSE_LOOP ) ) {
/* copy loop in reverse */
if ( ! ( sp - > v . mode_flags & SNDRV_SFNT_SAMPLE_8BITS ) ) {
int woffset ;
unsigned short * wblock = ( unsigned short * ) block ;
woffset = offset / 2 ;
snd_assert ( offset + loopsize * 2 < = blocksize , return - EINVAL ) ;
for ( i = 0 ; i < loopsize ; i + + )
wblock [ woffset + i ] = wblock [ woffset - i - 1 ] ;
offset + = loopsize * 2 ;
} else {
snd_assert ( offset + loopsize < = blocksize , return - EINVAL ) ;
for ( i = 0 ; i < loopsize ; i + + )
block [ offset + i ] = block [ offset - i - 1 ] ;
offset + = loopsize ;
}
/* modify loop pointers */
if ( sp - > v . mode_flags & SNDRV_SFNT_SAMPLE_BIDIR_LOOP ) {
sp - > v . loopend + = loopsize ;
} else {
sp - > v . loopstart + = loopsize ;
sp - > v . loopend + = loopsize ;
}
/* add sample pointer */
sp - > v . end + = loopsize ;
}
# endif
/* loopend -> sample end */
size = sp - > v . size - loopend ;
snd_assert ( size > = 0 , return - EINVAL ) ;
if ( ! ( sp - > v . mode_flags & SNDRV_SFNT_SAMPLE_8BITS ) )
size * = 2 ;
if ( snd_emu10k1_synth_copy_from_user ( emu , sp - > block , offset , data , size ) ) {
snd_emu10k1_synth_free ( emu , sp - > block ) ;
sp - > block = NULL ;
return - EFAULT ;
}
offset + = size ;
/* clear rest of samples (if any) */
if ( offset < blocksize )
snd_emu10k1_synth_bzero ( emu , sp - > block , offset , blocksize - offset ) ;
if ( sp - > v . mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK ) {
/* if no blank loop is attached in the sample, add it */
if ( sp - > v . mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT ) {
sp - > v . loopstart = sp - > v . end + BLANK_LOOP_START ;
sp - > v . loopend = sp - > v . end + BLANK_LOOP_END ;
}
}
#if 0 /* not supported yet */
if ( sp - > v . mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED ) {
/* unsigned -> signed */
if ( ! ( sp - > v . mode_flags & SNDRV_SFNT_SAMPLE_8BITS ) ) {
unsigned short * wblock = ( unsigned short * ) block ;
for ( i = 0 ; i < truesize ; i + + )
wblock [ i ] ^ = 0x8000 ;
} else {
for ( i = 0 ; i < truesize ; i + + )
block [ i ] ^ = 0x80 ;
}
}
# endif
/* recalculate offset */
start_addr = BLANK_HEAD_SIZE * 2 ;
if ( ! ( sp - > v . mode_flags & SNDRV_SFNT_SAMPLE_8BITS ) )
start_addr > > = 1 ;
sp - > v . start + = start_addr ;
sp - > v . end + = start_addr ;
sp - > v . loopstart + = start_addr ;
sp - > v . loopend + = start_addr ;
return 0 ;
}
/*
* free a sample block
*/
int
2005-11-17 16:50:13 +03:00
snd_emu10k1_sample_free ( struct snd_emux * rec , struct snd_sf_sample * sp ,
struct snd_util_memhdr * hdr )
2005-04-17 02:20:36 +04:00
{
2005-11-17 16:50:13 +03:00
struct snd_emu10k1 * emu ;
2005-04-17 02:20:36 +04:00
emu = rec - > hw ;
snd_assert ( sp ! = NULL , return - EINVAL ) ;
snd_assert ( hdr ! = NULL , return - EINVAL ) ;
if ( sp - > block ) {
snd_emu10k1_synth_free ( emu , sp - > block ) ;
sp - > block = NULL ;
}
return 0 ;
}