linux/sound/pci/emu10k1/emu10k1_patch.c
<<
>>
Prefs
   1/*
   2 *  Patch transfer callback for Emu10k1
   3 *
   4 *  Copyright (C) 2000 Takashi iwai <tiwai@suse.de>
   5 *
   6 *   This program is free software; you can redistribute it and/or modify
   7 *   it under the terms of the GNU General Public License as published by
   8 *   the Free Software Foundation; either version 2 of the License, or
   9 *   (at your option) any later version.
  10 *
  11 *   This program is distributed in the hope that it will be useful,
  12 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 *   GNU General Public License for more details.
  15 *
  16 *   You should have received a copy of the GNU General Public License
  17 *   along with this program; if not, write to the Free Software
  18 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  19 */
  20/*
  21 * All the code for loading in a patch.  There is very little that is
  22 * chip specific here.  Just the actual writing to the board.
  23 */
  24
  25#include "emu10k1_synth_local.h"
  26
  27/*
  28 */
  29#define BLANK_LOOP_START        4
  30#define BLANK_LOOP_END          8
  31#define BLANK_LOOP_SIZE         12
  32#define BLANK_HEAD_SIZE         32
  33
  34/*
  35 * allocate a sample block and copy data from userspace
  36 */
  37int
  38snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
  39                       struct snd_util_memhdr *hdr,
  40                       const void __user *data, long count)
  41{
  42        int offset;
  43        int truesize, size, loopsize, blocksize;
  44        int loopend, sampleend;
  45        unsigned int start_addr;
  46        struct snd_emu10k1 *emu;
  47
  48        emu = rec->hw;
  49        if (snd_BUG_ON(!sp || !hdr))
  50                return -EINVAL;
  51
  52        if (sp->v.size == 0) {
  53                snd_printd("emu: rom font for sample %d\n", sp->v.sample);
  54                return 0;
  55        }
  56
  57        /* recalculate address offset */
  58        sp->v.end -= sp->v.start;
  59        sp->v.loopstart -= sp->v.start;
  60        sp->v.loopend -= sp->v.start;
  61        sp->v.start = 0;
  62
  63        /* some samples have invalid data.  the addresses are corrected in voice info */
  64        sampleend = sp->v.end;
  65        if (sampleend > sp->v.size)
  66                sampleend = sp->v.size;
  67        loopend = sp->v.loopend;
  68        if (loopend > sampleend)
  69                loopend = sampleend;
  70
  71        /* be sure loop points start < end */
  72        if (sp->v.loopstart >= sp->v.loopend) {
  73                int tmp = sp->v.loopstart;
  74                sp->v.loopstart = sp->v.loopend;
  75                sp->v.loopend = tmp;
  76        }
  77
  78        /* compute true data size to be loaded */
  79        truesize = sp->v.size + BLANK_HEAD_SIZE;
  80        loopsize = 0;
  81#if 0 /* not supported */
  82        if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP))
  83                loopsize = sp->v.loopend - sp->v.loopstart;
  84        truesize += loopsize;
  85#endif
  86        if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK)
  87                truesize += BLANK_LOOP_SIZE;
  88
  89        /* try to allocate a memory block */
  90        blocksize = truesize;
  91        if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
  92                blocksize *= 2;
  93        sp->block = snd_emu10k1_synth_alloc(emu, blocksize);
  94        if (sp->block == NULL) {
  95                snd_printd("emu10k1: synth malloc failed (size=%d)\n", blocksize);
  96                /* not ENOMEM (for compatibility with OSS) */
  97                return -ENOSPC;
  98        }
  99        /* set the total size */
 100        sp->v.truesize = blocksize;
 101
 102        /* write blank samples at head */
 103        offset = 0;
 104        size = BLANK_HEAD_SIZE;
 105        if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
 106                size *= 2;
 107        if (offset + size > blocksize)
 108                return -EINVAL;
 109        snd_emu10k1_synth_bzero(emu, sp->block, offset, size);
 110        offset += size;
 111
 112        /* copy start->loopend */
 113        size = loopend;
 114        if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
 115                size *= 2;
 116        if (offset + size > blocksize)
 117                return -EINVAL;
 118        if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) {
 119                snd_emu10k1_synth_free(emu, sp->block);
 120                sp->block = NULL;
 121                return -EFAULT;
 122        }
 123        offset += size;
 124        data += size;
 125
 126#if 0 /* not suppported yet */
 127        /* handle reverse (or bidirectional) loop */
 128        if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) {
 129                /* copy loop in reverse */
 130                if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) {
 131                        int woffset;
 132                        unsigned short *wblock = (unsigned short*)block;
 133                        woffset = offset / 2;
 134                        if (offset + loopsize * 2 > blocksize)
 135                                return -EINVAL;
 136                        for (i = 0; i < loopsize; i++)
 137                                wblock[woffset + i] = wblock[woffset - i -1];
 138                        offset += loopsize * 2;
 139                } else {
 140                        if (offset + loopsize > blocksize)
 141                                return -EINVAL;
 142                        for (i = 0; i < loopsize; i++)
 143                                block[offset + i] = block[offset - i -1];
 144                        offset += loopsize;
 145                }
 146
 147                /* modify loop pointers */
 148                if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_BIDIR_LOOP) {
 149                        sp->v.loopend += loopsize;
 150                } else {
 151                        sp->v.loopstart += loopsize;
 152                        sp->v.loopend += loopsize;
 153                }
 154                /* add sample pointer */
 155                sp->v.end += loopsize;
 156        }
 157#endif
 158
 159        /* loopend -> sample end */
 160        size = sp->v.size - loopend;
 161        if (size < 0)
 162                return -EINVAL;
 163        if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
 164                size *= 2;
 165        if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) {
 166                snd_emu10k1_synth_free(emu, sp->block);
 167                sp->block = NULL;
 168                return -EFAULT;
 169        }
 170        offset += size;
 171
 172        /* clear rest of samples (if any) */
 173        if (offset < blocksize)
 174                snd_emu10k1_synth_bzero(emu, sp->block, offset, blocksize - offset);
 175
 176        if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) {
 177                /* if no blank loop is attached in the sample, add it */
 178                if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) {
 179                        sp->v.loopstart = sp->v.end + BLANK_LOOP_START;
 180                        sp->v.loopend = sp->v.end + BLANK_LOOP_END;
 181                }
 182        }
 183
 184#if 0 /* not supported yet */
 185        if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) {
 186                /* unsigned -> signed */
 187                if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) {
 188                        unsigned short *wblock = (unsigned short*)block;
 189                        for (i = 0; i < truesize; i++)
 190                                wblock[i] ^= 0x8000;
 191                } else {
 192                        for (i = 0; i < truesize; i++)
 193                                block[i] ^= 0x80;
 194                }
 195        }
 196#endif
 197
 198        /* recalculate offset */
 199        start_addr = BLANK_HEAD_SIZE * 2;
 200        if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
 201                start_addr >>= 1;
 202        sp->v.start += start_addr;
 203        sp->v.end += start_addr;
 204        sp->v.loopstart += start_addr;
 205        sp->v.loopend += start_addr;
 206
 207        return 0;
 208}
 209
 210/*
 211 * free a sample block
 212 */
 213int
 214snd_emu10k1_sample_free(struct snd_emux *rec, struct snd_sf_sample *sp,
 215                        struct snd_util_memhdr *hdr)
 216{
 217        struct snd_emu10k1 *emu;
 218
 219        emu = rec->hw;
 220        if (snd_BUG_ON(!sp || !hdr))
 221                return -EINVAL;
 222
 223        if (sp->block) {
 224                snd_emu10k1_synth_free(emu, sp->block);
 225                sp->block = NULL;
 226        }
 227        return 0;
 228}
 229
 230