Chris@137: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@137: Chris@137: /* Chris@137: Sonic Annotator Chris@137: A utility for batch feature extraction from audio files. Chris@137: Mark Levy, Chris Sutton and Chris Cannam, Queen Mary, University of London. Chris@137: Copyright 2007-2014 QMUL. Chris@137: Chris@137: This program is free software; you can redistribute it and/or Chris@137: modify it under the terms of the GNU General Public License as Chris@137: published by the Free Software Foundation; either version 2 of the Chris@137: License, or (at your option) any later version. See the file Chris@137: COPYING included with this distribution for more information. Chris@137: */ Chris@137: Chris@137: #include "MIDIFeatureWriter.h" Chris@137: Chris@137: using namespace std; Chris@137: using Vamp::Plugin; Chris@137: using Vamp::PluginBase; Chris@137: Chris@137: #include "base/Exceptions.h" Chris@137: #include "data/fileio/MIDIFileWriter.h" Chris@137: Chris@137: MIDIFeatureWriter::MIDIFeatureWriter() : Chris@137: FileFeatureWriter(SupportOneFilePerTrackTransform | Chris@137: SupportOneFilePerTrack | Chris@137: SupportOneFileTotal, Chris@137: "mid") Chris@137: { Chris@137: } Chris@137: Chris@137: MIDIFeatureWriter::~MIDIFeatureWriter() Chris@137: { Chris@137: } Chris@137: Chris@137: MIDIFeatureWriter::ParameterList Chris@137: MIDIFeatureWriter::getSupportedParameters() const Chris@137: { Chris@137: ParameterList pl = FileFeatureWriter::getSupportedParameters(); Chris@137: return pl; Chris@137: } Chris@137: Chris@137: void Chris@137: MIDIFeatureWriter::setParameters(map ¶ms) Chris@137: { Chris@137: FileFeatureWriter::setParameters(params); Chris@137: } Chris@137: Chris@137: void Chris@137: MIDIFeatureWriter::setTrackMetadata(QString, TrackMetadata) Chris@137: { Chris@137: cerr << "MIDIFeatureWriter::setTrackMetadata: not supported (yet?)" << endl; Chris@137: } Chris@137: Chris@137: void Chris@137: MIDIFeatureWriter::write(QString trackId, Chris@137: const Transform &transform, Chris@137: const Plugin::OutputDescriptor& output, Chris@137: const Plugin::FeatureList& features, Chris@141: std::string /* summaryType */) Chris@137: { Chris@140: QString transformId = transform.getIdentifier(); Chris@140: Chris@140: QString filename = getOutputFilename(trackId, transformId); Chris@137: if (filename == "") { Chris@140: throw FailedToOpenOutputStream(trackId, transformId); Chris@137: } Chris@137: Chris@141: int sampleRate = transform.getSampleRate(); Chris@141: Chris@140: if (m_rates.find(filename) == m_rates.end()) { Chris@140: m_rates[filename] = sampleRate; Chris@140: } Chris@140: Chris@140: if (m_fileTransforms[filename].find(transformId) == Chris@140: m_fileTransforms[filename].end()) { Chris@140: Chris@140: // This transform is new to the file, give it a channel number Chris@140: Chris@140: int channel = m_nextChannels[filename]; Chris@140: m_nextChannels[filename] = channel + 1; Chris@140: Chris@140: m_fileTransforms[filename].insert(transformId); Chris@140: m_channels[transformId] = channel; Chris@140: } Chris@140: Chris@140: NoteList notes = m_notes[filename]; Chris@140: Chris@141: bool freq = (output.unit == "Hz" || Chris@141: output.unit == "hz" || Chris@141: output.unit == "HZ"); Chris@141: Chris@141: for (int i = 0; i < (int)features.size(); ++i) { Chris@141: Chris@141: const Plugin::Feature &feature(features[i]); Chris@141: Chris@141: Vamp::RealTime timestamp = feature.timestamp; Chris@141: int frame = Vamp::RealTime::realTime2Frame(timestamp, sampleRate); Chris@141: Chris@141: int duration = 1; Chris@141: if (feature.hasDuration) { Chris@141: duration = Vamp::RealTime::realTime2Frame(feature.duration, sampleRate); Chris@141: } Chris@141: Chris@141: int pitch = 60; Chris@141: if (feature.values.size() > 0) { Chris@141: float pval = feature.values[0]; Chris@141: if (freq) { Chris@141: pitch = Pitch::getPitchForFrequency(pval); Chris@141: } else { Chris@141: pitch = int(pval + 0.5); Chris@141: } Chris@141: } Chris@141: Chris@141: int velocity = 100; Chris@141: if (feature.values.size() > 1) { Chris@141: float vval = feature.values[1]; Chris@141: if (vval < 128) { Chris@141: velocity = int(vval + 0.5); Chris@141: } Chris@141: } Chris@141: Chris@141: NoteData note(frame, duration, pitch, velocity); Chris@141: Chris@141: note.channel = m_channels[transformId]; Chris@141: Chris@141: notes.push_back(note); Chris@141: } Chris@140: Chris@140: m_notes[filename] = notes; Chris@137: } Chris@137: Chris@137: void Chris@137: MIDIFeatureWriter::finish() Chris@137: { Chris@137: for (NoteMap::const_iterator i = m_notes.begin(); i != m_notes.end(); ++i) { Chris@137: Chris@137: QString filename = i->first; Chris@137: NoteList notes = i->second; Chris@137: float rate = m_rates[filename]; Chris@137: Chris@137: TrivialNoteExportable exportable(notes); Chris@137: Chris@137: { Chris@137: MIDIFileWriter writer(filename, &exportable, rate); Chris@137: if (!writer.isOK()) { Chris@137: cerr << "ERROR: Failed to create MIDI writer: " Chris@137: << writer.getError() << endl; Chris@137: throw FileOperationFailed(filename, "create MIDI writer"); Chris@137: } Chris@137: writer.write(); Chris@137: } Chris@137: } Chris@137: } Chris@137: