mirror of
https://github.com/ZDoom/fluidsynth.git
synced 2024-11-27 14:32:12 +00:00
142 lines
4.5 KiB
Text
142 lines
4.5 KiB
Text
/*!
|
|
|
|
\page Sequencer Using the MIDI sequencer
|
|
|
|
FluidSynth's sequencer can be used to play MIDI events in a more flexible way
|
|
than using the MIDI file player, which expects the events to be stored as
|
|
Standard MIDI Files. Using the sequencer, you can provide the events one by
|
|
one, with an optional timestamp for scheduling.
|
|
|
|
The client program should first initialize the sequencer instance using the
|
|
function new_fluid_sequencer2(). There is a complementary function
|
|
delete_fluid_sequencer() to delete it. After creating the sequencer instance,
|
|
the destinations can be registered using
|
|
fluid_sequencer_register_fluidsynth() for the synthesizer destination, and
|
|
optionally using fluid_sequencer_register_client() for the client destination
|
|
providing a suitable callback function. It can be unregistered using
|
|
fluid_sequencer_unregister_client(). After the initialization, events can be
|
|
sent with fluid_sequencer_send_now() and scheduled to the future with
|
|
fluid_sequencer_send_at(). The registration functions return identifiers,
|
|
that can be used as destinations of an event using fluid_event_set_dest().
|
|
|
|
The function fluid_sequencer_get_tick() returns the current playing position.
|
|
A program may choose a new timescale in milliseconds using
|
|
fluid_sequencer_set_time_scale().
|
|
|
|
The following example uses the fluidsynth sequencer to implement a sort of
|
|
music box. FluidSynth internal clock is used to schedule repetitive sequences
|
|
of notes. The next sequence is scheduled on advance before the end of the
|
|
current one, using a timer event that triggers a callback function. The
|
|
scheduling times are always absolute values, to avoid slippage.
|
|
|
|
\code
|
|
#include "fluidsynth.h"
|
|
|
|
fluid_synth_t* synth;
|
|
fluid_audio_driver_t* adriver;
|
|
fluid_sequencer_t* sequencer;
|
|
short synthSeqID, mySeqID;
|
|
unsigned int now;
|
|
unsigned int seqduration;
|
|
|
|
// prototype
|
|
void seq_callback(unsigned int time, fluid_event_t* event, fluid_sequencer_t* seq, void* data);
|
|
|
|
void createsynth()
|
|
{
|
|
fluid_settings_t* settings;
|
|
settings = new_fluid_settings();
|
|
fluid_settings_setint(settings, "synth.reverb.active", 0);
|
|
fluid_settings_setint(settings, "synth.chorus.active", 0);
|
|
synth = new_fluid_synth(settings);
|
|
adriver = new_fluid_audio_driver(settings, synth);
|
|
sequencer = new_fluid_sequencer2(0);
|
|
|
|
// register synth as first destination
|
|
synthSeqID = fluid_sequencer_register_fluidsynth(sequencer, synth);
|
|
|
|
// register myself as second destination
|
|
mySeqID = fluid_sequencer_register_client(sequencer, "me", seq_callback, NULL);
|
|
|
|
// the sequence duration, in ms
|
|
seqduration = 1000;
|
|
}
|
|
|
|
void deletesynth()
|
|
{
|
|
delete_fluid_sequencer(sequencer);
|
|
delete_fluid_audio_driver(adriver);
|
|
delete_fluid_synth(synth);
|
|
}
|
|
|
|
void loadsoundfont()
|
|
{
|
|
int fluid_res;
|
|
// put your own path here
|
|
fluid_res = fluid_synth_sfload(synth, "Inside:VintageDreamsWaves-v2.sf2", 1);
|
|
}
|
|
|
|
void sendnoteon(int chan, short key, unsigned int date)
|
|
{
|
|
int fluid_res;
|
|
fluid_event_t *evt = new_fluid_event();
|
|
fluid_event_set_source(evt, -1);
|
|
fluid_event_set_dest(evt, synthSeqID);
|
|
fluid_event_noteon(evt, chan, key, 127);
|
|
fluid_res = fluid_sequencer_send_at(sequencer, evt, date, 1);
|
|
delete_fluid_event(evt);
|
|
}
|
|
|
|
void schedule_next_callback()
|
|
{
|
|
int fluid_res;
|
|
// I want to be called back before the end of the next sequence
|
|
unsigned int callbackdate = now + seqduration/2;
|
|
fluid_event_t *evt = new_fluid_event();
|
|
fluid_event_set_source(evt, -1);
|
|
fluid_event_set_dest(evt, mySeqID);
|
|
fluid_event_timer(evt, NULL);
|
|
fluid_res = fluid_sequencer_send_at(sequencer, evt, callbackdate, 1);
|
|
delete_fluid_event(evt);
|
|
}
|
|
|
|
void schedule_next_sequence() {
|
|
// Called more or less before each sequence start
|
|
// the next sequence start date
|
|
now = now + seqduration;
|
|
|
|
// the sequence to play
|
|
|
|
// the beat : 2 beats per sequence
|
|
sendnoteon(0, 60, now + seqduration/2);
|
|
sendnoteon(0, 60, now + seqduration);
|
|
|
|
// melody
|
|
sendnoteon(1, 45, now + seqduration/10);
|
|
sendnoteon(1, 50, now + 4*seqduration/10);
|
|
sendnoteon(1, 55, now + 8*seqduration/10);
|
|
|
|
// so that we are called back early enough to schedule the next sequence
|
|
schedule_next_callback();
|
|
}
|
|
|
|
/* sequencer callback */
|
|
void seq_callback(unsigned int time, fluid_event_t* event, fluid_sequencer_t* seq, void* data) {
|
|
schedule_next_sequence();
|
|
}
|
|
|
|
int main(void) {
|
|
createsynth();
|
|
loadsoundfont();
|
|
|
|
// initialize our absolute date
|
|
now = fluid_sequencer_get_tick(sequencer);
|
|
schedule_next_sequence();
|
|
|
|
sleep(100000);
|
|
deletesynth();
|
|
return 0;
|
|
}
|
|
\endcode
|
|
|
|
*/
|