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@137
|
37 MIDIFeatureWriter::ParameterList
|
Chris@137
|
38 MIDIFeatureWriter::getSupportedParameters() const
|
Chris@137
|
39 {
|
Chris@137
|
40 ParameterList pl = FileFeatureWriter::getSupportedParameters();
|
Chris@137
|
41 return pl;
|
Chris@137
|
42 }
|
Chris@137
|
43
|
Chris@137
|
44 void
|
Chris@137
|
45 MIDIFeatureWriter::setParameters(map<string, string> ¶ms)
|
Chris@137
|
46 {
|
Chris@137
|
47 FileFeatureWriter::setParameters(params);
|
Chris@137
|
48 }
|
Chris@137
|
49
|
Chris@137
|
50 void
|
Chris@137
|
51 MIDIFeatureWriter::setTrackMetadata(QString, TrackMetadata)
|
Chris@137
|
52 {
|
Chris@137
|
53 cerr << "MIDIFeatureWriter::setTrackMetadata: not supported (yet?)" << endl;
|
Chris@137
|
54 }
|
Chris@137
|
55
|
Chris@137
|
56 void
|
Chris@137
|
57 MIDIFeatureWriter::write(QString trackId,
|
Chris@137
|
58 const Transform &transform,
|
Chris@137
|
59 const Plugin::OutputDescriptor& output,
|
Chris@137
|
60 const Plugin::FeatureList& features,
|
Chris@141
|
61 std::string /* summaryType */)
|
Chris@137
|
62 {
|
Chris@140
|
63 QString transformId = transform.getIdentifier();
|
Chris@140
|
64
|
Chris@140
|
65 QString filename = getOutputFilename(trackId, transformId);
|
Chris@137
|
66 if (filename == "") {
|
Chris@140
|
67 throw FailedToOpenOutputStream(trackId, transformId);
|
Chris@137
|
68 }
|
Chris@137
|
69
|
Chris@141
|
70 int sampleRate = transform.getSampleRate();
|
Chris@141
|
71
|
Chris@140
|
72 if (m_rates.find(filename) == m_rates.end()) {
|
Chris@140
|
73 m_rates[filename] = sampleRate;
|
Chris@140
|
74 }
|
Chris@140
|
75
|
Chris@140
|
76 if (m_fileTransforms[filename].find(transformId) ==
|
Chris@140
|
77 m_fileTransforms[filename].end()) {
|
Chris@140
|
78
|
Chris@140
|
79 // This transform is new to the file, give it a channel number
|
Chris@140
|
80
|
Chris@140
|
81 int channel = m_nextChannels[filename];
|
Chris@140
|
82 m_nextChannels[filename] = channel + 1;
|
Chris@140
|
83
|
Chris@140
|
84 m_fileTransforms[filename].insert(transformId);
|
Chris@140
|
85 m_channels[transformId] = channel;
|
Chris@140
|
86 }
|
Chris@140
|
87
|
Chris@140
|
88 NoteList notes = m_notes[filename];
|
Chris@140
|
89
|
Chris@141
|
90 bool freq = (output.unit == "Hz" ||
|
Chris@141
|
91 output.unit == "hz" ||
|
Chris@141
|
92 output.unit == "HZ");
|
Chris@141
|
93
|
Chris@141
|
94 for (int i = 0; i < (int)features.size(); ++i) {
|
Chris@141
|
95
|
Chris@141
|
96 const Plugin::Feature &feature(features[i]);
|
Chris@141
|
97
|
Chris@141
|
98 Vamp::RealTime timestamp = feature.timestamp;
|
Chris@141
|
99 int frame = Vamp::RealTime::realTime2Frame(timestamp, sampleRate);
|
Chris@141
|
100
|
Chris@141
|
101 int duration = 1;
|
Chris@141
|
102 if (feature.hasDuration) {
|
Chris@141
|
103 duration = Vamp::RealTime::realTime2Frame(feature.duration, sampleRate);
|
Chris@141
|
104 }
|
Chris@141
|
105
|
Chris@141
|
106 int pitch = 60;
|
Chris@141
|
107 if (feature.values.size() > 0) {
|
Chris@141
|
108 float pval = feature.values[0];
|
Chris@141
|
109 if (freq) {
|
Chris@141
|
110 pitch = Pitch::getPitchForFrequency(pval);
|
Chris@141
|
111 } else {
|
Chris@141
|
112 pitch = int(pval + 0.5);
|
Chris@141
|
113 }
|
Chris@141
|
114 }
|
Chris@141
|
115
|
Chris@141
|
116 int velocity = 100;
|
Chris@141
|
117 if (feature.values.size() > 1) {
|
Chris@141
|
118 float vval = feature.values[1];
|
Chris@141
|
119 if (vval < 128) {
|
Chris@141
|
120 velocity = int(vval + 0.5);
|
Chris@141
|
121 }
|
Chris@141
|
122 }
|
Chris@141
|
123
|
Chris@141
|
124 NoteData note(frame, duration, pitch, velocity);
|
Chris@141
|
125
|
Chris@141
|
126 note.channel = m_channels[transformId];
|
Chris@141
|
127
|
Chris@141
|
128 notes.push_back(note);
|
Chris@141
|
129 }
|
Chris@140
|
130
|
Chris@140
|
131 m_notes[filename] = notes;
|
Chris@137
|
132 }
|
Chris@137
|
133
|
Chris@137
|
134 void
|
Chris@137
|
135 MIDIFeatureWriter::finish()
|
Chris@137
|
136 {
|
Chris@137
|
137 for (NoteMap::const_iterator i = m_notes.begin(); i != m_notes.end(); ++i) {
|
Chris@137
|
138
|
Chris@137
|
139 QString filename = i->first;
|
Chris@137
|
140 NoteList notes = i->second;
|
Chris@137
|
141 float rate = m_rates[filename];
|
Chris@137
|
142
|
Chris@137
|
143 TrivialNoteExportable exportable(notes);
|
Chris@137
|
144
|
Chris@137
|
145 {
|
Chris@137
|
146 MIDIFileWriter writer(filename, &exportable, rate);
|
Chris@137
|
147 if (!writer.isOK()) {
|
Chris@137
|
148 cerr << "ERROR: Failed to create MIDI writer: "
|
Chris@137
|
149 << writer.getError() << endl;
|
Chris@137
|
150 throw FileOperationFailed(filename, "create MIDI writer");
|
Chris@137
|
151 }
|
Chris@137
|
152 writer.write();
|
Chris@137
|
153 }
|
Chris@137
|
154 }
|
Chris@137
|
155 }
|
Chris@137
|
156
|