aboutsummaryrefslogtreecommitdiffstats
path: root/src/engine
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine')
-rw-r--r--src/engine/JackDriver.cpp56
-rw-r--r--src/engine/MachineBuilder.cpp246
-rw-r--r--src/engine/Makefile.am4
-rw-r--r--src/engine/Recorder.cpp66
-rw-r--r--src/engine/SMFDriver.cpp208
-rw-r--r--src/engine/machina/Driver.hpp4
-rw-r--r--src/engine/machina/JackDriver.hpp11
-rw-r--r--src/engine/machina/MachineBuilder.hpp69
-rw-r--r--src/engine/machina/Makefile.am4
-rw-r--r--src/engine/machina/Recorder.hpp53
-rw-r--r--src/engine/machina/SMFDriver.hpp28
11 files changed, 536 insertions, 213 deletions
diff --git a/src/engine/JackDriver.cpp b/src/engine/JackDriver.cpp
index 606a8ff..ed40e2a 100644
--- a/src/engine/JackDriver.cpp
+++ b/src/engine/JackDriver.cpp
@@ -33,6 +33,7 @@ JackDriver::JackDriver(SharedPtr<Machine> machine)
, _cycle_time(1/48000.0, 120.0)
, _bpm(120.0)
, _quantization(120.0)
+ , _recording(0)
{
if (!_machine)
_machine = SharedPtr<Machine>(new Machine());
@@ -95,11 +96,15 @@ JackDriver::detach()
void
JackDriver::set_machine(SharedPtr<Machine> machine)
-{
+{
+ SharedPtr<Machine> last_machine = _last_machine; // Keep a reference
+ _machine_changed.reset(0);
+ assert(!last_machine.unique());
_machine = machine;
if (is_activated())
_machine_changed.wait();
assert(_machine == machine);
+ last_machine.reset();
}
@@ -108,10 +113,26 @@ JackDriver::process_input(SharedPtr<Machine> machine, const TimeSlice& time)
{
// We only actually read Jack input at the beginning of a cycle
assert(time.offset_ticks() == 0);
+ assert(_input_port);
using namespace std;
- //if (_learn_node) {
+ if (_recording.get()) {
+ const jack_nframes_t nframes = time.length_ticks();
+ void* jack_buffer = jack_port_get_buffer(_input_port, nframes);
+ const jack_nframes_t event_count = jack_midi_get_event_count(jack_buffer, nframes);
+
+ for (jack_nframes_t i=0; i < event_count; ++i) {
+ jack_midi_event_t ev;
+ jack_midi_event_get(&ev, jack_buffer, i, nframes);
+
+ _recorder->write(_record_time + ev.time, ev.size, ev.buffer);
+ }
+
+ if (event_count > 0)
+ _recorder->whip();
+
+ } else {
const jack_nframes_t nframes = time.length_ticks();
void* jack_buffer = jack_port_get_buffer(_input_port, nframes);
const jack_nframes_t event_count = jack_midi_get_event_count(jack_buffer, nframes);
@@ -149,7 +170,7 @@ JackDriver::process_input(SharedPtr<Machine> machine, const TimeSlice& time)
//std::cerr << "EVENT: " << std::hex << (int)ev.buffer[0] << "\n";
}
- //}
+ }
}
@@ -199,13 +220,16 @@ JackDriver::on_process(jack_nframes_t nframes)
// Machine was switched since last cycle, finalize old machine.
if (machine != _last_machine) {
if (_last_machine) {
+ assert(!_last_machine.unique()); // Realtime, can't delete
_last_machine->reset(); // Exit all active states
- assert(_last_machine.use_count() > 1); // Realtime, can't delete
_last_machine.reset(); // Cut our reference
}
_machine_changed.post(); // Signal we're done with it
}
+ if (_recording.get())
+ _record_time += nframes;
+
if (!machine)
return;
@@ -256,4 +280,28 @@ JackDriver::on_process(jack_nframes_t nframes)
}
+void
+JackDriver::start_record()
+{
+ std::cerr << "START RECORD" << std::endl;
+ // FIXME: hardcoded size
+ _recorder = SharedPtr<Recorder>(new Recorder(1024, 1.0/(double)sample_rate()));
+ _recorder->start();
+ _record_time = 0;
+ _recording = 1;
+}
+
+
+void
+JackDriver::finish_record()
+{
+ _recording = 0;
+ SharedPtr<Machine> machine = _recorder->finish();
+ std::cout << "Learned machine! " << machine->nodes().size() << " nodes." << std::endl;
+ _recorder.reset();
+ machine->activate();
+ set_machine(machine);
+}
+
+
} // namespace Machina
diff --git a/src/engine/MachineBuilder.cpp b/src/engine/MachineBuilder.cpp
new file mode 100644
index 0000000..a1a6ed8
--- /dev/null
+++ b/src/engine/MachineBuilder.cpp
@@ -0,0 +1,246 @@
+/* This file is part of Machina.
+ * Copyright (C) 2007 Dave Robillard <http://drobilla.net>
+ *
+ * Machina is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <algorithm>
+#include <raul/midi_events.h>
+#include "machina/MachineBuilder.hpp"
+#include "machina/Machine.hpp"
+#include "machina/Node.hpp"
+#include "machina/Edge.hpp"
+
+using namespace std;
+
+namespace Machina {
+
+
+MachineBuilder::MachineBuilder(SharedPtr<Machine> machine)
+ : _time(0)
+ , _machine(machine)
+ , _initial_node(new Node())
+ , _connect_node(_initial_node)
+ , _connect_node_end_time(0)
+{
+ _initial_node->set_initial(true);
+}
+
+
+void
+MachineBuilder::reset()
+{
+ _initial_node = SharedPtr<Node>(new Node());
+ _initial_node->set_initial(true);
+ _connect_node = _initial_node;
+ _connect_node_end_time = 0;
+ _time = 0;
+}
+
+
+bool
+MachineBuilder::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>
+MachineBuilder::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;
+
+ //cerr << "Connect nodes durations: " << tail->duration() << " .. " << head->duration() << endl;
+ //cerr << "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
+MachineBuilder::event(Raul::BeatTime time_offset,
+ size_t ev_size,
+ unsigned char* buf)
+{
+ Raul::BeatTime t = _time + time_offset;
+
+ cerr << "t = " << t << endl;
+
+ if (ev_size > 0) {
+ 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->set_enter_action(SharedPtr<Action>(new MidiAction(ev_size, buf)));
+
+ SharedPtr<Node> delay_node = connect_nodes(_machine,
+ _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 (ActiveList::iterator i = _active_nodes.begin(); i != _active_nodes.end(); ++i) {
+ SharedPtr<MidiAction> action = PtrCast<MidiAction>((*i)->enter_action());
+ if (!action)
+ continue;
+
+ const size_t ev_size = action->event_size();
+ const unsigned char* ev = action->event();
+ if (ev_size == 3 && (ev[0] & 0xF0) == MIDI_CMD_NOTE_ON
+ && (ev[0] & 0x0F) == (buf[0] & 0x0F) // same channel
+ && ev[1] == buf[1]) // same note
+ {
+ //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());
+
+ _connect_node_end_time = t;
+
+ if (_active_nodes.size() == 1) {
+ if (_poly_nodes.size() > 0) {
+
+ _connect_node = SharedPtr<Node>(new Node());
+ _machine->add_node(_connect_node);
+
+ connect_nodes(_machine, resolved, t, _connect_node, t);
+
+ for (PolyList::iterator j = _poly_nodes.begin();
+ j != _poly_nodes.end(); ++j) {
+ _machine->add_node(j->second);
+ connect_nodes(_machine, j->second, j->first + j->second->duration(),
+ _connect_node, t);
+ }
+ _poly_nodes.clear();
+
+ _machine->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())->head() == 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;
+ if (_machine->nodes().find(_connect_node) == _machine->nodes().end())
+ _machine->add_node(_connect_node);
+ } else {
+ _connect_node = resolved;
+ _machine->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;
+ }
+ }
+ }
+ }
+}
+
+
+/** Resolve any stuck notes.
+ */
+void
+MachineBuilder::resolve()
+{
+ if ( ! _active_nodes.empty()) {
+ for (list<SharedPtr<Node> >::iterator i = _active_nodes.begin(); i != _active_nodes.end(); ++i) {
+ cerr << "WARNING: Resolving stuck note from MIDI file." << endl;
+ SharedPtr<MidiAction> action = PtrCast<MidiAction>((*i)->enter_action());
+ if (!action)
+ continue;
+
+ const size_t ev_size = action->event_size();
+ 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)->set_exit_action(SharedPtr<Action>(new MidiAction(3, note_off)));
+ (*i)->set_duration(_time - (*i)->enter_time());
+ (*i)->exit(SharedPtr<Raul::MIDISink>(), _time);
+ _machine->add_node((*i));
+ }
+ }
+ _active_nodes.clear();
+ }
+
+ if (_machine->nodes().size() > 0
+ && _machine->nodes().find(_initial_node) == _machine->nodes().end())
+ _machine->add_node(_initial_node);
+}
+
+
+SharedPtr<Machine>
+MachineBuilder::finish()
+{
+ resolve();
+
+ return _machine;
+}
+
+
+} // namespace Machina
diff --git a/src/engine/Makefile.am b/src/engine/Makefile.am
index c526633..dc7e8a4 100644
--- a/src/engine/Makefile.am
+++ b/src/engine/Makefile.am
@@ -15,7 +15,9 @@ libmachina_la_SOURCES = \
ActionFactory.cpp \
SMFDriver.cpp \
Engine.cpp \
- LearnRequest.cpp
+ LearnRequest.cpp \
+ Recorder.cpp \
+ MachineBuilder.cpp
if WITH_JACK
libmachina_la_SOURCES += JackDriver.cpp
diff --git a/src/engine/Recorder.cpp b/src/engine/Recorder.cpp
new file mode 100644
index 0000000..78eafe6
--- /dev/null
+++ b/src/engine/Recorder.cpp
@@ -0,0 +1,66 @@
+/* This file is part of Machina.
+ * Copyright (C) 2007 Dave Robillard <http://drobilla.net>
+ *
+ * Machina is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <iostream>
+#include <ios>
+#include "machina/Recorder.hpp"
+#include "machina/MachineBuilder.hpp"
+
+using namespace std;
+using namespace Raul;
+
+namespace Machina {
+
+
+Recorder::Recorder(size_t buffer_size, double tick_rate)
+ : _tick_rate(tick_rate)
+ , _record_buffer(buffer_size)
+ , _builder(new MachineBuilder(SharedPtr<Machine>(new Machine())))
+{
+}
+
+
+void
+Recorder::_whipped()
+{
+ cerr << "Whipped" << endl;
+
+ TickTime t;
+ size_t size;
+ unsigned char buf[4];
+
+ while (_record_buffer.read(&t, &size, buf)) {
+ //cout << "RECORD EVENT: t=" << t * _tick_rate << ", size=" << size << ", buf=0x"
+ // << std::hex << (int)((unsigned char)buf[0]) << std::dec << endl;
+
+ _builder->set_time(t * _tick_rate);
+ _builder->event(0, size, buf);
+ }
+}
+
+
+SharedPtr<Machine>
+Recorder::finish()
+{
+ SharedPtr<Machine> machine = _builder->finish();
+ _builder.reset();
+ return machine;
+}
+
+
+}
+
diff --git a/src/engine/SMFDriver.cpp b/src/engine/SMFDriver.cpp
index 5b6d940..dc7ca59 100644
--- a/src/engine/SMFDriver.cpp
+++ b/src/engine/SMFDriver.cpp
@@ -20,7 +20,6 @@
#include <glibmm/convert.h>
#include <raul/Quantizer.h>
#include <raul/SharedPtr.h>
-#include <raul/midi_events.h>
#include <raul/SMFWriter.h>
#include <raul/SMFReader.h>
#include "machina/Machine.hpp"
@@ -50,7 +49,7 @@ SharedPtr<Machine>
SMFDriver::learn(const string& filename, unsigned track, double q, Raul::BeatTime max_duration)
{
SharedPtr<Machine> m(new Machine());
-
+ SharedPtr<MachineBuilder> builder = SharedPtr<MachineBuilder>(new MachineBuilder(m));
Raul::SMFReader reader;
if (!reader.open(filename)) {
@@ -61,7 +60,7 @@ SMFDriver::learn(const string& filename, unsigned track, double q, Raul::BeatTim
if (track > reader.num_tracks())
return SharedPtr<Machine>();
else
- learn_track(m, reader, track, q, max_duration);
+ learn_track(builder, reader, track, q, max_duration);
m->reset();
@@ -80,15 +79,17 @@ SharedPtr<Machine>
SMFDriver::learn(const string& filename, double q, Raul::BeatTime max_duration)
{
SharedPtr<Machine> m(new Machine());
-
+ SharedPtr<MachineBuilder> builder = SharedPtr<MachineBuilder>(new MachineBuilder(m));
Raul::SMFReader reader;
+
if (!reader.open(filename)) {
cerr << "Unable to open MIDI file " << filename << endl;
return SharedPtr<Machine>();
}
for (unsigned t=1; t <= reader.num_tracks(); ++t) {
- learn_track(m, reader, t, q, max_duration);
+ builder->reset();
+ learn_track(builder, reader, t, q, max_duration);
}
m->reset();
@@ -100,80 +101,17 @@ 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;
-
- //cerr << "Connect nodes durations: " << tail->duration() << " .. " << head->duration() << endl;
- //cerr << "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,
- unsigned track,
- double q,
- Raul::BeatTime max_duration)
+SMFDriver::learn_track(SharedPtr<MachineBuilder> builder,
+ Raul::SMFReader& reader,
+ unsigned track,
+ double q,
+ Raul::BeatTime max_duration)
{
const bool found_track = reader.seek_to_track(track);
if (!found_track)
return;
- 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);
- //m->add_node(initial_node);
-
- SharedPtr<Node> connect_node = initial_node;
- Raul::BeatTime connect_node_end_time = 0;
-
- unsigned added_nodes = 0;
-
Raul::BeatTime unquantized_t = 0;
Raul::BeatTime t = 0;
unsigned char buf[4];
@@ -183,130 +121,16 @@ SMFDriver::learn_track(SharedPtr<Machine> m,
unquantized_t += ev_time / (double)reader.ppqn();
t = Raul::Quantizer::quantize(q, unquantized_t);
+ builder->set_time(t);
+
if (max_duration != 0 && t > max_duration)
break;
- //cerr << "t = " << t << endl;
- if (ev_size > 0) {
- 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->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 (ActiveList::iterator i = active_nodes.begin(); i != active_nodes.end(); ++i) {
- SharedPtr<MidiAction> action = PtrCast<MidiAction>((*i)->enter_action());
- if (!action)
- continue;
-
- const size_t ev_size = action->event_size();
- const unsigned char* ev = action->event();
- if (ev_size == 3 && (ev[0] & 0xF0) == MIDI_CMD_NOTE_ON
- && (ev[0] & 0x0F) == (buf[0] & 0x0F) // same channel
- && ev[1] == buf[1]) // same note
- {
- //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) {
- 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())->head() == 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;
- if (m->nodes().find(connect_node) == m->nodes().end())
- m->add_node(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;
- }
- }
- }
- }
+ if (ev_size > 0)
+ builder->event(0, ev_size, buf);
}
-
- // Resolve any stuck notes when the rest of the machine is finished
- if ( ! active_nodes.empty()) {
- for (list<SharedPtr<Node> >::iterator i = active_nodes.begin(); i != active_nodes.end(); ++i) {
- cerr << "WARNING: Resolving stuck note from MIDI file." << endl;
- SharedPtr<MidiAction> action = PtrCast<MidiAction>((*i)->enter_action());
- if (!action)
- continue;
- const size_t ev_size = action->event_size();
- 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)->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));
- ++added_nodes;
- }
- }
- active_nodes.clear();
- }
-
- if (m->nodes().find(initial_node) == m->nodes().end())
- m->add_node(initial_node);
+ builder->resolve();
}
diff --git a/src/engine/machina/Driver.hpp b/src/engine/machina/Driver.hpp
index a1a38a2..bf4d2be 100644
--- a/src/engine/machina/Driver.hpp
+++ b/src/engine/machina/Driver.hpp
@@ -39,6 +39,10 @@ public:
virtual void activate() {}
virtual void deactivate() {}
+ virtual bool recording() { return false; }
+ virtual void start_record() {}
+ virtual void finish_record() {}
+
protected:
SharedPtr<Machine> _machine;
};
diff --git a/src/engine/machina/JackDriver.hpp b/src/engine/machina/JackDriver.hpp
index a048c0c..47b718d 100644
--- a/src/engine/machina/JackDriver.hpp
+++ b/src/engine/machina/JackDriver.hpp
@@ -22,11 +22,12 @@
#include <raul/JackDriver.h>
#include <raul/SharedPtr.h>
#include <raul/DoubleBuffer.h>
+#include <raul/MIDIRingBuffer.h>
#include <raul/Semaphore.h>
#include <jack/midiport.h>
#include "Machine.hpp"
#include "Driver.hpp"
-
+#include "Recorder.hpp"
namespace Machina {
@@ -61,6 +62,10 @@ public:
void set_bpm(double bpm) { _bpm.set(bpm); }
void set_quantization(double quantization) { _quantization.set(quantization); }
+ bool recording() { return _recording.get(); }
+ void start_record();
+ void finish_record();
+
private:
void process_input(SharedPtr<Machine> machine,
const Raul::TimeSlice& time);
@@ -76,6 +81,10 @@ private:
Raul::DoubleBuffer<double> _bpm;
Raul::DoubleBuffer<double> _quantization;
+
+ Raul::TickTime _record_time;
+ Raul::AtomicInt _recording;
+ SharedPtr<Recorder> _recorder;
};
diff --git a/src/engine/machina/MachineBuilder.hpp b/src/engine/machina/MachineBuilder.hpp
new file mode 100644
index 0000000..a01ebd2
--- /dev/null
+++ b/src/engine/machina/MachineBuilder.hpp
@@ -0,0 +1,69 @@
+/* This file is part of Machina.
+ * Copyright (C) 2007 Dave Robillard <http://drobilla.net>
+ *
+ * Machina is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef MACHINA_MACHINEBUILDER_HPP
+#define MACHINA_MACHINEBUILDER_HPP
+
+#include <list>
+#include <raul/types.h>
+#include <raul/SharedPtr.h>
+
+namespace Machina {
+
+class Machine;
+class Node;
+
+
+class MachineBuilder {
+public:
+ MachineBuilder(SharedPtr<Machine> machine);
+
+ void set_time(Raul::BeatTime time) { _time = time; }
+
+ void event(Raul::BeatTime time_offset, size_t size, unsigned char* buf);
+
+ void reset();
+ void resolve();
+
+ SharedPtr<Machine> finish();
+
+private:
+ bool is_delay_node(SharedPtr<Node> node) const;
+
+ SharedPtr<Node>
+ connect_nodes(SharedPtr<Machine> m,
+ SharedPtr<Node> tail, Raul::BeatTime tail_end_time,
+ SharedPtr<Node> head, Raul::BeatTime head_start_time);
+
+ typedef std::list<SharedPtr<Node> > ActiveList;
+ ActiveList _active_nodes;
+
+ typedef std::list<std::pair<Raul::BeatTime, SharedPtr<Node> > > PolyList;
+ PolyList _poly_nodes;
+
+ Raul::BeatTime _time;
+
+ SharedPtr<Machine> _machine;
+ SharedPtr<Node> _initial_node;
+ SharedPtr<Node> _connect_node;
+ Raul::BeatTime _connect_node_end_time;
+};
+
+
+} // namespace Machina
+
+#endif // MACHINA_MACHINEBUILDER_HPP
diff --git a/src/engine/machina/Makefile.am b/src/engine/machina/Makefile.am
index ce19080..5907be5 100644
--- a/src/engine/machina/Makefile.am
+++ b/src/engine/machina/Makefile.am
@@ -11,7 +11,9 @@ libmachinainclude_HEADERS = \
ActionFactory.hpp \
Driver.hpp \
LearnRequest.hpp \
- Engine.hpp
+ Engine.hpp \
+ Recorder.hpp \
+ MachineBuilder.hpp
if WITH_JACK
JackDriver.hpp
diff --git a/src/engine/machina/Recorder.hpp b/src/engine/machina/Recorder.hpp
new file mode 100644
index 0000000..b969df0
--- /dev/null
+++ b/src/engine/machina/Recorder.hpp
@@ -0,0 +1,53 @@
+/* This file is part of Machina.
+ * Copyright (C) 2007 Dave Robillard <http://drobilla.net>
+ *
+ * Machina is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef MACHINA_RECORDER_HPP
+#define MACHINA_RECORDER_HPP
+
+#include <raul/types.h>
+#include <raul/Slave.h>
+#include <raul/SharedPtr.h>
+#include <raul/MIDIRingBuffer.h>
+#include "Machine.hpp"
+
+namespace Machina {
+
+class MachineBuilder;
+
+
+class Recorder : public Raul::Slave {
+public:
+ Recorder(size_t buffer_size, double tick_rate);
+
+ inline void write(Raul::TickTime time, size_t size, const unsigned char* buf) {
+ _record_buffer.write(time, size, buf);
+ }
+
+ SharedPtr<Machine> finish();
+
+private:
+ virtual void _whipped();
+
+ double _tick_rate;
+ Raul::MIDIRingBuffer _record_buffer;
+ SharedPtr<MachineBuilder> _builder;
+};
+
+
+} // namespace Machina
+
+#endif // MACHINA_RECORDER_HPP
diff --git a/src/engine/machina/SMFDriver.hpp b/src/engine/machina/SMFDriver.hpp
index 7fec256..7b22a26 100644
--- a/src/engine/machina/SMFDriver.hpp
+++ b/src/engine/machina/SMFDriver.hpp
@@ -25,6 +25,7 @@
#include <raul/SMFReader.h>
#include "machina/types.hpp"
#include "machina/Driver.hpp"
+#include "machina/MachineBuilder.hpp"
namespace Machina {
@@ -37,8 +38,14 @@ class SMFDriver : public Driver,
public:
SMFDriver(SharedPtr<Machine> machine = SharedPtr<Machine>());
- SharedPtr<Machine> learn(const std::string& filename, double q=0.0, Raul::BeatTime max_duration=0);
- SharedPtr<Machine> learn(const std::string& filename, unsigned track, double q=0.0, Raul::BeatTime max_duration=0);
+ SharedPtr<Machine> learn(const std::string& filename,
+ double q=0.0,
+ Raul::BeatTime max_duration=0);
+
+ SharedPtr<Machine> learn(const std::string& filename,
+ unsigned track,
+ double q=0.0,
+ Raul::BeatTime max_duration=0);
void run(SharedPtr<Machine> machine, Raul::BeatTime max_time);
@@ -54,19 +61,12 @@ public:
private:
SharedPtr<Raul::SMFWriter> _writer;
-
- bool is_delay_node(SharedPtr<Node> node) const;
-
- SharedPtr<Node>
- connect_nodes(SharedPtr<Machine> m,
- SharedPtr<Node> tail, Raul::BeatTime tail_end_time,
- SharedPtr<Node> head, Raul::BeatTime head_start_time);
- void learn_track(SharedPtr<Machine> machine,
- Raul::SMFReader& reader,
- unsigned track,
- double q,
- Raul::BeatTime max_duration=0);
+ void learn_track(SharedPtr<MachineBuilder> builder,
+ Raul::SMFReader& reader,
+ unsigned track,
+ double q,
+ Raul::BeatTime max_duration=0);
};