Path: blob/master/libs/fluidsynth/src/synth/fluid_mod.c
4396 views
/* FluidSynth - A Software Synthesizer1*2* Copyright (C) 2003 Peter Hanappe and others.3*4* This library is free software; you can redistribute it and/or5* modify it under the terms of the GNU Lesser General Public License6* as published by the Free Software Foundation; either version 2.1 of7* the License, or (at your option) any later version.8*9* This library is distributed in the hope that it will be useful, but10* WITHOUT ANY WARRANTY; without even the implied warranty of11* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU12* Lesser General Public License for more details.13*14* You should have received a copy of the GNU Lesser General Public15* License along with this library; if not, write to the Free16* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA17* 02110-1301, USA18*/1920#include "fluid_mod.h"21#include "fluid_chan.h"22#include "fluid_voice.h"2324/**25* Clone the modulators destination, sources, flags and amount.26*27* @param mod the modulator to store the copy to28* @param src the source modulator to retrieve the information from29*30* @note The \c next member of \c mod will be left unchanged.31*/32void33fluid_mod_clone(fluid_mod_t *mod, const fluid_mod_t *src)34{35mod->dest = src->dest;36mod->src1 = src->src1;37mod->flags1 = src->flags1;38mod->src2 = src->src2;39mod->flags2 = src->flags2;40mod->amount = src->amount;41mod->trans = src->trans;42}4344/**45* Set a modulator's primary source controller and flags.46*47* @param mod The modulator instance48* @param src Modulator source (#fluid_mod_src or a MIDI controller number)49* @param flags Flags determining mapping function and whether the source50* controller is a general controller (#FLUID_MOD_GC) or a MIDI CC controller51* (#FLUID_MOD_CC), see #fluid_mod_flags.52*/53void54fluid_mod_set_source1(fluid_mod_t *mod, int src, int flags)55{56mod->src1 = src;57mod->flags1 = flags;58}5960/**61* Set a modulator's secondary source controller and flags.62*63* @param mod The modulator instance64* @param src Modulator source (#fluid_mod_src or a MIDI controller number)65* @param flags Flags determining mapping function and whether the source66* controller is a general controller (#FLUID_MOD_GC) or a MIDI CC controller67* (#FLUID_MOD_CC), see #fluid_mod_flags.68*/69void70fluid_mod_set_source2(fluid_mod_t *mod, int src, int flags)71{72mod->src2 = src;73mod->flags2 = flags;74}7576/**77* Set the destination effect of a modulator.78*79* @param mod The modulator instance80* @param dest Destination generator (#fluid_gen_type)81*/82void83fluid_mod_set_dest(fluid_mod_t *mod, int dest)84{85mod->dest = dest;86}8788/**89* Set the scale amount of a modulator.90*91* @param mod The modulator instance92* @param amount Scale amount to assign93*/94void95fluid_mod_set_amount(fluid_mod_t *mod, double amount)96{97mod->amount = (double) amount;98}99100/**101* Set the transform type of a modulator.102*103* @param mod The modulator instance104* @param type Transform type, see #fluid_mod_transforms105*/106void107fluid_mod_set_transform(fluid_mod_t *mod, int type)108{109unsigned char flag = (unsigned char) type;110if(flag != FLUID_MOD_TRANSFORM_LINEAR && flag != FLUID_MOD_TRANSFORM_ABS)111{112FLUID_LOG(FLUID_ERR, "fluid_mod_set_transform() called with invalid transform type %d", type);113return;114}115mod->trans = flag;116}117118/**119* Get the primary source value from a modulator.120*121* @param mod The modulator instance122* @return The primary source value (#fluid_mod_src or a MIDI CC controller value).123*/124int125fluid_mod_get_source1(const fluid_mod_t *mod)126{127return mod->src1;128}129130/**131* Get primary source flags from a modulator.132*133* @param mod The modulator instance134* @return The primary source flags (#fluid_mod_flags).135*/136int137fluid_mod_get_flags1(const fluid_mod_t *mod)138{139return mod->flags1;140}141142/**143* Get the secondary source value from a modulator.144*145* @param mod The modulator instance146* @return The secondary source value (#fluid_mod_src or a MIDI CC controller value).147*/148int149fluid_mod_get_source2(const fluid_mod_t *mod)150{151return mod->src2;152}153154/**155* Get secondary source flags from a modulator.156*157* @param mod The modulator instance158* @return The secondary source flags (#fluid_mod_flags).159*/160int161fluid_mod_get_flags2(const fluid_mod_t *mod)162{163return mod->flags2;164}165166/**167* Get destination effect from a modulator.168*169* @param mod The modulator instance170* @return Destination generator (#fluid_gen_type)171*/172int173fluid_mod_get_dest(const fluid_mod_t *mod)174{175return mod->dest;176}177178/**179* Get the scale amount from a modulator.180*181* @param mod The modulator instance182* @return Scale amount183*/184double185fluid_mod_get_amount(const fluid_mod_t *mod)186{187return (double) mod->amount;188}189190/**191* Get the transform type of a modulator.192*193* @param mod The modulator instance194* @param type Transform type, see #fluid_mod_transforms195*/196int197fluid_mod_get_transform(fluid_mod_t *mod)198{199return (int) mod->trans;200}201202/*203* retrieves the initial value from the given source of the modulator204*/205static fluid_real_t206fluid_mod_get_source_value(const unsigned char mod_src,207const unsigned char mod_flags,208fluid_real_t *range,209const fluid_voice_t *voice210)211{212const fluid_channel_t *chan = voice->channel;213fluid_real_t val;214215if(mod_flags & FLUID_MOD_CC)216{217val = fluid_channel_get_cc(chan, mod_src);218219if(mod_src == PORTAMENTO_CTRL)220{221// an invalid portamento fromkey should be treated as 0 when it's actually used for modulating222if(!fluid_channel_is_valid_note(val))223{224val = 0;225}226}227}228else229{230switch(mod_src)231{232case FLUID_MOD_NONE: /* SF 2.01 8.2.1 item 0: src enum=0 => value is 1 */233val = *range;234break;235236case FLUID_MOD_VELOCITY:237val = fluid_voice_get_actual_velocity(voice);238break;239240case FLUID_MOD_KEY:241val = fluid_voice_get_actual_key(voice);242break;243244case FLUID_MOD_KEYPRESSURE:245val = fluid_channel_get_key_pressure(chan, voice->key);246break;247248case FLUID_MOD_CHANNELPRESSURE:249val = fluid_channel_get_channel_pressure(chan);250break;251252case FLUID_MOD_PITCHWHEEL:253val = fluid_channel_get_pitch_bend(chan);254*range = 0x4000;255break;256257case FLUID_MOD_PITCHWHEELSENS:258val = fluid_channel_get_pitch_wheel_sensitivity(chan);259break;260261default:262FLUID_LOG(FLUID_ERR, "Unknown modulator source '%d', disabling modulator.", mod_src);263val = 0.0;264}265}266267return val;268}269270/**271* transforms the initial value retrieved by \c fluid_mod_get_source_value into [0.0;1.0]272*/273fluid_real_t274fluid_mod_transform_source_value(fluid_real_t val, unsigned char mod_flags, const fluid_real_t range)275{276/* normalized value, i.e. usually in the range [0;1] */277const fluid_real_t val_norm = val / range;278279/* we could also only switch case the lower nibble of mod_flags, however280* this would keep us from adding further mod types in the future281*282* instead just remove the flag(s) we already took care of283*/284mod_flags &= ~FLUID_MOD_CC;285286switch(mod_flags/* & 0x0f*/)287{288case FLUID_MOD_LINEAR | FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE: /* =0 */289val = val_norm;290break;291292case FLUID_MOD_LINEAR | FLUID_MOD_UNIPOLAR | FLUID_MOD_NEGATIVE: /* =1 */293val = 1.0f - val_norm;294break;295296case FLUID_MOD_LINEAR | FLUID_MOD_BIPOLAR | FLUID_MOD_POSITIVE: /* =2 */297val = -1.0f + 2.0f * val_norm;298break;299300case FLUID_MOD_LINEAR | FLUID_MOD_BIPOLAR | FLUID_MOD_NEGATIVE: /* =3 */301val = 1.0f - 2.0f * val_norm;302break;303304case FLUID_MOD_CONCAVE | FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE: /* =4 */305val = fluid_concave(127 * (val_norm));306break;307308case FLUID_MOD_CONCAVE | FLUID_MOD_UNIPOLAR | FLUID_MOD_NEGATIVE: /* =5 */309val = fluid_concave(127 * (1.0f - val_norm));310break;311312case FLUID_MOD_CONCAVE | FLUID_MOD_BIPOLAR | FLUID_MOD_POSITIVE: /* =6 */313val = (val_norm > 0.5f) ? fluid_concave(127 * 2 * (val_norm - 0.5f))314: -fluid_concave(127 * 2 * (0.5f - val_norm));315break;316317case FLUID_MOD_CONCAVE | FLUID_MOD_BIPOLAR | FLUID_MOD_NEGATIVE: /* =7 */318val = (val_norm > 0.5f) ? -fluid_concave(127 * 2 * (val_norm - 0.5f))319: fluid_concave(127 * 2 * (0.5f - val_norm));320break;321322case FLUID_MOD_CONVEX | FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE: /* =8 */323val = fluid_convex(127 * (val_norm));324break;325326case FLUID_MOD_CONVEX | FLUID_MOD_UNIPOLAR | FLUID_MOD_NEGATIVE: /* =9 */327val = fluid_convex(127 * (1.0f - val_norm));328break;329330case FLUID_MOD_CONVEX | FLUID_MOD_BIPOLAR | FLUID_MOD_POSITIVE: /* =10 */331val = (val_norm > 0.5f) ? fluid_convex(127 * 2 * (val_norm - 0.5f))332: -fluid_convex(127 * 2 * (0.5f - val_norm));333break;334335case FLUID_MOD_CONVEX | FLUID_MOD_BIPOLAR | FLUID_MOD_NEGATIVE: /* =11 */336val = (val_norm > 0.5f) ? -fluid_convex(127 * 2 * (val_norm - 0.5f))337: fluid_convex(127 * 2 * (0.5f - val_norm));338break;339340case FLUID_MOD_SWITCH | FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE: /* =12 */341val = (val_norm >= 0.5f) ? 1.0f : 0.0f;342break;343344case FLUID_MOD_SWITCH | FLUID_MOD_UNIPOLAR | FLUID_MOD_NEGATIVE: /* =13 */345val = (val_norm >= 0.5f) ? 0.0f : 1.0f;346break;347348case FLUID_MOD_SWITCH | FLUID_MOD_BIPOLAR | FLUID_MOD_POSITIVE: /* =14 */349val = (val_norm >= 0.5f) ? 1.0f : -1.0f;350break;351352case FLUID_MOD_SWITCH | FLUID_MOD_BIPOLAR | FLUID_MOD_NEGATIVE: /* =15 */353val = (val_norm >= 0.5f) ? -1.0f : 1.0f;354break;355356/*357* MIDI CCs only have a resolution of 7 bits. The closer val_norm gets to 1,358* the less will be the resulting change of the sinus. When using this sin()359* for scaling the cutoff frequency, there will be no audible difference between360* MIDI CCs 118 to 127. To avoid this waste of CCs multiply with 0.87361* (at least for unipolar) which makes sin() never get to 1.0 but to 0.98 which362* is close enough.363*/364case FLUID_MOD_SIN | FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE: /* custom sin(x) */365val = FLUID_SIN((FLUID_M_PI / 2.0f * 0.87f) * val_norm);366break;367368case FLUID_MOD_SIN | FLUID_MOD_UNIPOLAR | FLUID_MOD_NEGATIVE: /* custom */369val = FLUID_SIN((FLUID_M_PI / 2.0f * 0.87f) * (1.0f - val_norm));370break;371372case FLUID_MOD_SIN | FLUID_MOD_BIPOLAR | FLUID_MOD_POSITIVE: /* custom */373val = (val_norm > 0.5f) ? FLUID_SIN(FLUID_M_PI * (val_norm - 0.5f))374: -FLUID_SIN(FLUID_M_PI * (0.5f - val_norm));375break;376377case FLUID_MOD_SIN | FLUID_MOD_BIPOLAR | FLUID_MOD_NEGATIVE: /* custom */378val = (val_norm > 0.5f) ? -FLUID_SIN(FLUID_M_PI * (val_norm - 0.5f))379: FLUID_SIN(FLUID_M_PI * (0.5f - val_norm));380break;381382default:383FLUID_LOG(FLUID_ERR, "Unknown modulator type '%d', disabling modulator.", mod_flags);384val = 0.0f;385break;386}387388return val;389}390391/*392* fluid_mod_get_value.393* Computes and return modulator output following SF2.01394* (See SoundFont Modulator Controller Model Chapter 9.5).395*396* Output = Transform(Amount * Map(primary source input) * Map(secondary source input))397*398* Note:399* fluid_mod_get_value ignores the Transform operator. The result is:400*401* Output = Amount * Map(primary source input) * Map(secondary source input)402*/403fluid_real_t404fluid_mod_get_value(fluid_mod_t *mod, fluid_voice_t *voice)405{406extern fluid_mod_t default_vel2filter_mod;407408fluid_real_t v1, v2;409fluid_real_t final_value;410/* The wording of the default modulators refers to a range of 127/128.411* And the table in section 9.5.3 suggests, that this mapping should be applied412* to all unipolar and bipolar mappings respectively.413*414* Thinking about this further, this is actually pretty clever, as this is properly415* addresses MIDI Recommended Practice (RP-036) Default Pan Formula416* "Since MIDI controller values range from 0 to 127, the exact center417* of the range, 63.5, cannot be represented."418*419* When changing the overall range to 127/128 however, the "middle pan" value of 64420* can be correctly represented.421*/422fluid_real_t range1 = 128.0, range2 = 128.0;423424/* 'special treatment' for default controller425*426* Reference: SF2.01 section 8.4.2427*428* The GM default controller 'vel-to-filter cut off' is not clearly429* defined: If implemented according to the specs, the filter430* frequency jumps between vel=63 and vel=64. To maintain431* compatibility with existing sound fonts, the implementation is432* 'hardcoded', it is impossible to implement using only one433* modulator otherwise.434*435* I assume here, that the 'intention' of the paragraph is one436* octave (1200 cents) filter frequency shift between vel=127 and437* vel=64. 'amount' is (-2400), at least as long as the controller438* is set to default.439*440* Further, the 'appearance' of the modulator (source enumerator,441* destination enumerator, flags etc) is different from that442* described in section 8.4.2, but it matches the definition used in443* several SF2.1 sound fonts (where it is used only to turn it off).444* */445if(fluid_mod_test_identity(mod, &default_vel2filter_mod))446{447/*448if (voice->vel < 64){449return (fluid_real_t) mod->amount / 2.0;450} else {451return (fluid_real_t) mod->amount * (127 - voice->vel) / 127;452}453return (fluid_real_t) mod->amount / 2.0;454*/455// S. Christian Collins' mod, to stop forcing velocity based filtering456return 0;457}458459/* Get the initial value of the first source.460*461* Even if the src is FLUID_MOD_NONE, the value has to be transformed, see #1389462*/463v1 = fluid_mod_get_source_value(mod->src1, mod->flags1, &range1, voice);464465/* transform the input value */466v1 = fluid_mod_transform_source_value(v1, mod->flags1, range1);467468/* get the second input source */469v2 = fluid_mod_get_source_value(mod->src2, mod->flags2, &range2, voice);470471/* transform the second input value */472v2 = fluid_mod_transform_source_value(v2, mod->flags2, range2);473474/* it indeed is as simple as that: */475final_value = (fluid_real_t) mod->amount * v1 * v2;476477/* check for absolute value transform */478if(mod->trans == FLUID_MOD_TRANSFORM_ABS)479{480final_value = FLUID_FABS(final_value);481}482return final_value;483}484485/**486* Create a new uninitialized modulator structure.487*488* @return New allocated modulator or NULL if out of memory489*/490fluid_mod_t *491new_fluid_mod(void)492{493fluid_mod_t *mod = FLUID_NEW(fluid_mod_t);494495if(mod == NULL)496{497FLUID_LOG(FLUID_ERR, "Out of memory");498return NULL;499}500// for the sake of backward compatibility501mod->trans = FLUID_MOD_TRANSFORM_LINEAR;502return mod;503}504505/**506* Free a modulator structure.507*508* @param mod Modulator to free509*/510void511delete_fluid_mod(fluid_mod_t *mod)512{513FLUID_FREE(mod);514}515516/**517* Returns the size of the fluid_mod_t structure.518*519* @return Size of fluid_mod_t in bytes520*521* Useful in low latency scenarios e.g. to allocate a modulator on the stack.522*/523size_t fluid_mod_sizeof(void)524{525return sizeof(fluid_mod_t);526}527528/**529* Checks if modulator with source 1 other than CC is FLUID_MOD_NONE.530*531* @param mod, modulator.532* @return TRUE if modulator source 1 other than cc is FLUID_MOD_NONE, FALSE otherwise.533*/534static int535fluid_mod_is_src1_none(const fluid_mod_t *mod)536{537return(((mod->flags1 & FLUID_MOD_CC) == 0) && (mod->src1 == FLUID_MOD_NONE));538}539540/**541* Checks if modulators source other than CC source is invalid.542*543* @param mod, modulator.544* @param src1_select, source input selection to check.545* 1 to check src1 source.546* 0 to check src2 source.547* @return FALSE if selected modulator source other than cc is invalid, TRUE otherwise.548*549* (specs SF 2.01 7.4, 7.8, 8.2.1)550*/551static int552fluid_mod_check_non_cc_source(const fluid_mod_t *mod, unsigned char src1_select)553{554unsigned char flags, src;555556if(src1_select)557{558flags = mod->flags1;559src = mod->src1;560}561else562{563flags = mod->flags2;564src = mod->src2;565}566567return(((flags & FLUID_MOD_CC) != 0) /* src is a CC */568/* SF2.01 section 8.2.1: Constant value */569|| ((src == FLUID_MOD_NONE)570|| (src == FLUID_MOD_VELOCITY) /* Note-on velocity */571|| (src == FLUID_MOD_KEY) /* Note-on key number */572|| (src == FLUID_MOD_KEYPRESSURE) /* Poly pressure */573|| (src == FLUID_MOD_CHANNELPRESSURE) /* Channel pressure */574|| (src == FLUID_MOD_PITCHWHEEL) /* Pitch wheel */575|| (src == FLUID_MOD_PITCHWHEELSENS) /* Pitch wheel sensitivity */576));577}578579/**580* Checks if modulator CC source is invalid (specs SF 2.01 7.4, 7.8, 8.2.1).581*582* @param mod, modulator.583* @src1_select, source input selection:584* 1 to check src1 source or585* 0 to check src2 source.586* @return FALSE if selected modulator's source CC is invalid, TRUE otherwise.587*/588static int589fluid_mod_check_cc_source(const fluid_mod_t *mod, unsigned char src1_select)590{591unsigned char flags, src;592593if(src1_select)594{595flags = mod->flags1;596src = mod->src1;597}598else599{600flags = mod->flags2;601src = mod->src2;602}603604return(((flags & FLUID_MOD_CC) == 0) /* src is non CC */605|| ((src != BANK_SELECT_MSB)606&& (src != BANK_SELECT_LSB)607&& (src != DATA_ENTRY_MSB)608&& (src != DATA_ENTRY_LSB)609/* is src not NRPN_LSB, NRPN_MSB, RPN_LSB, RPN_MSB */610&& ((src < NRPN_LSB) || (RPN_MSB < src))611/* is src not ALL_SOUND_OFF, ALL_CTRL_OFF, LOCAL_CONTROL, ALL_NOTES_OFF ? */612/* is src not OMNI_OFF, OMNI_ON, POLY_OFF, POLY_ON ? */613&& (src < ALL_SOUND_OFF)614/* CC lsb shouldn't allowed to modulate (spec SF 2.01 - 8.2.1)615However, as long fluidsynth will use only CC 7 bits resolution,616it is safe to ignore these SF recommendations on CC receive.617See explanations in fluid_synth_cc_LOCAL() */618/* uncomment next line to forbid CC lsb */619/* && ((src < 32) || (63 < src)) */620));621}622623/**624* Checks valid modulator sources (specs SF 2.01 7.4, 7.8, 8.2.1)625*626* @param mod, modulator.627* @param name,if not NULL, pointer on a string displayed as a warning.628* @return TRUE if modulator sources src1, src2 are valid, FALSE otherwise.629*/630int fluid_mod_check_sources(const fluid_mod_t *mod, char *name)631{632static const char invalid_non_cc_src[] =633"Invalid modulator, using non-CC source %s.src%d=%d";634static const char invalid_cc_src[] =635"Invalid modulator, using CC source %s.src%d=%d";636static const char src1_is_none[] =637"Modulator with source 1 none %s.src1=%d";638639/* checks valid non cc sources */640if(!fluid_mod_check_non_cc_source(mod, 1)) /* check src1 */641{642if(name)643{644FLUID_LOG(FLUID_WARN, invalid_non_cc_src, name, 1, mod->src1);645}646647return FALSE;648}649650/*651When src1 is non CC source FLUID_MOD_NONE, the modulator is valid but652the output of this modulator will be forced to 0 at synthesis time.653Also this modulator cannot be used to overwrite a default modulator (as654there is no default modulator with src1 source equal to FLUID_MOD_NONE).655Consequently it is useful to return FALSE to indicate this modulator656being useless. It will be removed later with others invalid modulators.657*/658if(fluid_mod_is_src1_none(mod))659{660if(name)661{662FLUID_LOG(FLUID_WARN, src1_is_none, name, mod->src1);663}664665return FALSE;666}667668if(!fluid_mod_check_non_cc_source(mod, 0)) /* check src2 */669{670if(name)671{672FLUID_LOG(FLUID_WARN, invalid_non_cc_src, name, 2, mod->src2);673}674675return FALSE;676}677678/* checks valid cc sources */679if(!fluid_mod_check_cc_source(mod, 1)) /* check src1 */680{681if(name)682{683FLUID_LOG(FLUID_WARN, invalid_cc_src, name, 1, mod->src1);684}685686return FALSE;687}688689if(!fluid_mod_check_cc_source(mod, 0)) /* check src2 */690{691if(name)692{693FLUID_LOG(FLUID_WARN, invalid_cc_src, name, 2, mod->src2);694}695696return FALSE;697}698699return TRUE;700}701702/**703* Checks if two modulators are identical in sources, flags and destination.704*705* @param mod1 First modulator706* @param mod2 Second modulator707* @return TRUE if identical, FALSE otherwise708*709* SF2.01 section 9.5.1 page 69, 'bullet' 3 defines 'identical'.710*/711int712fluid_mod_test_identity(const fluid_mod_t *mod1, const fluid_mod_t *mod2)713{714return mod1->dest == mod2->dest715&& mod1->src1 == mod2->src1716&& mod1->src2 == mod2->src2717&& mod1->flags1 == mod2->flags1718&& mod1->flags2 == mod2->flags2;719}720721/**722* Check if the modulator has the given source.723*724* @param mod The modulator instance725* @param cc Boolean value indicating if ctrl is a CC controller or not726* @param ctrl The source to check for (if \c cc == FALSE : a value of type #fluid_mod_src, else the value of the MIDI CC to check for)727*728* @return TRUE if the modulator has the given source, FALSE otherwise.729*/730int fluid_mod_has_source(const fluid_mod_t *mod, int cc, int ctrl)731{732return733(734(735((mod->src1 == ctrl) && ((mod->flags1 & FLUID_MOD_CC) != 0) && (cc != 0))736|| ((mod->src1 == ctrl) && ((mod->flags1 & FLUID_MOD_CC) == 0) && (cc == 0))737)738||739(740((mod->src2 == ctrl) && ((mod->flags2 & FLUID_MOD_CC) != 0) && (cc != 0))741|| ((mod->src2 == ctrl) && ((mod->flags2 & FLUID_MOD_CC) == 0) && (cc == 0))742)743);744}745746/**747* Check if the modulator has the given destination.748*749* @param mod The modulator instance750* @param gen The destination generator of type #fluid_gen_type to check for751* @return TRUE if the modulator has the given destination, FALSE otherwise.752*/753int fluid_mod_has_dest(const fluid_mod_t *mod, int gen)754{755return mod->dest == gen;756}757758759/* debug function: Prints the contents of a modulator */760#ifdef DEBUG761void fluid_dump_modulator(fluid_mod_t *mod)762{763int src1 = mod->src1;764int dest = mod->dest;765int src2 = mod->src2;766int flags1 = mod->flags1;767int flags2 = mod->flags2;768fluid_real_t amount = (fluid_real_t)mod->amount;769770printf("Src: ");771772if(flags1 & FLUID_MOD_CC)773{774printf("MIDI CC=%i", src1);775}776else777{778switch(src1)779{780case FLUID_MOD_NONE:781printf("None");782break;783784case FLUID_MOD_VELOCITY:785printf("note-on velocity");786break;787788case FLUID_MOD_KEY:789printf("Key nr");790break;791792case FLUID_MOD_KEYPRESSURE:793printf("Poly pressure");794break;795796case FLUID_MOD_CHANNELPRESSURE:797printf("Chan pressure");798break;799800case FLUID_MOD_PITCHWHEEL:801printf("Pitch Wheel");802break;803804case FLUID_MOD_PITCHWHEELSENS:805printf("Pitch Wheel sens");806break;807808default:809printf("(unknown: %i)", src1);810}; /* switch src1 */811}; /* if not CC */812813if(flags1 & FLUID_MOD_NEGATIVE)814{815printf("- ");816}817else818{819printf("+ ");820};821822if(flags1 & FLUID_MOD_BIPOLAR)823{824printf("bip ");825}826else827{828printf("unip ");829};830831printf("-> ");832833switch(dest)834{835case GEN_FILTERQ:836printf("Q");837break;838839case GEN_FILTERFC:840printf("fc");841break;842843case GEN_CUSTOM_FILTERQ:844printf("custom-Q");845break;846847case GEN_CUSTOM_FILTERFC:848printf("custom-fc");849break;850851case GEN_VIBLFOTOPITCH:852printf("VibLFO-to-pitch");853break;854855case GEN_MODENVTOPITCH:856printf("ModEnv-to-pitch");857break;858859case GEN_MODLFOTOPITCH:860printf("ModLFO-to-pitch");861break;862863case GEN_CHORUSSEND:864printf("Chorus send");865break;866867case GEN_REVERBSEND:868printf("Reverb send");869break;870871case GEN_PAN:872printf("pan");873break;874875case GEN_CUSTOM_BALANCE:876printf("balance");877break;878879case GEN_ATTENUATION:880printf("att");881break;882883default:884printf("dest %i", dest);885}; /* switch dest */886887printf(", amount %f flags %i src2 %i flags2 %i\n", amount, flags1, src2, flags2);888};889#endif890891892