diff options
Diffstat (limited to 'sound/pci/emu10k1/emu10k1_patch.c')
-rw-r--r-- | sound/pci/emu10k1/emu10k1_patch.c | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/sound/pci/emu10k1/emu10k1_patch.c b/sound/pci/emu10k1/emu10k1_patch.c new file mode 100644 index 00000000..e10f027b --- /dev/null +++ b/sound/pci/emu10k1/emu10k1_patch.c @@ -0,0 +1,229 @@ +/* + * 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 +snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp, + struct snd_util_memhdr *hdr, + const void __user *data, long count) +{ + int offset; + int truesize, size, loopsize, blocksize; + int loopend, sampleend; + unsigned int start_addr; + struct snd_emu10k1 *emu; + + emu = rec->hw; + if (snd_BUG_ON(!sp || !hdr)) + 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; + if (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; + if (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; + if (offset + loopsize * 2 > blocksize) + return -EINVAL; + for (i = 0; i < loopsize; i++) + wblock[woffset + i] = wblock[woffset - i -1]; + offset += loopsize * 2; + } else { + if (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; + if (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 +snd_emu10k1_sample_free(struct snd_emux *rec, struct snd_sf_sample *sp, + struct snd_util_memhdr *hdr) +{ + struct snd_emu10k1 *emu; + + emu = rec->hw; + if (snd_BUG_ON(!sp || !hdr)) + return -EINVAL; + + if (sp->block) { + snd_emu10k1_synth_free(emu, sp->block); + sp->block = NULL; + } + return 0; +} + |