/*!

\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

*/
