/* TiMidity++ -- MIDI to WAVE converter and player Copyright (C) 1999-2008 Masanao Izumo Copyright (C) 1995 Tuukka Toivonen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include "timidity.h" #include "common.h" #include "instrum.h" #include "quantity.h" namespace TimidityPlus { #define MAXWORDS 130 #define CHECKERRLIMIT \ if(++errcnt >= 10) { \ printMessage(CMSG_ERROR, VERB_NORMAL, \ "Too many errors... Give up read %s", name); \ reuse_mblock(&varbuf); \ tf_close(tf); return 1; } typedef struct { const char *name; int mapid, isdrum; } MapNameEntry; static int mapnamecompare(const void *name, const void *entry) { return strcmp((const char *)name, ((const MapNameEntry *)entry)->name); } static int mapname2id(char *name, int *isdrum) { static const MapNameEntry data[] = { /* sorted in alphabetical order */ { "gm2", GM2_TONE_MAP, 0 }, { "gm2drum", GM2_DRUM_MAP, 1 }, { "sc55", SC_55_TONE_MAP, 0 }, { "sc55drum", SC_55_DRUM_MAP, 1 }, { "sc88", SC_88_TONE_MAP, 0 }, { "sc8850", SC_8850_TONE_MAP, 0 }, { "sc8850drum", SC_8850_DRUM_MAP, 1 }, { "sc88drum", SC_88_DRUM_MAP, 1 }, { "sc88pro", SC_88PRO_TONE_MAP, 0 }, { "sc88prodrum", SC_88PRO_DRUM_MAP, 1 }, { "xg", XG_NORMAL_MAP, 0 }, { "xgdrum", XG_DRUM_MAP, 1 }, { "xgsfx126", XG_SFX126_MAP, 1 }, { "xgsfx64", XG_SFX64_MAP, 0 } }; const MapNameEntry *found; found = (MapNameEntry *)bsearch(name, data, sizeof data / sizeof data[0], sizeof data[0], mapnamecompare); if (found != NULL) { *isdrum = found->isdrum; return found->mapid; } return -1; } static float *config_parse_tune(const char *cp, int *num) { const char *p; float *tune_list; int i; /* count num */ *num = 1, p = cp; while ((p = strchr(p, ',')) != NULL) (*num)++, p++; /* alloc */ tune_list = (float *)safe_malloc((*num) * sizeof(float)); /* regist */ for (i = 0, p = cp; i < *num; i++, p++) { tune_list[i] = atof(p); if (!(p = strchr(p, ','))) break; } return tune_list; } static int16_t *config_parse_int16(const char *cp, int *num) { const char *p; int16_t *list; int i; /* count num */ *num = 1, p = cp; while ((p = strchr(p, ',')) != NULL) (*num)++, p++; /* alloc */ list = (int16_t *)safe_malloc((*num) * sizeof(int16_t)); /* regist */ for (i = 0, p = cp; i < *num; i++, p++) { list[i] = atoi(p); if (!(p = strchr(p, ','))) break; } return list; } static int **config_parse_envelope(const char *cp, int *num) { const char *p, *px; int **env_list; int i, j; /* count num */ *num = 1, p = cp; while ((p = strchr(p, ',')) != NULL) (*num)++, p++; /* alloc */ env_list = (int **)safe_malloc((*num) * sizeof(int *)); for (i = 0; i < *num; i++) env_list[i] = (int *)safe_malloc(6 * sizeof(int)); /* init */ for (i = 0; i < *num; i++) for (j = 0; j < 6; j++) env_list[i][j] = -1; /* regist */ for (i = 0, p = cp; i < *num; i++, p++) { px = strchr(p, ','); for (j = 0; j < 6; j++, p++) { if (*p == ':') continue; env_list[i][j] = atoi(p); if (!(p = strchr(p, ':'))) break; if (px && p > px) break; } if (!(p = px)) break; } return env_list; } static Quantity **config_parse_modulation(const char *name, int line, const char *cp, int *num, int mod_type) { const char *p, *px, *err; char buf[128], *delim; Quantity **mod_list; int i, j; static const char * qtypestr[] = { "tremolo", "vibrato" }; static const uint16_t qtypes[] = { QUANTITY_UNIT_TYPE(TREMOLO_SWEEP), QUANTITY_UNIT_TYPE(TREMOLO_RATE), QUANTITY_UNIT_TYPE(DIRECT_INT), QUANTITY_UNIT_TYPE(VIBRATO_SWEEP), QUANTITY_UNIT_TYPE(VIBRATO_RATE), QUANTITY_UNIT_TYPE(DIRECT_INT) }; /* count num */ *num = 1, p = cp; while ((p = strchr(p, ',')) != NULL) (*num)++, p++; /* alloc */ mod_list = (Quantity **)safe_malloc((*num) * sizeof(Quantity *)); for (i = 0; i < *num; i++) mod_list[i] = (Quantity *)safe_malloc(3 * sizeof(Quantity)); /* init */ for (i = 0; i < *num; i++) for (j = 0; j < 3; j++) INIT_QUANTITY(mod_list[i][j]); buf[sizeof buf - 1] = '\0'; /* regist */ for (i = 0, p = cp; i < *num; i++, p++) { px = strchr(p, ','); for (j = 0; j < 3; j++, p++) { if (*p == ':') continue; if ((delim = strpbrk(strncpy(buf, p, sizeof buf - 1), ":,")) != NULL) *delim = '\0'; if (*buf != '\0' && (err = string_to_quantity(buf, &mod_list[i][j], qtypes[mod_type * 3 + j])) != NULL) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: %s: parameter %d of item %d: %s (%s)", name, line, qtypestr[mod_type], j + 1, i + 1, err, buf); free_ptr_list(mod_list, *num); mod_list = NULL; *num = 0; return NULL; } if (!(p = strchr(p, ':'))) break; if (px && p > px) break; } if (!(p = px)) break; } return mod_list; } /*! copy bank and, if necessary, map appropriately */ void Instruments::copybank(ToneBank *to, ToneBank *from, int mapid, int bankmapfrom, int bankno) { ToneBankElement *toelm, *fromelm; int i; if (from == NULL) return; for (i = 0; i < 128; i++) { toelm = &to->tone[i]; fromelm = &from->tone[i]; if (fromelm->name == NULL) continue; copy_tone_bank_element(toelm, fromelm); toelm->instrument = NULL; if (mapid != INST_NO_MAP) set_instrument_map(mapid, bankmapfrom, i, bankno, i); } } /*! copy the whole mapped bank. returns 0 if no error. */ int Instruments::copymap(int mapto, int mapfrom, int isdrum) { ToneBank **tb = isdrum ? drumset : tonebank; int i, bankfrom, bankto; for (i = 0; i < 128; i++) { bankfrom = find_instrument_map_bank(isdrum, mapfrom, i); if (bankfrom <= 0) /* not mapped */ continue; bankto = alloc_instrument_map_bank(isdrum, mapto, i); if (bankto == -1) /* failed */ return 1; copybank(tb[bankto], tb[bankfrom], mapto, i, bankto); } return 0; } int Instruments::set_gus_patchconf_opts(const char *name, int line, char *opts, ToneBankElement *tone) { char *cp; int k; if (!(cp = strchr(opts, '='))) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: bad patch option %s", name, line, opts); return 1; } *cp++ = 0; if (!strcmp(opts, "amp")) { k = atoi(cp); if ((k < 0 || k > MAX_AMPLIFICATION) || (*cp < '0' || *cp > '9')) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: amplification must be between 0 and %d", name, line, MAX_AMPLIFICATION); return 1; } tone->amp = k; } else if (!strcmp(opts, "note")) { k = atoi(cp); if ((k < 0 || k > 127) || (*cp < '0' || *cp > '9')) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: note must be between 0 and 127", name, line); return 1; } tone->note = k; tone->scltune = config_parse_int16("100", &tone->scltunenum); } else if (!strcmp(opts, "pan")) { if (!strcmp(cp, "center")) k = 64; else if (!strcmp(cp, "left")) k = 0; else if (!strcmp(cp, "right")) k = 127; else { k = ((atoi(cp) + 100) * 100) / 157; if ((k < 0 || k > 127) || (k == 0 && *cp != '-' && (*cp < '0' || *cp > '9'))) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: panning must be left, right, " "center, or between -100 and 100", name, line); return 1; } } tone->pan = k; } else if (!strcmp(opts, "tune")) tone->tune = config_parse_tune(cp, &tone->tunenum); else if (!strcmp(opts, "rate")) tone->envrate = config_parse_envelope(cp, &tone->envratenum); else if (!strcmp(opts, "offset")) tone->envofs = config_parse_envelope(cp, &tone->envofsnum); else if (!strcmp(opts, "keep")) { if (!strcmp(cp, "env")) tone->strip_envelope = 0; else if (!strcmp(cp, "loop")) tone->strip_loop = 0; else { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: keep must be env or loop", name, line); return 1; } } else if (!strcmp(opts, "strip")) { if (!strcmp(cp, "env")) tone->strip_envelope = 1; else if (!strcmp(cp, "loop")) tone->strip_loop = 1; else if (!strcmp(cp, "tail")) tone->strip_tail = 1; else { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: strip must be env, loop, or tail", name, line); return 1; } } else if (!strcmp(opts, "tremolo")) { if ((tone->trem = config_parse_modulation(name, line, cp, &tone->tremnum, 0)) == NULL) return 1; } else if (!strcmp(opts, "vibrato")) { if ((tone->vib = config_parse_modulation(name, line, cp, &tone->vibnum, 1)) == NULL) return 1; } else if (!strcmp(opts, "sclnote")) tone->sclnote = config_parse_int16(cp, &tone->sclnotenum); else if (!strcmp(opts, "scltune")) tone->scltune = config_parse_int16(cp, &tone->scltunenum); else if (!strcmp(opts, "comm")) { char *p; if (tone->comment) free(tone->comment); p = tone->comment = safe_strdup(cp); while (*p) { if (*p == ',') *p = ' '; p++; } } else if (!strcmp(opts, "modrate")) tone->modenvrate = config_parse_envelope(cp, &tone->modenvratenum); else if (!strcmp(opts, "modoffset")) tone->modenvofs = config_parse_envelope(cp, &tone->modenvofsnum); else if (!strcmp(opts, "envkeyf")) tone->envkeyf = config_parse_envelope(cp, &tone->envkeyfnum); else if (!strcmp(opts, "envvelf")) tone->envvelf = config_parse_envelope(cp, &tone->envvelfnum); else if (!strcmp(opts, "modkeyf")) tone->modenvkeyf = config_parse_envelope(cp, &tone->modenvkeyfnum); else if (!strcmp(opts, "modvelf")) tone->modenvvelf = config_parse_envelope(cp, &tone->modenvvelfnum); else if (!strcmp(opts, "trempitch")) tone->trempitch = config_parse_int16(cp, &tone->trempitchnum); else if (!strcmp(opts, "tremfc")) tone->tremfc = config_parse_int16(cp, &tone->tremfcnum); else if (!strcmp(opts, "modpitch")) tone->modpitch = config_parse_int16(cp, &tone->modpitchnum); else if (!strcmp(opts, "modfc")) tone->modfc = config_parse_int16(cp, &tone->modfcnum); else if (!strcmp(opts, "fc")) tone->fc = config_parse_int16(cp, &tone->fcnum); else if (!strcmp(opts, "q")) tone->reso = config_parse_int16(cp, &tone->resonum); else if (!strcmp(opts, "fckeyf")) /* filter key-follow */ tone->key_to_fc = atoi(cp); else if (!strcmp(opts, "fcvelf")) /* filter velocity-follow */ tone->vel_to_fc = atoi(cp); else if (!strcmp(opts, "qvelf")) /* resonance velocity-follow */ tone->vel_to_resonance = atoi(cp); else { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: bad patch option %s", name, line, opts); return 1; } return 0; } void Instruments::reinit_tone_bank_element(ToneBankElement *tone) { free_tone_bank_element(tone); tone->note = tone->pan = -1; tone->strip_loop = tone->strip_envelope = tone->strip_tail = -1; tone->amp = -1; tone->rnddelay = 0; tone->loop_timeout = 0; tone->legato = tone->damper_mode = tone->key_to_fc = tone->vel_to_fc = 0; tone->reverb_send = tone->chorus_send = tone->delay_send = -1; tone->tva_level = -1; tone->play_note = -1; } int Instruments::set_gus_patchconf(const char *name, int line, ToneBankElement *tone, char *pat, char **opts) { int j; reinit_tone_bank_element(tone); if (strcmp(pat, "%font") == 0) /* Font extention */ { /* %font filename bank prog [note-to-use] * %font filename 128 bank key */ if (opts[0] == NULL || opts[1] == NULL || opts[2] == NULL || (atoi(opts[1]) == 128 && opts[3] == NULL)) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: Syntax error", name, line); return 1; } tone->name = safe_strdup(opts[0]); tone->instype = 1; if (atoi(opts[1]) == 128) /* drum */ { tone->font_bank = 128; tone->font_preset = atoi(opts[2]); tone->font_keynote = atoi(opts[3]); opts += 4; } else { tone->font_bank = atoi(opts[1]); tone->font_preset = atoi(opts[2]); if (opts[3] && isdigit(opts[3][0])) { tone->font_keynote = atoi(opts[3]); opts += 4; } else { tone->font_keynote = -1; opts += 3; } } } else if (strcmp(pat, "%sample") == 0) /* Sample extention */ { /* %sample filename */ if (opts[0] == NULL) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: Syntax error", name, line); return 1; } tone->name = safe_strdup(opts[0]); tone->instype = 2; opts++; } else { tone->instype = 0; tone->name = safe_strdup(pat); } for (j = 0; opts[j] != NULL; j++) { int err; if ((err = set_gus_patchconf_opts(name, line, opts[j], tone)) != 0) return err; } if (tone->comment == NULL) tone->comment = safe_strdup(tone->name); return 0; } int Instruments::set_patchconf(const char *name, int line, ToneBank *bank, char *w[], int dr, int mapid, int bankmapfrom, int bankno) { int i; i = atoi(w[0]); if (!dr) i -= progbase; if (i < 0 || i > 127) { if (dr) printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: Drum number must be between " "0 and 127", name, line); else printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: Program must be between " "%d and %d", name, line, progbase, 127 + progbase); return 1; } if (!bank) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: Must specify tone bank or drum set " "before assignment", name, line); return 1; } if (set_gus_patchconf(name, line, &bank->tone[i], w[1], w + 2)) return 1; if (mapid != INST_NO_MAP) set_instrument_map(mapid, bankmapfrom, i, bankno, i); return 0; } /* string[0] should not be '#' */ int Instruments::strip_trailing_comment(char *string, int next_token_index) { if (string[next_token_index - 1] == '#' /* strip \1 in /^\S+(#*[ \t].*)/ */ && (string[next_token_index] == ' ' || string[next_token_index] == '\t')) { string[next_token_index] = '\0'; /* new c-string terminator */ while (string[--next_token_index - 1] == '#') ; } return next_token_index; } char *Instruments::expand_variables(char *string, MBlockList *varbuf, const char *basedir) { char *p, *expstr; const char *copystr; int limlen, copylen, explen, varlen, braced; if ((p = strchr(string, '$')) == NULL) return string; varlen = (int)strlen(basedir); explen = limlen = 0; expstr = NULL; copystr = string; copylen = p - string; string = p; for (;;) { if (explen + copylen + 1 > limlen) { limlen += copylen + 128; expstr = (char*)memcpy(new_segment(varbuf, limlen), expstr, explen); } memcpy(&expstr[explen], copystr, copylen); explen += copylen; if (*string == '\0') break; else if (*string == '$') { braced = *++string == '{'; if (braced) { if ((p = strchr(string + 1, '}')) == NULL) p = string; /* no closing brace */ else string++; } else for (p = string; isalnum(*p) || *p == '_'; p++); if (p == string) /* empty */ { copystr = "${"; copylen = 1 + braced; } else { if (p - string == 7 && memcmp(string, "basedir", 7) == 0) { copystr = basedir; copylen = varlen; } else /* undefined variable */ copylen = 0; string = p + braced; } } else /* search next */ { p = strchr(string, '$'); if (p == NULL) copylen = (int)strlen(string); else copylen = int(p - string); copystr = string; string += copylen; } } expstr[explen] = '\0'; return expstr; } int Instruments::read_config_file(const char *name, int self, int allow_missing_file) { timidity_file *tf; char buf[1024], *tmp, *w[MAXWORDS + 1], *cp; ToneBank *bank = NULL; int i, j, k, line = 0, words, errcnt = 0; static int rcf_count = 0; int dr = 0, bankno = 0, mapid = INST_NO_MAP, origbankno = 0x7FFFFFFF; int extension_flag, param_parse_err; MBlockList varbuf; const char *basedir; char *sep; if (rcf_count > 50) { printMessage(CMSG_ERROR, VERB_NORMAL, "Probable source loop in configuration files"); return READ_CONFIG_RECURSION; } tf = open_file(name, sfreader); if (tf == NULL) return allow_missing_file ? READ_CONFIG_FILE_NOT_FOUND : READ_CONFIG_ERROR; init_mblock(&varbuf); if (!self) { char *c = strdup_mblock(&varbuf, tf->filename.c_str()); basedir = c; for (; *c; c++) if (*c == '\\') *c = '/'; sep = (char*)strrchr(basedir, '/'); } else sep = NULL; if (sep == NULL) { basedir = "."; } else { if ((cp = (char*)strchr(sep, '#')) != NULL) sep = cp + 1; /* inclusive of '#' */ *sep = '\0'; } while (tf_gets(buf, sizeof(buf), tf)) { line++; if (strncmp(buf, "#extension", 10) == 0) { extension_flag = 1; i = 10; } else { extension_flag = 0; i = 0; } while (isspace(buf[i])) /* skip /^\s*(?#)/ */ i++; if (buf[i] == '#' || buf[i] == '\0') /* /^#|^$/ */ continue; tmp = expand_variables(buf, &varbuf, basedir); j = (int)strcspn(tmp + i, " \t\r\n\240"); if (j == 0) j = (int)strlen(tmp + i); j = strip_trailing_comment(tmp + i, j); tmp[i + j] = '\0'; /* terminate the first token */ w[0] = tmp + i; i += j + 1; words = param_parse_err = 0; while (words < MAXWORDS - 1) /* -1 : next arg */ { char *terminator; while (isspace(tmp[i])) /* skip /^\s*(?#)/ */ i++; if (tmp[i] == '\0' || tmp[i] == '#') /* /\s#/ */ break; if ((tmp[i] == '"' || tmp[i] == '\'') && (terminator = strchr(tmp + i + 1, tmp[i])) != NULL) { if (!isspace(terminator[1]) && terminator[1] != '\0') { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: there must be at least one whitespace between " "string terminator (%c) and the next parameter", name, line, tmp[i]); CHECKERRLIMIT; param_parse_err = 1; break; } w[++words] = tmp + i + 1; i = terminator - tmp + 1; *terminator = '\0'; } else /* not terminated */ { j = (int)strcspn(tmp + i, " \t\r\n\240"); if (j > 0) j = strip_trailing_comment(tmp + i, j); w[++words] = tmp + i; i += j; if (tmp[i] != '\0') /* unless at the end-of-string (i.e. EOF) */ tmp[i++] = '\0'; /* terminate the token */ } } if (param_parse_err) continue; w[++words] = NULL; /* * #extension [something...] */ /* #extension timeout program sec */ if (strcmp(w[0], "timeout") == 0) { if (words != 3) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: syntax error", name, line); CHECKERRLIMIT; continue; } if (!bank) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: Must specify tone bank or drum set " "before assignment", name, line); CHECKERRLIMIT; continue; } i = atoi(w[1]); if (i < 0 || i > 127) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: extension timeout " "must be between 0 and 127", name, line); CHECKERRLIMIT; continue; } bank->tone[i].loop_timeout = atoi(w[2]); } /* #extension copydrumset drumset */ else if (strcmp(w[0], "copydrumset") == 0) { if (words < 2) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: No copydrumset number given", name, line); CHECKERRLIMIT; continue; } i = atoi(w[1]); if (i < 0 || i > 127) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: extension copydrumset " "must be between 0 and 127", name, line); CHECKERRLIMIT; continue; } if (!bank) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: Must specify tone bank or " "drum set before assignment", name, line); CHECKERRLIMIT; continue; } copybank(bank, drumset[i], mapid, origbankno, bankno); } /* #extension copybank bank */ else if (strcmp(w[0], "copybank") == 0) { if (words < 2) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: No copybank number given", name, line); CHECKERRLIMIT; continue; } i = atoi(w[1]); if (i < 0 || i > 127) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: extension copybank " "must be between 0 and 127", name, line); CHECKERRLIMIT; continue; } if (!bank) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: Must specify tone bank or " "drum set before assignment", name, line); CHECKERRLIMIT; continue; } copybank(bank, tonebank[i], mapid, origbankno, bankno); } /* #extension copymap tomapid frommapid */ else if (strcmp(w[0], "copymap") == 0) { int mapto, mapfrom; int toisdrum, fromisdrum; if (words != 3) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: syntax error", name, line); CHECKERRLIMIT; continue; } if ((mapto = mapname2id(w[1], &toisdrum)) == -1) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: Invalid map name: %s", name, line, w[1]); CHECKERRLIMIT; continue; } if ((mapfrom = mapname2id(w[2], &fromisdrum)) == -1) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: Invalid map name: %s", name, line, w[2]); CHECKERRLIMIT; continue; } if (toisdrum != fromisdrum) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: Map type should be matched", name, line); CHECKERRLIMIT; continue; } if (copymap(mapto, mapfrom, toisdrum)) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: No free %s available to map", name, line, toisdrum ? "drum set" : "tone bank"); CHECKERRLIMIT; continue; } } /* #extension undef program */ else if (strcmp(w[0], "undef") == 0) { if (words < 2) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: No undef number given", name, line); CHECKERRLIMIT; continue; } i = atoi(w[1]); if (i < 0 || i > 127) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: extension undef " "must be between 0 and 127", name, line); CHECKERRLIMIT; continue; } if (!bank) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: Must specify tone bank or " "drum set before assignment", name, line); CHECKERRLIMIT; continue; } free_tone_bank_element(&bank->tone[i]); } /* #extension altassign numbers... */ else if (strcmp(w[0], "altassign") == 0) { ToneBank *bk; if (!bank) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: Must specify tone bank or drum set " "before altassign", name, line); CHECKERRLIMIT; continue; } if (words < 2) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: No alternate assignment", name, line); CHECKERRLIMIT; continue; } if (!dr) { printMessage(CMSG_WARNING, VERB_NORMAL, "%s: line %d: Warning: Not a drumset altassign" " (ignored)", name, line); continue; } bk = drumset[bankno]; bk->alt = add_altassign_string(bk->alt, w + 1, words - 1); } /* #extension legato [program] [0 or 1] */ else if (strcmp(w[0], "legato") == 0) { if (words != 3) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: syntax error", name, line); CHECKERRLIMIT; continue; } if (!bank) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: Must specify tone bank or drum set " "before assignment", name, line); CHECKERRLIMIT; continue; } i = atoi(w[1]); if (i < 0 || i > 127) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: extension legato " "must be between 0 and 127", name, line); CHECKERRLIMIT; continue; } bank->tone[i].legato = atoi(w[2]); } /* #extension damper [program] [0 or 1] */ else if (strcmp(w[0], "damper") == 0) { if (words != 3) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: syntax error", name, line); CHECKERRLIMIT; continue; } if (!bank) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: Must specify tone bank or drum set " "before assignment", name, line); CHECKERRLIMIT; continue; } i = atoi(w[1]); if (i < 0 || i > 127) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: extension damper " "must be between 0 and 127", name, line); CHECKERRLIMIT; continue; } bank->tone[i].damper_mode = atoi(w[2]); } /* #extension rnddelay [program] [0 or 1] */ else if (strcmp(w[0], "rnddelay") == 0) { if (words != 3) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: syntax error", name, line); CHECKERRLIMIT; continue; } if (!bank) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: Must specify tone bank or drum set " "before assignment", name, line); CHECKERRLIMIT; continue; } i = atoi(w[1]); if (i < 0 || i > 127) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: extension rnddelay " "must be between 0 and 127", name, line); CHECKERRLIMIT; continue; } bank->tone[i].rnddelay = atoi(w[2]); } /* #extension level program tva_level */ else if (strcmp(w[0], "level") == 0) { if (words != 3) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: syntax error", name, line); CHECKERRLIMIT; continue; } if (!bank) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: Must specify tone bank or drum set " "before assignment", name, line); CHECKERRLIMIT; continue; } i = atoi(w[2]); if (i < 0 || i > 127) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: extension level " "must be between 0 and 127", name, line); CHECKERRLIMIT; continue; } cp = w[1]; do { if (string_to_7bit_range(cp, &j, &k)) { while (j <= k) bank->tone[j++].tva_level = i; } cp = strchr(cp, ','); } while (cp++ != NULL); } /* #extension reverbsend */ else if (strcmp(w[0], "reverbsend") == 0) { if (words != 3) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: syntax error", name, line); CHECKERRLIMIT; continue; } if (!bank) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: Must specify tone bank or drum set " "before assignment", name, line); CHECKERRLIMIT; continue; } i = atoi(w[2]); if (i < 0 || i > 127) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: extension reverbsend " "must be between 0 and 127", name, line); CHECKERRLIMIT; continue; } cp = w[1]; do { if (string_to_7bit_range(cp, &j, &k)) { while (j <= k) bank->tone[j++].reverb_send = i; } cp = strchr(cp, ','); } while (cp++ != NULL); } /* #extension chorussend */ else if (strcmp(w[0], "chorussend") == 0) { if (words != 3) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: syntax error", name, line); CHECKERRLIMIT; continue; } if (!bank) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: Must specify tone bank or drum set " "before assignment", name, line); CHECKERRLIMIT; continue; } i = atoi(w[2]); if (i < 0 || i > 127) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: extension chorussend " "must be between 0 and 127", name, line); CHECKERRLIMIT; continue; } cp = w[1]; do { if (string_to_7bit_range(cp, &j, &k)) { while (j <= k) bank->tone[j++].chorus_send = i; } cp = strchr(cp, ','); } while (cp++ != NULL); } /* #extension delaysend */ else if (strcmp(w[0], "delaysend") == 0) { if (words != 3) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: syntax error", name, line); CHECKERRLIMIT; continue; } if (!bank) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: Must specify tone bank or drum set " "before assignment", name, line); CHECKERRLIMIT; continue; } i = atoi(w[2]); if (i < 0 || i > 127) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: extension delaysend " "must be between 0 and 127", name, line); CHECKERRLIMIT; continue; } cp = w[1]; do { if (string_to_7bit_range(cp, &j, &k)) { while (j <= k) bank->tone[j++].delay_send = i; } cp = strchr(cp, ','); } while (cp++ != NULL); } /* #extension playnote */ else if (strcmp(w[0], "playnote") == 0) { if (words != 3) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: syntax error", name, line); CHECKERRLIMIT; continue; } if (!bank) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: Must specify tone bank or drum set " "before assignment", name, line); CHECKERRLIMIT; continue; } i = atoi(w[2]); if (i < 0 || i > 127) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: extension playnote" "must be between 0 and 127", name, line); CHECKERRLIMIT; continue; } cp = w[1]; do { if (string_to_7bit_range(cp, &j, &k)) { while (j <= k) bank->tone[j++].play_note = i; } cp = strchr(cp, ','); } while (cp++ != NULL); } else if (!strcmp(w[0], "soundfont")) { int order, cutoff, isremove, reso, amp; char *sf_file; if (words < 2) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: No soundfont file given", name, line); CHECKERRLIMIT; continue; } sf_file = w[1]; order = cutoff = reso = amp = -1; isremove = 0; for (j = 2; j < words; j++) { if (strcmp(w[j], "remove") == 0) { isremove = 1; break; } if (!(cp = strchr(w[j], '='))) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: bad patch option %s", name, line, w[j]); CHECKERRLIMIT; break; } *cp++ = 0; k = atoi(cp); if (!strcmp(w[j], "order")) { if (k < 0 || (*cp < '0' || *cp > '9')) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: order must be a digit", name, line); CHECKERRLIMIT; break; } order = k; } else if (!strcmp(w[j], "cutoff")) { if (k < 0 || (*cp < '0' || *cp > '9')) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: cutoff must be a digit", name, line); CHECKERRLIMIT; break; } cutoff = k; } else if (!strcmp(w[j], "reso")) { if (k < 0 || (*cp < '0' || *cp > '9')) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: reso must be a digit", name, line); CHECKERRLIMIT; break; } reso = k; } else if (!strcmp(w[j], "amp")) { amp = k; } } if (isremove) remove_soundfont(sf_file); else add_soundfont(sf_file, order, cutoff, reso, amp); } else if (!strcmp(w[0], "font")) { int bank, preset, keynote; if (words < 2) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: no font command", name, line); CHECKERRLIMIT; continue; } if (!strcmp(w[1], "exclude")) { if (words < 3) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: No bank/preset/key is given", name, line); CHECKERRLIMIT; continue; } bank = atoi(w[2]); if (words >= 4) preset = atoi(w[3]) - progbase; else preset = -1; if (words >= 5) keynote = atoi(w[4]); else keynote = -1; if (exclude_soundfont(bank, preset, keynote)) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: No soundfont is given", name, line); CHECKERRLIMIT; } } else if (!strcmp(w[1], "order")) { int order; if (words < 4) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: No order/bank is given", name, line); CHECKERRLIMIT; continue; } order = atoi(w[2]); bank = atoi(w[3]); if (words >= 5) preset = atoi(w[4]) - progbase; else preset = -1; if (words >= 6) keynote = atoi(w[5]); else keynote = -1; if (order_soundfont(bank, preset, keynote, order)) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: No soundfont is given", name, line); CHECKERRLIMIT; } } } else if (!strcmp(w[0], "progbase")) { if (words < 2 || *w[1] < '0' || *w[1] > '9') { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: syntax error", name, line); CHECKERRLIMIT; continue; } progbase = atoi(w[1]); } else if (!strcmp(w[0], "map")) /* map set1 elem1 set2 elem2 */ { int arg[5], isdrum; if (words != 6) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: syntax error", name, line); CHECKERRLIMIT; continue; } if ((arg[0] = mapname2id(w[1], &isdrum)) == -1) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: Invalid map name: %s", name, line, w[1]); CHECKERRLIMIT; continue; } for (i = 2; i < 6; i++) arg[i - 1] = atoi(w[i]); if (isdrum) { arg[1] -= progbase; arg[3] -= progbase; } else { arg[2] -= progbase; arg[4] -= progbase; } for (i = 1; i < 5; i++) if (arg[i] < 0 || arg[i] > 127) break; if (i != 5) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: Invalid parameter", name, line); CHECKERRLIMIT; continue; } set_instrument_map(arg[0], arg[1], arg[2], arg[3], arg[4]); } /* * Standard configurations */ else if (!strcmp(w[0], "dir")) { if (words < 2) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: No directory given", name, line); CHECKERRLIMIT; continue; } for (i = 1; i < words; i++) sfreader->add_search_path(w[i]); } else if (!strcmp(w[0], "source") || !strcmp(w[0], "trysource")) { if (words < 2) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: No file name given", name, line); CHECKERRLIMIT; continue; } for (i = 1; i < words; i++) { int status; rcf_count++; status = read_config_file(w[i], 0, !strcmp(w[0], "trysource")); rcf_count--; switch (status) { case READ_CONFIG_SUCCESS: break; case READ_CONFIG_ERROR: CHECKERRLIMIT; continue; case READ_CONFIG_RECURSION: reuse_mblock(&varbuf); tf_close(tf); return READ_CONFIG_RECURSION; case READ_CONFIG_FILE_NOT_FOUND: break; } } } else if (!strcmp(w[0], "default")) { if (words != 2) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: Must specify exactly one patch name", name, line); CHECKERRLIMIT; continue; } strncpy(def_instr_name, w[1], 255); def_instr_name[255] = '\0'; default_instrument_name = def_instr_name; } /* drumset [mapid] num */ else if (!strcmp(w[0], "drumset")) { int newmapid, isdrum, newbankno; if (words < 2) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: No drum set number given", name, line); CHECKERRLIMIT; continue; } if (words != 2 && !isdigit(*w[1])) { if ((newmapid = mapname2id(w[1], &isdrum)) == -1 || !isdrum) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: Invalid drum set map name: %s", name, line, w[1]); CHECKERRLIMIT; continue; } words--; memmove(&w[1], &w[2], sizeof w[0] * words); } else newmapid = INST_NO_MAP; i = atoi(w[1]) - progbase; if (i < 0 || i > 127) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: Drum set must be between %d and %d", name, line, progbase, progbase + 127); CHECKERRLIMIT; continue; } newbankno = i; i = alloc_instrument_map_bank(1, newmapid, i); if (i == -1) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: No free drum set available to map", name, line); CHECKERRLIMIT; continue; } if (words == 2) { bank = drumset[i]; bankno = i; mapid = newmapid; origbankno = newbankno; dr = 1; } else { if (words < 4 || *w[2] < '0' || *w[2] > '9') { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: syntax error", name, line); CHECKERRLIMIT; continue; } if (set_patchconf(name, line, drumset[i], &w[2], 1, newmapid, newbankno, i)) { CHECKERRLIMIT; continue; } } } /* bank [mapid] num */ else if (!strcmp(w[0], "bank")) { int newmapid, isdrum, newbankno; if (words < 2) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: No bank number given", name, line); CHECKERRLIMIT; continue; } if (words != 2 && !isdigit(*w[1])) { if ((newmapid = mapname2id(w[1], &isdrum)) == -1 || isdrum) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: Invalid bank map name: %s", name, line, w[1]); CHECKERRLIMIT; continue; } words--; memmove(&w[1], &w[2], sizeof w[0] * words); } else newmapid = INST_NO_MAP; i = atoi(w[1]); if (i < 0 || i > 127) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: Tone bank must be between 0 and 127", name, line); CHECKERRLIMIT; continue; } newbankno = i; i = alloc_instrument_map_bank(0, newmapid, i); if (i == -1) { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: No free tone bank available to map", name, line); CHECKERRLIMIT; continue; } if (words == 2) { bank = tonebank[i]; bankno = i; mapid = newmapid; origbankno = newbankno; dr = 0; } else { if (words < 4 || *w[2] < '0' || *w[2] > '9') { printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: syntax error", name, line); CHECKERRLIMIT; continue; } if (set_patchconf(name, line, tonebank[i], &w[2], 0, newmapid, newbankno, i)) { CHECKERRLIMIT; continue; } } } else { if (words < 2 || *w[0] < '0' || *w[0] > '9') { if (extension_flag) continue; printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: syntax error", name, line); CHECKERRLIMIT; continue; } if (set_patchconf(name, line, bank, w, dr, mapid, origbankno, bankno)) { CHECKERRLIMIT; continue; } } } reuse_mblock(&varbuf); tf_close(tf); return (errcnt == 0) ? READ_CONFIG_SUCCESS : READ_CONFIG_ERROR; } }