| 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 | } |