Add optional per tick callback to player (#780)

Co-authored-by: Arthur Chaloin <arthur.chaloin@gmail.com>
Co-authored-by: derselbst <tom.mbrt@googlemail.com>
This commit is contained in:
Arthur Chaloin 2021-02-26 21:05:31 +01:00 committed by GitHub
parent 04c30be0b9
commit 13185d32b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 70 additions and 0 deletions

View File

@ -56,6 +56,43 @@ extern "C" {
* a \ref sequencer via fluid_sequencer_add_midi_event_to_buffer().
*/
typedef int (*handle_midi_event_func_t)(void *data, fluid_midi_event_t *event);
/**
* Generic callback function fired once by MIDI tick change.
*
* @param data User defined data pointer
* @param tick The current (zero-based) tick, which triggered the callback
* @return Should return #FLUID_OK on success, #FLUID_FAILED otherwise
*
* This callback is fired at a constant rate depending on the current BPM and PPQ.
* e.g. for PPQ = 192 and BPM = 140 the callback is fired 192 * 140 times per minute (448/sec).
*
* It can be used to sync external elements with the beat,
* or stop / loop the song on a given tick.
* Ticks being BPM-dependent, you can manipulate values such as bars or beats,
* without having to care about BPM.
*
* For example, this callback loops the song whenever it reaches the 5th bar :
*
* @code{.cpp}
int handle_tick(void *data, int tick)
{
fluid_player_t *player = (fluid_player_t *)data;
int ppq = 192; // From MIDI header
int beatsPerBar = 4; // From the song's time signature
int loopBar = 5;
int loopTick = (loopBar - 1) * ppq * beatsPerBar;
if (tick == loopTick)
{
return fluid_player_seek(player, 0);
}
return FLUID_OK;
}
* @endcode
*/
typedef int (*handle_midi_tick_func_t)(void *data, int tick);
/* @} */
/**
@ -239,6 +276,7 @@ FLUIDSYNTH_API int fluid_player_set_tempo(fluid_player_t *player, int tempo_type
FLUID_DEPRECATED FLUIDSYNTH_API int fluid_player_set_midi_tempo(fluid_player_t *player, int tempo);
FLUID_DEPRECATED FLUIDSYNTH_API int fluid_player_set_bpm(fluid_player_t *player, int bpm);
FLUIDSYNTH_API int fluid_player_set_playback_callback(fluid_player_t *player, handle_midi_event_func_t handler, void *handler_data);
FLUIDSYNTH_API int fluid_player_set_tick_callback(fluid_player_t *player, handle_midi_tick_func_t handler, void *handler_data);
FLUIDSYNTH_API int fluid_player_get_status(fluid_player_t *player);
FLUIDSYNTH_API int fluid_player_get_current_tick(fluid_player_t *player);

View File

@ -1677,8 +1677,10 @@ new_fluid_player(fluid_synth_t *synth)
player->deltatime = 4.0;
player->cur_msec = 0;
player->cur_ticks = 0;
player->last_callback_ticks = -1;
fluid_atomic_int_set(&player->seek_ticks, -1);
fluid_player_set_playback_callback(player, fluid_synth_handle_midi_event, synth);
fluid_player_set_tick_callback(player, NULL, NULL);
player->use_system_timer = fluid_settings_str_equal(synth->settings,
"player.timing-source", "system");
if(player->use_system_timer)
@ -1833,6 +1835,28 @@ fluid_player_set_playback_callback(fluid_player_t *player,
return FLUID_OK;
}
/**
* Add a listener function for every MIDI tick change.
*
* @param player MIDI player instance
* @param handler Pointer to callback function
* @param handler_data Opaque parameter to be sent to the callback function
* @returns #FLUID_OK
*
* This callback is not set by default, but can optionally
* be changed to a user-defined function for intercepting all MIDI
* tick changes and react to them with precision.
*
* @since 2.2.0
*/
int
fluid_player_set_tick_callback(fluid_player_t *player, handle_midi_tick_func_t handler, void *handler_data)
{
player->tick_callback = handler;
player->tick_userdata = handler_data;
return FLUID_OK;
}
/**
* Add a MIDI file to a player queue.
* @param player MIDI player instance
@ -2122,6 +2146,11 @@ fluid_player_callback(void *data, unsigned int msec)
__LINE__, (msec - player->begin_msec) / 1000.0);
loadnextfile = 1;
}
if (player->tick_callback != NULL && player->last_callback_ticks != player->cur_ticks) {
player->tick_callback(player->tick_userdata, player->cur_ticks);
player->last_callback_ticks = player->cur_ticks;
}
}
while(loadnextfile);

View File

@ -297,6 +297,7 @@ struct _fluid_player_t
fluid_atomic_int_t seek_ticks; /* new position in tempo ticks (midi ticks) for seeking */
int start_ticks; /* the number of tempo ticks passed at the last tempo change */
int cur_ticks; /* the number of tempo ticks passed */
int last_callback_ticks; /* the last tick number that was passed to player->tick_callback */
int begin_msec; /* the time (msec) of the beginning of the file */
int start_msec; /* the start time of the last tempo change */
int cur_msec; /* the current time */
@ -318,6 +319,8 @@ struct _fluid_player_t
handle_midi_event_func_t playback_callback; /* function fired on each midi event as it is played */
void *playback_userdata; /* pointer to user-defined data passed to playback_callback function */
handle_midi_tick_func_t tick_callback; /* function fired on each tick change */
void *tick_userdata; /* pointer to user-defined data passed to tick_callback function */
};
void fluid_player_settings(fluid_settings_t *settings);