diff options
Diffstat (limited to 'src/engine/SMFDriver.cpp')
-rw-r--r-- | src/engine/SMFDriver.cpp | 136 |
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)); |