/* 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(NULL, 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(NULL, 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; Raul::TimeUnit unit = Raul::TimeUnit(TimeUnit::BEATS, MACHINA_PPQN); uint64_t t = 0; while (reader.read_event(4, buf, &ev_size, &ev_delta_time) >= 0) { t += ev_delta_time; const uint32_t beats = t / (uint32_t)reader.ppqn(); const uint32_t smf_ticks = t % (uint32_t)reader.ppqn(); const double frac = smf_ticks / (double)reader.ppqn(); const uint32_t ticks = frac * MACHINA_PPQN; // TODO: quantize builder->set_time(TimeStamp(unit, beats, ticks)); 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.set_sink(this); context.time().set_slice(TimeStamp(max_time.unit(), 0, 0), context.time().beats_to_ticks(max_time)); machine->run(context, SharedPtr()); } } // namespace Machina