/*************************************************************************************** * * fluidsynth~ * * Fluid Synth soundfont synthesizer for Max/MSP. * * Fluid Synth is written by Peter Hanappe et al. * see http://www.fluidsynth.org/ * * Max/MSP integration by Norbert Schnell ATR IRCAM - Centre Pompidou * * 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.1 * of the License, or (at your option) any later version. * * See file COPYING.LIB for further informations on licensing terms. * * 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 02110-1301, USA. * */ /************************************************************************ * * versions: * (14): adapted to latest code base * (13): ??? * (12): fixed voice stealing * (11): fixed arguments of fluidmax_tuning_octave() (third has to be float) * (10): added micro-tuning methods * (9): bug fix: now polyphony and # of midi channel arguments take effect * (8): added message resample permitting to chose resampling interpolation method * (7): added names for soundfonts (== file name without path and postfix) * (6): added message 'info' * (5): fixed bogus path translation at file loading * */ #define FLUIDMAX_VERSION "01/2009 (14)" #include "ftmax.h" #include "fluidsynth.h" #include "fluid_synth.h" #include "fluid_sfont.h" #include "fluid_chan.h" #include "fluidsynth/version.h" typedef struct { ftmax_dsp_object_t obj; fluid_synth_t *synth; fluid_settings_t *settings; int reverb; int chorus; int mute; void *outlet; } fluidmax_t; static t_messlist *fluidmax_class; static ftmax_symbol_t sym_on = NULL; static ftmax_symbol_t sym_off = NULL; static ftmax_symbol_t sym_undefined = NULL; static ftmax_symbol_t sym_gain = NULL; static ftmax_symbol_t sym_channels = NULL; static ftmax_symbol_t sym_channel = NULL; static ftmax_symbol_t sym_soundfonts = NULL; static ftmax_symbol_t sym_soundfont = NULL; static ftmax_symbol_t sym_presets = NULL; static ftmax_symbol_t sym_preset = NULL; static ftmax_symbol_t sym_reverb = NULL; static ftmax_symbol_t sym_chorus = NULL; static ftmax_symbol_t sym_polyphony = NULL; static ftmax_symbol_t sym_nearest = NULL; static ftmax_symbol_t sym_linear = NULL; static ftmax_symbol_t sym_cubic = NULL; static ftmax_symbol_t sym_sinc = NULL; /*************************************************************** * * generators * */ typedef struct { int index; const char *name; const char *unit; } fluidmax_gen_descr_t; static fluidmax_gen_descr_t fluidmax_gen_info[] = { {0, "startAddrsOffset", "samples"}, {1, "endAddrsOffset", "samples"}, {2, "startloopAddrsOffset", "samples"}, {3, "endloopAddrsOffset", "samples"}, {4, "startAddrsCoarseOffset", "32k samples"}, {5, "modLfoToPitch", "cent fs"}, {6, "vibLfoToPitch", "cent fs"}, {7, "modEnvToPitch", "cent fs"}, {8, "initialFilterFc", "cent 8.176 Hz"}, {9, "initialFilterQ", "cB"}, {10, "modLfoToFilterFc", "cent fs"}, {11, "modEnvToFilterFc", "cent fs "}, {12, "endAddrsCoarseOffset", "32k samples"}, {13, "modLfoToVolume", "cB fs"}, {14, "unused1", ""}, {15, "chorusEffectsSend", "0.1%"}, {16, "reverbEffectsSend", "0.1% "}, {17, "pan", "0.1%"}, {18, "unused2", ""}, {19, "unused3", ""}, {20, "unused4", ""}, {21, "delayModLFO", "timecent"}, {22, "freqModLFO", "cent 8.176 "}, {23, "delayVibLFO", "timecent "}, {24, "freqVibLFO", "cent 8.176"}, {25, "delayModEnv", "timecent"}, {26, "attackModEnv", "timecent "}, {27, "holdModEnv", "timecent"}, {28, "decayModEnv", "timecent"}, {29, "sustainModEnv", "-0.1%"}, {30, "releaseModEnv", "timecent"}, {31, "keynumToModEnvHold", "tcent/key"}, {32, "keynumToModEnvDecay", "tcent/key"}, {33, "delayVolEnv", "timecent"}, {34, "attackVolEnv", "timecent"}, {35, "holdVolEnv", "timecent"}, {36, "decayVolEnv", "timecent"}, {37, "sustainVolEnv", "cB"}, {38, "releaseVolEnv", "timecent "}, {39, "keynumToVolEnvHold", "tcent/key"}, {40, "keynumToVolEnvDecay", "tcent/key "}, {41, "instrument", ""}, {42, "reserved1", ""}, {43, "keyRange MIDI", ""}, {44, "velRange MIDI", ""}, {45, "startloopAddrsCoarseOffset", "samples"}, {46, "keynum MIDI", ""}, {47, "velocity MIDI", ""}, {48, "initialAttenuation", "cB"}, {49, "reserved2", ""}, {50, "endloopAddrsCoarseOffset", "samples"}, {51, "coarseTune", "semitone"}, {52, "fineTune", "cent"}, {53, "sampleId", ""}, {54, "sampleModes", "Bit Flags"}, {55, "reserved3", ""}, {56, "scaleTuning", "cent/key"}, {57, "exclusiveClass", "arbitrary#"}, {58, "unused5", ""}, {59, "unused6", ""}, {60, "unused7", ""}, {61, "unused8", ""}, {62, "unused9", ""}, {63, "unused10", ""} }; /*************************************************************** * * dsp * */ static t_int * fluidmax_perform(t_int *w) { fluidmax_t *self = (fluidmax_t *)(w[1]); t_float *left = (t_float *)(w[2]); t_float *right = (t_float *)(w[3]); int n_tick = (int)(w[4]); if(self->mute == 0) { fluid_synth_write_float(self->synth, n_tick, left, 0, 1, right, 0, 1); } else { int i; for(i = 0; i < n_tick; i++) { left[i] = right[i] = 0.0; } } return (w + 5); } static void fluidmax_dsp(fluidmax_t *self, t_signal **sp, short *count) { int n_tick = sp[0]->s_n; dsp_add(fluidmax_perform, 4, self, sp[0]->s_vec, sp[1]->s_vec, n_tick); } /*************************************************************** * * load utlilities * */ static char * fluidmax_translate_fullpath(char *maxpath, char *fullpath) { int i; strcpy(fullpath, "/Volumes/"); for(i = 0; maxpath[i] != ':'; i++) { fullpath[i + 9] = maxpath[i]; } /* skip ':' */ i++; strcpy(fullpath + i + 8, maxpath + i); return fullpath; } static ftmax_symbol_t fluidmax_get_stripped_name(const char *fullpath) { char stripped[1024]; int i; for(i = strlen(fullpath) - 1; i >= 0; i--) { if(fullpath[i] == '/') { break; } } if(i != 0) { i++; } strcpy(stripped, fullpath + i); for(i = 0; stripped[i] != '\0'; i++) { if((stripped[i] == '.') && (stripped[i + 1] == 's' || stripped[i + 1] == 'S') && (stripped[i + 2] == 'f' || stripped[i + 2] == 'F') && (stripped[i + 3] == '2')) { stripped[i] = '\0'; break; } } return ftmax_new_symbol(stripped); } static ftmax_symbol_t fluidmax_sfont_get_name(fluid_sfont_t *sfont) { return fluidmax_get_stripped_name(fluid_sfont_get_name(sfont)); } static fluid_sfont_t * fluidmax_sfont_get_by_name(fluidmax_t *self, ftmax_symbol_t name) { int n = fluid_synth_sfcount(self->synth); int i; for(i = 0; i < n; i++) { fluid_sfont_t *sfont = fluid_synth_get_sfont(self->synth, i); if(fluidmax_sfont_get_name(sfont) == name) { return sfont; } } return NULL; } static void fluidmax_do_load(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; if(ac > 0 && ftmax_is_symbol(at)) { const char *filename = ftmax_symbol_name(ftmax_get_symbol(at)); ftmax_symbol_t name = fluidmax_get_stripped_name(filename); fluid_sfont_t *sf = fluidmax_sfont_get_by_name(self, name); if(sf == NULL) { int id = fluid_synth_sfload(self->synth, filename, 0); if(id >= 0) { post("fluidsynth~: loaded soundfont '%s' (id %d)", ftmax_symbol_name(name), id); fluid_synth_program_reset(self->synth); outlet_bang(self->outlet); } else { error("fluidsynth~: cannot load soundfont from file '%s'", filename); } } else { error("fluidsynth~: soundfont named '%s' is already loaded", ftmax_symbol_name(name)); return; } } } static void fluidmax_load_with_dialog(t_object *o, t_symbol *s, short argc, t_atom *argv) { char filename[256]; char maxpath[1024]; char fullpath[1024]; long type; short path; open_promptset("open SoundFont 2 file"); if(open_dialog(filename, &path, &type, 0, 0)) { return; } if(path_topotentialname(path, filename, maxpath, 0) == 0) { ftmax_atom_t a; ftmax_set_symbol(&a, ftmax_new_symbol(fluidmax_translate_fullpath(maxpath, fullpath))); fluidmax_do_load(o, NULL, 1, &a); } } /*************************************************************** * * user methods * */ static void fluidmax_load(t_object *o, Symbol *s, short ac, Atom *at) { if(ac == 0) { defer(o, (method)fluidmax_load_with_dialog, NULL, 0, 0); } else { if(ftmax_is_symbol(at)) { ftmax_symbol_t name = ftmax_get_symbol(at); char *string = (char *)ftmax_symbol_name(name); if(string[0] == '/') { defer(o, (method)fluidmax_do_load, NULL, ac, at); } else { char maxpath[1024]; char fullpath[1024]; short path; long type; ftmax_atom_t a; if(locatefile_extended(string, &path, &type, 0, 0) || path_topotentialname(path, string, maxpath, 0) != 0) { error("fluidsynth~: cannot find file '%s'", string); return; } ftmax_set_symbol(&a, ftmax_new_symbol(fluidmax_translate_fullpath(maxpath, fullpath))); defer(o, (method)fluidmax_do_load, NULL, 1, &a); } } } } static void fluidmax_unload(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; if(ac > 0) { if(ftmax_is_number(at)) { int id = ftmax_get_number_int(at); fluid_sfont_t *sf = fluid_synth_get_sfont_by_id(self->synth, id); if(sf != NULL) { ftmax_symbol_t name = fluidmax_sfont_get_name(sf); if(fluid_synth_sfunload(self->synth, id, 0) >= 0) { post("fluidsynth~: unloaded soundfont '%s' (id %d)", ftmax_symbol_name(name), id); return; } } error("fluidsynth~: cannot unload soundfont %d", id); } else if(ftmax_is_symbol(at)) { ftmax_symbol_t sym = ftmax_get_symbol(at); if(sym == ftmax_new_symbol("all")) { fluid_sfont_t *sf = fluid_synth_get_sfont(self->synth, 0); fluid_synth_system_reset(self->synth); while(sf != NULL) { ftmax_symbol_t name = fluidmax_sfont_get_name(sf); unsigned int id = fluid_sfont_get_id(sf); if(fluid_synth_sfunload(self->synth, id, 0) >= 0) { post("fluidsynth~: unloaded soundfont '%s' (id %d)", ftmax_symbol_name(name), id); } else { error("fluidsynth~: cannot unload soundfont '%s' (id %d)", ftmax_symbol_name(name), id); } sf = fluid_synth_get_sfont(self->synth, 0); } } else { fluid_sfont_t *sf = fluidmax_sfont_get_by_name(self, sym); if(sf != NULL) { unsigned int id = fluid_sfont_get_id(sf); if(fluid_synth_sfunload(self->synth, id, 0) >= 0) { post("fluidsynth~: unloaded soundfont '%s' (id %d)", ftmax_symbol_name(sym), id); return; } } error("fluidsynth~: cannot unload soundfont '%s'", ftmax_symbol_name(sym)); } } } } static void fluidmax_reload(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; if(ac > 0) { if(ftmax_is_number(at)) { int id = ftmax_get_number_int(at); fluid_sfont_t *sf = fluid_synth_get_sfont_by_id(self->synth, id); if(sf != NULL) { if(fluid_synth_sfreload(self->synth, id) >= 0) { post("fluidsynth~: reloaded soundfont '%s' (id %d)", id); return; } error("fluidsynth~: cannot reload soundfont %d", id); } } else if(ftmax_is_symbol(at)) { ftmax_symbol_t sym = ftmax_get_symbol(at); if(sym == ftmax_new_symbol("all")) { int n = fluid_synth_sfcount(self->synth); int i; fluid_synth_system_reset(self->synth); for(i = 0; i < n; i++) { fluid_sfont_t *sf = fluid_synth_get_sfont(self->synth, i); ftmax_symbol_t name = fluidmax_sfont_get_name(sf); unsigned int id = fluid_sfont_get_id(sf); if(fluid_synth_sfreload(self->synth, id) >= 0) { post("fluidsynth~: reloaded soundfont '%s' (id %d)", ftmax_symbol_name(name), id); } else { error("fluidsynth~: cannot reload soundfont '%s' (id %d)", ftmax_symbol_name(name), id); } } } else { fluid_sfont_t *sf = fluidmax_sfont_get_by_name(self, sym); if(sf != NULL) { unsigned int id = fluid_sfont_get_id(sf); if(fluid_synth_sfreload(self->synth, id) >= 0) { post("fluidsynth~: reloaded soundfont '%s' (id %d)", ftmax_symbol_name(sym), id); return; } } error("fluidsynth~: cannot reload soundfont '%s'", ftmax_symbol_name(sym)); } } } } static void fluidmax_note(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; if(ac > 0 && ftmax_is_number(at)) { int velocity = 64; int channel = 1; switch(ac) { default: case 3: if(ftmax_is_number(at + 2)) { channel = ftmax_get_number_int(at + 2); if(channel < 1) { channel = 1; } else if(channel > fluid_synth_count_midi_channels(self->synth)) { channel = fluid_synth_count_midi_channels(self->synth); } } case 2: if(ftmax_is_number(at + 1)) { velocity = ftmax_get_number_int(at + 1); } case 1: fluid_synth_noteon(self->synth, channel - 1, ftmax_get_number_int(at), velocity); case 0: break; } } } static void fluidmax_list(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_note(o, NULL, ac, at); } static void fluidmax_control_change(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; if(ac > 0 && ftmax_is_number(at)) { int value = 64; int channel = 1; switch(ac) { default: case 3: if(ftmax_is_number(at + 2)) { channel = ftmax_get_number_int(at + 2); if(channel < 1) { channel = 1; } else if(channel > fluid_synth_count_midi_channels(self->synth)) { channel = fluid_synth_count_midi_channels(self->synth); } } case 2: if(ftmax_is_number(at + 1)) { value = ftmax_get_number_int(at + 1); } case 1: fluid_synth_cc(self->synth, channel - 1, ftmax_get_number_int(at), value); case 0: break; } } } static void fluidmax_mod(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; if(ac > 1 && ftmax_is_number(at) && ftmax_is_number(at + 1)) { int param = ftmax_get_number_int(at); float value = ftmax_get_number_float(at + 1); int channel = 1; if(ac > 2 && ftmax_is_number(at + 2)) { channel = ftmax_get_number_int(at + 2); if(channel < 1) { channel = 1; } else if(channel > fluid_synth_count_midi_channels(self->synth)) { channel = fluid_synth_count_midi_channels(self->synth); } } fluid_synth_set_gen(self->synth, channel - 1, param, value); } } static void fluidmax_pitch_bend(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; if(ac > 0 && ftmax_is_number(at)) { int channel = 1; double bend = 0.0; if(ac > 1 && ftmax_is_number(at + 1)) { channel = ftmax_get_number_int(at + 1); if(channel < 1) { channel = 1; } else if(channel > fluid_synth_count_midi_channels(self->synth)) { channel = fluid_synth_count_midi_channels(self->synth); } } bend = ftmax_get_number_float(at); if(bend < 0.0) { bend = 0.0; } else if(bend > 127.0) { bend = 127.0; } fluid_synth_pitch_bend(self->synth, channel - 1, (int)(bend * 128.0)); } } static void fluidmax_pitch_bend_wheel(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; if(ac > 0 && ftmax_is_number(at)) { int channel = 1; if(ac > 1 && ftmax_is_number(at + 1)) { channel = ftmax_get_number_int(at + 1); } fluid_synth_pitch_wheel_sens(self->synth, channel - 1, ftmax_get_number_int(at)); } } static void fluidmax_program_change(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; if(ac > 0 && ftmax_is_number(at)) { int channel = 1; if(ac > 1 && ftmax_is_number(at + 1)) { channel = ftmax_get_number_int(at + 1); if(channel < 1) { channel = 1; } else if(channel > fluid_synth_count_midi_channels(self->synth)) { channel = fluid_synth_count_midi_channels(self->synth); } } fluid_synth_program_change(self->synth, channel - 1, ftmax_get_number_int(at)); } } static void fluidmax_bank_select(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; if(ac > 0 && ftmax_is_number(at)) { int channel = 1; unsigned int sf_id; unsigned int bank_num; unsigned int prog_num; if(ac > 1 && ftmax_is_number(at + 1)) { channel = ftmax_get_number_int(at + 1); if(channel < 1) { channel = 1; } else if(channel > fluid_synth_count_midi_channels(self->synth)) { channel = fluid_synth_count_midi_channels(self->synth); } } fluid_synth_bank_select(self->synth, channel - 1, ftmax_get_number_int(at)); fluid_synth_get_program(self->synth, channel - 1, &sf_id, &bank_num, &prog_num); fluid_synth_program_change(self->synth, channel - 1, prog_num); } } static void fluidmax_select(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; unsigned int bank = 0; unsigned int preset = 0; int channel = 1; switch(ac) { default: case 4: if(ftmax_is_number(at + 3)) { channel = ftmax_get_number_int(at + 3); } if(channel < 1) { channel = 1; } else if(channel > fluid_synth_count_midi_channels(self->synth)) { channel = fluid_synth_count_midi_channels(self->synth); } case 3: if(ftmax_is_number(at + 2)) { preset = ftmax_get_number_int(at + 2); } case 2: if(ftmax_is_number(at + 1)) { bank = ftmax_get_number_int(at + 1); } case 1: if(ftmax_is_number(at)) { fluid_synth_program_select(self->synth, channel - 1, ftmax_get_number_int(at), bank, preset); } else if(ftmax_is_symbol(at)) { ftmax_symbol_t name = ftmax_get_symbol(at); fluid_sfont_t *sf = fluidmax_sfont_get_by_name(self, name); if(sf != NULL) { fluid_synth_program_select(self->synth, channel - 1, fluid_sfont_get_id(sf), bank, preset); } else { error("fluidsynth~ select: cannot find soundfont named '%s'", ftmax_symbol_name(name)); } } case 0: break; } } static void fluidmax_reverb(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; if(ac == 0) { fluid_synth_set_reverb_on(self->synth, 1); fluid_synth_reset_reverb(self->synth); self->reverb = 1; } else if(ftmax_is_number(at)) { double room = fluid_synth_get_reverb_roomsize(self->synth); double damping = fluid_synth_get_reverb_damp(self->synth); double width = fluid_synth_get_reverb_width(self->synth); fluid_synth_set_reverb_on(self->synth, 1); self->reverb = 1; switch(ac) { default: case 4: if(ftmax_is_number(at + 3)) { width = ftmax_get_number_float(at + 3); } case 3: if(ftmax_is_number(at + 2)) { damping = ftmax_get_number_float(at + 2); } case 2: if(ftmax_is_number(at + 1)) { room = ftmax_get_number_float(at + 1); } case 1: fluid_synth_set_reverb(self->synth, room, damping, width, ftmax_get_number_float(at)); case 0: break; } } else if(ftmax_is_symbol(at)) { ftmax_symbol_t sym = ftmax_get_symbol(at); if(sym == sym_on) { fluid_synth_set_reverb_on(self->synth, 1); self->reverb = 1; } else if(sym == sym_off) { fluid_synth_set_reverb_on(self->synth, 0); self->reverb = 0; } } } static void fluidmax_chorus(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; if(ac == 0) { fluid_synth_set_chorus_on(self->synth, 1); fluid_synth_reset_chorus(self->synth); self->chorus = 1; } else if(ftmax_is_number(at)) { double speed = fluid_synth_get_chorus_speed(self->synth); double depth = fluid_synth_get_chorus_depth(self->synth); int type = fluid_synth_get_chorus_type(self->synth); int nr = fluid_synth_get_chorus_nr(self->synth); fluid_synth_set_chorus_on(self->synth, 1); self->chorus = 1; switch(ac) { default: case 5: if(ftmax_is_number(at + 4)) { nr = ftmax_get_number_int(at + 4); } case 4: if(ftmax_is_number(at + 3)) { type = ftmax_get_number_int(at + 3); } case 3: if(ftmax_is_number(at + 2)) { depth = ftmax_get_number_float(at + 2); } case 2: if(ftmax_is_number(at + 1)) { speed = ftmax_get_number_float(at + 1); } case 1: fluid_synth_set_chorus(self->synth, nr, ftmax_get_number_float(at), speed, depth, type); case 0: break; } } else if(ftmax_is_symbol(at)) { ftmax_symbol_t sym = ftmax_get_symbol(at); if(sym == sym_on) { fluid_synth_set_chorus_on(self->synth, 1); self->chorus = 1; } else if(sym == sym_off) { fluid_synth_set_chorus_on(self->synth, 0); self->chorus = 0; } } } static void fluidmax_set_gain(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; if(ac > 0 && ftmax_is_number(at)) { float gain = ftmax_get_number_float(at); fluid_synth_set_gain(self->synth, gain); } } static void fluidmax_set_resampling_method(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; if(ac > 0) { if(ftmax_is_number(at)) { int ip = ftmax_get_int(at); if(ip == 0) { fluid_synth_set_interp_method(self->synth, -1, FLUID_INTERP_NONE); } else if(ip < 3) { fluid_synth_set_interp_method(self->synth, -1, FLUID_INTERP_LINEAR); } else if(ip < 6) { fluid_synth_set_interp_method(self->synth, -1, FLUID_INTERP_4THORDER); } else { fluid_synth_set_interp_method(self->synth, -1, FLUID_INTERP_7THORDER); } } else if(ftmax_is_symbol(at)) { ftmax_symbol_t sym = ftmax_get_symbol(at); if(sym == sym_nearest) { fluid_synth_set_interp_method(self->synth, -1, FLUID_INTERP_NONE); } else if(sym == sym_linear) { fluid_synth_set_interp_method(self->synth, -1, FLUID_INTERP_LINEAR); } else if(sym == sym_cubic) { fluid_synth_set_interp_method(self->synth, -1, FLUID_INTERP_4THORDER); } else if(sym == sym_sinc) { fluid_synth_set_interp_method(self->synth, -1, FLUID_INTERP_7THORDER); } else { error("fluidsynth~: undefined resampling method: %s", ftmax_symbol_name(sym)); } } } } static void fluidmax_panic(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; fluid_synth_system_reset(self->synth); } static void fluidmax_reset(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; int n = fluid_synth_count_midi_channels(self->synth); int i; for(i = 0; i < n; i++) { fluid_channel_reset(self->synth->channel[i]); } } static void fluidmax_mute(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; int mute = 1; if(ac > 0 && ftmax_is_number(at)) { mute = (ftmax_get_number_int(at) != 0); } fluid_synth_system_reset(self->synth); self->mute = mute; } static void fluidmax_unmute(t_object *o) { ftmax_atom_t a; ftmax_set_int(&a, 0); fluidmax_mute(o, NULL, 1, &a); } /* int fluid_synth_count_audio_channels (fluid_synth_t *synth) int fluid_synth_count_audio_groups (fluid_synth_t *synth) int fluid_synth_count_effects_channels (fluid_synth_t *synth) */ static void fluidmax_tuning_octave(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; ftmax_symbol_t name; int tuning_bank = 0; int tuning_prog = 0; double pitch[12]; int i, n; if(ac > 0 && ftmax_is_symbol(at)) { name = ftmax_get_symbol(at); at++; ac--; } n = ac - 2; if(n > 12) { n = 12; } if(ac > 0 && ftmax_is_number(at)) { tuning_bank = ftmax_get_number_int(at) % 128; } if(ac > 1 && ftmax_is_number(at + 1)) { tuning_prog = ftmax_get_number_int(at) % 128; } for(i = 0; i < n; i++) { if(ftmax_is_number(at + i + 2)) { pitch[i] = ftmax_get_number_float(at + i + 2); } else { pitch[i] = 0.0; } } for(; i < 12; n++) { pitch[i] = 0.0; } fluid_synth_create_octave_tuning(self->synth, tuning_bank, tuning_prog, ftmax_symbol_name(name), pitch); } static void fluidmax_tuning_select(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; int tuning_bank = 0; int tuning_prog = 0; int channel = 1; if(ac > 0 && ftmax_is_number(at)) { tuning_bank = ftmax_get_number_int(at) % 128; } if(ac > 1 && ftmax_is_number(at + 1)) { tuning_prog = ftmax_get_number_int(at + 1) % 128; } if(ac > 2 && ftmax_is_number(at + 2)) { channel = ftmax_get_number_int(at + 2); } if(channel < 1) { channel = 1; } else if(channel > fluid_synth_count_midi_channels(self->synth)) { channel = fluid_synth_count_midi_channels(self->synth); } fluid_synth_select_tuning(self->synth, channel - 1, tuning_bank, tuning_prog); } static void fluidmax_tuning_reset(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; int channel = 0; if(ac > 0 && ftmax_is_number(at)) { channel = ftmax_get_number_int(at); } if(channel < 1) { channel = 1; } else if(channel > fluid_synth_count_midi_channels(self->synth)) { channel = fluid_synth_count_midi_channels(self->synth); } fluid_synth_reset_tuning(self->synth, channel - 1); } /* more tuning ?? fluid_synth_create_key_tuning (fluid_synth_t *synth, int tuning_bank, int tuning_prog, char *name, double *pitch) fluid_synth_tune_notes (fluid_synth_t *synth, int tuning_bank, int tuning_prog, int len, int *keys, double *pitch, int apply) fluid_synth_tuning_iteration_start (fluid_synth_t *synth) fluid_synth_tuning_iteration_next (fluid_synth_t *synth, int *bank, int *prog) fluid_synth_tuning_dump (fluid_synth_t *synth, int bank, int prog, char *name, int len, double *pitch) */ static void fluidmax_version(t_object *o) { post("fluidsynth~, version %s (based on FluidSynth %s)", FLUIDMAX_VERSION, FLUIDSYNTH_VERSION); post(" FluidSynth is written by Peter Hanappe et al. (see fluidsynth.org)"); post(" Max/MSP integration by Norbert Schnell IMTR IRCAM - Centre Pompidou"); } extern const fluid_gen_info_t fluid_gen_info[]; static void fluidmax_print(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; if(ac > 0) { if(ftmax_is_symbol(at)) { ftmax_symbol_t sym = ftmax_get_symbol(at); if(sym == sym_soundfonts) { int n = fluid_synth_sfcount(self->synth); int i; if(n > 0) { post("fluidsynth~ soundfonts:"); } else { post("fluidsynth~: no soundfonts loaded"); } for(i = 0; i < n; i++) { fluid_sfont_t *sf = fluid_synth_get_sfont(self->synth, i); ftmax_symbol_t name = fluidmax_sfont_get_name(sf); unsigned int id = fluid_sfont_get_id(sf); post(" %d: '%s' (id %d)", i, ftmax_symbol_name(name), id); } } else if(sym == sym_presets) { int n = fluid_synth_sfcount(self->synth); if(n > 0) { if(ac > 1) { fluid_sfont_t *sf = NULL; ftmax_symbol_t name; if(ftmax_is_symbol(at + 1)) { name = ftmax_get_symbol(at + 1); sf = fluidmax_sfont_get_by_name(self, name); } else if(ftmax_is_int(at + 1)) { int id = ftmax_get_int(at + 1); sf = fluid_synth_get_sfont_by_id(self->synth, id); name = fluidmax_sfont_get_name(sf); } if(sf != NULL) { fluid_preset_t *preset; fluid_sfont_iteration_start(sf); post("fluidsynth~ presets of soundfont '%s':", ftmax_symbol_name(name)); while((preset = fluid_sfont_iteration_next(sf)) != NULL) { char *preset_str = fluid_preset_get_name(preset); ftmax_symbol_t preset_name = ftmax_new_symbol(preset_str); int bank_num = fluid_preset_get_banknum(preset); int prog_num = fluid_preset_get_num(preset); post(" '%s': bank %d, program %d", ftmax_symbol_name(preset_name), bank_num, prog_num); } } } else { int i; post("fluidsynth~ presets:"); for(i = 0; i < 128; i++) { int j; for(j = 0; j < 128; j++) { fluid_preset_t *preset = NULL; fluid_sfont_t *sf = NULL; int k; for(k = 0; k < n; k++) { sf = fluid_synth_get_sfont(self->synth, k); preset = fluid_sfont_get_preset(sf, i, j); if(preset != NULL) { break; } } if(preset != NULL) { ftmax_symbol_t sf_name = fluidmax_sfont_get_name(sf); char *preset_str = fluid_preset_get_name(preset); ftmax_symbol_t preset_name = ftmax_new_symbol(preset_str); post(" '%s': soundfont '%s', bank %d, program %d", ftmax_symbol_name(preset_name), ftmax_symbol_name(sf_name), i, j); } } } } } else { error("fluidsynth~: no soundfonts loaded"); } } else if(sym == sym_channels) { int n = fluid_synth_count_midi_channels(self->synth); int i; post("fluidsynth~ channels:"); for(i = 0; i < n; i++) { fluid_preset_t *preset = fluid_synth_get_channel_preset(self->synth, i); if(preset != NULL) { char *preset_str = fluid_preset_get_name(preset); ftmax_symbol_t preset_name = ftmax_new_symbol(preset_str); unsigned int sf_id; unsigned int bank_num; unsigned int prog_num; fluid_sfont_t *sf; fluid_synth_get_program(self->synth, i, &sf_id, &bank_num, &prog_num); sf = fluid_synth_get_sfont_by_id(self->synth, sf_id); post(" %d: soundfont '%s', bank %d, program %d: '%s'", i + 1, ftmax_symbol_name(fluidmax_sfont_get_name(sf)), bank_num, prog_num, ftmax_symbol_name(preset_name)); } else { post(" channel %d: no preset", i + 1); } } } else if(sym == ftmax_new_symbol("generators")) { int channel = 1; int n = GEN_LAST; int i; if(ac > 1 && ftmax_is_number(at + 1)) { channel = ftmax_get_number_int(at + 1); } if(channel < 1) { channel = 1; } else if(channel > fluid_synth_count_midi_channels(self->synth)) { channel = fluid_synth_count_midi_channels(self->synth); } post("fluidsynth~ generators of channel %d:", channel); for(i = 0; i < n; i++) { const char *name = fluidmax_gen_info[i].name; const char *unit = fluidmax_gen_info[i].unit; double incr = fluid_synth_get_gen(self->synth, channel - 1, i); double min = fluid_gen_info[i].min; double max = fluid_gen_info[i].max; post(" %d '%s': %s %g [%g ... %g] (%s)", i, name, (incr >= 0) ? "" : "-", fabs(incr), min, max, unit); } } else if(sym == sym_gain) { double gain = fluid_synth_get_gain(self->synth); post("gain: %g", gain); } else if(sym == sym_reverb) { double level = fluid_synth_get_reverb_level(self->synth); double room = fluid_synth_get_reverb_roomsize(self->synth); double damping = fluid_synth_get_reverb_damp(self->synth); double width = fluid_synth_get_reverb_width(self->synth); if(self->reverb != 0) { post("fluidsynth~ current reverb parameters:"); post(" level: %f", level); post(" room size: %f", room); post(" damping: %f", damping); post(" width: %f", width); } else { post("fluidsynth~: reverb off"); } } else if(sym == sym_chorus) { if(self->chorus != 0) { double level = fluid_synth_get_chorus_level(self->synth); double speed = fluid_synth_get_chorus_speed(self->synth); double depth = fluid_synth_get_chorus_depth(self->synth); int type = fluid_synth_get_chorus_type(self->synth); int nr = fluid_synth_get_chorus_nr(self->synth); post("fluidsynth~ current chorus parameters:"); post(" level: %f", level); post(" speed: %f Hz", speed); post(" depth: %f msec", depth); post(" type: %d (%s)", type, type ? "triangle" : "sine"); post(" %d units", nr); } else { post("fluidsynth~: chorus off"); } } } } } static void fluidmax_info(t_object *o, Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)o; if(ac > 0) { if(ftmax_is_symbol(at)) { ftmax_symbol_t sym = ftmax_get_symbol(at); if(sym == sym_soundfonts) { int n = fluid_synth_sfcount(self->synth); int i; for(i = 0; i < n; i++) { fluid_sfont_t *sf = fluid_synth_get_sfont(self->synth, i); unsigned int id = fluid_sfont_get_id(sf); ftmax_atom_t a[2]; ftmax_set_int(a, i); ftmax_set_symbol(a + 1, fluidmax_sfont_get_name(sf)); ftmax_set_int(a + 2, id); outlet_anything(self->outlet, sym_soundfont, 3, a); } } else if(sym == sym_presets) { int n = fluid_synth_sfcount(self->synth); if(n > 0) { if(ac > 1) { fluid_sfont_t *sf = NULL; ftmax_symbol_t sf_name; if(ftmax_is_symbol(at + 1)) { sf_name = ftmax_get_symbol(at + 1); sf = fluidmax_sfont_get_by_name(self, sf_name); } else if(ftmax_is_int(at + 1)) { int id = ftmax_get_int(at + 1); sf = fluid_synth_get_sfont_by_id(self->synth, id); sf_name = fluidmax_sfont_get_name(sf); } if(sf != NULL) { fluid_preset_t *preset; fluid_sfont_iteration_start(sf); while((preset = fluid_sfont_iteration_next(sf)) != NULL) { char *preset_str = fluid_preset_get_name(preset); ftmax_symbol_t preset_name = ftmax_new_symbol(preset_str); int bank_num = fluid_preset_get_banknum(preset); int prog_num = fluid_preset_get_num(preset); ftmax_atom_t a[4]; ftmax_set_symbol(a, preset_name); ftmax_set_symbol(a + 1, sf_name); ftmax_set_int(a + 2, bank_num); ftmax_set_int(a + 3, prog_num); outlet_anything(self->outlet, sym_preset, 4, a); } } } else { int i; for(i = 0; i < 128; i++) { int j; for(j = 0; j < 128; j++) { fluid_preset_t *preset = NULL; fluid_sfont_t *sf = NULL; int k; for(k = 0; k < n; k++) { sf = fluid_synth_get_sfont(self->synth, k); preset = fluid_sfont_get_preset(sf, i, j); if(preset != NULL) { break; } } if(preset != NULL) { ftmax_symbol_t sf_name = fluidmax_sfont_get_name(sf); char *preset_str = fluid_preset_get_name(preset); ftmax_symbol_t preset_name = ftmax_new_symbol(preset_str); ftmax_atom_t a[4]; ftmax_set_symbol(a, preset_name); ftmax_set_symbol(a + 1, sf_name); ftmax_set_int(a + 2, i); ftmax_set_int(a + 3, j); outlet_anything(self->outlet, sym_preset, 4, a); } } } } } else { error("fluidsynth~ info: no soundfonts loaded"); } } else if(sym == sym_channels) { int n = fluid_synth_count_midi_channels(self->synth); int i; for(i = 0; i < n; i++) { fluid_preset_t *preset = fluid_synth_get_channel_preset(self->synth, i); if(preset != NULL) { char *preset_str = fluid_preset_get_name(preset); ftmax_symbol_t preset_name = ftmax_new_symbol(preset_str); unsigned int sf_id, bank_num, prog_num; fluid_sfont_t *sf; ftmax_atom_t a[5]; fluid_synth_get_program(self->synth, i, &sf_id, &bank_num, &prog_num); sf = fluid_synth_get_sfont_by_id(self->synth, sf_id); ftmax_set_int(a, i + 1); ftmax_set_symbol(a + 1, fluidmax_sfont_get_name(sf)); ftmax_set_int(a + 2, bank_num); ftmax_set_int(a + 3, prog_num); ftmax_set_symbol(a + 4, preset_name); outlet_anything(self->outlet, sym_channel, 5, a); } else { ftmax_atom_t a[2]; ftmax_set_int(a, i + 1); ftmax_set_symbol(a + 1, sym_undefined); outlet_anything(self->outlet, sym_channel, 2, a); } } } else if(sym == sym_gain) { ftmax_atom_t a; double gain = fluid_synth_get_gain(self->synth); ftmax_set_float(&a, gain); outlet_anything(self->outlet, sym_channel, 1, &a); } else if(sym == sym_reverb) { if(self->reverb != 0) { double level = fluid_synth_get_reverb_level(self->synth); double room = fluid_synth_get_reverb_roomsize(self->synth); double damping = fluid_synth_get_reverb_damp(self->synth); double width = fluid_synth_get_reverb_width(self->synth); ftmax_atom_t a[4]; ftmax_set_float(a, level); ftmax_set_float(a + 1, room); ftmax_set_float(a + 2, damping); ftmax_set_float(a + 3, width); outlet_anything(self->outlet, sym_reverb, 4, a); } else { ftmax_atom_t a; ftmax_set_symbol(&a, sym_off); outlet_anything(self->outlet, sym_reverb, 1, &a); } } else if(sym == sym_chorus) { if(self->chorus != 0) { double level = fluid_synth_get_chorus_level(self->synth); double speed = fluid_synth_get_chorus_speed(self->synth); double depth = fluid_synth_get_chorus_depth(self->synth); int type = fluid_synth_get_chorus_type(self->synth); int nr = fluid_synth_get_chorus_nr(self->synth); ftmax_atom_t a[5]; ftmax_set_float(a, level); ftmax_set_float(a + 1, speed); ftmax_set_float(a + 2, depth); ftmax_set_int(a + 3, type); ftmax_set_int(a + 4, nr); outlet_anything(self->outlet, sym_chorus, 5, a); } else { ftmax_atom_t a; ftmax_set_symbol(&a, sym_off); outlet_anything(self->outlet, sym_chorus, 1, &a); } } else if(sym == sym_polyphony) { int polyphony = fluid_synth_get_polyphony(self->synth); ftmax_atom_t a; ftmax_set_int(&a, polyphony); outlet_anything(self->outlet, sym_polyphony, 1, &a); } } } } /*************************************************************** * * class * */ static void * fluidmax_new(Symbol *s, short ac, Atom *at) { fluidmax_t *self = (fluidmax_t *)newobject(fluidmax_class); int polyphony = 256; int midi_channels = 16; self->outlet = outlet_new(self, "anything"); dsp_setup((t_pxobject *)self, 0); outlet_new(self, "signal"); outlet_new(self, "signal"); self->synth = NULL; self->settings = new_fluid_settings(); self->reverb = 0; self->chorus = 0; self->mute = 0; if(ac > 0 && ftmax_is_number(at)) { polyphony = ftmax_get_number_int(at); ac--; at++; } if(ac > 0 && ftmax_is_number(at)) { midi_channels = ftmax_get_number_int(at); ac--; at++; } if(ac > 0 && ftmax_is_symbol(at)) { fluidmax_load((t_object *)self, NULL, 1, at); } if(self->settings != NULL) { fluid_settings_setint(self->settings, "synth.midi-channels", midi_channels); fluid_settings_setint(self->settings, "synth.polyphony", polyphony); fluid_settings_setnum(self->settings, "synth.gain", 0.600000); fluid_settings_setnum(self->settings, "synth.sample-rate", sys_getsr()); self->synth = new_fluid_synth(self->settings); if(self->synth != NULL) { fluid_synth_set_reverb_on(self->synth, 0); fluid_synth_set_chorus_on(self->synth, 0); if(ac > 0 && ftmax_is_symbol(at)) { fluidmax_load((t_object *)self, NULL, ac, at); } return self; } delete_fluid_settings(self->settings); } error("fluidsynth~: cannot create FluidSynth core"); return NULL; } static void fluidmax_free(t_pxobject *o) { fluidmax_t *self = (fluidmax_t *)o; if(self->settings != NULL) { delete_fluid_settings(self->settings); } if(self->synth != NULL) { delete_fluid_synth(self->synth); } dsp_free(o); } int main(void) { setup(&fluidmax_class, (method)fluidmax_new, (method)fluidmax_free, (short)sizeof(fluidmax_t), 0, A_GIMME, 0); dsp_initclass(); addmess((method)fluidmax_dsp, "dsp", A_CANT, 0); addmess((method)fluidmax_version, "version", 0); addmess((method)fluidmax_print, "print", A_GIMME, 0); addmess((method)fluidmax_load, "load", A_GIMME, 0); addmess((method)fluidmax_unload, "unload", A_GIMME, 0); addmess((method)fluidmax_reload, "reload", A_GIMME, 0); addmess((method)fluidmax_info, "info", A_GIMME, 0); addmess((method)fluidmax_panic, "panic", A_GIMME, 0); addmess((method)fluidmax_reset, "reset", A_GIMME, 0); addmess((method)fluidmax_mute, "mute", A_GIMME, 0); addmess((method)fluidmax_unmute, "unmute", 0); /*addmess((method)fluidmax_tuning_keys, "tuning-keys", A_GIMME, 0);*/ addmess((method)fluidmax_tuning_octave, "tuning-octave", A_GIMME, 0); addmess((method)fluidmax_tuning_select, "tuning-select", A_GIMME, 0); addmess((method)fluidmax_tuning_reset, "tuning-reset", A_GIMME, 0); addmess((method)fluidmax_reverb, "reverb", A_GIMME, 0); addmess((method)fluidmax_chorus, "chorus", A_GIMME, 0); addmess((method)fluidmax_set_gain, "gain", A_GIMME, 0); addmess((method)fluidmax_set_resampling_method, "resample", A_GIMME, 0); addmess((method)fluidmax_note, "note", A_GIMME, 0); addmess((method)fluidmax_list, "list", A_GIMME, 0); addmess((method)fluidmax_control_change, "control", A_GIMME, 0); addmess((method)fluidmax_mod, "mod", A_GIMME, 0); addmess((method)fluidmax_pitch_bend, "bend", A_GIMME, 0); addmess((method)fluidmax_pitch_bend_wheel, "wheel", A_GIMME, 0); addmess((method)fluidmax_program_change, "program", A_GIMME, 0); addmess((method)fluidmax_bank_select, "bank", A_GIMME, 0); addmess((method)fluidmax_select, "select", A_GIMME, 0); sym_on = ftmax_new_symbol("on"); sym_off = ftmax_new_symbol("off"); sym_undefined = ftmax_new_symbol("undefined"); sym_gain = ftmax_new_symbol("gain"); sym_channels = ftmax_new_symbol("channels"); sym_channel = ftmax_new_symbol("channel"); sym_soundfonts = ftmax_new_symbol("soundfonts"); sym_soundfont = ftmax_new_symbol("soundfont"); sym_presets = ftmax_new_symbol("presets"); sym_preset = ftmax_new_symbol("preset"); sym_reverb = ftmax_new_symbol("reverb"); sym_chorus = ftmax_new_symbol("chorus"); sym_polyphony = ftmax_new_symbol("polyphony"); sym_nearest = ftmax_new_symbol("nearest"); sym_linear = ftmax_new_symbol("linear"); sym_cubic = ftmax_new_symbol("cubic"); sym_sinc = ftmax_new_symbol("sinc"); fluidmax_version(NULL); return 0; }