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

M Donalies ingeniousnebbish at cox.net
Mon Dec 24 00:02:02 UTC 2012


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



More information about the Linux-audio-dev mailing list