18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Soundfont generic routines. 48c2ecf20Sopenharmony_ci * It is intended that these should be used by any driver that is willing 58c2ecf20Sopenharmony_ci * to accept soundfont patches. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 1999 Steve Ratcliffe 88c2ecf20Sopenharmony_ci * Copyright (c) 1999-2000 Takashi Iwai <tiwai@suse.de> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci/* 118c2ecf20Sopenharmony_ci * Deal with reading in of a soundfont. Code follows the OSS way 128c2ecf20Sopenharmony_ci * of doing things so that the old sfxload utility can be used. 138c2ecf20Sopenharmony_ci * Everything may change when there is an alsa way of doing things. 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/export.h> 188c2ecf20Sopenharmony_ci#include <sound/core.h> 198c2ecf20Sopenharmony_ci#include <sound/soundfont.h> 208c2ecf20Sopenharmony_ci#include <sound/seq_oss_legacy.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* Prototypes for static functions */ 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic int open_patch(struct snd_sf_list *sflist, const char __user *data, 258c2ecf20Sopenharmony_ci int count, int client); 268c2ecf20Sopenharmony_cistatic struct snd_soundfont *newsf(struct snd_sf_list *sflist, int type, char *name); 278c2ecf20Sopenharmony_cistatic int is_identical_font(struct snd_soundfont *sf, int type, unsigned char *name); 288c2ecf20Sopenharmony_cistatic int close_patch(struct snd_sf_list *sflist); 298c2ecf20Sopenharmony_cistatic int probe_data(struct snd_sf_list *sflist, int sample_id); 308c2ecf20Sopenharmony_cistatic void set_zone_counter(struct snd_sf_list *sflist, 318c2ecf20Sopenharmony_ci struct snd_soundfont *sf, struct snd_sf_zone *zp); 328c2ecf20Sopenharmony_cistatic struct snd_sf_zone *sf_zone_new(struct snd_sf_list *sflist, 338c2ecf20Sopenharmony_ci struct snd_soundfont *sf); 348c2ecf20Sopenharmony_cistatic void set_sample_counter(struct snd_sf_list *sflist, 358c2ecf20Sopenharmony_ci struct snd_soundfont *sf, struct snd_sf_sample *sp); 368c2ecf20Sopenharmony_cistatic struct snd_sf_sample *sf_sample_new(struct snd_sf_list *sflist, 378c2ecf20Sopenharmony_ci struct snd_soundfont *sf); 388c2ecf20Sopenharmony_cistatic void sf_sample_delete(struct snd_sf_list *sflist, 398c2ecf20Sopenharmony_ci struct snd_soundfont *sf, struct snd_sf_sample *sp); 408c2ecf20Sopenharmony_cistatic int load_map(struct snd_sf_list *sflist, const void __user *data, int count); 418c2ecf20Sopenharmony_cistatic int load_info(struct snd_sf_list *sflist, const void __user *data, long count); 428c2ecf20Sopenharmony_cistatic int remove_info(struct snd_sf_list *sflist, struct snd_soundfont *sf, 438c2ecf20Sopenharmony_ci int bank, int instr); 448c2ecf20Sopenharmony_cistatic void init_voice_info(struct soundfont_voice_info *avp); 458c2ecf20Sopenharmony_cistatic void init_voice_parm(struct soundfont_voice_parm *pp); 468c2ecf20Sopenharmony_cistatic struct snd_sf_sample *set_sample(struct snd_soundfont *sf, 478c2ecf20Sopenharmony_ci struct soundfont_voice_info *avp); 488c2ecf20Sopenharmony_cistatic struct snd_sf_sample *find_sample(struct snd_soundfont *sf, int sample_id); 498c2ecf20Sopenharmony_cistatic int load_data(struct snd_sf_list *sflist, const void __user *data, long count); 508c2ecf20Sopenharmony_cistatic void rebuild_presets(struct snd_sf_list *sflist); 518c2ecf20Sopenharmony_cistatic void add_preset(struct snd_sf_list *sflist, struct snd_sf_zone *cur); 528c2ecf20Sopenharmony_cistatic void delete_preset(struct snd_sf_list *sflist, struct snd_sf_zone *zp); 538c2ecf20Sopenharmony_cistatic struct snd_sf_zone *search_first_zone(struct snd_sf_list *sflist, 548c2ecf20Sopenharmony_ci int bank, int preset, int key); 558c2ecf20Sopenharmony_cistatic int search_zones(struct snd_sf_list *sflist, int *notep, int vel, 568c2ecf20Sopenharmony_ci int preset, int bank, struct snd_sf_zone **table, 578c2ecf20Sopenharmony_ci int max_layers, int level); 588c2ecf20Sopenharmony_cistatic int get_index(int bank, int instr, int key); 598c2ecf20Sopenharmony_cistatic void snd_sf_init(struct snd_sf_list *sflist); 608c2ecf20Sopenharmony_cistatic void snd_sf_clear(struct snd_sf_list *sflist); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/* 638c2ecf20Sopenharmony_ci * lock access to sflist 648c2ecf20Sopenharmony_ci */ 658c2ecf20Sopenharmony_cistatic void 668c2ecf20Sopenharmony_cilock_preset(struct snd_sf_list *sflist) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci unsigned long flags; 698c2ecf20Sopenharmony_ci mutex_lock(&sflist->presets_mutex); 708c2ecf20Sopenharmony_ci spin_lock_irqsave(&sflist->lock, flags); 718c2ecf20Sopenharmony_ci sflist->presets_locked = 1; 728c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sflist->lock, flags); 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/* 778c2ecf20Sopenharmony_ci * remove lock 788c2ecf20Sopenharmony_ci */ 798c2ecf20Sopenharmony_cistatic void 808c2ecf20Sopenharmony_ciunlock_preset(struct snd_sf_list *sflist) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci unsigned long flags; 838c2ecf20Sopenharmony_ci spin_lock_irqsave(&sflist->lock, flags); 848c2ecf20Sopenharmony_ci sflist->presets_locked = 0; 858c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sflist->lock, flags); 868c2ecf20Sopenharmony_ci mutex_unlock(&sflist->presets_mutex); 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci/* 918c2ecf20Sopenharmony_ci * close the patch if the patch was opened by this client. 928c2ecf20Sopenharmony_ci */ 938c2ecf20Sopenharmony_ciint 948c2ecf20Sopenharmony_cisnd_soundfont_close_check(struct snd_sf_list *sflist, int client) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci unsigned long flags; 978c2ecf20Sopenharmony_ci spin_lock_irqsave(&sflist->lock, flags); 988c2ecf20Sopenharmony_ci if (sflist->open_client == client) { 998c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sflist->lock, flags); 1008c2ecf20Sopenharmony_ci return close_patch(sflist); 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sflist->lock, flags); 1038c2ecf20Sopenharmony_ci return 0; 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci/* 1088c2ecf20Sopenharmony_ci * Deal with a soundfont patch. Any driver could use these routines 1098c2ecf20Sopenharmony_ci * although it was designed for the AWE64. 1108c2ecf20Sopenharmony_ci * 1118c2ecf20Sopenharmony_ci * The sample_write and callargs pararameters allow a callback into 1128c2ecf20Sopenharmony_ci * the actual driver to write sample data to the board or whatever 1138c2ecf20Sopenharmony_ci * it wants to do with it. 1148c2ecf20Sopenharmony_ci */ 1158c2ecf20Sopenharmony_ciint 1168c2ecf20Sopenharmony_cisnd_soundfont_load(struct snd_sf_list *sflist, const void __user *data, 1178c2ecf20Sopenharmony_ci long count, int client) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci struct soundfont_patch_info patch; 1208c2ecf20Sopenharmony_ci unsigned long flags; 1218c2ecf20Sopenharmony_ci int rc; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if (count < (long)sizeof(patch)) { 1248c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "patch record too small %ld\n", count); 1258c2ecf20Sopenharmony_ci return -EINVAL; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci if (copy_from_user(&patch, data, sizeof(patch))) 1288c2ecf20Sopenharmony_ci return -EFAULT; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci count -= sizeof(patch); 1318c2ecf20Sopenharmony_ci data += sizeof(patch); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci if (patch.key != SNDRV_OSS_SOUNDFONT_PATCH) { 1348c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "The wrong kind of patch %x\n", patch.key); 1358c2ecf20Sopenharmony_ci return -EINVAL; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci if (count < patch.len) { 1388c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "Patch too short %ld, need %d\n", 1398c2ecf20Sopenharmony_ci count, patch.len); 1408c2ecf20Sopenharmony_ci return -EINVAL; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci if (patch.len < 0) { 1438c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "poor length %d\n", patch.len); 1448c2ecf20Sopenharmony_ci return -EINVAL; 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (patch.type == SNDRV_SFNT_OPEN_PATCH) { 1488c2ecf20Sopenharmony_ci /* grab sflist to open */ 1498c2ecf20Sopenharmony_ci lock_preset(sflist); 1508c2ecf20Sopenharmony_ci rc = open_patch(sflist, data, count, client); 1518c2ecf20Sopenharmony_ci unlock_preset(sflist); 1528c2ecf20Sopenharmony_ci return rc; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci /* check if other client already opened patch */ 1568c2ecf20Sopenharmony_ci spin_lock_irqsave(&sflist->lock, flags); 1578c2ecf20Sopenharmony_ci if (sflist->open_client != client) { 1588c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sflist->lock, flags); 1598c2ecf20Sopenharmony_ci return -EBUSY; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sflist->lock, flags); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci lock_preset(sflist); 1648c2ecf20Sopenharmony_ci rc = -EINVAL; 1658c2ecf20Sopenharmony_ci switch (patch.type) { 1668c2ecf20Sopenharmony_ci case SNDRV_SFNT_LOAD_INFO: 1678c2ecf20Sopenharmony_ci rc = load_info(sflist, data, count); 1688c2ecf20Sopenharmony_ci break; 1698c2ecf20Sopenharmony_ci case SNDRV_SFNT_LOAD_DATA: 1708c2ecf20Sopenharmony_ci rc = load_data(sflist, data, count); 1718c2ecf20Sopenharmony_ci break; 1728c2ecf20Sopenharmony_ci case SNDRV_SFNT_CLOSE_PATCH: 1738c2ecf20Sopenharmony_ci rc = close_patch(sflist); 1748c2ecf20Sopenharmony_ci break; 1758c2ecf20Sopenharmony_ci case SNDRV_SFNT_REPLACE_DATA: 1768c2ecf20Sopenharmony_ci /*rc = replace_data(&patch, data, count);*/ 1778c2ecf20Sopenharmony_ci break; 1788c2ecf20Sopenharmony_ci case SNDRV_SFNT_MAP_PRESET: 1798c2ecf20Sopenharmony_ci rc = load_map(sflist, data, count); 1808c2ecf20Sopenharmony_ci break; 1818c2ecf20Sopenharmony_ci case SNDRV_SFNT_PROBE_DATA: 1828c2ecf20Sopenharmony_ci rc = probe_data(sflist, patch.optarg); 1838c2ecf20Sopenharmony_ci break; 1848c2ecf20Sopenharmony_ci case SNDRV_SFNT_REMOVE_INFO: 1858c2ecf20Sopenharmony_ci /* patch must be opened */ 1868c2ecf20Sopenharmony_ci if (!sflist->currsf) { 1878c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "soundfont: remove_info: " 1888c2ecf20Sopenharmony_ci "patch not opened\n"); 1898c2ecf20Sopenharmony_ci rc = -EINVAL; 1908c2ecf20Sopenharmony_ci } else { 1918c2ecf20Sopenharmony_ci int bank, instr; 1928c2ecf20Sopenharmony_ci bank = ((unsigned short)patch.optarg >> 8) & 0xff; 1938c2ecf20Sopenharmony_ci instr = (unsigned short)patch.optarg & 0xff; 1948c2ecf20Sopenharmony_ci if (! remove_info(sflist, sflist->currsf, bank, instr)) 1958c2ecf20Sopenharmony_ci rc = -EINVAL; 1968c2ecf20Sopenharmony_ci else 1978c2ecf20Sopenharmony_ci rc = 0; 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci break; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci unlock_preset(sflist); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci return rc; 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci/* check if specified type is special font (GUS or preset-alias) */ 2088c2ecf20Sopenharmony_cistatic inline int 2098c2ecf20Sopenharmony_ciis_special_type(int type) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci type &= 0x0f; 2128c2ecf20Sopenharmony_ci return (type == SNDRV_SFNT_PAT_TYPE_GUS || 2138c2ecf20Sopenharmony_ci type == SNDRV_SFNT_PAT_TYPE_MAP); 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci/* open patch; create sf list */ 2188c2ecf20Sopenharmony_cistatic int 2198c2ecf20Sopenharmony_ciopen_patch(struct snd_sf_list *sflist, const char __user *data, 2208c2ecf20Sopenharmony_ci int count, int client) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci struct soundfont_open_parm parm; 2238c2ecf20Sopenharmony_ci struct snd_soundfont *sf; 2248c2ecf20Sopenharmony_ci unsigned long flags; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci spin_lock_irqsave(&sflist->lock, flags); 2278c2ecf20Sopenharmony_ci if (sflist->open_client >= 0 || sflist->currsf) { 2288c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sflist->lock, flags); 2298c2ecf20Sopenharmony_ci return -EBUSY; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sflist->lock, flags); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci if (copy_from_user(&parm, data, sizeof(parm))) 2348c2ecf20Sopenharmony_ci return -EFAULT; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (is_special_type(parm.type)) { 2378c2ecf20Sopenharmony_ci parm.type |= SNDRV_SFNT_PAT_SHARED; 2388c2ecf20Sopenharmony_ci sf = newsf(sflist, parm.type, NULL); 2398c2ecf20Sopenharmony_ci } else 2408c2ecf20Sopenharmony_ci sf = newsf(sflist, parm.type, parm.name); 2418c2ecf20Sopenharmony_ci if (sf == NULL) { 2428c2ecf20Sopenharmony_ci return -ENOMEM; 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci spin_lock_irqsave(&sflist->lock, flags); 2468c2ecf20Sopenharmony_ci sflist->open_client = client; 2478c2ecf20Sopenharmony_ci sflist->currsf = sf; 2488c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sflist->lock, flags); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci return 0; 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci/* 2548c2ecf20Sopenharmony_ci * Allocate a new soundfont structure. 2558c2ecf20Sopenharmony_ci */ 2568c2ecf20Sopenharmony_cistatic struct snd_soundfont * 2578c2ecf20Sopenharmony_cinewsf(struct snd_sf_list *sflist, int type, char *name) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci struct snd_soundfont *sf; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci /* check the shared fonts */ 2628c2ecf20Sopenharmony_ci if (type & SNDRV_SFNT_PAT_SHARED) { 2638c2ecf20Sopenharmony_ci for (sf = sflist->fonts; sf; sf = sf->next) { 2648c2ecf20Sopenharmony_ci if (is_identical_font(sf, type, name)) { 2658c2ecf20Sopenharmony_ci return sf; 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci /* not found -- create a new one */ 2718c2ecf20Sopenharmony_ci sf = kzalloc(sizeof(*sf), GFP_KERNEL); 2728c2ecf20Sopenharmony_ci if (sf == NULL) 2738c2ecf20Sopenharmony_ci return NULL; 2748c2ecf20Sopenharmony_ci sf->id = sflist->fonts_size; 2758c2ecf20Sopenharmony_ci sflist->fonts_size++; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci /* prepend this record */ 2788c2ecf20Sopenharmony_ci sf->next = sflist->fonts; 2798c2ecf20Sopenharmony_ci sflist->fonts = sf; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci sf->type = type; 2828c2ecf20Sopenharmony_ci sf->zones = NULL; 2838c2ecf20Sopenharmony_ci sf->samples = NULL; 2848c2ecf20Sopenharmony_ci if (name) 2858c2ecf20Sopenharmony_ci memcpy(sf->name, name, SNDRV_SFNT_PATCH_NAME_LEN); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci return sf; 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci/* check if the given name matches to the existing list */ 2918c2ecf20Sopenharmony_cistatic int 2928c2ecf20Sopenharmony_ciis_identical_font(struct snd_soundfont *sf, int type, unsigned char *name) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci return ((sf->type & SNDRV_SFNT_PAT_SHARED) && 2958c2ecf20Sopenharmony_ci (sf->type & 0x0f) == (type & 0x0f) && 2968c2ecf20Sopenharmony_ci (name == NULL || 2978c2ecf20Sopenharmony_ci memcmp(sf->name, name, SNDRV_SFNT_PATCH_NAME_LEN) == 0)); 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci/* 3018c2ecf20Sopenharmony_ci * Close the current patch. 3028c2ecf20Sopenharmony_ci */ 3038c2ecf20Sopenharmony_cistatic int 3048c2ecf20Sopenharmony_ciclose_patch(struct snd_sf_list *sflist) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci unsigned long flags; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci spin_lock_irqsave(&sflist->lock, flags); 3098c2ecf20Sopenharmony_ci sflist->currsf = NULL; 3108c2ecf20Sopenharmony_ci sflist->open_client = -1; 3118c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sflist->lock, flags); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci rebuild_presets(sflist); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci return 0; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci/* probe sample in the current list -- nothing to be loaded */ 3208c2ecf20Sopenharmony_cistatic int 3218c2ecf20Sopenharmony_ciprobe_data(struct snd_sf_list *sflist, int sample_id) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci /* patch must be opened */ 3248c2ecf20Sopenharmony_ci if (sflist->currsf) { 3258c2ecf20Sopenharmony_ci /* search the specified sample by optarg */ 3268c2ecf20Sopenharmony_ci if (find_sample(sflist->currsf, sample_id)) 3278c2ecf20Sopenharmony_ci return 0; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci return -EINVAL; 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci/* 3338c2ecf20Sopenharmony_ci * increment zone counter 3348c2ecf20Sopenharmony_ci */ 3358c2ecf20Sopenharmony_cistatic void 3368c2ecf20Sopenharmony_ciset_zone_counter(struct snd_sf_list *sflist, struct snd_soundfont *sf, 3378c2ecf20Sopenharmony_ci struct snd_sf_zone *zp) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci zp->counter = sflist->zone_counter++; 3408c2ecf20Sopenharmony_ci if (sf->type & SNDRV_SFNT_PAT_LOCKED) 3418c2ecf20Sopenharmony_ci sflist->zone_locked = sflist->zone_counter; 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci/* 3458c2ecf20Sopenharmony_ci * allocate a new zone record 3468c2ecf20Sopenharmony_ci */ 3478c2ecf20Sopenharmony_cistatic struct snd_sf_zone * 3488c2ecf20Sopenharmony_cisf_zone_new(struct snd_sf_list *sflist, struct snd_soundfont *sf) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci struct snd_sf_zone *zp; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci if ((zp = kzalloc(sizeof(*zp), GFP_KERNEL)) == NULL) 3538c2ecf20Sopenharmony_ci return NULL; 3548c2ecf20Sopenharmony_ci zp->next = sf->zones; 3558c2ecf20Sopenharmony_ci sf->zones = zp; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci init_voice_info(&zp->v); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci set_zone_counter(sflist, sf, zp); 3608c2ecf20Sopenharmony_ci return zp; 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci/* 3658c2ecf20Sopenharmony_ci * increment sample counter 3668c2ecf20Sopenharmony_ci */ 3678c2ecf20Sopenharmony_cistatic void 3688c2ecf20Sopenharmony_ciset_sample_counter(struct snd_sf_list *sflist, struct snd_soundfont *sf, 3698c2ecf20Sopenharmony_ci struct snd_sf_sample *sp) 3708c2ecf20Sopenharmony_ci{ 3718c2ecf20Sopenharmony_ci sp->counter = sflist->sample_counter++; 3728c2ecf20Sopenharmony_ci if (sf->type & SNDRV_SFNT_PAT_LOCKED) 3738c2ecf20Sopenharmony_ci sflist->sample_locked = sflist->sample_counter; 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci/* 3778c2ecf20Sopenharmony_ci * allocate a new sample list record 3788c2ecf20Sopenharmony_ci */ 3798c2ecf20Sopenharmony_cistatic struct snd_sf_sample * 3808c2ecf20Sopenharmony_cisf_sample_new(struct snd_sf_list *sflist, struct snd_soundfont *sf) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci struct snd_sf_sample *sp; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci if ((sp = kzalloc(sizeof(*sp), GFP_KERNEL)) == NULL) 3858c2ecf20Sopenharmony_ci return NULL; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci sp->next = sf->samples; 3888c2ecf20Sopenharmony_ci sf->samples = sp; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci set_sample_counter(sflist, sf, sp); 3918c2ecf20Sopenharmony_ci return sp; 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci/* 3958c2ecf20Sopenharmony_ci * delete sample list -- this is an exceptional job. 3968c2ecf20Sopenharmony_ci * only the last allocated sample can be deleted. 3978c2ecf20Sopenharmony_ci */ 3988c2ecf20Sopenharmony_cistatic void 3998c2ecf20Sopenharmony_cisf_sample_delete(struct snd_sf_list *sflist, struct snd_soundfont *sf, 4008c2ecf20Sopenharmony_ci struct snd_sf_sample *sp) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci /* only last sample is accepted */ 4038c2ecf20Sopenharmony_ci if (sp == sf->samples) { 4048c2ecf20Sopenharmony_ci sf->samples = sp->next; 4058c2ecf20Sopenharmony_ci kfree(sp); 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci/* load voice map */ 4118c2ecf20Sopenharmony_cistatic int 4128c2ecf20Sopenharmony_ciload_map(struct snd_sf_list *sflist, const void __user *data, int count) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci struct snd_sf_zone *zp, *prevp; 4158c2ecf20Sopenharmony_ci struct snd_soundfont *sf; 4168c2ecf20Sopenharmony_ci struct soundfont_voice_map map; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci /* get the link info */ 4198c2ecf20Sopenharmony_ci if (count < (int)sizeof(map)) 4208c2ecf20Sopenharmony_ci return -EINVAL; 4218c2ecf20Sopenharmony_ci if (copy_from_user(&map, data, sizeof(map))) 4228c2ecf20Sopenharmony_ci return -EFAULT; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci if (map.map_instr < 0 || map.map_instr >= SF_MAX_INSTRUMENTS) 4258c2ecf20Sopenharmony_ci return -EINVAL; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci sf = newsf(sflist, SNDRV_SFNT_PAT_TYPE_MAP|SNDRV_SFNT_PAT_SHARED, NULL); 4288c2ecf20Sopenharmony_ci if (sf == NULL) 4298c2ecf20Sopenharmony_ci return -ENOMEM; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci prevp = NULL; 4328c2ecf20Sopenharmony_ci for (zp = sf->zones; zp; prevp = zp, zp = zp->next) { 4338c2ecf20Sopenharmony_ci if (zp->mapped && 4348c2ecf20Sopenharmony_ci zp->instr == map.map_instr && 4358c2ecf20Sopenharmony_ci zp->bank == map.map_bank && 4368c2ecf20Sopenharmony_ci zp->v.low == map.map_key && 4378c2ecf20Sopenharmony_ci zp->v.start == map.src_instr && 4388c2ecf20Sopenharmony_ci zp->v.end == map.src_bank && 4398c2ecf20Sopenharmony_ci zp->v.fixkey == map.src_key) { 4408c2ecf20Sopenharmony_ci /* the same mapping is already present */ 4418c2ecf20Sopenharmony_ci /* relink this record to the link head */ 4428c2ecf20Sopenharmony_ci if (prevp) { 4438c2ecf20Sopenharmony_ci prevp->next = zp->next; 4448c2ecf20Sopenharmony_ci zp->next = sf->zones; 4458c2ecf20Sopenharmony_ci sf->zones = zp; 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci /* update the counter */ 4488c2ecf20Sopenharmony_ci set_zone_counter(sflist, sf, zp); 4498c2ecf20Sopenharmony_ci return 0; 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci /* create a new zone */ 4548c2ecf20Sopenharmony_ci if ((zp = sf_zone_new(sflist, sf)) == NULL) 4558c2ecf20Sopenharmony_ci return -ENOMEM; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci zp->bank = map.map_bank; 4588c2ecf20Sopenharmony_ci zp->instr = map.map_instr; 4598c2ecf20Sopenharmony_ci zp->mapped = 1; 4608c2ecf20Sopenharmony_ci if (map.map_key >= 0) { 4618c2ecf20Sopenharmony_ci zp->v.low = map.map_key; 4628c2ecf20Sopenharmony_ci zp->v.high = map.map_key; 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci zp->v.start = map.src_instr; 4658c2ecf20Sopenharmony_ci zp->v.end = map.src_bank; 4668c2ecf20Sopenharmony_ci zp->v.fixkey = map.src_key; 4678c2ecf20Sopenharmony_ci zp->v.sf_id = sf->id; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci add_preset(sflist, zp); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci return 0; 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci/* remove the present instrument layers */ 4768c2ecf20Sopenharmony_cistatic int 4778c2ecf20Sopenharmony_ciremove_info(struct snd_sf_list *sflist, struct snd_soundfont *sf, 4788c2ecf20Sopenharmony_ci int bank, int instr) 4798c2ecf20Sopenharmony_ci{ 4808c2ecf20Sopenharmony_ci struct snd_sf_zone *prev, *next, *p; 4818c2ecf20Sopenharmony_ci int removed = 0; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci prev = NULL; 4848c2ecf20Sopenharmony_ci for (p = sf->zones; p; p = next) { 4858c2ecf20Sopenharmony_ci next = p->next; 4868c2ecf20Sopenharmony_ci if (! p->mapped && 4878c2ecf20Sopenharmony_ci p->bank == bank && p->instr == instr) { 4888c2ecf20Sopenharmony_ci /* remove this layer */ 4898c2ecf20Sopenharmony_ci if (prev) 4908c2ecf20Sopenharmony_ci prev->next = next; 4918c2ecf20Sopenharmony_ci else 4928c2ecf20Sopenharmony_ci sf->zones = next; 4938c2ecf20Sopenharmony_ci removed++; 4948c2ecf20Sopenharmony_ci kfree(p); 4958c2ecf20Sopenharmony_ci } else 4968c2ecf20Sopenharmony_ci prev = p; 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci if (removed) 4998c2ecf20Sopenharmony_ci rebuild_presets(sflist); 5008c2ecf20Sopenharmony_ci return removed; 5018c2ecf20Sopenharmony_ci} 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci/* 5058c2ecf20Sopenharmony_ci * Read an info record from the user buffer and save it on the current 5068c2ecf20Sopenharmony_ci * open soundfont. 5078c2ecf20Sopenharmony_ci */ 5088c2ecf20Sopenharmony_cistatic int 5098c2ecf20Sopenharmony_ciload_info(struct snd_sf_list *sflist, const void __user *data, long count) 5108c2ecf20Sopenharmony_ci{ 5118c2ecf20Sopenharmony_ci struct snd_soundfont *sf; 5128c2ecf20Sopenharmony_ci struct snd_sf_zone *zone; 5138c2ecf20Sopenharmony_ci struct soundfont_voice_rec_hdr hdr; 5148c2ecf20Sopenharmony_ci int i; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci /* patch must be opened */ 5178c2ecf20Sopenharmony_ci if ((sf = sflist->currsf) == NULL) 5188c2ecf20Sopenharmony_ci return -EINVAL; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci if (is_special_type(sf->type)) 5218c2ecf20Sopenharmony_ci return -EINVAL; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci if (count < (long)sizeof(hdr)) { 5248c2ecf20Sopenharmony_ci printk(KERN_ERR "Soundfont error: invalid patch zone length\n"); 5258c2ecf20Sopenharmony_ci return -EINVAL; 5268c2ecf20Sopenharmony_ci } 5278c2ecf20Sopenharmony_ci if (copy_from_user((char*)&hdr, data, sizeof(hdr))) 5288c2ecf20Sopenharmony_ci return -EFAULT; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci data += sizeof(hdr); 5318c2ecf20Sopenharmony_ci count -= sizeof(hdr); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci if (hdr.nvoices <= 0 || hdr.nvoices >= 100) { 5348c2ecf20Sopenharmony_ci printk(KERN_ERR "Soundfont error: Illegal voice number %d\n", 5358c2ecf20Sopenharmony_ci hdr.nvoices); 5368c2ecf20Sopenharmony_ci return -EINVAL; 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci if (count < (long)sizeof(struct soundfont_voice_info) * hdr.nvoices) { 5408c2ecf20Sopenharmony_ci printk(KERN_ERR "Soundfont Error: " 5418c2ecf20Sopenharmony_ci "patch length(%ld) is smaller than nvoices(%d)\n", 5428c2ecf20Sopenharmony_ci count, hdr.nvoices); 5438c2ecf20Sopenharmony_ci return -EINVAL; 5448c2ecf20Sopenharmony_ci } 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci switch (hdr.write_mode) { 5478c2ecf20Sopenharmony_ci case SNDRV_SFNT_WR_EXCLUSIVE: 5488c2ecf20Sopenharmony_ci /* exclusive mode - if the instrument already exists, 5498c2ecf20Sopenharmony_ci return error */ 5508c2ecf20Sopenharmony_ci for (zone = sf->zones; zone; zone = zone->next) { 5518c2ecf20Sopenharmony_ci if (!zone->mapped && 5528c2ecf20Sopenharmony_ci zone->bank == hdr.bank && 5538c2ecf20Sopenharmony_ci zone->instr == hdr.instr) 5548c2ecf20Sopenharmony_ci return -EINVAL; 5558c2ecf20Sopenharmony_ci } 5568c2ecf20Sopenharmony_ci break; 5578c2ecf20Sopenharmony_ci case SNDRV_SFNT_WR_REPLACE: 5588c2ecf20Sopenharmony_ci /* replace mode - remove the instrument if it already exists */ 5598c2ecf20Sopenharmony_ci remove_info(sflist, sf, hdr.bank, hdr.instr); 5608c2ecf20Sopenharmony_ci break; 5618c2ecf20Sopenharmony_ci } 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci for (i = 0; i < hdr.nvoices; i++) { 5648c2ecf20Sopenharmony_ci struct snd_sf_zone tmpzone; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci /* copy awe_voice_info parameters */ 5678c2ecf20Sopenharmony_ci if (copy_from_user(&tmpzone.v, data, sizeof(tmpzone.v))) { 5688c2ecf20Sopenharmony_ci return -EFAULT; 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci data += sizeof(tmpzone.v); 5728c2ecf20Sopenharmony_ci count -= sizeof(tmpzone.v); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci tmpzone.bank = hdr.bank; 5758c2ecf20Sopenharmony_ci tmpzone.instr = hdr.instr; 5768c2ecf20Sopenharmony_ci tmpzone.mapped = 0; 5778c2ecf20Sopenharmony_ci tmpzone.v.sf_id = sf->id; 5788c2ecf20Sopenharmony_ci if (tmpzone.v.mode & SNDRV_SFNT_MODE_INIT_PARM) 5798c2ecf20Sopenharmony_ci init_voice_parm(&tmpzone.v.parm); 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci /* create a new zone */ 5828c2ecf20Sopenharmony_ci if ((zone = sf_zone_new(sflist, sf)) == NULL) { 5838c2ecf20Sopenharmony_ci return -ENOMEM; 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci /* copy the temporary data */ 5878c2ecf20Sopenharmony_ci zone->bank = tmpzone.bank; 5888c2ecf20Sopenharmony_ci zone->instr = tmpzone.instr; 5898c2ecf20Sopenharmony_ci zone->v = tmpzone.v; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci /* look up the sample */ 5928c2ecf20Sopenharmony_ci zone->sample = set_sample(sf, &zone->v); 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci return 0; 5968c2ecf20Sopenharmony_ci} 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci/* initialize voice_info record */ 6008c2ecf20Sopenharmony_cistatic void 6018c2ecf20Sopenharmony_ciinit_voice_info(struct soundfont_voice_info *avp) 6028c2ecf20Sopenharmony_ci{ 6038c2ecf20Sopenharmony_ci memset(avp, 0, sizeof(*avp)); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci avp->root = 60; 6068c2ecf20Sopenharmony_ci avp->high = 127; 6078c2ecf20Sopenharmony_ci avp->velhigh = 127; 6088c2ecf20Sopenharmony_ci avp->fixkey = -1; 6098c2ecf20Sopenharmony_ci avp->fixvel = -1; 6108c2ecf20Sopenharmony_ci avp->fixpan = -1; 6118c2ecf20Sopenharmony_ci avp->pan = -1; 6128c2ecf20Sopenharmony_ci avp->amplitude = 127; 6138c2ecf20Sopenharmony_ci avp->scaleTuning = 100; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci init_voice_parm(&avp->parm); 6168c2ecf20Sopenharmony_ci} 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci/* initialize voice_parm record: 6198c2ecf20Sopenharmony_ci * Env1/2: delay=0, attack=0, hold=0, sustain=0, decay=0, release=0. 6208c2ecf20Sopenharmony_ci * Vibrato and Tremolo effects are zero. 6218c2ecf20Sopenharmony_ci * Cutoff is maximum. 6228c2ecf20Sopenharmony_ci * Chorus and Reverb effects are zero. 6238c2ecf20Sopenharmony_ci */ 6248c2ecf20Sopenharmony_cistatic void 6258c2ecf20Sopenharmony_ciinit_voice_parm(struct soundfont_voice_parm *pp) 6268c2ecf20Sopenharmony_ci{ 6278c2ecf20Sopenharmony_ci memset(pp, 0, sizeof(*pp)); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci pp->moddelay = 0x8000; 6308c2ecf20Sopenharmony_ci pp->modatkhld = 0x7f7f; 6318c2ecf20Sopenharmony_ci pp->moddcysus = 0x7f7f; 6328c2ecf20Sopenharmony_ci pp->modrelease = 0x807f; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci pp->voldelay = 0x8000; 6358c2ecf20Sopenharmony_ci pp->volatkhld = 0x7f7f; 6368c2ecf20Sopenharmony_ci pp->voldcysus = 0x7f7f; 6378c2ecf20Sopenharmony_ci pp->volrelease = 0x807f; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci pp->lfo1delay = 0x8000; 6408c2ecf20Sopenharmony_ci pp->lfo2delay = 0x8000; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci pp->cutoff = 0xff; 6438c2ecf20Sopenharmony_ci} 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci/* search the specified sample */ 6468c2ecf20Sopenharmony_cistatic struct snd_sf_sample * 6478c2ecf20Sopenharmony_ciset_sample(struct snd_soundfont *sf, struct soundfont_voice_info *avp) 6488c2ecf20Sopenharmony_ci{ 6498c2ecf20Sopenharmony_ci struct snd_sf_sample *sample; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci sample = find_sample(sf, avp->sample); 6528c2ecf20Sopenharmony_ci if (sample == NULL) 6538c2ecf20Sopenharmony_ci return NULL; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci /* add in the actual sample offsets: 6568c2ecf20Sopenharmony_ci * The voice_info addresses define only the relative offset 6578c2ecf20Sopenharmony_ci * from sample pointers. Here we calculate the actual DRAM 6588c2ecf20Sopenharmony_ci * offset from sample pointers. 6598c2ecf20Sopenharmony_ci */ 6608c2ecf20Sopenharmony_ci avp->start += sample->v.start; 6618c2ecf20Sopenharmony_ci avp->end += sample->v.end; 6628c2ecf20Sopenharmony_ci avp->loopstart += sample->v.loopstart; 6638c2ecf20Sopenharmony_ci avp->loopend += sample->v.loopend; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci /* copy mode flags */ 6668c2ecf20Sopenharmony_ci avp->sample_mode = sample->v.mode_flags; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci return sample; 6698c2ecf20Sopenharmony_ci} 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci/* find the sample pointer with the given id in the soundfont */ 6728c2ecf20Sopenharmony_cistatic struct snd_sf_sample * 6738c2ecf20Sopenharmony_cifind_sample(struct snd_soundfont *sf, int sample_id) 6748c2ecf20Sopenharmony_ci{ 6758c2ecf20Sopenharmony_ci struct snd_sf_sample *p; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci if (sf == NULL) 6788c2ecf20Sopenharmony_ci return NULL; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci for (p = sf->samples; p; p = p->next) { 6818c2ecf20Sopenharmony_ci if (p->v.sample == sample_id) 6828c2ecf20Sopenharmony_ci return p; 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci return NULL; 6858c2ecf20Sopenharmony_ci} 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci/* 6898c2ecf20Sopenharmony_ci * Load sample information, this can include data to be loaded onto 6908c2ecf20Sopenharmony_ci * the soundcard. It can also just be a pointer into soundcard ROM. 6918c2ecf20Sopenharmony_ci * If there is data it will be written to the soundcard via the callback 6928c2ecf20Sopenharmony_ci * routine. 6938c2ecf20Sopenharmony_ci */ 6948c2ecf20Sopenharmony_cistatic int 6958c2ecf20Sopenharmony_ciload_data(struct snd_sf_list *sflist, const void __user *data, long count) 6968c2ecf20Sopenharmony_ci{ 6978c2ecf20Sopenharmony_ci struct snd_soundfont *sf; 6988c2ecf20Sopenharmony_ci struct soundfont_sample_info sample_info; 6998c2ecf20Sopenharmony_ci struct snd_sf_sample *sp; 7008c2ecf20Sopenharmony_ci long off; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci /* patch must be opened */ 7038c2ecf20Sopenharmony_ci if ((sf = sflist->currsf) == NULL) 7048c2ecf20Sopenharmony_ci return -EINVAL; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci if (is_special_type(sf->type)) 7078c2ecf20Sopenharmony_ci return -EINVAL; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci if (copy_from_user(&sample_info, data, sizeof(sample_info))) 7108c2ecf20Sopenharmony_ci return -EFAULT; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci off = sizeof(sample_info); 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci if (sample_info.size != (count-off)/2) 7158c2ecf20Sopenharmony_ci return -EINVAL; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci /* Check for dup */ 7188c2ecf20Sopenharmony_ci if (find_sample(sf, sample_info.sample)) { 7198c2ecf20Sopenharmony_ci /* if shared sample, skip this data */ 7208c2ecf20Sopenharmony_ci if (sf->type & SNDRV_SFNT_PAT_SHARED) 7218c2ecf20Sopenharmony_ci return 0; 7228c2ecf20Sopenharmony_ci return -EINVAL; 7238c2ecf20Sopenharmony_ci } 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci /* Allocate a new sample structure */ 7268c2ecf20Sopenharmony_ci if ((sp = sf_sample_new(sflist, sf)) == NULL) 7278c2ecf20Sopenharmony_ci return -ENOMEM; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci sp->v = sample_info; 7308c2ecf20Sopenharmony_ci sp->v.sf_id = sf->id; 7318c2ecf20Sopenharmony_ci sp->v.dummy = 0; 7328c2ecf20Sopenharmony_ci sp->v.truesize = sp->v.size; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci /* 7358c2ecf20Sopenharmony_ci * If there is wave data then load it. 7368c2ecf20Sopenharmony_ci */ 7378c2ecf20Sopenharmony_ci if (sp->v.size > 0) { 7388c2ecf20Sopenharmony_ci int rc; 7398c2ecf20Sopenharmony_ci rc = sflist->callback.sample_new 7408c2ecf20Sopenharmony_ci (sflist->callback.private_data, sp, sflist->memhdr, 7418c2ecf20Sopenharmony_ci data + off, count - off); 7428c2ecf20Sopenharmony_ci if (rc < 0) { 7438c2ecf20Sopenharmony_ci sf_sample_delete(sflist, sf, sp); 7448c2ecf20Sopenharmony_ci return rc; 7458c2ecf20Sopenharmony_ci } 7468c2ecf20Sopenharmony_ci sflist->mem_used += sp->v.truesize; 7478c2ecf20Sopenharmony_ci } 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci return count; 7508c2ecf20Sopenharmony_ci} 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci/* log2_tbl[i] = log2(i+128) * 0x10000 */ 7548c2ecf20Sopenharmony_cistatic const int log_tbl[129] = { 7558c2ecf20Sopenharmony_ci 0x70000, 0x702df, 0x705b9, 0x7088e, 0x70b5d, 0x70e26, 0x710eb, 0x713aa, 7568c2ecf20Sopenharmony_ci 0x71663, 0x71918, 0x71bc8, 0x71e72, 0x72118, 0x723b9, 0x72655, 0x728ed, 7578c2ecf20Sopenharmony_ci 0x72b80, 0x72e0e, 0x73098, 0x7331d, 0x7359e, 0x7381b, 0x73a93, 0x73d08, 7588c2ecf20Sopenharmony_ci 0x73f78, 0x741e4, 0x7444c, 0x746b0, 0x74910, 0x74b6c, 0x74dc4, 0x75019, 7598c2ecf20Sopenharmony_ci 0x75269, 0x754b6, 0x75700, 0x75946, 0x75b88, 0x75dc7, 0x76002, 0x7623a, 7608c2ecf20Sopenharmony_ci 0x7646e, 0x766a0, 0x768cd, 0x76af8, 0x76d1f, 0x76f43, 0x77164, 0x77382, 7618c2ecf20Sopenharmony_ci 0x7759d, 0x777b4, 0x779c9, 0x77bdb, 0x77dea, 0x77ff5, 0x781fe, 0x78404, 7628c2ecf20Sopenharmony_ci 0x78608, 0x78808, 0x78a06, 0x78c01, 0x78df9, 0x78fef, 0x791e2, 0x793d2, 7638c2ecf20Sopenharmony_ci 0x795c0, 0x797ab, 0x79993, 0x79b79, 0x79d5d, 0x79f3e, 0x7a11d, 0x7a2f9, 7648c2ecf20Sopenharmony_ci 0x7a4d3, 0x7a6ab, 0x7a880, 0x7aa53, 0x7ac24, 0x7adf2, 0x7afbe, 0x7b188, 7658c2ecf20Sopenharmony_ci 0x7b350, 0x7b515, 0x7b6d8, 0x7b899, 0x7ba58, 0x7bc15, 0x7bdd0, 0x7bf89, 7668c2ecf20Sopenharmony_ci 0x7c140, 0x7c2f5, 0x7c4a7, 0x7c658, 0x7c807, 0x7c9b3, 0x7cb5e, 0x7cd07, 7678c2ecf20Sopenharmony_ci 0x7ceae, 0x7d053, 0x7d1f7, 0x7d398, 0x7d538, 0x7d6d6, 0x7d872, 0x7da0c, 7688c2ecf20Sopenharmony_ci 0x7dba4, 0x7dd3b, 0x7ded0, 0x7e063, 0x7e1f4, 0x7e384, 0x7e512, 0x7e69f, 7698c2ecf20Sopenharmony_ci 0x7e829, 0x7e9b3, 0x7eb3a, 0x7ecc0, 0x7ee44, 0x7efc7, 0x7f148, 0x7f2c8, 7708c2ecf20Sopenharmony_ci 0x7f446, 0x7f5c2, 0x7f73d, 0x7f8b7, 0x7fa2f, 0x7fba5, 0x7fd1a, 0x7fe8d, 7718c2ecf20Sopenharmony_ci 0x80000, 7728c2ecf20Sopenharmony_ci}; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci/* convert from linear to log value 7758c2ecf20Sopenharmony_ci * 7768c2ecf20Sopenharmony_ci * conversion: value = log2(amount / base) * ratio 7778c2ecf20Sopenharmony_ci * 7788c2ecf20Sopenharmony_ci * argument: 7798c2ecf20Sopenharmony_ci * amount = linear value (unsigned, 32bit max) 7808c2ecf20Sopenharmony_ci * offset = base offset (:= log2(base) * 0x10000) 7818c2ecf20Sopenharmony_ci * ratio = division ratio 7828c2ecf20Sopenharmony_ci * 7838c2ecf20Sopenharmony_ci */ 7848c2ecf20Sopenharmony_ciint 7858c2ecf20Sopenharmony_cisnd_sf_linear_to_log(unsigned int amount, int offset, int ratio) 7868c2ecf20Sopenharmony_ci{ 7878c2ecf20Sopenharmony_ci int v; 7888c2ecf20Sopenharmony_ci int s, low, bit; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci if (amount < 2) 7918c2ecf20Sopenharmony_ci return 0; 7928c2ecf20Sopenharmony_ci for (bit = 0; ! (amount & 0x80000000L); bit++) 7938c2ecf20Sopenharmony_ci amount <<= 1; 7948c2ecf20Sopenharmony_ci s = (amount >> 24) & 0x7f; 7958c2ecf20Sopenharmony_ci low = (amount >> 16) & 0xff; 7968c2ecf20Sopenharmony_ci /* linear approxmimation by lower 8 bit */ 7978c2ecf20Sopenharmony_ci v = (log_tbl[s + 1] * low + log_tbl[s] * (0x100 - low)) >> 8; 7988c2ecf20Sopenharmony_ci v -= offset; 7998c2ecf20Sopenharmony_ci v = (v * ratio) >> 16; 8008c2ecf20Sopenharmony_ci v += (24 - bit) * ratio; 8018c2ecf20Sopenharmony_ci return v; 8028c2ecf20Sopenharmony_ci} 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_sf_linear_to_log); 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci#define OFFSET_MSEC 653117 /* base = 1000 */ 8088c2ecf20Sopenharmony_ci#define OFFSET_ABSCENT 851781 /* base = 8176 */ 8098c2ecf20Sopenharmony_ci#define OFFSET_SAMPLERATE 1011119 /* base = 44100 */ 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci#define ABSCENT_RATIO 1200 8128c2ecf20Sopenharmony_ci#define TIMECENT_RATIO 1200 8138c2ecf20Sopenharmony_ci#define SAMPLERATE_RATIO 4096 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci/* 8168c2ecf20Sopenharmony_ci * mHz to abscent 8178c2ecf20Sopenharmony_ci * conversion: abscent = log2(MHz / 8176) * 1200 8188c2ecf20Sopenharmony_ci */ 8198c2ecf20Sopenharmony_cistatic int 8208c2ecf20Sopenharmony_cifreq_to_note(int mhz) 8218c2ecf20Sopenharmony_ci{ 8228c2ecf20Sopenharmony_ci return snd_sf_linear_to_log(mhz, OFFSET_ABSCENT, ABSCENT_RATIO); 8238c2ecf20Sopenharmony_ci} 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci/* convert Hz to AWE32 rate offset: 8268c2ecf20Sopenharmony_ci * sample pitch offset for the specified sample rate 8278c2ecf20Sopenharmony_ci * rate=44100 is no offset, each 4096 is 1 octave (twice). 8288c2ecf20Sopenharmony_ci * eg, when rate is 22050, this offset becomes -4096. 8298c2ecf20Sopenharmony_ci * 8308c2ecf20Sopenharmony_ci * conversion: offset = log2(Hz / 44100) * 4096 8318c2ecf20Sopenharmony_ci */ 8328c2ecf20Sopenharmony_cistatic int 8338c2ecf20Sopenharmony_cicalc_rate_offset(int hz) 8348c2ecf20Sopenharmony_ci{ 8358c2ecf20Sopenharmony_ci return snd_sf_linear_to_log(hz, OFFSET_SAMPLERATE, SAMPLERATE_RATIO); 8368c2ecf20Sopenharmony_ci} 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci/* calculate GUS envelope time */ 8408c2ecf20Sopenharmony_cistatic int 8418c2ecf20Sopenharmony_cicalc_gus_envelope_time(int rate, int start, int end) 8428c2ecf20Sopenharmony_ci{ 8438c2ecf20Sopenharmony_ci int r, p, t; 8448c2ecf20Sopenharmony_ci r = (3 - ((rate >> 6) & 3)) * 3; 8458c2ecf20Sopenharmony_ci p = rate & 0x3f; 8468c2ecf20Sopenharmony_ci if (!p) 8478c2ecf20Sopenharmony_ci p = 1; 8488c2ecf20Sopenharmony_ci t = end - start; 8498c2ecf20Sopenharmony_ci if (t < 0) t = -t; 8508c2ecf20Sopenharmony_ci if (13 > r) 8518c2ecf20Sopenharmony_ci t = t << (13 - r); 8528c2ecf20Sopenharmony_ci else 8538c2ecf20Sopenharmony_ci t = t >> (r - 13); 8548c2ecf20Sopenharmony_ci return (t * 10) / (p * 441); 8558c2ecf20Sopenharmony_ci} 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci/* convert envelope time parameter to soundfont parameters */ 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci/* attack & decay/release time table (msec) */ 8608c2ecf20Sopenharmony_cistatic const short attack_time_tbl[128] = { 8618c2ecf20Sopenharmony_ci32767, 32767, 5989, 4235, 2994, 2518, 2117, 1780, 1497, 1373, 1259, 1154, 1058, 970, 890, 816, 8628c2ecf20Sopenharmony_ci707, 691, 662, 634, 607, 581, 557, 533, 510, 489, 468, 448, 429, 411, 393, 377, 8638c2ecf20Sopenharmony_ci361, 345, 331, 317, 303, 290, 278, 266, 255, 244, 234, 224, 214, 205, 196, 188, 8648c2ecf20Sopenharmony_ci180, 172, 165, 158, 151, 145, 139, 133, 127, 122, 117, 112, 107, 102, 98, 94, 8658c2ecf20Sopenharmony_ci90, 86, 82, 79, 75, 72, 69, 66, 63, 61, 58, 56, 53, 51, 49, 47, 8668c2ecf20Sopenharmony_ci45, 43, 41, 39, 37, 36, 34, 33, 31, 30, 29, 28, 26, 25, 24, 23, 8678c2ecf20Sopenharmony_ci22, 21, 20, 19, 19, 18, 17, 16, 16, 15, 15, 14, 13, 13, 12, 12, 8688c2ecf20Sopenharmony_ci11, 11, 10, 10, 10, 9, 9, 8, 8, 8, 8, 7, 7, 7, 6, 0, 8698c2ecf20Sopenharmony_ci}; 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_cistatic const short decay_time_tbl[128] = { 8728c2ecf20Sopenharmony_ci32767, 32767, 22614, 15990, 11307, 9508, 7995, 6723, 5653, 5184, 4754, 4359, 3997, 3665, 3361, 3082, 8738c2ecf20Sopenharmony_ci2828, 2765, 2648, 2535, 2428, 2325, 2226, 2132, 2042, 1955, 1872, 1793, 1717, 1644, 1574, 1507, 8748c2ecf20Sopenharmony_ci1443, 1382, 1324, 1267, 1214, 1162, 1113, 1066, 978, 936, 897, 859, 822, 787, 754, 722, 8758c2ecf20Sopenharmony_ci691, 662, 634, 607, 581, 557, 533, 510, 489, 468, 448, 429, 411, 393, 377, 361, 8768c2ecf20Sopenharmony_ci345, 331, 317, 303, 290, 278, 266, 255, 244, 234, 224, 214, 205, 196, 188, 180, 8778c2ecf20Sopenharmony_ci172, 165, 158, 151, 145, 139, 133, 127, 122, 117, 112, 107, 102, 98, 94, 90, 8788c2ecf20Sopenharmony_ci86, 82, 79, 75, 72, 69, 66, 63, 61, 58, 56, 53, 51, 49, 47, 45, 8798c2ecf20Sopenharmony_ci43, 41, 39, 37, 36, 34, 33, 31, 30, 29, 28, 26, 25, 24, 23, 22, 8808c2ecf20Sopenharmony_ci}; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci/* delay time = 0x8000 - msec/92 */ 8838c2ecf20Sopenharmony_ciint 8848c2ecf20Sopenharmony_cisnd_sf_calc_parm_hold(int msec) 8858c2ecf20Sopenharmony_ci{ 8868c2ecf20Sopenharmony_ci int val = (0x7f * 92 - msec) / 92; 8878c2ecf20Sopenharmony_ci if (val < 1) val = 1; 8888c2ecf20Sopenharmony_ci if (val >= 126) val = 126; 8898c2ecf20Sopenharmony_ci return val; 8908c2ecf20Sopenharmony_ci} 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci/* search an index for specified time from given time table */ 8938c2ecf20Sopenharmony_cistatic int 8948c2ecf20Sopenharmony_cicalc_parm_search(int msec, const short *table) 8958c2ecf20Sopenharmony_ci{ 8968c2ecf20Sopenharmony_ci int left = 1, right = 127, mid; 8978c2ecf20Sopenharmony_ci while (left < right) { 8988c2ecf20Sopenharmony_ci mid = (left + right) / 2; 8998c2ecf20Sopenharmony_ci if (msec < (int)table[mid]) 9008c2ecf20Sopenharmony_ci left = mid + 1; 9018c2ecf20Sopenharmony_ci else 9028c2ecf20Sopenharmony_ci right = mid; 9038c2ecf20Sopenharmony_ci } 9048c2ecf20Sopenharmony_ci return left; 9058c2ecf20Sopenharmony_ci} 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci/* attack time: search from time table */ 9088c2ecf20Sopenharmony_ciint 9098c2ecf20Sopenharmony_cisnd_sf_calc_parm_attack(int msec) 9108c2ecf20Sopenharmony_ci{ 9118c2ecf20Sopenharmony_ci return calc_parm_search(msec, attack_time_tbl); 9128c2ecf20Sopenharmony_ci} 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci/* decay/release time: search from time table */ 9158c2ecf20Sopenharmony_ciint 9168c2ecf20Sopenharmony_cisnd_sf_calc_parm_decay(int msec) 9178c2ecf20Sopenharmony_ci{ 9188c2ecf20Sopenharmony_ci return calc_parm_search(msec, decay_time_tbl); 9198c2ecf20Sopenharmony_ci} 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ciint snd_sf_vol_table[128] = { 9228c2ecf20Sopenharmony_ci 255,111,95,86,79,74,70,66,63,61,58,56,54,52,50,49, 9238c2ecf20Sopenharmony_ci 47,46,45,43,42,41,40,39,38,37,36,35,34,34,33,32, 9248c2ecf20Sopenharmony_ci 31,31,30,29,29,28,27,27,26,26,25,24,24,23,23,22, 9258c2ecf20Sopenharmony_ci 22,21,21,21,20,20,19,19,18,18,18,17,17,16,16,16, 9268c2ecf20Sopenharmony_ci 15,15,15,14,14,14,13,13,13,12,12,12,11,11,11,10, 9278c2ecf20Sopenharmony_ci 10,10,10,9,9,9,8,8,8,8,7,7,7,7,6,6, 9288c2ecf20Sopenharmony_ci 6,6,5,5,5,5,5,4,4,4,4,3,3,3,3,3, 9298c2ecf20Sopenharmony_ci 2,2,2,2,2,1,1,1,1,1,0,0,0,0,0,0, 9308c2ecf20Sopenharmony_ci}; 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci#define calc_gus_sustain(val) (0x7f - snd_sf_vol_table[(val)/2]) 9348c2ecf20Sopenharmony_ci#define calc_gus_attenuation(val) snd_sf_vol_table[(val)/2] 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci/* load GUS patch */ 9378c2ecf20Sopenharmony_cistatic int 9388c2ecf20Sopenharmony_ciload_guspatch(struct snd_sf_list *sflist, const char __user *data, 9398c2ecf20Sopenharmony_ci long count, int client) 9408c2ecf20Sopenharmony_ci{ 9418c2ecf20Sopenharmony_ci struct patch_info patch; 9428c2ecf20Sopenharmony_ci struct snd_soundfont *sf; 9438c2ecf20Sopenharmony_ci struct snd_sf_zone *zone; 9448c2ecf20Sopenharmony_ci struct snd_sf_sample *smp; 9458c2ecf20Sopenharmony_ci int note, sample_id; 9468c2ecf20Sopenharmony_ci int rc; 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci if (count < (long)sizeof(patch)) { 9498c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "patch record too small %ld\n", count); 9508c2ecf20Sopenharmony_ci return -EINVAL; 9518c2ecf20Sopenharmony_ci } 9528c2ecf20Sopenharmony_ci if (copy_from_user(&patch, data, sizeof(patch))) 9538c2ecf20Sopenharmony_ci return -EFAULT; 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci count -= sizeof(patch); 9568c2ecf20Sopenharmony_ci data += sizeof(patch); 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci sf = newsf(sflist, SNDRV_SFNT_PAT_TYPE_GUS|SNDRV_SFNT_PAT_SHARED, NULL); 9598c2ecf20Sopenharmony_ci if (sf == NULL) 9608c2ecf20Sopenharmony_ci return -ENOMEM; 9618c2ecf20Sopenharmony_ci if ((smp = sf_sample_new(sflist, sf)) == NULL) 9628c2ecf20Sopenharmony_ci return -ENOMEM; 9638c2ecf20Sopenharmony_ci sample_id = sflist->sample_counter; 9648c2ecf20Sopenharmony_ci smp->v.sample = sample_id; 9658c2ecf20Sopenharmony_ci smp->v.start = 0; 9668c2ecf20Sopenharmony_ci smp->v.end = patch.len; 9678c2ecf20Sopenharmony_ci smp->v.loopstart = patch.loop_start; 9688c2ecf20Sopenharmony_ci smp->v.loopend = patch.loop_end; 9698c2ecf20Sopenharmony_ci smp->v.size = patch.len; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci /* set up mode flags */ 9728c2ecf20Sopenharmony_ci smp->v.mode_flags = 0; 9738c2ecf20Sopenharmony_ci if (!(patch.mode & WAVE_16_BITS)) 9748c2ecf20Sopenharmony_ci smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_8BITS; 9758c2ecf20Sopenharmony_ci if (patch.mode & WAVE_UNSIGNED) 9768c2ecf20Sopenharmony_ci smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_UNSIGNED; 9778c2ecf20Sopenharmony_ci smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_NO_BLANK; 9788c2ecf20Sopenharmony_ci if (!(patch.mode & (WAVE_LOOPING|WAVE_BIDIR_LOOP|WAVE_LOOP_BACK))) 9798c2ecf20Sopenharmony_ci smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_SINGLESHOT; 9808c2ecf20Sopenharmony_ci if (patch.mode & WAVE_BIDIR_LOOP) 9818c2ecf20Sopenharmony_ci smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_BIDIR_LOOP; 9828c2ecf20Sopenharmony_ci if (patch.mode & WAVE_LOOP_BACK) 9838c2ecf20Sopenharmony_ci smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_REVERSE_LOOP; 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci if (patch.mode & WAVE_16_BITS) { 9868c2ecf20Sopenharmony_ci /* convert to word offsets */ 9878c2ecf20Sopenharmony_ci smp->v.size /= 2; 9888c2ecf20Sopenharmony_ci smp->v.end /= 2; 9898c2ecf20Sopenharmony_ci smp->v.loopstart /= 2; 9908c2ecf20Sopenharmony_ci smp->v.loopend /= 2; 9918c2ecf20Sopenharmony_ci } 9928c2ecf20Sopenharmony_ci /*smp->v.loopend++;*/ 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci smp->v.dummy = 0; 9958c2ecf20Sopenharmony_ci smp->v.truesize = 0; 9968c2ecf20Sopenharmony_ci smp->v.sf_id = sf->id; 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci /* set up voice info */ 9998c2ecf20Sopenharmony_ci if ((zone = sf_zone_new(sflist, sf)) == NULL) { 10008c2ecf20Sopenharmony_ci sf_sample_delete(sflist, sf, smp); 10018c2ecf20Sopenharmony_ci return -ENOMEM; 10028c2ecf20Sopenharmony_ci } 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci /* 10058c2ecf20Sopenharmony_ci * load wave data 10068c2ecf20Sopenharmony_ci */ 10078c2ecf20Sopenharmony_ci if (sflist->callback.sample_new) { 10088c2ecf20Sopenharmony_ci rc = sflist->callback.sample_new 10098c2ecf20Sopenharmony_ci (sflist->callback.private_data, smp, sflist->memhdr, 10108c2ecf20Sopenharmony_ci data, count); 10118c2ecf20Sopenharmony_ci if (rc < 0) { 10128c2ecf20Sopenharmony_ci sf_sample_delete(sflist, sf, smp); 10138c2ecf20Sopenharmony_ci kfree(zone); 10148c2ecf20Sopenharmony_ci return rc; 10158c2ecf20Sopenharmony_ci } 10168c2ecf20Sopenharmony_ci /* memory offset is updated after */ 10178c2ecf20Sopenharmony_ci } 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci /* update the memory offset here */ 10208c2ecf20Sopenharmony_ci sflist->mem_used += smp->v.truesize; 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci zone->v.sample = sample_id; /* the last sample */ 10238c2ecf20Sopenharmony_ci zone->v.rate_offset = calc_rate_offset(patch.base_freq); 10248c2ecf20Sopenharmony_ci note = freq_to_note(patch.base_note); 10258c2ecf20Sopenharmony_ci zone->v.root = note / 100; 10268c2ecf20Sopenharmony_ci zone->v.tune = -(note % 100); 10278c2ecf20Sopenharmony_ci zone->v.low = (freq_to_note(patch.low_note) + 99) / 100; 10288c2ecf20Sopenharmony_ci zone->v.high = freq_to_note(patch.high_note) / 100; 10298c2ecf20Sopenharmony_ci /* panning position; -128 - 127 => 0-127 */ 10308c2ecf20Sopenharmony_ci zone->v.pan = (patch.panning + 128) / 2; 10318c2ecf20Sopenharmony_ci#if 0 10328c2ecf20Sopenharmony_ci snd_printk(KERN_DEBUG 10338c2ecf20Sopenharmony_ci "gus: basefrq=%d (ofs=%d) root=%d,tune=%d, range:%d-%d\n", 10348c2ecf20Sopenharmony_ci (int)patch.base_freq, zone->v.rate_offset, 10358c2ecf20Sopenharmony_ci zone->v.root, zone->v.tune, zone->v.low, zone->v.high); 10368c2ecf20Sopenharmony_ci#endif 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci /* detuning is ignored */ 10398c2ecf20Sopenharmony_ci /* 6points volume envelope */ 10408c2ecf20Sopenharmony_ci if (patch.mode & WAVE_ENVELOPES) { 10418c2ecf20Sopenharmony_ci int attack, hold, decay, release; 10428c2ecf20Sopenharmony_ci attack = calc_gus_envelope_time 10438c2ecf20Sopenharmony_ci (patch.env_rate[0], 0, patch.env_offset[0]); 10448c2ecf20Sopenharmony_ci hold = calc_gus_envelope_time 10458c2ecf20Sopenharmony_ci (patch.env_rate[1], patch.env_offset[0], 10468c2ecf20Sopenharmony_ci patch.env_offset[1]); 10478c2ecf20Sopenharmony_ci decay = calc_gus_envelope_time 10488c2ecf20Sopenharmony_ci (patch.env_rate[2], patch.env_offset[1], 10498c2ecf20Sopenharmony_ci patch.env_offset[2]); 10508c2ecf20Sopenharmony_ci release = calc_gus_envelope_time 10518c2ecf20Sopenharmony_ci (patch.env_rate[3], patch.env_offset[1], 10528c2ecf20Sopenharmony_ci patch.env_offset[4]); 10538c2ecf20Sopenharmony_ci release += calc_gus_envelope_time 10548c2ecf20Sopenharmony_ci (patch.env_rate[4], patch.env_offset[3], 10558c2ecf20Sopenharmony_ci patch.env_offset[4]); 10568c2ecf20Sopenharmony_ci release += calc_gus_envelope_time 10578c2ecf20Sopenharmony_ci (patch.env_rate[5], patch.env_offset[4], 10588c2ecf20Sopenharmony_ci patch.env_offset[5]); 10598c2ecf20Sopenharmony_ci zone->v.parm.volatkhld = 10608c2ecf20Sopenharmony_ci (snd_sf_calc_parm_hold(hold) << 8) | 10618c2ecf20Sopenharmony_ci snd_sf_calc_parm_attack(attack); 10628c2ecf20Sopenharmony_ci zone->v.parm.voldcysus = (calc_gus_sustain(patch.env_offset[2]) << 8) | 10638c2ecf20Sopenharmony_ci snd_sf_calc_parm_decay(decay); 10648c2ecf20Sopenharmony_ci zone->v.parm.volrelease = 0x8000 | snd_sf_calc_parm_decay(release); 10658c2ecf20Sopenharmony_ci zone->v.attenuation = calc_gus_attenuation(patch.env_offset[0]); 10668c2ecf20Sopenharmony_ci#if 0 10678c2ecf20Sopenharmony_ci snd_printk(KERN_DEBUG 10688c2ecf20Sopenharmony_ci "gus: atkhld=%x, dcysus=%x, volrel=%x, att=%d\n", 10698c2ecf20Sopenharmony_ci zone->v.parm.volatkhld, 10708c2ecf20Sopenharmony_ci zone->v.parm.voldcysus, 10718c2ecf20Sopenharmony_ci zone->v.parm.volrelease, 10728c2ecf20Sopenharmony_ci zone->v.attenuation); 10738c2ecf20Sopenharmony_ci#endif 10748c2ecf20Sopenharmony_ci } 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci /* fast release */ 10778c2ecf20Sopenharmony_ci if (patch.mode & WAVE_FAST_RELEASE) { 10788c2ecf20Sopenharmony_ci zone->v.parm.volrelease = 0x807f; 10798c2ecf20Sopenharmony_ci } 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci /* tremolo effect */ 10828c2ecf20Sopenharmony_ci if (patch.mode & WAVE_TREMOLO) { 10838c2ecf20Sopenharmony_ci int rate = (patch.tremolo_rate * 1000 / 38) / 42; 10848c2ecf20Sopenharmony_ci zone->v.parm.tremfrq = ((patch.tremolo_depth / 2) << 8) | rate; 10858c2ecf20Sopenharmony_ci } 10868c2ecf20Sopenharmony_ci /* vibrato effect */ 10878c2ecf20Sopenharmony_ci if (patch.mode & WAVE_VIBRATO) { 10888c2ecf20Sopenharmony_ci int rate = (patch.vibrato_rate * 1000 / 38) / 42; 10898c2ecf20Sopenharmony_ci zone->v.parm.fm2frq2 = ((patch.vibrato_depth / 6) << 8) | rate; 10908c2ecf20Sopenharmony_ci } 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci /* scale_freq, scale_factor, volume, and fractions not implemented */ 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci if (!(smp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT)) 10958c2ecf20Sopenharmony_ci zone->v.mode = SNDRV_SFNT_MODE_LOOPING; 10968c2ecf20Sopenharmony_ci else 10978c2ecf20Sopenharmony_ci zone->v.mode = 0; 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci /* append to the tail of the list */ 11008c2ecf20Sopenharmony_ci /*zone->bank = ctrls[AWE_MD_GUS_BANK];*/ 11018c2ecf20Sopenharmony_ci zone->bank = 0; 11028c2ecf20Sopenharmony_ci zone->instr = patch.instr_no; 11038c2ecf20Sopenharmony_ci zone->mapped = 0; 11048c2ecf20Sopenharmony_ci zone->v.sf_id = sf->id; 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci zone->sample = set_sample(sf, &zone->v); 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci /* rebuild preset now */ 11098c2ecf20Sopenharmony_ci add_preset(sflist, zone); 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci return 0; 11128c2ecf20Sopenharmony_ci} 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci/* load GUS patch */ 11158c2ecf20Sopenharmony_ciint 11168c2ecf20Sopenharmony_cisnd_soundfont_load_guspatch(struct snd_sf_list *sflist, const char __user *data, 11178c2ecf20Sopenharmony_ci long count, int client) 11188c2ecf20Sopenharmony_ci{ 11198c2ecf20Sopenharmony_ci int rc; 11208c2ecf20Sopenharmony_ci lock_preset(sflist); 11218c2ecf20Sopenharmony_ci rc = load_guspatch(sflist, data, count, client); 11228c2ecf20Sopenharmony_ci unlock_preset(sflist); 11238c2ecf20Sopenharmony_ci return rc; 11248c2ecf20Sopenharmony_ci} 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci/* 11288c2ecf20Sopenharmony_ci * Rebuild the preset table. This is like a hash table in that it allows 11298c2ecf20Sopenharmony_ci * quick access to the zone information. For each preset there are zone 11308c2ecf20Sopenharmony_ci * structures linked by next_instr and by next_zone. Former is the whole 11318c2ecf20Sopenharmony_ci * link for this preset, and latter is the link for zone (i.e. instrument/ 11328c2ecf20Sopenharmony_ci * bank/key combination). 11338c2ecf20Sopenharmony_ci */ 11348c2ecf20Sopenharmony_cistatic void 11358c2ecf20Sopenharmony_cirebuild_presets(struct snd_sf_list *sflist) 11368c2ecf20Sopenharmony_ci{ 11378c2ecf20Sopenharmony_ci struct snd_soundfont *sf; 11388c2ecf20Sopenharmony_ci struct snd_sf_zone *cur; 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci /* clear preset table */ 11418c2ecf20Sopenharmony_ci memset(sflist->presets, 0, sizeof(sflist->presets)); 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci /* search all fonts and insert each font */ 11448c2ecf20Sopenharmony_ci for (sf = sflist->fonts; sf; sf = sf->next) { 11458c2ecf20Sopenharmony_ci for (cur = sf->zones; cur; cur = cur->next) { 11468c2ecf20Sopenharmony_ci if (! cur->mapped && cur->sample == NULL) { 11478c2ecf20Sopenharmony_ci /* try again to search the corresponding sample */ 11488c2ecf20Sopenharmony_ci cur->sample = set_sample(sf, &cur->v); 11498c2ecf20Sopenharmony_ci if (cur->sample == NULL) 11508c2ecf20Sopenharmony_ci continue; 11518c2ecf20Sopenharmony_ci } 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci add_preset(sflist, cur); 11548c2ecf20Sopenharmony_ci } 11558c2ecf20Sopenharmony_ci } 11568c2ecf20Sopenharmony_ci} 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci/* 11608c2ecf20Sopenharmony_ci * add the given zone to preset table 11618c2ecf20Sopenharmony_ci */ 11628c2ecf20Sopenharmony_cistatic void 11638c2ecf20Sopenharmony_ciadd_preset(struct snd_sf_list *sflist, struct snd_sf_zone *cur) 11648c2ecf20Sopenharmony_ci{ 11658c2ecf20Sopenharmony_ci struct snd_sf_zone *zone; 11668c2ecf20Sopenharmony_ci int index; 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci zone = search_first_zone(sflist, cur->bank, cur->instr, cur->v.low); 11698c2ecf20Sopenharmony_ci if (zone && zone->v.sf_id != cur->v.sf_id) { 11708c2ecf20Sopenharmony_ci /* different instrument was already defined */ 11718c2ecf20Sopenharmony_ci struct snd_sf_zone *p; 11728c2ecf20Sopenharmony_ci /* compare the allocated time */ 11738c2ecf20Sopenharmony_ci for (p = zone; p; p = p->next_zone) { 11748c2ecf20Sopenharmony_ci if (p->counter > cur->counter) 11758c2ecf20Sopenharmony_ci /* the current is older.. skipped */ 11768c2ecf20Sopenharmony_ci return; 11778c2ecf20Sopenharmony_ci } 11788c2ecf20Sopenharmony_ci /* remove old zones */ 11798c2ecf20Sopenharmony_ci delete_preset(sflist, zone); 11808c2ecf20Sopenharmony_ci zone = NULL; /* do not forget to clear this! */ 11818c2ecf20Sopenharmony_ci } 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci /* prepend this zone */ 11848c2ecf20Sopenharmony_ci if ((index = get_index(cur->bank, cur->instr, cur->v.low)) < 0) 11858c2ecf20Sopenharmony_ci return; 11868c2ecf20Sopenharmony_ci cur->next_zone = zone; /* zone link */ 11878c2ecf20Sopenharmony_ci cur->next_instr = sflist->presets[index]; /* preset table link */ 11888c2ecf20Sopenharmony_ci sflist->presets[index] = cur; 11898c2ecf20Sopenharmony_ci} 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci/* 11928c2ecf20Sopenharmony_ci * delete the given zones from preset_table 11938c2ecf20Sopenharmony_ci */ 11948c2ecf20Sopenharmony_cistatic void 11958c2ecf20Sopenharmony_cidelete_preset(struct snd_sf_list *sflist, struct snd_sf_zone *zp) 11968c2ecf20Sopenharmony_ci{ 11978c2ecf20Sopenharmony_ci int index; 11988c2ecf20Sopenharmony_ci struct snd_sf_zone *p; 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci if ((index = get_index(zp->bank, zp->instr, zp->v.low)) < 0) 12018c2ecf20Sopenharmony_ci return; 12028c2ecf20Sopenharmony_ci for (p = sflist->presets[index]; p; p = p->next_instr) { 12038c2ecf20Sopenharmony_ci while (p->next_instr == zp) { 12048c2ecf20Sopenharmony_ci p->next_instr = zp->next_instr; 12058c2ecf20Sopenharmony_ci zp = zp->next_zone; 12068c2ecf20Sopenharmony_ci if (zp == NULL) 12078c2ecf20Sopenharmony_ci return; 12088c2ecf20Sopenharmony_ci } 12098c2ecf20Sopenharmony_ci } 12108c2ecf20Sopenharmony_ci} 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci/* 12148c2ecf20Sopenharmony_ci * Search matching zones from preset table. 12158c2ecf20Sopenharmony_ci * The note can be rewritten by preset mapping (alias). 12168c2ecf20Sopenharmony_ci * The found zones are stored on 'table' array. max_layers defines 12178c2ecf20Sopenharmony_ci * the maximum number of elements in this array. 12188c2ecf20Sopenharmony_ci * This function returns the number of found zones. 0 if not found. 12198c2ecf20Sopenharmony_ci */ 12208c2ecf20Sopenharmony_ciint 12218c2ecf20Sopenharmony_cisnd_soundfont_search_zone(struct snd_sf_list *sflist, int *notep, int vel, 12228c2ecf20Sopenharmony_ci int preset, int bank, 12238c2ecf20Sopenharmony_ci int def_preset, int def_bank, 12248c2ecf20Sopenharmony_ci struct snd_sf_zone **table, int max_layers) 12258c2ecf20Sopenharmony_ci{ 12268c2ecf20Sopenharmony_ci int nvoices; 12278c2ecf20Sopenharmony_ci unsigned long flags; 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci /* this function is supposed to be called atomically, 12308c2ecf20Sopenharmony_ci * so we check the lock. if it's busy, just returns 0 to 12318c2ecf20Sopenharmony_ci * tell the caller the busy state 12328c2ecf20Sopenharmony_ci */ 12338c2ecf20Sopenharmony_ci spin_lock_irqsave(&sflist->lock, flags); 12348c2ecf20Sopenharmony_ci if (sflist->presets_locked) { 12358c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sflist->lock, flags); 12368c2ecf20Sopenharmony_ci return 0; 12378c2ecf20Sopenharmony_ci } 12388c2ecf20Sopenharmony_ci nvoices = search_zones(sflist, notep, vel, preset, bank, 12398c2ecf20Sopenharmony_ci table, max_layers, 0); 12408c2ecf20Sopenharmony_ci if (! nvoices) { 12418c2ecf20Sopenharmony_ci if (preset != def_preset || bank != def_bank) 12428c2ecf20Sopenharmony_ci nvoices = search_zones(sflist, notep, vel, 12438c2ecf20Sopenharmony_ci def_preset, def_bank, 12448c2ecf20Sopenharmony_ci table, max_layers, 0); 12458c2ecf20Sopenharmony_ci } 12468c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sflist->lock, flags); 12478c2ecf20Sopenharmony_ci return nvoices; 12488c2ecf20Sopenharmony_ci} 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci/* 12528c2ecf20Sopenharmony_ci * search the first matching zone 12538c2ecf20Sopenharmony_ci */ 12548c2ecf20Sopenharmony_cistatic struct snd_sf_zone * 12558c2ecf20Sopenharmony_cisearch_first_zone(struct snd_sf_list *sflist, int bank, int preset, int key) 12568c2ecf20Sopenharmony_ci{ 12578c2ecf20Sopenharmony_ci int index; 12588c2ecf20Sopenharmony_ci struct snd_sf_zone *zp; 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci if ((index = get_index(bank, preset, key)) < 0) 12618c2ecf20Sopenharmony_ci return NULL; 12628c2ecf20Sopenharmony_ci for (zp = sflist->presets[index]; zp; zp = zp->next_instr) { 12638c2ecf20Sopenharmony_ci if (zp->instr == preset && zp->bank == bank) 12648c2ecf20Sopenharmony_ci return zp; 12658c2ecf20Sopenharmony_ci } 12668c2ecf20Sopenharmony_ci return NULL; 12678c2ecf20Sopenharmony_ci} 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci/* 12718c2ecf20Sopenharmony_ci * search matching zones from sflist. can be called recursively. 12728c2ecf20Sopenharmony_ci */ 12738c2ecf20Sopenharmony_cistatic int 12748c2ecf20Sopenharmony_cisearch_zones(struct snd_sf_list *sflist, int *notep, int vel, 12758c2ecf20Sopenharmony_ci int preset, int bank, struct snd_sf_zone **table, 12768c2ecf20Sopenharmony_ci int max_layers, int level) 12778c2ecf20Sopenharmony_ci{ 12788c2ecf20Sopenharmony_ci struct snd_sf_zone *zp; 12798c2ecf20Sopenharmony_ci int nvoices; 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci zp = search_first_zone(sflist, bank, preset, *notep); 12828c2ecf20Sopenharmony_ci nvoices = 0; 12838c2ecf20Sopenharmony_ci for (; zp; zp = zp->next_zone) { 12848c2ecf20Sopenharmony_ci if (*notep >= zp->v.low && *notep <= zp->v.high && 12858c2ecf20Sopenharmony_ci vel >= zp->v.vellow && vel <= zp->v.velhigh) { 12868c2ecf20Sopenharmony_ci if (zp->mapped) { 12878c2ecf20Sopenharmony_ci /* search preset mapping (aliasing) */ 12888c2ecf20Sopenharmony_ci int key = zp->v.fixkey; 12898c2ecf20Sopenharmony_ci preset = zp->v.start; 12908c2ecf20Sopenharmony_ci bank = zp->v.end; 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci if (level > 5) /* too deep alias level */ 12938c2ecf20Sopenharmony_ci return 0; 12948c2ecf20Sopenharmony_ci if (key < 0) 12958c2ecf20Sopenharmony_ci key = *notep; 12968c2ecf20Sopenharmony_ci nvoices = search_zones(sflist, &key, vel, 12978c2ecf20Sopenharmony_ci preset, bank, table, 12988c2ecf20Sopenharmony_ci max_layers, level + 1); 12998c2ecf20Sopenharmony_ci if (nvoices > 0) 13008c2ecf20Sopenharmony_ci *notep = key; 13018c2ecf20Sopenharmony_ci break; 13028c2ecf20Sopenharmony_ci } 13038c2ecf20Sopenharmony_ci table[nvoices++] = zp; 13048c2ecf20Sopenharmony_ci if (nvoices >= max_layers) 13058c2ecf20Sopenharmony_ci break; 13068c2ecf20Sopenharmony_ci } 13078c2ecf20Sopenharmony_ci } 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci return nvoices; 13108c2ecf20Sopenharmony_ci} 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci/* calculate the index of preset table: 13148c2ecf20Sopenharmony_ci * drums are mapped from 128 to 255 according to its note key. 13158c2ecf20Sopenharmony_ci * other instruments are mapped from 0 to 127. 13168c2ecf20Sopenharmony_ci * if the index is out of range, return -1. 13178c2ecf20Sopenharmony_ci */ 13188c2ecf20Sopenharmony_cistatic int 13198c2ecf20Sopenharmony_ciget_index(int bank, int instr, int key) 13208c2ecf20Sopenharmony_ci{ 13218c2ecf20Sopenharmony_ci int index; 13228c2ecf20Sopenharmony_ci if (SF_IS_DRUM_BANK(bank)) 13238c2ecf20Sopenharmony_ci index = key + SF_MAX_INSTRUMENTS; 13248c2ecf20Sopenharmony_ci else 13258c2ecf20Sopenharmony_ci index = instr; 13268c2ecf20Sopenharmony_ci index = index % SF_MAX_PRESETS; 13278c2ecf20Sopenharmony_ci if (index < 0) 13288c2ecf20Sopenharmony_ci return -1; 13298c2ecf20Sopenharmony_ci return index; 13308c2ecf20Sopenharmony_ci} 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci/* 13338c2ecf20Sopenharmony_ci * Initialise the sflist structure. 13348c2ecf20Sopenharmony_ci */ 13358c2ecf20Sopenharmony_cistatic void 13368c2ecf20Sopenharmony_cisnd_sf_init(struct snd_sf_list *sflist) 13378c2ecf20Sopenharmony_ci{ 13388c2ecf20Sopenharmony_ci memset(sflist->presets, 0, sizeof(sflist->presets)); 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci sflist->mem_used = 0; 13418c2ecf20Sopenharmony_ci sflist->currsf = NULL; 13428c2ecf20Sopenharmony_ci sflist->open_client = -1; 13438c2ecf20Sopenharmony_ci sflist->fonts = NULL; 13448c2ecf20Sopenharmony_ci sflist->fonts_size = 0; 13458c2ecf20Sopenharmony_ci sflist->zone_counter = 0; 13468c2ecf20Sopenharmony_ci sflist->sample_counter = 0; 13478c2ecf20Sopenharmony_ci sflist->zone_locked = 0; 13488c2ecf20Sopenharmony_ci sflist->sample_locked = 0; 13498c2ecf20Sopenharmony_ci} 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ci/* 13528c2ecf20Sopenharmony_ci * Release all list records 13538c2ecf20Sopenharmony_ci */ 13548c2ecf20Sopenharmony_cistatic void 13558c2ecf20Sopenharmony_cisnd_sf_clear(struct snd_sf_list *sflist) 13568c2ecf20Sopenharmony_ci{ 13578c2ecf20Sopenharmony_ci struct snd_soundfont *sf, *nextsf; 13588c2ecf20Sopenharmony_ci struct snd_sf_zone *zp, *nextzp; 13598c2ecf20Sopenharmony_ci struct snd_sf_sample *sp, *nextsp; 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci for (sf = sflist->fonts; sf; sf = nextsf) { 13628c2ecf20Sopenharmony_ci nextsf = sf->next; 13638c2ecf20Sopenharmony_ci for (zp = sf->zones; zp; zp = nextzp) { 13648c2ecf20Sopenharmony_ci nextzp = zp->next; 13658c2ecf20Sopenharmony_ci kfree(zp); 13668c2ecf20Sopenharmony_ci } 13678c2ecf20Sopenharmony_ci for (sp = sf->samples; sp; sp = nextsp) { 13688c2ecf20Sopenharmony_ci nextsp = sp->next; 13698c2ecf20Sopenharmony_ci if (sflist->callback.sample_free) 13708c2ecf20Sopenharmony_ci sflist->callback.sample_free(sflist->callback.private_data, 13718c2ecf20Sopenharmony_ci sp, sflist->memhdr); 13728c2ecf20Sopenharmony_ci kfree(sp); 13738c2ecf20Sopenharmony_ci } 13748c2ecf20Sopenharmony_ci kfree(sf); 13758c2ecf20Sopenharmony_ci } 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci snd_sf_init(sflist); 13788c2ecf20Sopenharmony_ci} 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ci/* 13828c2ecf20Sopenharmony_ci * Create a new sflist structure 13838c2ecf20Sopenharmony_ci */ 13848c2ecf20Sopenharmony_cistruct snd_sf_list * 13858c2ecf20Sopenharmony_cisnd_sf_new(struct snd_sf_callback *callback, struct snd_util_memhdr *hdr) 13868c2ecf20Sopenharmony_ci{ 13878c2ecf20Sopenharmony_ci struct snd_sf_list *sflist; 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci if ((sflist = kzalloc(sizeof(*sflist), GFP_KERNEL)) == NULL) 13908c2ecf20Sopenharmony_ci return NULL; 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci mutex_init(&sflist->presets_mutex); 13938c2ecf20Sopenharmony_ci spin_lock_init(&sflist->lock); 13948c2ecf20Sopenharmony_ci sflist->memhdr = hdr; 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci if (callback) 13978c2ecf20Sopenharmony_ci sflist->callback = *callback; 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci snd_sf_init(sflist); 14008c2ecf20Sopenharmony_ci return sflist; 14018c2ecf20Sopenharmony_ci} 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci/* 14058c2ecf20Sopenharmony_ci * Free everything allocated off the sflist structure. 14068c2ecf20Sopenharmony_ci */ 14078c2ecf20Sopenharmony_civoid 14088c2ecf20Sopenharmony_cisnd_sf_free(struct snd_sf_list *sflist) 14098c2ecf20Sopenharmony_ci{ 14108c2ecf20Sopenharmony_ci if (sflist == NULL) 14118c2ecf20Sopenharmony_ci return; 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci lock_preset(sflist); 14148c2ecf20Sopenharmony_ci if (sflist->callback.sample_reset) 14158c2ecf20Sopenharmony_ci sflist->callback.sample_reset(sflist->callback.private_data); 14168c2ecf20Sopenharmony_ci snd_sf_clear(sflist); 14178c2ecf20Sopenharmony_ci unlock_preset(sflist); 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci kfree(sflist); 14208c2ecf20Sopenharmony_ci} 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci/* 14238c2ecf20Sopenharmony_ci * Remove all samples 14248c2ecf20Sopenharmony_ci * The soundcard should be silet before calling this function. 14258c2ecf20Sopenharmony_ci */ 14268c2ecf20Sopenharmony_ciint 14278c2ecf20Sopenharmony_cisnd_soundfont_remove_samples(struct snd_sf_list *sflist) 14288c2ecf20Sopenharmony_ci{ 14298c2ecf20Sopenharmony_ci lock_preset(sflist); 14308c2ecf20Sopenharmony_ci if (sflist->callback.sample_reset) 14318c2ecf20Sopenharmony_ci sflist->callback.sample_reset(sflist->callback.private_data); 14328c2ecf20Sopenharmony_ci snd_sf_clear(sflist); 14338c2ecf20Sopenharmony_ci unlock_preset(sflist); 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_ci return 0; 14368c2ecf20Sopenharmony_ci} 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_ci/* 14398c2ecf20Sopenharmony_ci * Remove unlocked samples. 14408c2ecf20Sopenharmony_ci * The soundcard should be silent before calling this function. 14418c2ecf20Sopenharmony_ci */ 14428c2ecf20Sopenharmony_ciint 14438c2ecf20Sopenharmony_cisnd_soundfont_remove_unlocked(struct snd_sf_list *sflist) 14448c2ecf20Sopenharmony_ci{ 14458c2ecf20Sopenharmony_ci struct snd_soundfont *sf; 14468c2ecf20Sopenharmony_ci struct snd_sf_zone *zp, *nextzp; 14478c2ecf20Sopenharmony_ci struct snd_sf_sample *sp, *nextsp; 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci lock_preset(sflist); 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_ci if (sflist->callback.sample_reset) 14528c2ecf20Sopenharmony_ci sflist->callback.sample_reset(sflist->callback.private_data); 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci /* to be sure */ 14558c2ecf20Sopenharmony_ci memset(sflist->presets, 0, sizeof(sflist->presets)); 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci for (sf = sflist->fonts; sf; sf = sf->next) { 14588c2ecf20Sopenharmony_ci for (zp = sf->zones; zp; zp = nextzp) { 14598c2ecf20Sopenharmony_ci if (zp->counter < sflist->zone_locked) 14608c2ecf20Sopenharmony_ci break; 14618c2ecf20Sopenharmony_ci nextzp = zp->next; 14628c2ecf20Sopenharmony_ci sf->zones = nextzp; 14638c2ecf20Sopenharmony_ci kfree(zp); 14648c2ecf20Sopenharmony_ci } 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_ci for (sp = sf->samples; sp; sp = nextsp) { 14678c2ecf20Sopenharmony_ci if (sp->counter < sflist->sample_locked) 14688c2ecf20Sopenharmony_ci break; 14698c2ecf20Sopenharmony_ci nextsp = sp->next; 14708c2ecf20Sopenharmony_ci sf->samples = nextsp; 14718c2ecf20Sopenharmony_ci sflist->mem_used -= sp->v.truesize; 14728c2ecf20Sopenharmony_ci if (sflist->callback.sample_free) 14738c2ecf20Sopenharmony_ci sflist->callback.sample_free(sflist->callback.private_data, 14748c2ecf20Sopenharmony_ci sp, sflist->memhdr); 14758c2ecf20Sopenharmony_ci kfree(sp); 14768c2ecf20Sopenharmony_ci } 14778c2ecf20Sopenharmony_ci } 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ci sflist->zone_counter = sflist->zone_locked; 14808c2ecf20Sopenharmony_ci sflist->sample_counter = sflist->sample_locked; 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci rebuild_presets(sflist); 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci unlock_preset(sflist); 14858c2ecf20Sopenharmony_ci return 0; 14868c2ecf20Sopenharmony_ci} 1487