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-devReceived 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