/* This file is part of Machina. * Copyright 2007-2011 David Robillard * * 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 3 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 more details. * * You should have received a copy of the GNU General Public License * along with Machina. If not, see . */ #include #include #include #include "raul/SharedPtr.hpp" #include "machina/Context.hpp" #include "machina/Machine.hpp" #include "Edge.hpp" #include "SMFDriver.hpp" #include "SMFReader.hpp" #include "SMFWriter.hpp" #include "quantize.hpp" using namespace std; namespace Machina { SMFDriver::SMFDriver(Raul::Forge& forge, Raul::TimeUnit unit) : Driver(forge, SharedPtr()) { _writer = SharedPtr(new SMFWriter(unit)); } /** Learn a single track from the MIDI file at @a uri * * @track selects which track of the MIDI file to import, starting from 1. * * Currently only file:// URIs are supported. * @return the resulting machine. */ SharedPtr SMFDriver::learn(const string& filename, unsigned track, double q, Raul::TimeDuration max_duration) { //assert(q.unit() == max_duration.unit()); SharedPtr m(new Machine(max_duration.unit())); SharedPtr builder = SharedPtr(new MachineBuilder(m, q, false)); SMFReader reader; if (!reader.open(filename)) { cerr << "Unable to open MIDI file " << filename << endl; return SharedPtr(); } if (track > reader.num_tracks()) return SharedPtr(); else learn_track(builder, reader, track, q, max_duration); m->reset(m->time()); if (m->nodes().size() > 1) return m; else return SharedPtr(); } /** Learn all tracks from a MIDI file into a single machine. * * This will result in one disjoint subgraph in the machine for each track. */ SharedPtr SMFDriver::learn(const string& filename, double q, Raul::TimeStamp max_duration) { SharedPtr m(new Machine(max_duration.unit())); SharedPtr builder = SharedPtr(new MachineBuilder(m, q, false)); SMFReader reader; if (!reader.open(filename)) { cerr << "Unable to open MIDI file " << filename << endl; return SharedPtr(); } for (unsigned t=1; t <= reader.num_tracks(); ++t) { builder->reset(); learn_track(builder, reader, t, q, max_duration); } m->reset(m->time()); if (m->nodes().size() > 1) return m; else return SharedPtr(); } void SMFDriver::learn_track(SharedPtr builder, SMFReader& reader, unsigned track, double q, Raul::TimeDuration max_duration) { const bool found_track = reader.seek_to_track(track); if (!found_track) return; uint8_t buf[4]; uint32_t ev_size; uint32_t ev_delta_time; uint64_t t = 0; double unquantized_t = 0; while (reader.read_event(4, buf, &ev_size, &ev_delta_time) >= 0) { unquantized_t += ev_delta_time; t = quantize(q, unquantized_t); builder->set_time(TimeStamp(max_duration.unit(), (double)t)); if ((!max_duration.is_zero()) && t > max_duration.to_double()) break; if (ev_size > 0) builder->event(TimeStamp(max_duration.unit(), 0, 0), ev_size, buf); } builder->resolve(); } void SMFDriver::run(SharedPtr machine, Raul::TimeStamp max_time) { // FIXME: unit kludge (tempo only) Context context(_forge, machine->time().unit().ppt(), _writer->unit().ppt(), 120.0); context.time().set_slice(TimeStamp(max_time.unit(), 0, 0), context.time().beats_to_ticks(max_time)); machine->set_sink(shared_from_this()); machine->run(context, SharedPtr()); } } // namespace Machina