Hi Guys, I'm looking at doing some PortAudio / Native Jack builds of fluidsynth for Miditzer (main code from 2006/2007) but their fluidsynth.dll contains the following patch, which was added in order to use an in-app MIDI file player.
I have just found this in an archive, so have not even attempted anything with it yet. Would anyone else be able to look at it, and what are the chances of being able to add this to 1.1.7? Kind regards, GrahamG Re: [fluid-dev] Some questions about Midi playback From: Sebastien Frippiat Subject: Re: [fluid-dev] Some questions about Midi playback Date: Mon, 26 Jun 2006 14:27:16 +0200 User-agent: Mozilla Thunderbird 1.0.7-1.4.1 (X11/20050929) Hi again ! I finally managed to do what I wanted to do without too much trouble. I know it is not the main purpose of fluidsynth but as I don't think of my modifications are a ugly hack, I post my code here. In fact, it add three functions : - fluid_player_set_tempo_multiplier : multiply all received tempo by a specified multiplier (useful for accelerating / slowing down music playback) - fluid_player_set_velocity_multiplier : multiply all notes velocities by a specified multiplier (useful for decreasing volume) - fluid_player_set_midi_event_callback : call a specified callback function on any Midi event and that function can specify whether FluidSynth must forward the event to the synthesizer I wrote the first two functions last week and I posted the code on the mailing list. For a quick explanation, I needed these functions to be able to play some sort of adaptive music (like with DirectMusic for those who know it). The last one is in fact to redirect the Midi events to a real Midi device. There seems to be something planned/ (?) in FluidSynth about routers which would allow to apply filters to Midi events. However, like said in the docs, it is only used for Midi inputs (and not Midi file playback). With my modification, if you specify a callback and tell FluidSynth to forward the event to the synthesizer, you'll be able to provide feedback to the user of your application (add events to log, show visual feedback like notes being played...). I personnaly used it to prevent FluidSynth playing the Midi file and processed the event myself. It allows me to load a .mid file and to play it without having to take care of timing as fluidsynth already do it. Even if it is not the main purpose of this lib, I do think that my code is quite clean (I also wrote the Doxygen documentation to the functions) and that it could be useful to some other people. Anyway, whether you want it or not... here it is, attached to my post. You need to copy the patch file into the fluidsynth directory and run "patch -u -i fluidsynth_modifs.patch -p 1". Then copy the mididefs.h file in the include/fluidsynth.h directory. I had to add it because the _fluid_midi_event_t was defined in src/fluid_midi.h but not in include/fluidsynth/midi.h and it was not accessible to users of the library. Thanks for your advices, Sebastien Frippiat diff -r -u fluidsynth-1.0.7/include/fluidsynth/midi.h fluidsynth-1.0.7-new/include/fluidsynth/midi.h --- fluidsynth-1.0.7/include/fluidsynth/midi.h Tue Mar 11 17:57:01 2003 +++ fluidsynth-1.0.7-new/include/fluidsynth/midi.h Mon Jun 26 12:49:59 2006 @@ -47,7 +47,6 @@ FLUIDSYNTH_API int fluid_midi_event_get_pitch(fluid_midi_event_t* evt); FLUIDSYNTH_API int fluid_midi_event_set_pitch(fluid_midi_event_t* evt, int val); - /* Generic callback function for MIDI events. * Will be used between * - MIDI driver and MIDI router @@ -126,6 +125,52 @@ FLUIDSYNTH_API int fluid_player_set_loop(fluid_player_t* player, int loop); FLUIDSYNTH_API int fluid_player_set_midi_tempo(fluid_player_t* player, int tempo); FLUIDSYNTH_API int fluid_player_set_bpm(fluid_player_t* player, int bpm); + + +/** \brief Set a multiplier value to be applied to all tempo events + * + * \param player Pointer to player to be modified + * \param multiplier Multiplier to be applied : + * - multiplier in ]0,1] : increase tempo + * - multiplier o, ]1,oo] : decrease tempo + * \return One of these : + * - 0 on success + * - -1 if multiplier is <= 0 + */ +FLUIDSYNTH_API int fluid_player_set_tempo_multiplier(fluid_player_t* player, float multiplier); + +/** \brief Set a multiplier value to be applied to all velocities specified in NOTE_ON events + * + * \param player Pointer to player to be modified + * \param multiplier Multiplier to be applied, must be in ]0,1] (can only decrease velocity) + * \return One of these : + * - 0 on success + * - -1 if multiplier is not in ]0,1] + */ +FLUIDSYNTH_API int fluid_player_set_velocity_multiplier(fluid_player_t* player, float multiplier); + +/** \brief Set a callback to be called each time a Midi event is generated + * + * \param player Pointer to player to be modified + * \param callback Pointer to callback function + * \return Always 0 + * + * The callback function should return 0 if it wants the FluidSynth synthetizer to play the sound and + * it should return -1 if it wants to inhibit the FluidSynth synthetizer. For example, in the first case, + * it can be used as a logger and in the other one it can be used to process the Midi events by yourself + * and send them to a Midi device). + * + * Here is a sample callback function which inhibits the FluidSynth software synthetizer and output + * Midi events to a Midi device (pMidiOStream is a PortMidi output stream) : + * <PRE> + int myCallback (void* data, fluid_midi_event_t* event) + { + Pm_WriteShort(pMidiOStream, 0, Pm_Message(event->type | event->channel, event->param1, event->param2)); + return -1; + } + </PRE> + */ +FLUIDSYNTH_API int fluid_player_set_midi_event_callback(fluid_player_t* player, handle_midi_event_func_t callback); #ifdef __cplusplus } Only in fluidsynth-1.0.7-new/include/fluidsynth: mididefs.h diff -r -u fluidsynth-1.0.7/src/fluid_midi.c fluidsynth-1.0.7-new/src/fluid_midi.c --- fluidsynth-1.0.7/src/fluid_midi.c Mon Mar 29 12:05:17 2004 +++ fluidsynth-1.0.7-new/src/fluid_midi.c Mon Jun 26 12:49:59 2006 @@ -1072,6 +1072,13 @@ player->send_program_change = 1; player->miditempo = 480000; player->deltatime = 4.0; + + player->tempo_multiplier = 1.0f; + player->tempo_last_multiplier = 1.0f; + player->tempo_last_value = player->miditempo; + player->velocity_multiplier = 1.0f; + player->midi_event_callback = NULL; + return player; } @@ -1107,9 +1114,39 @@ player->send_program_change = 1; player->miditempo = 480000; player->deltatime = 4.0; + return 0; } +int fluid_player_set_tempo_multiplier(fluid_player_t* player, float multiplier) +{ + if (multiplier < 0) { + return -1; + } + + player->tempo_multiplier = multiplier; + + return 0; +} + +int fluid_player_set_velocity_multiplier(fluid_player_t* player, float multiplier) +{ + if ((multiplier <= 0) || (multiplier > 1)) { + return -1; + } + + player->velocity_multiplier = multiplier; + + return 0; +} + +int fluid_player_set_midi_event_callback(fluid_player_t* player, handle_midi_event_func_t callback) +{ + player->midi_event_callback = callback; + + return 0; +} + /* * fluid_player_add_track */ @@ -1487,37 +1524,69 @@ */ int fluid_midi_send_event(fluid_synth_t* synth, fluid_player_t* player, fluid_midi_event_t* event) { + fluid_midi_event_t callbackEvent; + + /* handle tempo multiplier modification */ + if (player != NULL) { + if (player->tempo_last_multiplier != player->tempo_multiplier) { + if (fluid_player_set_midi_tempo(player, player->tempo_last_value * player->tempo_multiplier) != FLUID_OK) { + return FLUID_FAILED; + } + player->tempo_last_multiplier = player->tempo_multiplier; + } + + memcpy(&callbackEvent, event, sizeof(fluid_midi_event_t)); + } + + /* handle event */ switch (event->type) { case NOTE_ON: - if (fluid_synth_noteon(synth, event->channel, event->param1, event->param2) != FLUID_OK) { - return FLUID_FAILED; + callbackEvent.param2 *= player->velocity_multiplier; + if ((player->midi_event_callback != NULL) && (player->midi_event_callback(NULL, &callbackEvent) == 0)) { + if (fluid_synth_noteon(synth, event->channel, event->param1, event->param2 * player->velocity_multiplier) != FLUID_OK) { + return FLUID_FAILED; + } } break; case NOTE_OFF: - if (fluid_synth_noteoff(synth, event->channel, event->param1) != FLUID_OK) { - return FLUID_FAILED; + if ((player->midi_event_callback != NULL) && (player->midi_event_callback(NULL, &callbackEvent) == 0)) { + if (fluid_synth_noteoff(synth, event->channel, event->param1) != FLUID_OK) { + return FLUID_FAILED; + } } break; case CONTROL_CHANGE: - if (fluid_synth_cc(synth, event->channel, event->param1, event->param2) != FLUID_OK) { - return FLUID_FAILED; + if ((player->midi_event_callback != NULL) && (player->midi_event_callback(NULL, &callbackEvent) == 0)) { + if (fluid_synth_cc(synth, event->channel, event->param1, event->param2) != FLUID_OK) { + return FLUID_FAILED; + } } break; case MIDI_SET_TEMPO: if (player != NULL) { - if (fluid_player_set_midi_tempo(player, event->param1) != FLUID_OK) { - return FLUID_FAILED; + callbackEvent.param1 *= player->tempo_multiplier; + if (player->midi_event_callback != NULL) { + player->midi_event_callback(NULL, &callbackEvent); + } + if (fluid_player_set_midi_tempo(player, event->param1 * player->tempo_multiplier) != FLUID_OK) { + return FLUID_FAILED; } + player->tempo_last_value = event->param1; + player->tempo_last_multiplier = player->tempo_multiplier; } break; case PROGRAM_CHANGE: - if (fluid_synth_program_change(synth, event->channel, event->param1) != FLUID_OK) { - return FLUID_FAILED; + if ((player->midi_event_callback != NULL) && (player->midi_event_callback(NULL, &callbackEvent) == 0)) { + if (fluid_synth_program_change(synth, event->channel, event->param1) != FLUID_OK) { + return FLUID_FAILED; + } } break; case PITCH_BEND: - if (fluid_synth_pitch_bend(synth, event->channel, event->param1) != FLUID_OK) { - return FLUID_FAILED; + if ((player->midi_event_callback != NULL) && (player->midi_event_callback(NULL, &callbackEvent) == 0)) { + if (fluid_synth_pitch_bend(synth, event->channel, event->param1) != FLUID_OK) { + return FLUID_FAILED; + } } break; default: diff -r -u fluidsynth-1.0.7/src/fluid_midi.h fluidsynth-1.0.7-new/src/fluid_midi.h --- fluidsynth-1.0.7/src/fluid_midi.h Mon Mar 29 12:05:18 2004 +++ fluidsynth-1.0.7-new/src/fluid_midi.h Mon Jun 26 12:49:59 2006 @@ -251,6 +251,13 @@ int miditempo; /* as indicated by MIDI SetTempo: n 24th of a usec per midi-clock. bravo! */ double deltatime; /* milliseconds per midi tick. depends on set-tempo */ unsigned int division; + + float tempo_multiplier; /* all tempo events will be multiplied by this one (if > 1, will slow down the play) */ + float tempo_last_multiplier; + float tempo_last_value; + float velocity_multiplier;/* all velocities will be multiplied by this one (must be in ]0,1]) */ + + handle_midi_event_func_t midi_event_callback; /* customized function for handling Midi events */ }; int fluid_player_add_track(fluid_player_t* player, fluid_track_t* track); /* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public License * as published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307, USA */ /* This file should be included by anyone willing to use a callback function to handle Midi events. */ #ifndef _FLUIDSYNTH_MIDIDEFS_H #define _FLUIDSYNTH_MIDIDEFS_H #ifdef __cplusplus extern "C" { #endif /* * fluid_midi_event_t */ struct _fluid_midi_event_t { fluid_midi_event_t* next; /* Don't use it, it will dissappear. Used in midi tracks. */ unsigned int dtime; /* Delay (ticks) between this and previous event. midi tracks. */ unsigned char type; /* MIDI event type */ unsigned char channel; /* MIDI channel */ unsigned int param1; /* First parameter */ unsigned int param2; /* Second parameter */ }; #ifdef __cplusplus } #endif #endif /* _FLUIDSYNTH_MIDIDEFS_H */ _______________________________________________ fluid-dev mailing list fluid-dev@nongnu.org https://lists.nongnu.org/mailman/listinfo/fluid-dev