aboutsummaryrefslogtreecommitdiffstats
path: root/src/engine/SMFDriver.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine/SMFDriver.cpp')
-rw-r--r--src/engine/SMFDriver.cpp136
1 files changed, 114 insertions, 22 deletions
diff --git a/src/engine/SMFDriver.cpp b/src/engine/SMFDriver.cpp
index e14e04c..ff55e00 100644
--- a/src/engine/SMFDriver.cpp
+++ b/src/engine/SMFDriver.cpp
@@ -96,6 +96,54 @@ SMFDriver::learn(const string& filename, double q, Raul::BeatTime max_duration)
}
+bool
+SMFDriver::is_delay_node(SharedPtr<Node> node) const
+{
+ if (node->enter_action() || node->exit_action())
+ return false;
+ else
+ return true;
+}
+
+
+/** Connect two nodes, inserting a delay node between them if necessary.
+ *
+ * If a delay node is added to the machine, it is returned.
+ */
+SharedPtr<Node>
+SMFDriver::connect_nodes(SharedPtr<Machine> m,
+ SharedPtr<Node> tail, Raul::BeatTime tail_end_time,
+ SharedPtr<Node> head, Raul::BeatTime head_start_time)
+{
+ assert(tail != head);
+ assert(head_start_time >= tail_end_time);
+
+ SharedPtr<Node> delay_node;
+
+ cout << "Connect nodes durations: " << tail->duration() << " .. " << head->duration() << endl;
+ cout << "Connect nodes times: " << tail_end_time << " .. " << head_start_time << endl;
+
+ if (head_start_time == tail_end_time) {
+ // Connect directly
+ tail->add_outgoing_edge(SharedPtr<Edge>(new Edge(tail, head)));
+ } else if (is_delay_node(tail)) {
+ // Tail is a delay node, just accumulate the time difference into it
+ tail->set_duration(tail->duration() + head_start_time - tail_end_time);
+ tail->add_outgoing_edge(SharedPtr<Edge>(new Edge(tail, head)));
+ } else {
+ // Need to actually create a delay node
+ cerr << "Adding delay node for " << tail_end_time << " .. " << head_start_time << endl;
+ delay_node = SharedPtr<Node>(new Node());
+ delay_node->set_duration(head_start_time - tail_end_time);
+ tail->add_outgoing_edge(SharedPtr<Edge>(new Edge(tail, delay_node)));
+ delay_node->add_outgoing_edge(SharedPtr<Edge>(new Edge(delay_node, head)));
+ m->add_node(delay_node);
+ }
+
+ return delay_node;
+}
+
+
void
SMFDriver::learn_track(SharedPtr<Machine> m,
Raul::SMFReader& reader,
@@ -107,7 +155,11 @@ SMFDriver::learn_track(SharedPtr<Machine> m,
if (!found_track)
return;
- list<SharedPtr<Node> > active_nodes;
+ typedef list<SharedPtr<Node> > ActiveList;
+ ActiveList active_nodes;
+
+ typedef list<pair<Raul::BeatTime, SharedPtr<Node> > > PolyList;
+ PolyList poly_nodes;
SharedPtr<Node> initial_node(new Node());
initial_node->set_initial(true);
@@ -135,28 +187,21 @@ SMFDriver::learn_track(SharedPtr<Machine> m,
if ((buf[0] & 0xF0) == MIDI_CMD_NOTE_ON) {
//cerr << "NOTE ON: " << (int)buf[1] << ", channel = " << (int)(buf[0] & 0x0F) << endl;
SharedPtr<Node> node(new Node());
- node->add_enter_action(SharedPtr<Action>(new MidiAction(ev_size, buf)));
- assert(connect_node_end_time <= t);
- if (t == connect_node_end_time) {
- connect_node->add_outgoing_edge(SharedPtr<Edge>(new Edge(connect_node, node)));
- } else {
- SharedPtr<Node> delay_node(new Node());
- delay_node->set_duration(t - connect_node_end_time);
- connect_node->add_outgoing_edge(SharedPtr<Edge>(new Edge(connect_node, delay_node)));
- delay_node->add_outgoing_edge(SharedPtr<Edge>(new Edge(delay_node, node)));
- m->add_node(delay_node);
- ++added_nodes;
+ node->set_enter_action(SharedPtr<Action>(new MidiAction(ev_size, buf)));
+
+ SharedPtr<Node> delay_node = connect_nodes(m, connect_node, connect_node_end_time, node, t);
+ if (delay_node) {
connect_node = delay_node;
connect_node_end_time = t;
}
node->enter(SharedPtr<Raul::MIDISink>(), t);
active_nodes.push_back(node);
+
} else if ((buf[0] & 0xF0) == MIDI_CMD_NOTE_OFF) {
//cerr << "NOTE OFF: " << (int)buf[1] << endl;
- for (list<SharedPtr<Node> >::iterator i = active_nodes.begin();
- i != active_nodes.end(); ++i) {
+ for (ActiveList::iterator i = active_nodes.begin(); i != active_nodes.end(); ++i) {
SharedPtr<MidiAction> action = PtrCast<MidiAction>((*i)->enter_action());
if (!action)
continue;
@@ -167,17 +212,64 @@ SMFDriver::learn_track(SharedPtr<Machine> m,
&& (ev[0] & 0x0F) == (buf[0] & 0x0F) // same channel
&& ev[1] == buf[1]) // same note
{
- //cerr << "FOUND MATCHING NOTE ON!\n";
- (*i)->add_exit_action(SharedPtr<Action>(new MidiAction(ev_size, buf)));
- (*i)->set_duration(t - (*i)->enter_time());
- (*i)->exit(SharedPtr<Raul::MIDISink>(), t);
- m->add_node((*i));
+ //cerr << "FOUND MATCHING NOTE OFF!\n";
+
+ SharedPtr<Node> resolved = *i;
+
+ resolved->set_exit_action(SharedPtr<Action>(new MidiAction(ev_size, buf)));
+ resolved->set_duration(t - resolved->enter_time());
+
++added_nodes;
+
+ connect_node_end_time = t;
+
if (active_nodes.size() == 1) {
- connect_node = (*i);
- connect_node_end_time = t;
+ if (poly_nodes.size() > 0) {
+
+ connect_node = SharedPtr<Node>(new Node());
+ m->add_node(connect_node);
+
+ connect_nodes(m, resolved, t, connect_node, t);
+
+ for (PolyList::iterator j = poly_nodes.begin();
+ j != poly_nodes.end(); ++j) {
+ m->add_node(j->second);
+ connect_nodes(m, j->second, j->first + j->second->duration(),
+ connect_node, t);
+ }
+ poly_nodes.clear();
+
+ m->add_node(resolved);
+
+ } else {
+ // Trim useless delay node, if possible
+ // (these happen after polyphonic sections)
+ if (is_delay_node(connect_node) && connect_node->duration() == 0
+ && connect_node->outgoing_edges().size() == 1
+ && (*connect_node->outgoing_edges().begin())->dst() == resolved) {
+ connect_node->outgoing_edges().clear();
+ assert(connect_node->outgoing_edges().empty());
+ connect_node->set_enter_action(resolved->enter_action());
+ connect_node->set_exit_action(resolved->exit_action());
+ resolved->remove_enter_action();
+ resolved->remove_exit_action();
+ connect_node->set_duration(resolved->duration());
+ resolved = connect_node;
+ } else {
+ connect_node = resolved;
+ m->add_node(resolved);
+ }
+ }
+
+ } else {
+ poly_nodes.push_back(make_pair(resolved->enter_time(), resolved));
}
+
+ if (resolved->is_active())
+ resolved->exit(SharedPtr<Raul::MIDISink>(), t);
+
active_nodes.erase(i);
+
break;
}
}
@@ -197,7 +289,7 @@ SMFDriver::learn_track(SharedPtr<Machine> m,
const unsigned char* ev = action->event();
if (ev_size == 3 && (ev[0] & 0xF0) == MIDI_CMD_NOTE_ON) {
unsigned char note_off[3] = { ((MIDI_CMD_NOTE_OFF & 0xF0) | (ev[0] & 0x0F)), ev[1], 0x40 };
- (*i)->add_exit_action(SharedPtr<Action>(new MidiAction(3, note_off)));
+ (*i)->set_exit_action(SharedPtr<Action>(new MidiAction(3, note_off)));
(*i)->set_duration(t - (*i)->enter_time());
(*i)->exit(SharedPtr<Raul::MIDISink>(), t);
m->add_node((*i));