📄 soundfont.c
字号:
/* * Soundfont generic routines. * It is intended that these should be used by any driver that is willing * to accept soundfont patches. * * Copyright (C) 1999 Steve Ratcliffe * Copyright (c) 1999-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 *//* * Deal with reading in of a soundfont. Code follows the OSS way * of doing things so that the old sfxload utility can be used. * Everything may change when there is an alsa way of doing things. */#include <sound/driver.h>#include <asm/uaccess.h>#include <linux/slab.h>#include <sound/core.h>#include <sound/soundfont.h>#include <sound/seq_oss_legacy.h>/* Prototypes for static functions */static int open_patch(snd_sf_list_t *sflist, const char __user *data, int count, int client);static snd_soundfont_t *newsf(snd_sf_list_t *sflist, int type, char *name);static int is_identical_font(snd_soundfont_t *sf, int type, unsigned char *name);static int close_patch(snd_sf_list_t *sflist);static int probe_data(snd_sf_list_t *sflist, int sample_id);static void set_zone_counter(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_zone_t *zp);static snd_sf_zone_t *sf_zone_new(snd_sf_list_t *sflist, snd_soundfont_t *sf);static void set_sample_counter(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_sample_t *sp);static snd_sf_sample_t *sf_sample_new(snd_sf_list_t *sflist, snd_soundfont_t *sf);static void sf_sample_delete(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_sample_t *sp);static int load_map(snd_sf_list_t *sflist, const void __user *data, int count);static int load_info(snd_sf_list_t *sflist, const void __user *data, long count);static int remove_info(snd_sf_list_t *sflist, snd_soundfont_t *sf, int bank, int instr);static void init_voice_info(soundfont_voice_info_t *avp);static void init_voice_parm(soundfont_voice_parm_t *pp);static snd_sf_sample_t *set_sample(snd_soundfont_t *sf, soundfont_voice_info_t *avp);static snd_sf_sample_t *find_sample(snd_soundfont_t *sf, int sample_id);static int load_data(snd_sf_list_t *sflist, const void __user *data, long count);static void rebuild_presets(snd_sf_list_t *sflist);static void add_preset(snd_sf_list_t *sflist, snd_sf_zone_t *cur);static void delete_preset(snd_sf_list_t *sflist, snd_sf_zone_t *zp);static snd_sf_zone_t *search_first_zone(snd_sf_list_t *sflist, int bank, int preset, int key);static int search_zones(snd_sf_list_t *sflist, int *notep, int vel, int preset, int bank, snd_sf_zone_t **table, int max_layers, int level);static int get_index(int bank, int instr, int key);static void snd_sf_init(snd_sf_list_t *sflist);static void snd_sf_clear(snd_sf_list_t *sflist);/* * lock access to sflist */static voidlock_preset(snd_sf_list_t *sflist){ unsigned long flags; down(&sflist->presets_mutex); spin_lock_irqsave(&sflist->lock, flags); sflist->presets_locked = 1; spin_unlock_irqrestore(&sflist->lock, flags);}/* * remove lock */static voidunlock_preset(snd_sf_list_t *sflist){ unsigned long flags; spin_lock_irqsave(&sflist->lock, flags); sflist->presets_locked = 0; spin_unlock_irqrestore(&sflist->lock, flags); up(&sflist->presets_mutex);}/* * close the patch if the patch was opened by this client. */intsnd_soundfont_close_check(snd_sf_list_t *sflist, int client){ unsigned long flags; spin_lock_irqsave(&sflist->lock, flags); if (sflist->open_client == client) { spin_unlock_irqrestore(&sflist->lock, flags); return close_patch(sflist); } spin_unlock_irqrestore(&sflist->lock, flags); return 0;}/* * Deal with a soundfont patch. Any driver could use these routines * although it was designed for the AWE64. * * The sample_write and callargs pararameters allow a callback into * the actual driver to write sample data to the board or whatever * it wants to do with it. */intsnd_soundfont_load(snd_sf_list_t *sflist, const void __user *data, long count, int client){ soundfont_patch_info_t patch; unsigned long flags; int rc; if (count < (long)sizeof(patch)) { snd_printk("patch record too small %ld\n", count); return -EINVAL; } if (copy_from_user(&patch, data, sizeof(patch))) return -EFAULT; count -= sizeof(patch); data += sizeof(patch); if (patch.key != SNDRV_OSS_SOUNDFONT_PATCH) { snd_printk("'The wrong kind of patch' %x\n", patch.key); return -EINVAL; } if (count < patch.len) { snd_printk("Patch too short %ld, need %d\n", count, patch.len); return -EINVAL; } if (patch.len < 0) { snd_printk("poor length %d\n", patch.len); return -EINVAL; } if (patch.type == SNDRV_SFNT_OPEN_PATCH) { /* grab sflist to open */ lock_preset(sflist); rc = open_patch(sflist, data, count, client); unlock_preset(sflist); return rc; } /* check if other client already opened patch */ spin_lock_irqsave(&sflist->lock, flags); if (sflist->open_client != client) { spin_unlock_irqrestore(&sflist->lock, flags); return -EBUSY; } spin_unlock_irqrestore(&sflist->lock, flags); lock_preset(sflist); rc = -EINVAL; switch (patch.type) { case SNDRV_SFNT_LOAD_INFO: rc = load_info(sflist, data, count); break; case SNDRV_SFNT_LOAD_DATA: rc = load_data(sflist, data, count); break; case SNDRV_SFNT_CLOSE_PATCH: rc = close_patch(sflist); break; case SNDRV_SFNT_REPLACE_DATA: /*rc = replace_data(&patch, data, count);*/ break; case SNDRV_SFNT_MAP_PRESET: rc = load_map(sflist, data, count); break; case SNDRV_SFNT_PROBE_DATA: rc = probe_data(sflist, patch.optarg); break; case SNDRV_SFNT_REMOVE_INFO: /* patch must be opened */ if (sflist->currsf) { snd_printk("soundfont: remove_info: patch not opened\n"); rc = -EINVAL; } else { int bank, instr; bank = ((unsigned short)patch.optarg >> 8) & 0xff; instr = (unsigned short)patch.optarg & 0xff; if (! remove_info(sflist, sflist->currsf, bank, instr)) rc = -EINVAL; else rc = 0; } break; } unlock_preset(sflist); return rc;}/* check if specified type is special font (GUS or preset-alias) */static inline intis_special_type(int type){ type &= 0x0f; return (type == SNDRV_SFNT_PAT_TYPE_GUS || type == SNDRV_SFNT_PAT_TYPE_MAP);}/* open patch; create sf list */static intopen_patch(snd_sf_list_t *sflist, const char __user *data, int count, int client){ soundfont_open_parm_t parm; snd_soundfont_t *sf; unsigned long flags; spin_lock_irqsave(&sflist->lock, flags); if (sflist->open_client >= 0 || sflist->currsf) { spin_unlock_irqrestore(&sflist->lock, flags); return -EBUSY; } spin_unlock_irqrestore(&sflist->lock, flags); if (copy_from_user(&parm, data, sizeof(parm))) return -EFAULT; if (is_special_type(parm.type)) { parm.type |= SNDRV_SFNT_PAT_SHARED; sf = newsf(sflist, parm.type, NULL); } else sf = newsf(sflist, parm.type, parm.name); if (sf == NULL) { return -ENOMEM; } spin_lock_irqsave(&sflist->lock, flags); sflist->open_client = client; sflist->currsf = sf; spin_unlock_irqrestore(&sflist->lock, flags); return 0;}/* * Allocate a new soundfont structure. */static snd_soundfont_t *newsf(snd_sf_list_t *sflist, int type, char *name){ snd_soundfont_t *sf; /* check the shared fonts */ if (type & SNDRV_SFNT_PAT_SHARED) { for (sf = sflist->fonts; sf; sf = sf->next) { if (is_identical_font(sf, type, name)) { return sf; } } } /* not found -- create a new one */ sf = kzalloc(sizeof(*sf), GFP_KERNEL); if (sf == NULL) return NULL; sf->id = sflist->fonts_size; sflist->fonts_size++; /* prepend this record */ sf->next = sflist->fonts; sflist->fonts = sf; sf->type = type; sf->zones = NULL; sf->samples = NULL; if (name) memcpy(sf->name, name, SNDRV_SFNT_PATCH_NAME_LEN); return sf;}/* check if the given name matches to the existing list */static intis_identical_font(snd_soundfont_t *sf, int type, unsigned char *name){ return ((sf->type & SNDRV_SFNT_PAT_SHARED) && (sf->type & 0x0f) == (type & 0x0f) && (name == NULL || memcmp(sf->name, name, SNDRV_SFNT_PATCH_NAME_LEN) == 0));}/* * Close the current patch. */static intclose_patch(snd_sf_list_t *sflist){ unsigned long flags; spin_lock_irqsave(&sflist->lock, flags); sflist->currsf = NULL; sflist->open_client = -1; spin_unlock_irqrestore(&sflist->lock, flags); rebuild_presets(sflist); return 0;}/* probe sample in the current list -- nothing to be loaded */static intprobe_data(snd_sf_list_t *sflist, int sample_id){ /* patch must be opened */ if (sflist->currsf) { /* search the specified sample by optarg */ if (find_sample(sflist->currsf, sample_id)) return 0; } return -EINVAL;}/* * increment zone counter */static voidset_zone_counter(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_zone_t *zp){ zp->counter = sflist->zone_counter++; if (sf->type & SNDRV_SFNT_PAT_LOCKED) sflist->zone_locked = sflist->zone_counter;}/* * allocate a new zone record */static snd_sf_zone_t *sf_zone_new(snd_sf_list_t *sflist, snd_soundfont_t *sf){ snd_sf_zone_t *zp; if ((zp = kzalloc(sizeof(*zp), GFP_KERNEL)) == NULL) return NULL; zp->next = sf->zones; sf->zones = zp; init_voice_info(&zp->v); set_zone_counter(sflist, sf, zp); return zp;}/* * increment sample couter */static voidset_sample_counter(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_sample_t *sp){ sp->counter = sflist->sample_counter++; if (sf->type & SNDRV_SFNT_PAT_LOCKED) sflist->sample_locked = sflist->sample_counter;}/* * allocate a new sample list record */static snd_sf_sample_t *sf_sample_new(snd_sf_list_t *sflist, snd_soundfont_t *sf){ snd_sf_sample_t *sp; if ((sp = kzalloc(sizeof(*sp), GFP_KERNEL)) == NULL) return NULL; sp->next = sf->samples; sf->samples = sp; set_sample_counter(sflist, sf, sp); return sp;}/* * delete sample list -- this is an exceptional job. * only the last allocated sample can be deleted. */static voidsf_sample_delete(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_sample_t *sp){ /* only last sample is accepted */ if (sp == sf->samples) { sf->samples = sp->next; kfree(sp); }}/* load voice map */static intload_map(snd_sf_list_t *sflist, const void __user *data, int count){ snd_sf_zone_t *zp, *prevp; snd_soundfont_t *sf; soundfont_voice_map_t map; /* get the link info */ if (count < (int)sizeof(map)) return -EINVAL; if (copy_from_user(&map, data, sizeof(map))) return -EFAULT; if (map.map_instr < 0 || map.map_instr >= SF_MAX_INSTRUMENTS) return -EINVAL; sf = newsf(sflist, SNDRV_SFNT_PAT_TYPE_MAP|SNDRV_SFNT_PAT_SHARED, NULL); if (sf == NULL) return -ENOMEM; prevp = NULL; for (zp = sf->zones; zp; prevp = zp, zp = zp->next) { if (zp->mapped && zp->instr == map.map_instr && zp->bank == map.map_bank && zp->v.low == map.map_key && zp->v.start == map.src_instr && zp->v.end == map.src_bank && zp->v.fixkey == map.src_key) { /* the same mapping is already present */ /* relink this record to the link head */ if (prevp) { prevp->next = zp->next; zp->next = sf->zones; sf->zones = zp; } /* update the counter */ set_zone_counter(sflist, sf, zp); return 0; } } /* create a new zone */ if ((zp = sf_zone_new(sflist, sf)) == NULL) return -ENOMEM; zp->bank = map.map_bank; zp->instr = map.map_instr; zp->mapped = 1; if (map.map_key >= 0) { zp->v.low = map.map_key; zp->v.high = map.map_key; } zp->v.start = map.src_instr; zp->v.end = map.src_bank; zp->v.fixkey = map.src_key; zp->v.sf_id = sf->id; add_preset(sflist, zp); return 0;}/* remove the present instrument layers */static intremove_info(snd_sf_list_t *sflist, snd_soundfont_t *sf, int bank, int instr){ snd_sf_zone_t *prev, *next, *p; int removed = 0; prev = NULL; for (p = sf->zones; p; p = next) { next = p->next; if (! p->mapped && p->bank == bank && p->instr == instr) { /* remove this layer */ if (prev) prev->next = next; else sf->zones = next; removed++; kfree(p);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -