| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | *  Soundfont generic routines. | 
|  | 3 | *	It is intended that these should be used by any driver that is willing | 
|  | 4 | *	to accept soundfont patches. | 
|  | 5 | * | 
|  | 6 | *  Copyright (C) 1999 Steve Ratcliffe | 
|  | 7 | *  Copyright (c) 1999-2000 Takashi Iwai <tiwai@suse.de> | 
|  | 8 | * | 
|  | 9 | *   This program is free software; you can redistribute it and/or modify | 
|  | 10 | *   it under the terms of the GNU General Public License as published by | 
|  | 11 | *   the Free Software Foundation; either version 2 of the License, or | 
|  | 12 | *   (at your option) any later version. | 
|  | 13 | * | 
|  | 14 | *   This program is distributed in the hope that it will be useful, | 
|  | 15 | *   but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 16 | *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | 17 | *   GNU General Public License for more details. | 
|  | 18 | * | 
|  | 19 | *   You should have received a copy of the GNU General Public License | 
|  | 20 | *   along with this program; if not, write to the Free Software | 
|  | 21 | *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA | 
|  | 22 | */ | 
|  | 23 | /* | 
|  | 24 | * Deal with reading in of a soundfont.  Code follows the OSS way | 
|  | 25 | * of doing things so that the old sfxload utility can be used. | 
|  | 26 | * Everything may change when there is an alsa way of doing things. | 
|  | 27 | */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 28 | #include <asm/uaccess.h> | 
|  | 29 | #include <linux/slab.h> | 
|  | 30 | #include <sound/core.h> | 
|  | 31 | #include <sound/soundfont.h> | 
|  | 32 | #include <sound/seq_oss_legacy.h> | 
|  | 33 |  | 
|  | 34 | /* Prototypes for static functions */ | 
|  | 35 |  | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 36 | static int open_patch(struct snd_sf_list *sflist, const char __user *data, | 
|  | 37 | int count, int client); | 
|  | 38 | static struct snd_soundfont *newsf(struct snd_sf_list *sflist, int type, char *name); | 
|  | 39 | static int is_identical_font(struct snd_soundfont *sf, int type, unsigned char *name); | 
|  | 40 | static int close_patch(struct snd_sf_list *sflist); | 
|  | 41 | static int probe_data(struct snd_sf_list *sflist, int sample_id); | 
|  | 42 | static void set_zone_counter(struct snd_sf_list *sflist, | 
|  | 43 | struct snd_soundfont *sf, struct snd_sf_zone *zp); | 
|  | 44 | static struct snd_sf_zone *sf_zone_new(struct snd_sf_list *sflist, | 
|  | 45 | struct snd_soundfont *sf); | 
|  | 46 | static void set_sample_counter(struct snd_sf_list *sflist, | 
|  | 47 | struct snd_soundfont *sf, struct snd_sf_sample *sp); | 
|  | 48 | static struct snd_sf_sample *sf_sample_new(struct snd_sf_list *sflist, | 
|  | 49 | struct snd_soundfont *sf); | 
|  | 50 | static void sf_sample_delete(struct snd_sf_list *sflist, | 
|  | 51 | struct snd_soundfont *sf, struct snd_sf_sample *sp); | 
|  | 52 | static int load_map(struct snd_sf_list *sflist, const void __user *data, int count); | 
|  | 53 | static int load_info(struct snd_sf_list *sflist, const void __user *data, long count); | 
|  | 54 | static int remove_info(struct snd_sf_list *sflist, struct snd_soundfont *sf, | 
|  | 55 | int bank, int instr); | 
|  | 56 | static void init_voice_info(struct soundfont_voice_info *avp); | 
|  | 57 | static void init_voice_parm(struct soundfont_voice_parm *pp); | 
|  | 58 | static struct snd_sf_sample *set_sample(struct snd_soundfont *sf, | 
|  | 59 | struct soundfont_voice_info *avp); | 
|  | 60 | static struct snd_sf_sample *find_sample(struct snd_soundfont *sf, int sample_id); | 
|  | 61 | static int load_data(struct snd_sf_list *sflist, const void __user *data, long count); | 
|  | 62 | static void rebuild_presets(struct snd_sf_list *sflist); | 
|  | 63 | static void add_preset(struct snd_sf_list *sflist, struct snd_sf_zone *cur); | 
|  | 64 | static void delete_preset(struct snd_sf_list *sflist, struct snd_sf_zone *zp); | 
|  | 65 | static struct snd_sf_zone *search_first_zone(struct snd_sf_list *sflist, | 
|  | 66 | int bank, int preset, int key); | 
|  | 67 | static int search_zones(struct snd_sf_list *sflist, int *notep, int vel, | 
|  | 68 | int preset, int bank, struct snd_sf_zone **table, | 
|  | 69 | int max_layers, int level); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 70 | static int get_index(int bank, int instr, int key); | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 71 | static void snd_sf_init(struct snd_sf_list *sflist); | 
|  | 72 | static void snd_sf_clear(struct snd_sf_list *sflist); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 73 |  | 
|  | 74 | /* | 
|  | 75 | * lock access to sflist | 
|  | 76 | */ | 
|  | 77 | static void | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 78 | lock_preset(struct snd_sf_list *sflist) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 79 | { | 
|  | 80 | unsigned long flags; | 
| Ingo Molnar | ef9f0a4 | 2006-01-16 16:31:42 +0100 | [diff] [blame] | 81 | mutex_lock(&sflist->presets_mutex); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 82 | spin_lock_irqsave(&sflist->lock, flags); | 
|  | 83 | sflist->presets_locked = 1; | 
|  | 84 | spin_unlock_irqrestore(&sflist->lock, flags); | 
|  | 85 | } | 
|  | 86 |  | 
|  | 87 |  | 
|  | 88 | /* | 
|  | 89 | * remove lock | 
|  | 90 | */ | 
|  | 91 | static void | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 92 | unlock_preset(struct snd_sf_list *sflist) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 93 | { | 
|  | 94 | unsigned long flags; | 
|  | 95 | spin_lock_irqsave(&sflist->lock, flags); | 
|  | 96 | sflist->presets_locked = 0; | 
|  | 97 | spin_unlock_irqrestore(&sflist->lock, flags); | 
| Ingo Molnar | ef9f0a4 | 2006-01-16 16:31:42 +0100 | [diff] [blame] | 98 | mutex_unlock(&sflist->presets_mutex); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 99 | } | 
|  | 100 |  | 
|  | 101 |  | 
|  | 102 | /* | 
|  | 103 | * close the patch if the patch was opened by this client. | 
|  | 104 | */ | 
|  | 105 | int | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 106 | snd_soundfont_close_check(struct snd_sf_list *sflist, int client) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 107 | { | 
|  | 108 | unsigned long flags; | 
|  | 109 | spin_lock_irqsave(&sflist->lock, flags); | 
|  | 110 | if (sflist->open_client == client)  { | 
|  | 111 | spin_unlock_irqrestore(&sflist->lock, flags); | 
|  | 112 | return close_patch(sflist); | 
|  | 113 | } | 
|  | 114 | spin_unlock_irqrestore(&sflist->lock, flags); | 
|  | 115 | return 0; | 
|  | 116 | } | 
|  | 117 |  | 
|  | 118 |  | 
|  | 119 | /* | 
|  | 120 | * Deal with a soundfont patch.  Any driver could use these routines | 
|  | 121 | * although it was designed for the AWE64. | 
|  | 122 | * | 
|  | 123 | * The sample_write and callargs pararameters allow a callback into | 
|  | 124 | * the actual driver to write sample data to the board or whatever | 
|  | 125 | * it wants to do with it. | 
|  | 126 | */ | 
|  | 127 | int | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 128 | snd_soundfont_load(struct snd_sf_list *sflist, const void __user *data, | 
|  | 129 | long count, int client) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 130 | { | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 131 | struct soundfont_patch_info patch; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 132 | unsigned long flags; | 
|  | 133 | int  rc; | 
|  | 134 |  | 
|  | 135 | if (count < (long)sizeof(patch)) { | 
|  | 136 | snd_printk("patch record too small %ld\n", count); | 
|  | 137 | return -EINVAL; | 
|  | 138 | } | 
|  | 139 | if (copy_from_user(&patch, data, sizeof(patch))) | 
|  | 140 | return -EFAULT; | 
|  | 141 |  | 
|  | 142 | count -= sizeof(patch); | 
|  | 143 | data += sizeof(patch); | 
|  | 144 |  | 
|  | 145 | if (patch.key != SNDRV_OSS_SOUNDFONT_PATCH) { | 
|  | 146 | snd_printk("'The wrong kind of patch' %x\n", patch.key); | 
|  | 147 | return -EINVAL; | 
|  | 148 | } | 
|  | 149 | if (count < patch.len) { | 
|  | 150 | snd_printk("Patch too short %ld, need %d\n", count, patch.len); | 
|  | 151 | return -EINVAL; | 
|  | 152 | } | 
|  | 153 | if (patch.len < 0) { | 
|  | 154 | snd_printk("poor length %d\n", patch.len); | 
|  | 155 | return -EINVAL; | 
|  | 156 | } | 
|  | 157 |  | 
|  | 158 | if (patch.type == SNDRV_SFNT_OPEN_PATCH) { | 
|  | 159 | /* grab sflist to open */ | 
|  | 160 | lock_preset(sflist); | 
|  | 161 | rc = open_patch(sflist, data, count, client); | 
|  | 162 | unlock_preset(sflist); | 
|  | 163 | return rc; | 
|  | 164 | } | 
|  | 165 |  | 
|  | 166 | /* check if other client already opened patch */ | 
|  | 167 | spin_lock_irqsave(&sflist->lock, flags); | 
|  | 168 | if (sflist->open_client != client) { | 
|  | 169 | spin_unlock_irqrestore(&sflist->lock, flags); | 
|  | 170 | return -EBUSY; | 
|  | 171 | } | 
|  | 172 | spin_unlock_irqrestore(&sflist->lock, flags); | 
|  | 173 |  | 
|  | 174 | lock_preset(sflist); | 
|  | 175 | rc = -EINVAL; | 
|  | 176 | switch (patch.type) { | 
|  | 177 | case SNDRV_SFNT_LOAD_INFO: | 
|  | 178 | rc = load_info(sflist, data, count); | 
|  | 179 | break; | 
|  | 180 | case SNDRV_SFNT_LOAD_DATA: | 
|  | 181 | rc = load_data(sflist, data, count); | 
|  | 182 | break; | 
|  | 183 | case SNDRV_SFNT_CLOSE_PATCH: | 
|  | 184 | rc = close_patch(sflist); | 
|  | 185 | break; | 
|  | 186 | case SNDRV_SFNT_REPLACE_DATA: | 
|  | 187 | /*rc = replace_data(&patch, data, count);*/ | 
|  | 188 | break; | 
|  | 189 | case SNDRV_SFNT_MAP_PRESET: | 
|  | 190 | rc = load_map(sflist, data, count); | 
|  | 191 | break; | 
|  | 192 | case SNDRV_SFNT_PROBE_DATA: | 
|  | 193 | rc = probe_data(sflist, patch.optarg); | 
|  | 194 | break; | 
|  | 195 | case SNDRV_SFNT_REMOVE_INFO: | 
|  | 196 | /* patch must be opened */ | 
| Eric Sesterhenn | d20cad6 | 2006-05-31 11:55:17 +0200 | [diff] [blame] | 197 | if (!sflist->currsf) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 198 | snd_printk("soundfont: remove_info: patch not opened\n"); | 
|  | 199 | rc = -EINVAL; | 
|  | 200 | } else { | 
|  | 201 | int bank, instr; | 
|  | 202 | bank = ((unsigned short)patch.optarg >> 8) & 0xff; | 
|  | 203 | instr = (unsigned short)patch.optarg & 0xff; | 
|  | 204 | if (! remove_info(sflist, sflist->currsf, bank, instr)) | 
|  | 205 | rc = -EINVAL; | 
|  | 206 | else | 
|  | 207 | rc = 0; | 
|  | 208 | } | 
|  | 209 | break; | 
|  | 210 | } | 
|  | 211 | unlock_preset(sflist); | 
|  | 212 |  | 
|  | 213 | return rc; | 
|  | 214 | } | 
|  | 215 |  | 
|  | 216 |  | 
|  | 217 | /* check if specified type is special font (GUS or preset-alias) */ | 
|  | 218 | static inline int | 
|  | 219 | is_special_type(int type) | 
|  | 220 | { | 
|  | 221 | type &= 0x0f; | 
|  | 222 | return (type == SNDRV_SFNT_PAT_TYPE_GUS || | 
|  | 223 | type == SNDRV_SFNT_PAT_TYPE_MAP); | 
|  | 224 | } | 
|  | 225 |  | 
|  | 226 |  | 
|  | 227 | /* open patch; create sf list */ | 
|  | 228 | static int | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 229 | open_patch(struct snd_sf_list *sflist, const char __user *data, | 
|  | 230 | int count, int client) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 231 | { | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 232 | struct soundfont_open_parm parm; | 
|  | 233 | struct snd_soundfont *sf; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 234 | unsigned long flags; | 
|  | 235 |  | 
|  | 236 | spin_lock_irqsave(&sflist->lock, flags); | 
|  | 237 | if (sflist->open_client >= 0 || sflist->currsf) { | 
|  | 238 | spin_unlock_irqrestore(&sflist->lock, flags); | 
|  | 239 | return -EBUSY; | 
|  | 240 | } | 
|  | 241 | spin_unlock_irqrestore(&sflist->lock, flags); | 
|  | 242 |  | 
|  | 243 | if (copy_from_user(&parm, data, sizeof(parm))) | 
|  | 244 | return -EFAULT; | 
|  | 245 |  | 
|  | 246 | if (is_special_type(parm.type)) { | 
|  | 247 | parm.type |= SNDRV_SFNT_PAT_SHARED; | 
|  | 248 | sf = newsf(sflist, parm.type, NULL); | 
|  | 249 | } else | 
|  | 250 | sf = newsf(sflist, parm.type, parm.name); | 
|  | 251 | if (sf == NULL) { | 
|  | 252 | return -ENOMEM; | 
|  | 253 | } | 
|  | 254 |  | 
|  | 255 | spin_lock_irqsave(&sflist->lock, flags); | 
|  | 256 | sflist->open_client = client; | 
|  | 257 | sflist->currsf = sf; | 
|  | 258 | spin_unlock_irqrestore(&sflist->lock, flags); | 
|  | 259 |  | 
|  | 260 | return 0; | 
|  | 261 | } | 
|  | 262 |  | 
|  | 263 | /* | 
|  | 264 | * Allocate a new soundfont structure. | 
|  | 265 | */ | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 266 | static struct snd_soundfont * | 
|  | 267 | newsf(struct snd_sf_list *sflist, int type, char *name) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 268 | { | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 269 | struct snd_soundfont *sf; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 270 |  | 
|  | 271 | /* check the shared fonts */ | 
|  | 272 | if (type & SNDRV_SFNT_PAT_SHARED) { | 
|  | 273 | for (sf = sflist->fonts; sf; sf = sf->next) { | 
|  | 274 | if (is_identical_font(sf, type, name)) { | 
|  | 275 | return sf; | 
|  | 276 | } | 
|  | 277 | } | 
|  | 278 | } | 
|  | 279 |  | 
|  | 280 | /* not found -- create a new one */ | 
| Takashi Iwai | 561b220 | 2005-09-09 14:22:34 +0200 | [diff] [blame] | 281 | sf = kzalloc(sizeof(*sf), GFP_KERNEL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 282 | if (sf == NULL) | 
|  | 283 | return NULL; | 
|  | 284 | sf->id = sflist->fonts_size; | 
|  | 285 | sflist->fonts_size++; | 
|  | 286 |  | 
|  | 287 | /* prepend this record */ | 
|  | 288 | sf->next = sflist->fonts; | 
|  | 289 | sflist->fonts = sf; | 
|  | 290 |  | 
|  | 291 | sf->type = type; | 
|  | 292 | sf->zones = NULL; | 
|  | 293 | sf->samples = NULL; | 
|  | 294 | if (name) | 
|  | 295 | memcpy(sf->name, name, SNDRV_SFNT_PATCH_NAME_LEN); | 
|  | 296 |  | 
|  | 297 | return sf; | 
|  | 298 | } | 
|  | 299 |  | 
|  | 300 | /* check if the given name matches to the existing list */ | 
|  | 301 | static int | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 302 | is_identical_font(struct snd_soundfont *sf, int type, unsigned char *name) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 303 | { | 
|  | 304 | return ((sf->type & SNDRV_SFNT_PAT_SHARED) && | 
|  | 305 | (sf->type & 0x0f) == (type & 0x0f) && | 
|  | 306 | (name == NULL || | 
|  | 307 | memcmp(sf->name, name, SNDRV_SFNT_PATCH_NAME_LEN) == 0)); | 
|  | 308 | } | 
|  | 309 |  | 
|  | 310 | /* | 
|  | 311 | * Close the current patch. | 
|  | 312 | */ | 
|  | 313 | static int | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 314 | close_patch(struct snd_sf_list *sflist) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 315 | { | 
|  | 316 | unsigned long flags; | 
|  | 317 |  | 
|  | 318 | spin_lock_irqsave(&sflist->lock, flags); | 
|  | 319 | sflist->currsf = NULL; | 
|  | 320 | sflist->open_client = -1; | 
|  | 321 | spin_unlock_irqrestore(&sflist->lock, flags); | 
|  | 322 |  | 
|  | 323 | rebuild_presets(sflist); | 
|  | 324 |  | 
|  | 325 | return 0; | 
|  | 326 |  | 
|  | 327 | } | 
|  | 328 |  | 
|  | 329 | /* probe sample in the current list -- nothing to be loaded */ | 
|  | 330 | static int | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 331 | probe_data(struct snd_sf_list *sflist, int sample_id) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 332 | { | 
|  | 333 | /* patch must be opened */ | 
|  | 334 | if (sflist->currsf) { | 
|  | 335 | /* search the specified sample by optarg */ | 
|  | 336 | if (find_sample(sflist->currsf, sample_id)) | 
|  | 337 | return 0; | 
|  | 338 | } | 
|  | 339 | return -EINVAL; | 
|  | 340 | } | 
|  | 341 |  | 
|  | 342 | /* | 
|  | 343 | * increment zone counter | 
|  | 344 | */ | 
|  | 345 | static void | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 346 | set_zone_counter(struct snd_sf_list *sflist, struct snd_soundfont *sf, | 
|  | 347 | struct snd_sf_zone *zp) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 348 | { | 
|  | 349 | zp->counter = sflist->zone_counter++; | 
|  | 350 | if (sf->type & SNDRV_SFNT_PAT_LOCKED) | 
|  | 351 | sflist->zone_locked = sflist->zone_counter; | 
|  | 352 | } | 
|  | 353 |  | 
|  | 354 | /* | 
|  | 355 | * allocate a new zone record | 
|  | 356 | */ | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 357 | static struct snd_sf_zone * | 
|  | 358 | sf_zone_new(struct snd_sf_list *sflist, struct snd_soundfont *sf) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 359 | { | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 360 | struct snd_sf_zone *zp; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 361 |  | 
| Takashi Iwai | 561b220 | 2005-09-09 14:22:34 +0200 | [diff] [blame] | 362 | if ((zp = kzalloc(sizeof(*zp), GFP_KERNEL)) == NULL) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 363 | return NULL; | 
|  | 364 | zp->next = sf->zones; | 
|  | 365 | sf->zones = zp; | 
|  | 366 |  | 
|  | 367 | init_voice_info(&zp->v); | 
|  | 368 |  | 
|  | 369 | set_zone_counter(sflist, sf, zp); | 
|  | 370 | return zp; | 
|  | 371 | } | 
|  | 372 |  | 
|  | 373 |  | 
|  | 374 | /* | 
|  | 375 | * increment sample couter | 
|  | 376 | */ | 
|  | 377 | static void | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 378 | set_sample_counter(struct snd_sf_list *sflist, struct snd_soundfont *sf, | 
|  | 379 | struct snd_sf_sample *sp) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 380 | { | 
|  | 381 | sp->counter = sflist->sample_counter++; | 
|  | 382 | if (sf->type & SNDRV_SFNT_PAT_LOCKED) | 
|  | 383 | sflist->sample_locked = sflist->sample_counter; | 
|  | 384 | } | 
|  | 385 |  | 
|  | 386 | /* | 
|  | 387 | * allocate a new sample list record | 
|  | 388 | */ | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 389 | static struct snd_sf_sample * | 
|  | 390 | sf_sample_new(struct snd_sf_list *sflist, struct snd_soundfont *sf) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 391 | { | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 392 | struct snd_sf_sample *sp; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 393 |  | 
| Takashi Iwai | 561b220 | 2005-09-09 14:22:34 +0200 | [diff] [blame] | 394 | if ((sp = kzalloc(sizeof(*sp), GFP_KERNEL)) == NULL) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 395 | return NULL; | 
|  | 396 |  | 
|  | 397 | sp->next = sf->samples; | 
|  | 398 | sf->samples = sp; | 
|  | 399 |  | 
|  | 400 | set_sample_counter(sflist, sf, sp); | 
|  | 401 | return sp; | 
|  | 402 | } | 
|  | 403 |  | 
|  | 404 | /* | 
|  | 405 | * delete sample list -- this is an exceptional job. | 
|  | 406 | * only the last allocated sample can be deleted. | 
|  | 407 | */ | 
|  | 408 | static void | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 409 | sf_sample_delete(struct snd_sf_list *sflist, struct snd_soundfont *sf, | 
|  | 410 | struct snd_sf_sample *sp) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 411 | { | 
|  | 412 | /* only last sample is accepted */ | 
|  | 413 | if (sp == sf->samples) { | 
|  | 414 | sf->samples = sp->next; | 
|  | 415 | kfree(sp); | 
|  | 416 | } | 
|  | 417 | } | 
|  | 418 |  | 
|  | 419 |  | 
|  | 420 | /* load voice map */ | 
|  | 421 | static int | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 422 | load_map(struct snd_sf_list *sflist, const void __user *data, int count) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 423 | { | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 424 | struct snd_sf_zone *zp, *prevp; | 
|  | 425 | struct snd_soundfont *sf; | 
|  | 426 | struct soundfont_voice_map map; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 427 |  | 
|  | 428 | /* get the link info */ | 
|  | 429 | if (count < (int)sizeof(map)) | 
|  | 430 | return -EINVAL; | 
|  | 431 | if (copy_from_user(&map, data, sizeof(map))) | 
|  | 432 | return -EFAULT; | 
|  | 433 |  | 
|  | 434 | if (map.map_instr < 0 || map.map_instr >= SF_MAX_INSTRUMENTS) | 
|  | 435 | return -EINVAL; | 
|  | 436 |  | 
|  | 437 | sf = newsf(sflist, SNDRV_SFNT_PAT_TYPE_MAP|SNDRV_SFNT_PAT_SHARED, NULL); | 
|  | 438 | if (sf == NULL) | 
|  | 439 | return -ENOMEM; | 
|  | 440 |  | 
|  | 441 | prevp = NULL; | 
|  | 442 | for (zp = sf->zones; zp; prevp = zp, zp = zp->next) { | 
|  | 443 | if (zp->mapped && | 
|  | 444 | zp->instr == map.map_instr && | 
|  | 445 | zp->bank == map.map_bank && | 
|  | 446 | zp->v.low == map.map_key && | 
|  | 447 | zp->v.start == map.src_instr && | 
|  | 448 | zp->v.end == map.src_bank && | 
|  | 449 | zp->v.fixkey == map.src_key) { | 
|  | 450 | /* the same mapping is already present */ | 
|  | 451 | /* relink this record to the link head */ | 
|  | 452 | if (prevp) { | 
|  | 453 | prevp->next = zp->next; | 
|  | 454 | zp->next = sf->zones; | 
|  | 455 | sf->zones = zp; | 
|  | 456 | } | 
|  | 457 | /* update the counter */ | 
|  | 458 | set_zone_counter(sflist, sf, zp); | 
|  | 459 | return 0; | 
|  | 460 | } | 
|  | 461 | } | 
|  | 462 |  | 
|  | 463 | /* create a new zone */ | 
|  | 464 | if ((zp = sf_zone_new(sflist, sf)) == NULL) | 
|  | 465 | return -ENOMEM; | 
|  | 466 |  | 
|  | 467 | zp->bank = map.map_bank; | 
|  | 468 | zp->instr = map.map_instr; | 
|  | 469 | zp->mapped = 1; | 
|  | 470 | if (map.map_key >= 0) { | 
|  | 471 | zp->v.low = map.map_key; | 
|  | 472 | zp->v.high = map.map_key; | 
|  | 473 | } | 
|  | 474 | zp->v.start = map.src_instr; | 
|  | 475 | zp->v.end = map.src_bank; | 
|  | 476 | zp->v.fixkey = map.src_key; | 
|  | 477 | zp->v.sf_id = sf->id; | 
|  | 478 |  | 
|  | 479 | add_preset(sflist, zp); | 
|  | 480 |  | 
|  | 481 | return 0; | 
|  | 482 | } | 
|  | 483 |  | 
|  | 484 |  | 
|  | 485 | /* remove the present instrument layers */ | 
|  | 486 | static int | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 487 | remove_info(struct snd_sf_list *sflist, struct snd_soundfont *sf, | 
|  | 488 | int bank, int instr) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 489 | { | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 490 | struct snd_sf_zone *prev, *next, *p; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 491 | int removed = 0; | 
|  | 492 |  | 
|  | 493 | prev = NULL; | 
|  | 494 | for (p = sf->zones; p; p = next) { | 
|  | 495 | next = p->next; | 
|  | 496 | if (! p->mapped && | 
|  | 497 | p->bank == bank && p->instr == instr) { | 
|  | 498 | /* remove this layer */ | 
|  | 499 | if (prev) | 
|  | 500 | prev->next = next; | 
|  | 501 | else | 
|  | 502 | sf->zones = next; | 
|  | 503 | removed++; | 
|  | 504 | kfree(p); | 
|  | 505 | } else | 
|  | 506 | prev = p; | 
|  | 507 | } | 
|  | 508 | if (removed) | 
|  | 509 | rebuild_presets(sflist); | 
|  | 510 | return removed; | 
|  | 511 | } | 
|  | 512 |  | 
|  | 513 |  | 
|  | 514 | /* | 
|  | 515 | * Read an info record from the user buffer and save it on the current | 
|  | 516 | * open soundfont. | 
|  | 517 | */ | 
|  | 518 | static int | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 519 | load_info(struct snd_sf_list *sflist, const void __user *data, long count) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 520 | { | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 521 | struct snd_soundfont *sf; | 
|  | 522 | struct snd_sf_zone *zone; | 
|  | 523 | struct soundfont_voice_rec_hdr hdr; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 524 | int i; | 
|  | 525 |  | 
|  | 526 | /* patch must be opened */ | 
|  | 527 | if ((sf = sflist->currsf) == NULL) | 
|  | 528 | return -EINVAL; | 
|  | 529 |  | 
|  | 530 | if (is_special_type(sf->type)) | 
|  | 531 | return -EINVAL; | 
|  | 532 |  | 
|  | 533 | if (count < (long)sizeof(hdr)) { | 
|  | 534 | printk("Soundfont error: invalid patch zone length\n"); | 
|  | 535 | return -EINVAL; | 
|  | 536 | } | 
|  | 537 | if (copy_from_user((char*)&hdr, data, sizeof(hdr))) | 
|  | 538 | return -EFAULT; | 
|  | 539 |  | 
|  | 540 | data += sizeof(hdr); | 
|  | 541 | count -= sizeof(hdr); | 
|  | 542 |  | 
|  | 543 | if (hdr.nvoices <= 0 || hdr.nvoices >= 100) { | 
|  | 544 | printk("Soundfont error: Illegal voice number %d\n", hdr.nvoices); | 
|  | 545 | return -EINVAL; | 
|  | 546 | } | 
|  | 547 |  | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 548 | if (count < (long)sizeof(struct soundfont_voice_info) * hdr.nvoices) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 549 | printk("Soundfont Error: patch length(%ld) is smaller than nvoices(%d)\n", | 
|  | 550 | count, hdr.nvoices); | 
|  | 551 | return -EINVAL; | 
|  | 552 | } | 
|  | 553 |  | 
|  | 554 | switch (hdr.write_mode) { | 
|  | 555 | case SNDRV_SFNT_WR_EXCLUSIVE: | 
|  | 556 | /* exclusive mode - if the instrument already exists, | 
|  | 557 | return error */ | 
|  | 558 | for (zone = sf->zones; zone; zone = zone->next) { | 
|  | 559 | if (!zone->mapped && | 
|  | 560 | zone->bank == hdr.bank && | 
|  | 561 | zone->instr == hdr.instr) | 
|  | 562 | return -EINVAL; | 
|  | 563 | } | 
|  | 564 | break; | 
|  | 565 | case SNDRV_SFNT_WR_REPLACE: | 
|  | 566 | /* replace mode - remove the instrument if it already exists */ | 
|  | 567 | remove_info(sflist, sf, hdr.bank, hdr.instr); | 
|  | 568 | break; | 
|  | 569 | } | 
|  | 570 |  | 
|  | 571 | for (i = 0; i < hdr.nvoices; i++) { | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 572 | struct snd_sf_zone tmpzone; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 573 |  | 
|  | 574 | /* copy awe_voice_info parameters */ | 
|  | 575 | if (copy_from_user(&tmpzone.v, data, sizeof(tmpzone.v))) { | 
|  | 576 | return -EFAULT; | 
|  | 577 | } | 
|  | 578 |  | 
|  | 579 | data += sizeof(tmpzone.v); | 
|  | 580 | count -= sizeof(tmpzone.v); | 
|  | 581 |  | 
|  | 582 | tmpzone.bank = hdr.bank; | 
|  | 583 | tmpzone.instr = hdr.instr; | 
|  | 584 | tmpzone.mapped = 0; | 
|  | 585 | tmpzone.v.sf_id = sf->id; | 
|  | 586 | if (tmpzone.v.mode & SNDRV_SFNT_MODE_INIT_PARM) | 
|  | 587 | init_voice_parm(&tmpzone.v.parm); | 
|  | 588 |  | 
|  | 589 | /* create a new zone */ | 
|  | 590 | if ((zone = sf_zone_new(sflist, sf)) == NULL) { | 
|  | 591 | return -ENOMEM; | 
|  | 592 | } | 
|  | 593 |  | 
|  | 594 | /* copy the temporary data */ | 
|  | 595 | zone->bank = tmpzone.bank; | 
|  | 596 | zone->instr = tmpzone.instr; | 
|  | 597 | zone->v = tmpzone.v; | 
|  | 598 |  | 
|  | 599 | /* look up the sample */ | 
|  | 600 | zone->sample = set_sample(sf, &zone->v); | 
|  | 601 | } | 
|  | 602 |  | 
|  | 603 | return 0; | 
|  | 604 | } | 
|  | 605 |  | 
|  | 606 |  | 
|  | 607 | /* initialize voice_info record */ | 
|  | 608 | static void | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 609 | init_voice_info(struct soundfont_voice_info *avp) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 610 | { | 
|  | 611 | memset(avp, 0, sizeof(*avp)); | 
|  | 612 |  | 
|  | 613 | avp->root = 60; | 
|  | 614 | avp->high = 127; | 
|  | 615 | avp->velhigh = 127; | 
|  | 616 | avp->fixkey = -1; | 
|  | 617 | avp->fixvel = -1; | 
|  | 618 | avp->fixpan = -1; | 
|  | 619 | avp->pan = -1; | 
|  | 620 | avp->amplitude = 127; | 
|  | 621 | avp->scaleTuning = 100; | 
|  | 622 |  | 
|  | 623 | init_voice_parm(&avp->parm); | 
|  | 624 | } | 
|  | 625 |  | 
|  | 626 | /* initialize voice_parm record: | 
|  | 627 | * Env1/2: delay=0, attack=0, hold=0, sustain=0, decay=0, release=0. | 
|  | 628 | * Vibrato and Tremolo effects are zero. | 
|  | 629 | * Cutoff is maximum. | 
|  | 630 | * Chorus and Reverb effects are zero. | 
|  | 631 | */ | 
|  | 632 | static void | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 633 | init_voice_parm(struct soundfont_voice_parm *pp) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 634 | { | 
|  | 635 | memset(pp, 0, sizeof(*pp)); | 
|  | 636 |  | 
|  | 637 | pp->moddelay = 0x8000; | 
|  | 638 | pp->modatkhld = 0x7f7f; | 
|  | 639 | pp->moddcysus = 0x7f7f; | 
|  | 640 | pp->modrelease = 0x807f; | 
|  | 641 |  | 
|  | 642 | pp->voldelay = 0x8000; | 
|  | 643 | pp->volatkhld = 0x7f7f; | 
|  | 644 | pp->voldcysus = 0x7f7f; | 
|  | 645 | pp->volrelease = 0x807f; | 
|  | 646 |  | 
|  | 647 | pp->lfo1delay = 0x8000; | 
|  | 648 | pp->lfo2delay = 0x8000; | 
|  | 649 |  | 
|  | 650 | pp->cutoff = 0xff; | 
|  | 651 | } | 
|  | 652 |  | 
|  | 653 | /* search the specified sample */ | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 654 | static struct snd_sf_sample * | 
|  | 655 | set_sample(struct snd_soundfont *sf, struct soundfont_voice_info *avp) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 656 | { | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 657 | struct snd_sf_sample *sample; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 658 |  | 
|  | 659 | sample = find_sample(sf, avp->sample); | 
|  | 660 | if (sample == NULL) | 
|  | 661 | return NULL; | 
|  | 662 |  | 
|  | 663 | /* add in the actual sample offsets: | 
|  | 664 | * The voice_info addresses define only the relative offset | 
|  | 665 | * from sample pointers.  Here we calculate the actual DRAM | 
|  | 666 | * offset from sample pointers. | 
|  | 667 | */ | 
|  | 668 | avp->start += sample->v.start; | 
|  | 669 | avp->end += sample->v.end; | 
|  | 670 | avp->loopstart += sample->v.loopstart; | 
|  | 671 | avp->loopend += sample->v.loopend; | 
|  | 672 |  | 
|  | 673 | /* copy mode flags */ | 
|  | 674 | avp->sample_mode = sample->v.mode_flags; | 
|  | 675 |  | 
|  | 676 | return sample; | 
|  | 677 | } | 
|  | 678 |  | 
|  | 679 | /* find the sample pointer with the given id in the soundfont */ | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 680 | static struct snd_sf_sample * | 
|  | 681 | find_sample(struct snd_soundfont *sf, int sample_id) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 682 | { | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 683 | struct snd_sf_sample *p; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 684 |  | 
|  | 685 | if (sf == NULL) | 
|  | 686 | return NULL; | 
|  | 687 |  | 
|  | 688 | for (p = sf->samples; p; p = p->next) { | 
|  | 689 | if (p->v.sample == sample_id) | 
|  | 690 | return p; | 
|  | 691 | } | 
|  | 692 | return NULL; | 
|  | 693 | } | 
|  | 694 |  | 
|  | 695 |  | 
|  | 696 | /* | 
|  | 697 | * Load sample information, this can include data to be loaded onto | 
|  | 698 | * the soundcard.  It can also just be a pointer into soundcard ROM. | 
|  | 699 | * If there is data it will be written to the soundcard via the callback | 
|  | 700 | * routine. | 
|  | 701 | */ | 
|  | 702 | static int | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 703 | load_data(struct snd_sf_list *sflist, const void __user *data, long count) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 704 | { | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 705 | struct snd_soundfont *sf; | 
|  | 706 | struct soundfont_sample_info sample_info; | 
|  | 707 | struct snd_sf_sample *sp; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 708 | long off; | 
|  | 709 |  | 
|  | 710 | /* patch must be opened */ | 
|  | 711 | if ((sf = sflist->currsf) == NULL) | 
|  | 712 | return -EINVAL; | 
|  | 713 |  | 
|  | 714 | if (is_special_type(sf->type)) | 
|  | 715 | return -EINVAL; | 
|  | 716 |  | 
|  | 717 | if (copy_from_user(&sample_info, data, sizeof(sample_info))) | 
|  | 718 | return -EFAULT; | 
|  | 719 |  | 
|  | 720 | off = sizeof(sample_info); | 
|  | 721 |  | 
|  | 722 | if (sample_info.size != (count-off)/2) | 
|  | 723 | return -EINVAL; | 
|  | 724 |  | 
|  | 725 | /* Check for dup */ | 
|  | 726 | if (find_sample(sf, sample_info.sample)) { | 
|  | 727 | /* if shared sample, skip this data */ | 
|  | 728 | if (sf->type & SNDRV_SFNT_PAT_SHARED) | 
|  | 729 | return 0; | 
|  | 730 | return -EINVAL; | 
|  | 731 | } | 
|  | 732 |  | 
|  | 733 | /* Allocate a new sample structure */ | 
|  | 734 | if ((sp = sf_sample_new(sflist, sf)) == NULL) | 
|  | 735 | return -ENOMEM; | 
|  | 736 |  | 
|  | 737 | sp->v = sample_info; | 
|  | 738 | sp->v.sf_id = sf->id; | 
|  | 739 | sp->v.dummy = 0; | 
|  | 740 | sp->v.truesize = sp->v.size; | 
|  | 741 |  | 
|  | 742 | /* | 
|  | 743 | * If there is wave data then load it. | 
|  | 744 | */ | 
|  | 745 | if (sp->v.size > 0) { | 
|  | 746 | int  rc; | 
|  | 747 | rc = sflist->callback.sample_new | 
|  | 748 | (sflist->callback.private_data, sp, sflist->memhdr, | 
|  | 749 | data + off, count - off); | 
|  | 750 | if (rc < 0) { | 
|  | 751 | sf_sample_delete(sflist, sf, sp); | 
|  | 752 | return rc; | 
|  | 753 | } | 
|  | 754 | sflist->mem_used += sp->v.truesize; | 
|  | 755 | } | 
|  | 756 |  | 
|  | 757 | return count; | 
|  | 758 | } | 
|  | 759 |  | 
|  | 760 |  | 
|  | 761 | /* log2_tbl[i] = log2(i+128) * 0x10000 */ | 
|  | 762 | static int log_tbl[129] = { | 
|  | 763 | 0x70000, 0x702df, 0x705b9, 0x7088e, 0x70b5d, 0x70e26, 0x710eb, 0x713aa, | 
|  | 764 | 0x71663, 0x71918, 0x71bc8, 0x71e72, 0x72118, 0x723b9, 0x72655, 0x728ed, | 
|  | 765 | 0x72b80, 0x72e0e, 0x73098, 0x7331d, 0x7359e, 0x7381b, 0x73a93, 0x73d08, | 
|  | 766 | 0x73f78, 0x741e4, 0x7444c, 0x746b0, 0x74910, 0x74b6c, 0x74dc4, 0x75019, | 
|  | 767 | 0x75269, 0x754b6, 0x75700, 0x75946, 0x75b88, 0x75dc7, 0x76002, 0x7623a, | 
|  | 768 | 0x7646e, 0x766a0, 0x768cd, 0x76af8, 0x76d1f, 0x76f43, 0x77164, 0x77382, | 
|  | 769 | 0x7759d, 0x777b4, 0x779c9, 0x77bdb, 0x77dea, 0x77ff5, 0x781fe, 0x78404, | 
|  | 770 | 0x78608, 0x78808, 0x78a06, 0x78c01, 0x78df9, 0x78fef, 0x791e2, 0x793d2, | 
|  | 771 | 0x795c0, 0x797ab, 0x79993, 0x79b79, 0x79d5d, 0x79f3e, 0x7a11d, 0x7a2f9, | 
|  | 772 | 0x7a4d3, 0x7a6ab, 0x7a880, 0x7aa53, 0x7ac24, 0x7adf2, 0x7afbe, 0x7b188, | 
|  | 773 | 0x7b350, 0x7b515, 0x7b6d8, 0x7b899, 0x7ba58, 0x7bc15, 0x7bdd0, 0x7bf89, | 
|  | 774 | 0x7c140, 0x7c2f5, 0x7c4a7, 0x7c658, 0x7c807, 0x7c9b3, 0x7cb5e, 0x7cd07, | 
|  | 775 | 0x7ceae, 0x7d053, 0x7d1f7, 0x7d398, 0x7d538, 0x7d6d6, 0x7d872, 0x7da0c, | 
|  | 776 | 0x7dba4, 0x7dd3b, 0x7ded0, 0x7e063, 0x7e1f4, 0x7e384, 0x7e512, 0x7e69f, | 
|  | 777 | 0x7e829, 0x7e9b3, 0x7eb3a, 0x7ecc0, 0x7ee44, 0x7efc7, 0x7f148, 0x7f2c8, | 
|  | 778 | 0x7f446, 0x7f5c2, 0x7f73d, 0x7f8b7, 0x7fa2f, 0x7fba5, 0x7fd1a, 0x7fe8d, | 
|  | 779 | 0x80000, | 
|  | 780 | }; | 
|  | 781 |  | 
|  | 782 | /* convert from linear to log value | 
|  | 783 | * | 
|  | 784 | * conversion: value = log2(amount / base) * ratio | 
|  | 785 | * | 
|  | 786 | * argument: | 
|  | 787 | *   amount = linear value (unsigned, 32bit max) | 
|  | 788 | *   offset = base offset (:= log2(base) * 0x10000) | 
|  | 789 | *   ratio = division ratio | 
|  | 790 | * | 
|  | 791 | */ | 
|  | 792 | int | 
|  | 793 | snd_sf_linear_to_log(unsigned int amount, int offset, int ratio) | 
|  | 794 | { | 
|  | 795 | int v; | 
|  | 796 | int s, low, bit; | 
|  | 797 |  | 
|  | 798 | if (amount < 2) | 
|  | 799 | return 0; | 
|  | 800 | for (bit = 0; ! (amount & 0x80000000L); bit++) | 
|  | 801 | amount <<= 1; | 
|  | 802 | s = (amount >> 24) & 0x7f; | 
|  | 803 | low = (amount >> 16) & 0xff; | 
|  | 804 | /* linear approxmimation by lower 8 bit */ | 
|  | 805 | v = (log_tbl[s + 1] * low + log_tbl[s] * (0x100 - low)) >> 8; | 
|  | 806 | v -= offset; | 
|  | 807 | v = (v * ratio) >> 16; | 
|  | 808 | v += (24 - bit) * ratio; | 
|  | 809 | return v; | 
|  | 810 | } | 
|  | 811 |  | 
| Takashi Iwai | 95ff1756 | 2006-04-28 15:13:40 +0200 | [diff] [blame] | 812 | EXPORT_SYMBOL(snd_sf_linear_to_log); | 
|  | 813 |  | 
|  | 814 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 815 | #define OFFSET_MSEC		653117		/* base = 1000 */ | 
|  | 816 | #define OFFSET_ABSCENT		851781		/* base = 8176 */ | 
|  | 817 | #define OFFSET_SAMPLERATE	1011119		/* base = 44100 */ | 
|  | 818 |  | 
|  | 819 | #define ABSCENT_RATIO		1200 | 
|  | 820 | #define TIMECENT_RATIO		1200 | 
|  | 821 | #define SAMPLERATE_RATIO	4096 | 
|  | 822 |  | 
|  | 823 | /* | 
|  | 824 | * mHz to abscent | 
|  | 825 | * conversion: abscent = log2(MHz / 8176) * 1200 | 
|  | 826 | */ | 
|  | 827 | static int | 
|  | 828 | freq_to_note(int mhz) | 
|  | 829 | { | 
|  | 830 | return snd_sf_linear_to_log(mhz, OFFSET_ABSCENT, ABSCENT_RATIO); | 
|  | 831 | } | 
|  | 832 |  | 
|  | 833 | /* convert Hz to AWE32 rate offset: | 
|  | 834 | * sample pitch offset for the specified sample rate | 
|  | 835 | * rate=44100 is no offset, each 4096 is 1 octave (twice). | 
|  | 836 | * eg, when rate is 22050, this offset becomes -4096. | 
|  | 837 | * | 
|  | 838 | * conversion: offset = log2(Hz / 44100) * 4096 | 
|  | 839 | */ | 
|  | 840 | static int | 
|  | 841 | calc_rate_offset(int hz) | 
|  | 842 | { | 
|  | 843 | return snd_sf_linear_to_log(hz, OFFSET_SAMPLERATE, SAMPLERATE_RATIO); | 
|  | 844 | } | 
|  | 845 |  | 
|  | 846 |  | 
|  | 847 | /* calculate GUS envelope time */ | 
|  | 848 | static int | 
|  | 849 | calc_gus_envelope_time(int rate, int start, int end) | 
|  | 850 | { | 
|  | 851 | int r, p, t; | 
|  | 852 | r = (3 - ((rate >> 6) & 3)) * 3; | 
|  | 853 | p = rate & 0x3f; | 
|  | 854 | t = end - start; | 
|  | 855 | if (t < 0) t = -t; | 
|  | 856 | if (13 > r) | 
|  | 857 | t = t << (13 - r); | 
|  | 858 | else | 
|  | 859 | t = t >> (r - 13); | 
|  | 860 | return (t * 10) / (p * 441); | 
|  | 861 | } | 
|  | 862 |  | 
|  | 863 | /* convert envelope time parameter to soundfont parameters */ | 
|  | 864 |  | 
|  | 865 | /* attack & decay/release time table (msec) */ | 
|  | 866 | static short attack_time_tbl[128] = { | 
|  | 867 | 32767, 32767, 5989, 4235, 2994, 2518, 2117, 1780, 1497, 1373, 1259, 1154, 1058, 970, 890, 816, | 
|  | 868 | 707, 691, 662, 634, 607, 581, 557, 533, 510, 489, 468, 448, 429, 411, 393, 377, | 
|  | 869 | 361, 345, 331, 317, 303, 290, 278, 266, 255, 244, 234, 224, 214, 205, 196, 188, | 
|  | 870 | 180, 172, 165, 158, 151, 145, 139, 133, 127, 122, 117, 112, 107, 102, 98, 94, | 
|  | 871 | 90, 86, 82, 79, 75, 72, 69, 66, 63, 61, 58, 56, 53, 51, 49, 47, | 
|  | 872 | 45, 43, 41, 39, 37, 36, 34, 33, 31, 30, 29, 28, 26, 25, 24, 23, | 
|  | 873 | 22, 21, 20, 19, 19, 18, 17, 16, 16, 15, 15, 14, 13, 13, 12, 12, | 
|  | 874 | 11, 11, 10, 10, 10, 9, 9, 8, 8, 8, 8, 7, 7, 7, 6, 0, | 
|  | 875 | }; | 
|  | 876 |  | 
|  | 877 | static short decay_time_tbl[128] = { | 
|  | 878 | 32767, 32767, 22614, 15990, 11307, 9508, 7995, 6723, 5653, 5184, 4754, 4359, 3997, 3665, 3361, 3082, | 
|  | 879 | 2828, 2765, 2648, 2535, 2428, 2325, 2226, 2132, 2042, 1955, 1872, 1793, 1717, 1644, 1574, 1507, | 
|  | 880 | 1443, 1382, 1324, 1267, 1214, 1162, 1113, 1066, 978, 936, 897, 859, 822, 787, 754, 722, | 
|  | 881 | 691, 662, 634, 607, 581, 557, 533, 510, 489, 468, 448, 429, 411, 393, 377, 361, | 
|  | 882 | 345, 331, 317, 303, 290, 278, 266, 255, 244, 234, 224, 214, 205, 196, 188, 180, | 
|  | 883 | 172, 165, 158, 151, 145, 139, 133, 127, 122, 117, 112, 107, 102, 98, 94, 90, | 
|  | 884 | 86, 82, 79, 75, 72, 69, 66, 63, 61, 58, 56, 53, 51, 49, 47, 45, | 
|  | 885 | 43, 41, 39, 37, 36, 34, 33, 31, 30, 29, 28, 26, 25, 24, 23, 22, | 
|  | 886 | }; | 
|  | 887 |  | 
|  | 888 | /* delay time = 0x8000 - msec/92 */ | 
|  | 889 | int | 
|  | 890 | snd_sf_calc_parm_hold(int msec) | 
|  | 891 | { | 
|  | 892 | int val = (0x7f * 92 - msec) / 92; | 
|  | 893 | if (val < 1) val = 1; | 
|  | 894 | if (val >= 126) val = 126; | 
|  | 895 | return val; | 
|  | 896 | } | 
|  | 897 |  | 
|  | 898 | /* search an index for specified time from given time table */ | 
|  | 899 | static int | 
|  | 900 | calc_parm_search(int msec, short *table) | 
|  | 901 | { | 
|  | 902 | int left = 1, right = 127, mid; | 
|  | 903 | while (left < right) { | 
|  | 904 | mid = (left + right) / 2; | 
|  | 905 | if (msec < (int)table[mid]) | 
|  | 906 | left = mid + 1; | 
|  | 907 | else | 
|  | 908 | right = mid; | 
|  | 909 | } | 
|  | 910 | return left; | 
|  | 911 | } | 
|  | 912 |  | 
|  | 913 | /* attack time: search from time table */ | 
|  | 914 | int | 
|  | 915 | snd_sf_calc_parm_attack(int msec) | 
|  | 916 | { | 
|  | 917 | return calc_parm_search(msec, attack_time_tbl); | 
|  | 918 | } | 
|  | 919 |  | 
|  | 920 | /* decay/release time: search from time table */ | 
|  | 921 | int | 
|  | 922 | snd_sf_calc_parm_decay(int msec) | 
|  | 923 | { | 
|  | 924 | return calc_parm_search(msec, decay_time_tbl); | 
|  | 925 | } | 
|  | 926 |  | 
|  | 927 | int snd_sf_vol_table[128] = { | 
|  | 928 | 255,111,95,86,79,74,70,66,63,61,58,56,54,52,50,49, | 
|  | 929 | 47,46,45,43,42,41,40,39,38,37,36,35,34,34,33,32, | 
|  | 930 | 31,31,30,29,29,28,27,27,26,26,25,24,24,23,23,22, | 
|  | 931 | 22,21,21,21,20,20,19,19,18,18,18,17,17,16,16,16, | 
|  | 932 | 15,15,15,14,14,14,13,13,13,12,12,12,11,11,11,10, | 
|  | 933 | 10,10,10,9,9,9,8,8,8,8,7,7,7,7,6,6, | 
|  | 934 | 6,6,5,5,5,5,5,4,4,4,4,3,3,3,3,3, | 
|  | 935 | 2,2,2,2,2,1,1,1,1,1,0,0,0,0,0,0, | 
|  | 936 | }; | 
|  | 937 |  | 
|  | 938 |  | 
|  | 939 | #define calc_gus_sustain(val)  (0x7f - snd_sf_vol_table[(val)/2]) | 
|  | 940 | #define calc_gus_attenuation(val)	snd_sf_vol_table[(val)/2] | 
|  | 941 |  | 
|  | 942 | /* load GUS patch */ | 
|  | 943 | static int | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 944 | load_guspatch(struct snd_sf_list *sflist, const char __user *data, | 
|  | 945 | long count, int client) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 946 | { | 
|  | 947 | struct patch_info patch; | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 948 | struct snd_soundfont *sf; | 
|  | 949 | struct snd_sf_zone *zone; | 
|  | 950 | struct snd_sf_sample *smp; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 951 | int note, sample_id; | 
|  | 952 | int rc; | 
|  | 953 |  | 
|  | 954 | if (count < (long)sizeof(patch)) { | 
|  | 955 | snd_printk("patch record too small %ld\n", count); | 
|  | 956 | return -EINVAL; | 
|  | 957 | } | 
|  | 958 | if (copy_from_user(&patch, data, sizeof(patch))) | 
|  | 959 | return -EFAULT; | 
|  | 960 |  | 
|  | 961 | count -= sizeof(patch); | 
|  | 962 | data += sizeof(patch); | 
|  | 963 |  | 
|  | 964 | sf = newsf(sflist, SNDRV_SFNT_PAT_TYPE_GUS|SNDRV_SFNT_PAT_SHARED, NULL); | 
|  | 965 | if (sf == NULL) | 
|  | 966 | return -ENOMEM; | 
|  | 967 | if ((smp = sf_sample_new(sflist, sf)) == NULL) | 
|  | 968 | return -ENOMEM; | 
|  | 969 | sample_id = sflist->sample_counter; | 
|  | 970 | smp->v.sample = sample_id; | 
|  | 971 | smp->v.start = 0; | 
|  | 972 | smp->v.end = patch.len; | 
|  | 973 | smp->v.loopstart = patch.loop_start; | 
|  | 974 | smp->v.loopend = patch.loop_end; | 
|  | 975 | smp->v.size = patch.len; | 
|  | 976 |  | 
|  | 977 | /* set up mode flags */ | 
|  | 978 | smp->v.mode_flags = 0; | 
|  | 979 | if (!(patch.mode & WAVE_16_BITS)) | 
|  | 980 | smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_8BITS; | 
|  | 981 | if (patch.mode & WAVE_UNSIGNED) | 
|  | 982 | smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_UNSIGNED; | 
|  | 983 | smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_NO_BLANK; | 
|  | 984 | if (!(patch.mode & (WAVE_LOOPING|WAVE_BIDIR_LOOP|WAVE_LOOP_BACK))) | 
|  | 985 | smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_SINGLESHOT; | 
|  | 986 | if (patch.mode & WAVE_BIDIR_LOOP) | 
|  | 987 | smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_BIDIR_LOOP; | 
|  | 988 | if (patch.mode & WAVE_LOOP_BACK) | 
|  | 989 | smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_REVERSE_LOOP; | 
|  | 990 |  | 
|  | 991 | if (patch.mode & WAVE_16_BITS) { | 
|  | 992 | /* convert to word offsets */ | 
|  | 993 | smp->v.size /= 2; | 
|  | 994 | smp->v.end /= 2; | 
|  | 995 | smp->v.loopstart /= 2; | 
|  | 996 | smp->v.loopend /= 2; | 
|  | 997 | } | 
|  | 998 | /*smp->v.loopend++;*/ | 
|  | 999 |  | 
|  | 1000 | smp->v.dummy = 0; | 
|  | 1001 | smp->v.truesize = 0; | 
|  | 1002 | smp->v.sf_id = sf->id; | 
|  | 1003 |  | 
|  | 1004 | /* set up voice info */ | 
|  | 1005 | if ((zone = sf_zone_new(sflist, sf)) == NULL) { | 
|  | 1006 | sf_sample_delete(sflist, sf, smp); | 
|  | 1007 | return -ENOMEM; | 
|  | 1008 | } | 
|  | 1009 |  | 
|  | 1010 | /* | 
|  | 1011 | * load wave data | 
|  | 1012 | */ | 
|  | 1013 | if (sflist->callback.sample_new) { | 
|  | 1014 | rc = sflist->callback.sample_new | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 1015 | (sflist->callback.private_data, smp, sflist->memhdr, | 
|  | 1016 | data, count); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1017 | if (rc < 0) { | 
|  | 1018 | sf_sample_delete(sflist, sf, smp); | 
|  | 1019 | return rc; | 
|  | 1020 | } | 
|  | 1021 | /* memory offset is updated after */ | 
|  | 1022 | } | 
|  | 1023 |  | 
|  | 1024 | /* update the memory offset here */ | 
|  | 1025 | sflist->mem_used += smp->v.truesize; | 
|  | 1026 |  | 
|  | 1027 | zone->v.sample = sample_id; /* the last sample */ | 
|  | 1028 | zone->v.rate_offset = calc_rate_offset(patch.base_freq); | 
|  | 1029 | note = freq_to_note(patch.base_note); | 
|  | 1030 | zone->v.root = note / 100; | 
|  | 1031 | zone->v.tune = -(note % 100); | 
|  | 1032 | zone->v.low = (freq_to_note(patch.low_note) + 99) / 100; | 
|  | 1033 | zone->v.high = freq_to_note(patch.high_note) / 100; | 
|  | 1034 | /* panning position; -128 - 127 => 0-127 */ | 
|  | 1035 | zone->v.pan = (patch.panning + 128) / 2; | 
|  | 1036 | #if 0 | 
|  | 1037 | snd_printk("gus: basefrq=%d (ofs=%d) root=%d,tune=%d, range:%d-%d\n", | 
|  | 1038 | (int)patch.base_freq, zone->v.rate_offset, | 
|  | 1039 | zone->v.root, zone->v.tune, zone->v.low, zone->v.high); | 
|  | 1040 | #endif | 
|  | 1041 |  | 
|  | 1042 | /* detuning is ignored */ | 
|  | 1043 | /* 6points volume envelope */ | 
|  | 1044 | if (patch.mode & WAVE_ENVELOPES) { | 
|  | 1045 | int attack, hold, decay, release; | 
|  | 1046 | attack = calc_gus_envelope_time | 
|  | 1047 | (patch.env_rate[0], 0, patch.env_offset[0]); | 
|  | 1048 | hold = calc_gus_envelope_time | 
|  | 1049 | (patch.env_rate[1], patch.env_offset[0], | 
|  | 1050 | patch.env_offset[1]); | 
|  | 1051 | decay = calc_gus_envelope_time | 
|  | 1052 | (patch.env_rate[2], patch.env_offset[1], | 
|  | 1053 | patch.env_offset[2]); | 
|  | 1054 | release = calc_gus_envelope_time | 
|  | 1055 | (patch.env_rate[3], patch.env_offset[1], | 
|  | 1056 | patch.env_offset[4]); | 
|  | 1057 | release += calc_gus_envelope_time | 
|  | 1058 | (patch.env_rate[4], patch.env_offset[3], | 
|  | 1059 | patch.env_offset[4]); | 
|  | 1060 | release += calc_gus_envelope_time | 
|  | 1061 | (patch.env_rate[5], patch.env_offset[4], | 
|  | 1062 | patch.env_offset[5]); | 
|  | 1063 | zone->v.parm.volatkhld = | 
|  | 1064 | (snd_sf_calc_parm_hold(hold) << 8) | | 
|  | 1065 | snd_sf_calc_parm_attack(attack); | 
|  | 1066 | zone->v.parm.voldcysus = (calc_gus_sustain(patch.env_offset[2]) << 8) | | 
|  | 1067 | snd_sf_calc_parm_decay(decay); | 
|  | 1068 | zone->v.parm.volrelease = 0x8000 | snd_sf_calc_parm_decay(release); | 
|  | 1069 | zone->v.attenuation = calc_gus_attenuation(patch.env_offset[0]); | 
|  | 1070 | #if 0 | 
|  | 1071 | snd_printk("gus: atkhld=%x, dcysus=%x, volrel=%x, att=%d\n", | 
|  | 1072 | zone->v.parm.volatkhld, | 
|  | 1073 | zone->v.parm.voldcysus, | 
|  | 1074 | zone->v.parm.volrelease, | 
|  | 1075 | zone->v.attenuation); | 
|  | 1076 | #endif | 
|  | 1077 | } | 
|  | 1078 |  | 
|  | 1079 | /* fast release */ | 
|  | 1080 | if (patch.mode & WAVE_FAST_RELEASE) { | 
|  | 1081 | zone->v.parm.volrelease = 0x807f; | 
|  | 1082 | } | 
|  | 1083 |  | 
|  | 1084 | /* tremolo effect */ | 
|  | 1085 | if (patch.mode & WAVE_TREMOLO) { | 
|  | 1086 | int rate = (patch.tremolo_rate * 1000 / 38) / 42; | 
|  | 1087 | zone->v.parm.tremfrq = ((patch.tremolo_depth / 2) << 8) | rate; | 
|  | 1088 | } | 
|  | 1089 | /* vibrato effect */ | 
|  | 1090 | if (patch.mode & WAVE_VIBRATO) { | 
|  | 1091 | int rate = (patch.vibrato_rate * 1000 / 38) / 42; | 
|  | 1092 | zone->v.parm.fm2frq2 = ((patch.vibrato_depth / 6) << 8) | rate; | 
|  | 1093 | } | 
|  | 1094 |  | 
|  | 1095 | /* scale_freq, scale_factor, volume, and fractions not implemented */ | 
|  | 1096 |  | 
|  | 1097 | if (!(smp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT)) | 
|  | 1098 | zone->v.mode = SNDRV_SFNT_MODE_LOOPING; | 
|  | 1099 | else | 
|  | 1100 | zone->v.mode = 0; | 
|  | 1101 |  | 
|  | 1102 | /* append to the tail of the list */ | 
|  | 1103 | /*zone->bank = ctrls[AWE_MD_GUS_BANK];*/ | 
|  | 1104 | zone->bank = 0; | 
|  | 1105 | zone->instr = patch.instr_no; | 
|  | 1106 | zone->mapped = 0; | 
|  | 1107 | zone->v.sf_id = sf->id; | 
|  | 1108 |  | 
|  | 1109 | zone->sample = set_sample(sf, &zone->v); | 
|  | 1110 |  | 
|  | 1111 | /* rebuild preset now */ | 
|  | 1112 | add_preset(sflist, zone); | 
|  | 1113 |  | 
|  | 1114 | return 0; | 
|  | 1115 | } | 
|  | 1116 |  | 
|  | 1117 | /* load GUS patch */ | 
|  | 1118 | int | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 1119 | snd_soundfont_load_guspatch(struct snd_sf_list *sflist, const char __user *data, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1120 | long count, int client) | 
|  | 1121 | { | 
|  | 1122 | int rc; | 
|  | 1123 | lock_preset(sflist); | 
|  | 1124 | rc = load_guspatch(sflist, data, count, client); | 
|  | 1125 | unlock_preset(sflist); | 
|  | 1126 | return rc; | 
|  | 1127 | } | 
|  | 1128 |  | 
|  | 1129 |  | 
|  | 1130 | /* | 
|  | 1131 | * Rebuild the preset table.  This is like a hash table in that it allows | 
|  | 1132 | * quick access to the zone information.  For each preset there are zone | 
|  | 1133 | * structures linked by next_instr and by next_zone.  Former is the whole | 
|  | 1134 | * link for this preset, and latter is the link for zone (i.e. instrument/ | 
|  | 1135 | * bank/key combination). | 
|  | 1136 | */ | 
|  | 1137 | static void | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 1138 | rebuild_presets(struct snd_sf_list *sflist) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1139 | { | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 1140 | struct snd_soundfont *sf; | 
|  | 1141 | struct snd_sf_zone *cur; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1142 |  | 
|  | 1143 | /* clear preset table */ | 
|  | 1144 | memset(sflist->presets, 0, sizeof(sflist->presets)); | 
|  | 1145 |  | 
|  | 1146 | /* search all fonts and insert each font */ | 
|  | 1147 | for (sf = sflist->fonts; sf; sf = sf->next) { | 
|  | 1148 | for (cur = sf->zones; cur; cur = cur->next) { | 
|  | 1149 | if (! cur->mapped && cur->sample == NULL) { | 
|  | 1150 | /* try again to search the corresponding sample */ | 
|  | 1151 | cur->sample = set_sample(sf, &cur->v); | 
|  | 1152 | if (cur->sample == NULL) | 
|  | 1153 | continue; | 
|  | 1154 | } | 
|  | 1155 |  | 
|  | 1156 | add_preset(sflist, cur); | 
|  | 1157 | } | 
|  | 1158 | } | 
|  | 1159 | } | 
|  | 1160 |  | 
|  | 1161 |  | 
|  | 1162 | /* | 
|  | 1163 | * add the given zone to preset table | 
|  | 1164 | */ | 
|  | 1165 | static void | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 1166 | add_preset(struct snd_sf_list *sflist, struct snd_sf_zone *cur) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1167 | { | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 1168 | struct snd_sf_zone *zone; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1169 | int index; | 
|  | 1170 |  | 
|  | 1171 | zone = search_first_zone(sflist, cur->bank, cur->instr, cur->v.low); | 
|  | 1172 | if (zone && zone->v.sf_id != cur->v.sf_id) { | 
|  | 1173 | /* different instrument was already defined */ | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 1174 | struct snd_sf_zone *p; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1175 | /* compare the allocated time */ | 
|  | 1176 | for (p = zone; p; p = p->next_zone) { | 
|  | 1177 | if (p->counter > cur->counter) | 
|  | 1178 | /* the current is older.. skipped */ | 
|  | 1179 | return; | 
|  | 1180 | } | 
|  | 1181 | /* remove old zones */ | 
|  | 1182 | delete_preset(sflist, zone); | 
|  | 1183 | zone = NULL; /* do not forget to clear this! */ | 
|  | 1184 | } | 
|  | 1185 |  | 
|  | 1186 | /* prepend this zone */ | 
|  | 1187 | if ((index = get_index(cur->bank, cur->instr, cur->v.low)) < 0) | 
|  | 1188 | return; | 
|  | 1189 | cur->next_zone = zone; /* zone link */ | 
|  | 1190 | cur->next_instr = sflist->presets[index]; /* preset table link */ | 
|  | 1191 | sflist->presets[index] = cur; | 
|  | 1192 | } | 
|  | 1193 |  | 
|  | 1194 | /* | 
|  | 1195 | * delete the given zones from preset_table | 
|  | 1196 | */ | 
|  | 1197 | static void | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 1198 | delete_preset(struct snd_sf_list *sflist, struct snd_sf_zone *zp) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1199 | { | 
|  | 1200 | int index; | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 1201 | struct snd_sf_zone *p; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1202 |  | 
|  | 1203 | if ((index = get_index(zp->bank, zp->instr, zp->v.low)) < 0) | 
|  | 1204 | return; | 
|  | 1205 | for (p = sflist->presets[index]; p; p = p->next_instr) { | 
|  | 1206 | while (p->next_instr == zp) { | 
|  | 1207 | p->next_instr = zp->next_instr; | 
|  | 1208 | zp = zp->next_zone; | 
|  | 1209 | if (zp == NULL) | 
|  | 1210 | return; | 
|  | 1211 | } | 
|  | 1212 | } | 
|  | 1213 | } | 
|  | 1214 |  | 
|  | 1215 |  | 
|  | 1216 | /* | 
|  | 1217 | * Search matching zones from preset table. | 
|  | 1218 | * The note can be rewritten by preset mapping (alias). | 
|  | 1219 | * The found zones are stored on 'table' array.  max_layers defines | 
|  | 1220 | * the maximum number of elements in this array. | 
|  | 1221 | * This function returns the number of found zones.  0 if not found. | 
|  | 1222 | */ | 
|  | 1223 | int | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 1224 | snd_soundfont_search_zone(struct snd_sf_list *sflist, int *notep, int vel, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1225 | int preset, int bank, | 
|  | 1226 | int def_preset, int def_bank, | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 1227 | struct snd_sf_zone **table, int max_layers) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1228 | { | 
|  | 1229 | int nvoices; | 
|  | 1230 | unsigned long flags; | 
|  | 1231 |  | 
|  | 1232 | /* this function is supposed to be called atomically, | 
|  | 1233 | * so we check the lock.  if it's busy, just returns 0 to | 
|  | 1234 | * tell the caller the busy state | 
|  | 1235 | */ | 
|  | 1236 | spin_lock_irqsave(&sflist->lock, flags); | 
|  | 1237 | if (sflist->presets_locked) { | 
|  | 1238 | spin_unlock_irqrestore(&sflist->lock, flags); | 
|  | 1239 | return 0; | 
|  | 1240 | } | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 1241 | nvoices = search_zones(sflist, notep, vel, preset, bank, | 
|  | 1242 | table, max_layers, 0); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1243 | if (! nvoices) { | 
|  | 1244 | if (preset != def_preset || bank != def_bank) | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 1245 | nvoices = search_zones(sflist, notep, vel, | 
|  | 1246 | def_preset, def_bank, | 
|  | 1247 | table, max_layers, 0); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1248 | } | 
|  | 1249 | spin_unlock_irqrestore(&sflist->lock, flags); | 
|  | 1250 | return nvoices; | 
|  | 1251 | } | 
|  | 1252 |  | 
|  | 1253 |  | 
|  | 1254 | /* | 
|  | 1255 | * search the first matching zone | 
|  | 1256 | */ | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 1257 | static struct snd_sf_zone * | 
|  | 1258 | search_first_zone(struct snd_sf_list *sflist, int bank, int preset, int key) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1259 | { | 
|  | 1260 | int index; | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 1261 | struct snd_sf_zone *zp; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1262 |  | 
|  | 1263 | if ((index = get_index(bank, preset, key)) < 0) | 
|  | 1264 | return NULL; | 
|  | 1265 | for (zp = sflist->presets[index]; zp; zp = zp->next_instr) { | 
|  | 1266 | if (zp->instr == preset && zp->bank == bank) | 
|  | 1267 | return zp; | 
|  | 1268 | } | 
|  | 1269 | return NULL; | 
|  | 1270 | } | 
|  | 1271 |  | 
|  | 1272 |  | 
|  | 1273 | /* | 
|  | 1274 | * search matching zones from sflist.  can be called recursively. | 
|  | 1275 | */ | 
|  | 1276 | static int | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 1277 | search_zones(struct snd_sf_list *sflist, int *notep, int vel, | 
|  | 1278 | int preset, int bank, struct snd_sf_zone **table, | 
|  | 1279 | int max_layers, int level) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1280 | { | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 1281 | struct snd_sf_zone *zp; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1282 | int nvoices; | 
|  | 1283 |  | 
|  | 1284 | zp = search_first_zone(sflist, bank, preset, *notep); | 
|  | 1285 | nvoices = 0; | 
|  | 1286 | for (; zp; zp = zp->next_zone) { | 
|  | 1287 | if (*notep >= zp->v.low && *notep <= zp->v.high && | 
|  | 1288 | vel >= zp->v.vellow && vel <= zp->v.velhigh) { | 
|  | 1289 | if (zp->mapped) { | 
|  | 1290 | /* search preset mapping (aliasing) */ | 
|  | 1291 | int key = zp->v.fixkey; | 
|  | 1292 | preset = zp->v.start; | 
|  | 1293 | bank = zp->v.end; | 
|  | 1294 |  | 
|  | 1295 | if (level > 5) /* too deep alias level */ | 
|  | 1296 | return 0; | 
|  | 1297 | if (key < 0) | 
|  | 1298 | key = *notep; | 
|  | 1299 | nvoices = search_zones(sflist, &key, vel, | 
|  | 1300 | preset, bank, table, | 
|  | 1301 | max_layers, level + 1); | 
|  | 1302 | if (nvoices > 0) | 
|  | 1303 | *notep = key; | 
|  | 1304 | break; | 
|  | 1305 | } | 
|  | 1306 | table[nvoices++] = zp; | 
|  | 1307 | if (nvoices >= max_layers) | 
|  | 1308 | break; | 
|  | 1309 | } | 
|  | 1310 | } | 
|  | 1311 |  | 
|  | 1312 | return nvoices; | 
|  | 1313 | } | 
|  | 1314 |  | 
|  | 1315 |  | 
|  | 1316 | /* calculate the index of preset table: | 
|  | 1317 | * drums are mapped from 128 to 255 according to its note key. | 
|  | 1318 | * other instruments are mapped from 0 to 127. | 
|  | 1319 | * if the index is out of range, return -1. | 
|  | 1320 | */ | 
|  | 1321 | static int | 
|  | 1322 | get_index(int bank, int instr, int key) | 
|  | 1323 | { | 
|  | 1324 | int index; | 
|  | 1325 | if (SF_IS_DRUM_BANK(bank)) | 
|  | 1326 | index = key + SF_MAX_INSTRUMENTS; | 
|  | 1327 | else | 
|  | 1328 | index = instr; | 
|  | 1329 | index = index % SF_MAX_PRESETS; | 
|  | 1330 | if (index < 0) | 
|  | 1331 | return -1; | 
|  | 1332 | return index; | 
|  | 1333 | } | 
|  | 1334 |  | 
|  | 1335 | /* | 
|  | 1336 | * Initialise the sflist structure. | 
|  | 1337 | */ | 
|  | 1338 | static void | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 1339 | snd_sf_init(struct snd_sf_list *sflist) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1340 | { | 
|  | 1341 | memset(sflist->presets, 0, sizeof(sflist->presets)); | 
|  | 1342 |  | 
|  | 1343 | sflist->mem_used = 0; | 
|  | 1344 | sflist->currsf = NULL; | 
|  | 1345 | sflist->open_client = -1; | 
|  | 1346 | sflist->fonts = NULL; | 
|  | 1347 | sflist->fonts_size = 0; | 
|  | 1348 | sflist->zone_counter = 0; | 
|  | 1349 | sflist->sample_counter = 0; | 
|  | 1350 | sflist->zone_locked = 0; | 
|  | 1351 | sflist->sample_locked = 0; | 
|  | 1352 | } | 
|  | 1353 |  | 
|  | 1354 | /* | 
|  | 1355 | * Release all list records | 
|  | 1356 | */ | 
|  | 1357 | static void | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 1358 | snd_sf_clear(struct snd_sf_list *sflist) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1359 | { | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 1360 | struct snd_soundfont *sf, *nextsf; | 
|  | 1361 | struct snd_sf_zone *zp, *nextzp; | 
|  | 1362 | struct snd_sf_sample *sp, *nextsp; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1363 |  | 
|  | 1364 | for (sf = sflist->fonts; sf; sf = nextsf) { | 
|  | 1365 | nextsf = sf->next; | 
|  | 1366 | for (zp = sf->zones; zp; zp = nextzp) { | 
|  | 1367 | nextzp = zp->next; | 
|  | 1368 | kfree(zp); | 
|  | 1369 | } | 
|  | 1370 | for (sp = sf->samples; sp; sp = nextsp) { | 
|  | 1371 | nextsp = sp->next; | 
|  | 1372 | if (sflist->callback.sample_free) | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 1373 | sflist->callback.sample_free(sflist->callback.private_data, | 
|  | 1374 | sp, sflist->memhdr); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1375 | kfree(sp); | 
|  | 1376 | } | 
|  | 1377 | kfree(sf); | 
|  | 1378 | } | 
|  | 1379 |  | 
|  | 1380 | snd_sf_init(sflist); | 
|  | 1381 | } | 
|  | 1382 |  | 
|  | 1383 |  | 
|  | 1384 | /* | 
|  | 1385 | * Create a new sflist structure | 
|  | 1386 | */ | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 1387 | struct snd_sf_list * | 
|  | 1388 | snd_sf_new(struct snd_sf_callback *callback, struct snd_util_memhdr *hdr) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1389 | { | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 1390 | struct snd_sf_list *sflist; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1391 |  | 
| Takashi Iwai | 561b220 | 2005-09-09 14:22:34 +0200 | [diff] [blame] | 1392 | if ((sflist = kzalloc(sizeof(*sflist), GFP_KERNEL)) == NULL) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1393 | return NULL; | 
|  | 1394 |  | 
| Ingo Molnar | ef9f0a4 | 2006-01-16 16:31:42 +0100 | [diff] [blame] | 1395 | mutex_init(&sflist->presets_mutex); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1396 | spin_lock_init(&sflist->lock); | 
|  | 1397 | sflist->memhdr = hdr; | 
|  | 1398 |  | 
|  | 1399 | if (callback) | 
|  | 1400 | sflist->callback = *callback; | 
|  | 1401 |  | 
|  | 1402 | snd_sf_init(sflist); | 
|  | 1403 | return sflist; | 
|  | 1404 | } | 
|  | 1405 |  | 
|  | 1406 |  | 
|  | 1407 | /* | 
|  | 1408 | * Free everything allocated off the sflist structure. | 
|  | 1409 | */ | 
|  | 1410 | void | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 1411 | snd_sf_free(struct snd_sf_list *sflist) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1412 | { | 
|  | 1413 | if (sflist == NULL) | 
|  | 1414 | return; | 
|  | 1415 |  | 
|  | 1416 | lock_preset(sflist); | 
|  | 1417 | if (sflist->callback.sample_reset) | 
|  | 1418 | sflist->callback.sample_reset(sflist->callback.private_data); | 
|  | 1419 | snd_sf_clear(sflist); | 
|  | 1420 | unlock_preset(sflist); | 
|  | 1421 |  | 
|  | 1422 | kfree(sflist); | 
|  | 1423 | } | 
|  | 1424 |  | 
|  | 1425 | /* | 
|  | 1426 | * Remove all samples | 
|  | 1427 | * The soundcard should be silet before calling this function. | 
|  | 1428 | */ | 
|  | 1429 | int | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 1430 | snd_soundfont_remove_samples(struct snd_sf_list *sflist) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1431 | { | 
|  | 1432 | lock_preset(sflist); | 
|  | 1433 | if (sflist->callback.sample_reset) | 
|  | 1434 | sflist->callback.sample_reset(sflist->callback.private_data); | 
|  | 1435 | snd_sf_clear(sflist); | 
|  | 1436 | unlock_preset(sflist); | 
|  | 1437 |  | 
|  | 1438 | return 0; | 
|  | 1439 | } | 
|  | 1440 |  | 
|  | 1441 | /* | 
|  | 1442 | * Remove unlocked samples. | 
|  | 1443 | * The soundcard should be silent before calling this function. | 
|  | 1444 | */ | 
|  | 1445 | int | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 1446 | snd_soundfont_remove_unlocked(struct snd_sf_list *sflist) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1447 | { | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 1448 | struct snd_soundfont *sf; | 
|  | 1449 | struct snd_sf_zone *zp, *nextzp; | 
|  | 1450 | struct snd_sf_sample *sp, *nextsp; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1451 |  | 
|  | 1452 | lock_preset(sflist); | 
|  | 1453 |  | 
|  | 1454 | if (sflist->callback.sample_reset) | 
|  | 1455 | sflist->callback.sample_reset(sflist->callback.private_data); | 
|  | 1456 |  | 
|  | 1457 | /* to be sure */ | 
|  | 1458 | memset(sflist->presets, 0, sizeof(sflist->presets)); | 
|  | 1459 |  | 
|  | 1460 | for (sf = sflist->fonts; sf; sf = sf->next) { | 
|  | 1461 | for (zp = sf->zones; zp; zp = nextzp) { | 
|  | 1462 | if (zp->counter < sflist->zone_locked) | 
|  | 1463 | break; | 
|  | 1464 | nextzp = zp->next; | 
|  | 1465 | sf->zones = nextzp; | 
|  | 1466 | kfree(zp); | 
|  | 1467 | } | 
|  | 1468 |  | 
|  | 1469 | for (sp = sf->samples; sp; sp = nextsp) { | 
|  | 1470 | if (sp->counter < sflist->sample_locked) | 
|  | 1471 | break; | 
|  | 1472 | nextsp = sp->next; | 
|  | 1473 | sf->samples = nextsp; | 
|  | 1474 | sflist->mem_used -= sp->v.truesize; | 
|  | 1475 | if (sflist->callback.sample_free) | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 1476 | sflist->callback.sample_free(sflist->callback.private_data, | 
|  | 1477 | sp, sflist->memhdr); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1478 | kfree(sp); | 
|  | 1479 | } | 
|  | 1480 | } | 
|  | 1481 |  | 
|  | 1482 | sflist->zone_counter = sflist->zone_locked; | 
|  | 1483 | sflist->sample_counter = sflist->sample_locked; | 
|  | 1484 |  | 
|  | 1485 | rebuild_presets(sflist); | 
|  | 1486 |  | 
|  | 1487 | unlock_preset(sflist); | 
|  | 1488 | return 0; | 
|  | 1489 | } |