annotate runner/MIDIFeatureWriter.cpp @ 152:db83ea0e102d jams

Make the JSON well-formed (though still empty), except in the case where we are asked to write more than one file's features to the same output file or stdout
author Chris Cannam
date Tue, 14 Oct 2014 12:35:19 +0100
parents 56dcb5f67ed1
children 977b7007071f
rev   line source
Chris@137 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@137 2
Chris@137 3 /*
Chris@137 4 Sonic Annotator
Chris@137 5 A utility for batch feature extraction from audio files.
Chris@137 6 Mark Levy, Chris Sutton and Chris Cannam, Queen Mary, University of London.
Chris@137 7 Copyright 2007-2014 QMUL.
Chris@137 8
Chris@137 9 This program is free software; you can redistribute it and/or
Chris@137 10 modify it under the terms of the GNU General Public License as
Chris@137 11 published by the Free Software Foundation; either version 2 of the
Chris@137 12 License, or (at your option) any later version. See the file
Chris@137 13 COPYING included with this distribution for more information.
Chris@137 14 */
Chris@137 15
Chris@137 16 #include "MIDIFeatureWriter.h"
Chris@137 17
Chris@137 18 using namespace std;
Chris@137 19 using Vamp::Plugin;
Chris@137 20 using Vamp::PluginBase;
Chris@137 21
Chris@137 22 #include "base/Exceptions.h"
Chris@137 23 #include "data/fileio/MIDIFileWriter.h"
Chris@137 24
Chris@137 25 MIDIFeatureWriter::MIDIFeatureWriter() :
Chris@137 26 FileFeatureWriter(SupportOneFilePerTrackTransform |
Chris@137 27 SupportOneFilePerTrack |
Chris@137 28 SupportOneFileTotal,
Chris@137 29 "mid")
Chris@137 30 {
Chris@137 31 }
Chris@137 32
Chris@137 33 MIDIFeatureWriter::~MIDIFeatureWriter()
Chris@137 34 {
Chris@137 35 }
Chris@137 36
Chris@144 37 string
Chris@144 38 MIDIFeatureWriter::getDescription() const
Chris@144 39 {
Chris@144 40 return "Write features to MIDI files. All features are written as MIDI notes. If a feature has at least one value, its first value will be used as the note pitch, the second value (if present) for velocity. If a feature has units of Hz, then its pitch will be converted from frequency to an integer value in MIDI range, otherwise it will be written directly. Multiple (up to 16) transforms can be written to a single MIDI file, where they will be given separate MIDI channel numbers.";
Chris@144 41 }
Chris@144 42
Chris@137 43 MIDIFeatureWriter::ParameterList
Chris@137 44 MIDIFeatureWriter::getSupportedParameters() const
Chris@137 45 {
Chris@137 46 ParameterList pl = FileFeatureWriter::getSupportedParameters();
Chris@137 47 return pl;
Chris@137 48 }
Chris@137 49
Chris@137 50 void
Chris@137 51 MIDIFeatureWriter::setParameters(map<string, string> &params)
Chris@137 52 {
Chris@137 53 FileFeatureWriter::setParameters(params);
Chris@137 54 }
Chris@137 55
Chris@137 56 void
Chris@137 57 MIDIFeatureWriter::setTrackMetadata(QString, TrackMetadata)
Chris@137 58 {
Chris@137 59 cerr << "MIDIFeatureWriter::setTrackMetadata: not supported (yet?)" << endl;
Chris@137 60 }
Chris@137 61
Chris@137 62 void
Chris@137 63 MIDIFeatureWriter::write(QString trackId,
Chris@137 64 const Transform &transform,
Chris@137 65 const Plugin::OutputDescriptor& output,
Chris@137 66 const Plugin::FeatureList& features,
Chris@141 67 std::string /* summaryType */)
Chris@137 68 {
Chris@140 69 QString transformId = transform.getIdentifier();
Chris@140 70
Chris@140 71 QString filename = getOutputFilename(trackId, transformId);
Chris@137 72 if (filename == "") {
Chris@140 73 throw FailedToOpenOutputStream(trackId, transformId);
Chris@137 74 }
Chris@137 75
Chris@141 76 int sampleRate = transform.getSampleRate();
Chris@141 77
Chris@140 78 if (m_rates.find(filename) == m_rates.end()) {
Chris@140 79 m_rates[filename] = sampleRate;
Chris@140 80 }
Chris@140 81
Chris@140 82 if (m_fileTransforms[filename].find(transformId) ==
Chris@140 83 m_fileTransforms[filename].end()) {
Chris@140 84
Chris@140 85 // This transform is new to the file, give it a channel number
Chris@140 86
Chris@140 87 int channel = m_nextChannels[filename];
Chris@140 88 m_nextChannels[filename] = channel + 1;
Chris@140 89
Chris@140 90 m_fileTransforms[filename].insert(transformId);
Chris@140 91 m_channels[transformId] = channel;
Chris@140 92 }
Chris@140 93
Chris@140 94 NoteList notes = m_notes[filename];
Chris@140 95
Chris@141 96 bool freq = (output.unit == "Hz" ||
Chris@141 97 output.unit == "hz" ||
Chris@141 98 output.unit == "HZ");
Chris@141 99
Chris@141 100 for (int i = 0; i < (int)features.size(); ++i) {
Chris@141 101
Chris@141 102 const Plugin::Feature &feature(features[i]);
Chris@141 103
Chris@141 104 Vamp::RealTime timestamp = feature.timestamp;
Chris@141 105 int frame = Vamp::RealTime::realTime2Frame(timestamp, sampleRate);
Chris@141 106
Chris@141 107 int duration = 1;
Chris@141 108 if (feature.hasDuration) {
Chris@141 109 duration = Vamp::RealTime::realTime2Frame(feature.duration, sampleRate);
Chris@141 110 }
Chris@141 111
Chris@141 112 int pitch = 60;
Chris@141 113 if (feature.values.size() > 0) {
Chris@141 114 float pval = feature.values[0];
Chris@141 115 if (freq) {
Chris@141 116 pitch = Pitch::getPitchForFrequency(pval);
Chris@141 117 } else {
Chris@141 118 pitch = int(pval + 0.5);
Chris@141 119 }
Chris@141 120 }
Chris@141 121
Chris@141 122 int velocity = 100;
Chris@141 123 if (feature.values.size() > 1) {
Chris@141 124 float vval = feature.values[1];
Chris@141 125 if (vval < 128) {
Chris@141 126 velocity = int(vval + 0.5);
Chris@141 127 }
Chris@141 128 }
Chris@141 129
Chris@141 130 NoteData note(frame, duration, pitch, velocity);
Chris@141 131
Chris@141 132 note.channel = m_channels[transformId];
Chris@141 133
Chris@141 134 notes.push_back(note);
Chris@141 135 }
Chris@140 136
Chris@140 137 m_notes[filename] = notes;
Chris@137 138 }
Chris@137 139
Chris@137 140 void
Chris@137 141 MIDIFeatureWriter::finish()
Chris@137 142 {
Chris@137 143 for (NoteMap::const_iterator i = m_notes.begin(); i != m_notes.end(); ++i) {
Chris@137 144
Chris@137 145 QString filename = i->first;
Chris@137 146 NoteList notes = i->second;
Chris@137 147 float rate = m_rates[filename];
Chris@137 148
Chris@137 149 TrivialNoteExportable exportable(notes);
Chris@137 150
Chris@137 151 {
Chris@137 152 MIDIFileWriter writer(filename, &exportable, rate);
Chris@137 153 if (!writer.isOK()) {
Chris@137 154 cerr << "ERROR: Failed to create MIDI writer: "
Chris@137 155 << writer.getError() << endl;
Chris@137 156 throw FileOperationFailed(filename, "create MIDI writer");
Chris@137 157 }
Chris@147 158
Chris@137 159 writer.write();
Chris@147 160
Chris@147 161 if (!writer.isOK()) {
Chris@147 162 cerr << "ERROR: Failed to write to MIDI file: "
Chris@147 163 << writer.getError() << endl;
Chris@147 164 throw FileOperationFailed(filename, "MIDI write");
Chris@147 165 }
Chris@137 166 }
Chris@137 167 }
Chris@145 168
Chris@152 169 m_notes.clear();
Chris@152 170
Chris@145 171 FileFeatureWriter::finish();
Chris@137 172 }
Chris@137 173