/* * 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; }