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