fluid-dev
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

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 wheter FluidSynth must forward the event to the synthetizer

I wrote the first two functions last week and I posted the code on the mailing list. For a quick explaination, I needed these functions to be able to play some sort of adaptative 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 synthetizer, 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 */

reply via email to

[Prev in Thread] Current Thread [Next in Thread]