Re: [LAD] Writing a music notation editor and need some help with MIDI, Jack, and FluidSynth

From: M Donalies <ingeniousnebbish@email-addr-hidden>
Date: Mon Dec 24 2012 - 02:02:02 EET

Here's my first attempt. I figured I'd start with an alsa client that outlines
the basics of what I want it to do. Once I get this, I'll try making the jack
connections from within my program. For now, I'm using QJackCtl and QSynth to
get the others going and make the connection.

The abbreviated C++ code follows. I edited out command line and i/o stuff.
After reading a data file, the vector notes holds all the relevant stuff. If
anyone's interested, I can post the entire code and the sample data file.

The first thing I don't understand is in scheduleEvents() why passing absolute
times to the queue doesn't work. And once I have absolute times, how do I do
the play/pause in the transport loop in main().

// Trivial sequencer that reads note data from a file and plays the contents.

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <cstdlib>
#include <alsa/asoundlib.h>

using namespace std;

enum TransportState {Stopped = 0, Paused, Playing};

// Struct holding note start time, pitch, duration, volume.
// time and duration are in ticks.
struct NoteStruct {int t; int p; int d; int v;};

vector<NoteStruct> notes;

// Usage message and exit.
void usage(string msg);

// Exit with error message.
void die(string msg);

// Create a new client.
snd_seq_t *openClient()
{
        snd_seq_t *seq;
        int ret = snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, 0);
        if (ret < 0) die("Failed to open sequencer client.");
        snd_seq_set_client_name(seq, "AJDemo Client");
        return seq;
}

// Create a new output (read) port and return the port id.
int createOutputPort(snd_seq_t *seq)
{
        int id = snd_seq_create_simple_port(seq, "AJDemo Port",
                SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ,
                SND_SEQ_PORT_TYPE_MIDI_GENERIC);
        if (id < 0) die("Failed to create output port.");
        return id;
}

void setTempo(snd_seq_t *seq, int q, int bpm, int ppq)
{
        cout << "Setting tempo, bpm = " << bpm << ", ppq = " << ppq << endl;
        int microsec = 60 * 1000000 / bpm;
        snd_seq_queue_tempo_t *tempo;
        snd_seq_queue_tempo_alloca(&tempo);
        snd_seq_queue_tempo_set_tempo(tempo, microsec);
        snd_seq_queue_tempo_set_ppq(tempo, ppq);
        snd_seq_set_queue_tempo(seq, q, tempo);
}

// Put contents of notes on the queue as MIDI events.
void scheduleEvents(snd_seq_t *seq, int port, int q)
{
        cout << "Scheduling events.\n";
        int delta = 0;
        int t = 0; // abs time
        int prevT = 0;
        snd_seq_event_t ev;
        for (int i=0; i<notes.size(); i++)
        {
                t = notes[i].t;
                delta = t - prevT;
                snd_seq_ev_clear(&ev);
                snd_seq_ev_set_source(&ev, port);
                snd_seq_ev_set_subs(&ev);
                snd_seq_ev_schedule_tick(&ev, q, SND_SEQ_TIME_MODE_REL, delta);
// snd_seq_ev_schedule_tick(&ev, q, 0, t);
                snd_seq_ev_set_note(&ev, 1, notes[i].p, notes[i].v, notes[i].d);
                snd_seq_event_output(seq, &ev);
                t = prevT;
        }
}

void parseCmdLine(int argc, char *argv[], int &bpm, string &fname);

// Read file fname and put resulting note data into notes.
void readFile(string fname);

// Set queue position to time t (in ticks).
void setQueuePos(snd_seq_t *seq, int port, int q, int t)
{
        snd_seq_event_t ev;
        snd_seq_ev_clear(&ev);
        snd_seq_ev_set_source(&ev, port);
        snd_seq_ev_set_subs(&ev);
        snd_seq_ev_set_queue_pos_tick(&ev, q, 0);
}

int main(int argc, char *argv[])
{
        string fname = "";
        int bpm = 120;
        int ppq = 96;
        parseCmdLine(argc, argv, bpm, fname);
        readFile(fname);
        
        snd_seq_t *seq = openClient();
        
        int clientId = snd_seq_client_id(seq);
        int portId = createOutputPort(seq);
        
        int qId = snd_seq_alloc_named_queue(seq, "AJDemo Queue");
        setTempo(seq, qId, bpm, ppq);
        snd_seq_start_queue(seq, qId, NULL);
        snd_seq_drain_output(seq);

        cout << "Press p to play/pause, s to stop, q to exit.\n";
        TransportState state = Stopped;
        while (true)
        {
                char c;
                cin >> c;
                if (c == 'q') break;
                else if (c == 'p')
                {
                        if (state == Playing)
                        {
                                cout << "Pausing.\n";
                                state = Paused;
                                snd_seq_stop_queue(seq, qId, NULL);
                        }
                        else if (state == Paused)
                        {
                                cout << "Resuming playing.\n";
                                state = Playing;
                                setQueuePos(seq, portId, qId, 0);
                                snd_seq_continue_queue(seq, qId, NULL);
                        }
                        else
                        {
                                cout << "Playing.\n";
                                state = Playing;
                                snd_seq_start_queue(seq, qId, NULL);
                                scheduleEvents(seq, portId, qId);
                        }
                }
                else if (c == 's')
                {
                        cout << "Stopping.\n";
                        state = Stopped;
                        snd_seq_drop_input(seq);
                        snd_seq_drop_output(seq);
                        snd_seq_stop_queue(seq, qId, NULL);
                }

                snd_seq_drain_output(seq);
        }
        snd_seq_free_queue(seq, qId);
        return 0;
}

-- 
7:8
_______________________________________________
Linux-audio-dev mailing list
Linux-audio-dev@email-addr-hidden
http://lists.linuxaudio.org/listinfo/linux-audio-dev
Received on Mon Dec 24 04:15:04 2012

This archive was generated by hypermail 2.1.8 : Mon Dec 24 2012 - 04:15:04 EET