forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
216 lines
5.4 KiB
216 lines
5.4 KiB
// SPDX-License-Identifier: GPL-2.0-or-later |
|
/* |
|
* Patch transfer callback for Emu10k1 |
|
* |
|
* Copyright (C) 2000 Takashi iwai <[email protected]> |
|
*/ |
|
/* |
|
* 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, blocksize; |
|
__maybe_unused int loopsize; |
|
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) { |
|
dev_dbg(emu->card->dev, |
|
"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) |
|
swap(sp->v.loopstart, sp->v.loopend); |
|
|
|
/* 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) { |
|
dev_dbg(emu->card->dev, |
|
"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 supported 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; |
|
} |
|
|
|
|