Mercurial > hg > svcore
comparison transform/CSVFeatureWriter.cpp @ 1001:51bf067de517
Add fill-ends option to CSV writer (and test it)
author | Chris Cannam |
---|---|
date | Wed, 15 Oct 2014 10:18:13 +0100 |
parents | ec6e69373997 |
children | c2316a3bbb81 |
comparison
equal
deleted
inserted
replaced
1000:ec6e69373997 | 1001:51bf067de517 |
---|---|
32 SupportOneFileTotal | | 32 SupportOneFileTotal | |
33 SupportStdOut, | 33 SupportStdOut, |
34 "csv"), | 34 "csv"), |
35 m_separator(","), | 35 m_separator(","), |
36 m_sampleTiming(false), | 36 m_sampleTiming(false), |
37 m_endTimes(false) | 37 m_endTimes(false), |
38 m_forceEnd(false) | |
38 { | 39 { |
39 } | 40 } |
40 | 41 |
41 CSVFeatureWriter::~CSVFeatureWriter() | 42 CSVFeatureWriter::~CSVFeatureWriter() |
42 { | 43 { |
64 p.hasArg = false; | 65 p.hasArg = false; |
65 pl.push_back(p); | 66 pl.push_back(p); |
66 | 67 |
67 p.name = "end-times"; | 68 p.name = "end-times"; |
68 p.description = "Show start and end time instead of start and duration, for features with duration."; | 69 p.description = "Show start and end time instead of start and duration, for features with duration."; |
70 p.hasArg = false; | |
71 pl.push_back(p); | |
72 | |
73 p.name = "fill-ends"; | |
74 p.description = "Include durations (or end times) even for features without duration, by using the gap to the next feature instead."; | |
69 p.hasArg = false; | 75 p.hasArg = false; |
70 pl.push_back(p); | 76 pl.push_back(p); |
71 | 77 |
72 return pl; | 78 return pl; |
73 } | 79 } |
85 m_separator = i->second.c_str(); | 91 m_separator = i->second.c_str(); |
86 } else if (i->first == "sample-timing") { | 92 } else if (i->first == "sample-timing") { |
87 m_sampleTiming = true; | 93 m_sampleTiming = true; |
88 } else if (i->first == "end-times") { | 94 } else if (i->first == "end-times") { |
89 m_endTimes = true; | 95 m_endTimes = true; |
96 } else if (i->first == "fill-ends") { | |
97 m_forceEnd = true; | |
90 } | 98 } |
91 } | 99 } |
92 } | 100 } |
93 | 101 |
94 void | 102 void |
96 const Transform &transform, | 104 const Transform &transform, |
97 const Plugin::OutputDescriptor& , | 105 const Plugin::OutputDescriptor& , |
98 const Plugin::FeatureList& features, | 106 const Plugin::FeatureList& features, |
99 std::string summaryType) | 107 std::string summaryType) |
100 { | 108 { |
109 TransformId transformId = transform.getIdentifier(); | |
110 | |
101 // Select appropriate output file for our track/transform | 111 // Select appropriate output file for our track/transform |
102 // combination | 112 // combination |
103 | 113 |
104 QTextStream *sptr = getOutputStream(trackId, transform.getIdentifier()); | 114 QTextStream *sptr = getOutputStream(trackId, transformId); |
105 if (!sptr) { | 115 if (!sptr) { |
106 throw FailedToOpenOutputStream(trackId, transform.getIdentifier()); | 116 throw FailedToOpenOutputStream(trackId, transformId); |
107 } | 117 } |
108 | 118 |
109 QTextStream &stream = *sptr; | 119 QTextStream &stream = *sptr; |
110 | 120 |
111 for (unsigned int i = 0; i < features.size(); ++i) { | 121 int n = features.size(); |
112 | 122 |
113 if (m_stdout || m_singleFileName != "") { | 123 if (n == 0) return; |
114 if (trackId != m_prevPrintedTrackId) { | 124 |
115 stream << "\"" << trackId << "\"" << m_separator; | 125 TrackTransformPair tt(trackId, transformId); |
116 m_prevPrintedTrackId = trackId; | 126 |
127 if (m_rates.find(transformId) == m_rates.end()) { | |
128 m_rates[transformId] = transform.getSampleRate(); | |
129 } | |
130 | |
131 if (m_pending.find(tt) != m_pending.end()) { | |
132 writeFeature(tt, | |
133 stream, | |
134 m_pending[tt], | |
135 &features[0], | |
136 m_pendingSummaryTypes[tt]); | |
137 m_pending.erase(tt); | |
138 m_pendingSummaryTypes.erase(tt); | |
139 } | |
140 | |
141 if (m_forceEnd) { | |
142 // can't write final feature until we know its end time | |
143 --n; | |
144 m_pending[tt] = features[n]; | |
145 m_pendingSummaryTypes[tt] = summaryType; | |
146 } | |
147 | |
148 for (int i = 0; i < n; ++i) { | |
149 writeFeature(tt, | |
150 stream, | |
151 features[i], | |
152 m_forceEnd ? &features[i+1] : 0, | |
153 summaryType); | |
154 } | |
155 } | |
156 | |
157 void | |
158 CSVFeatureWriter::finish() | |
159 { | |
160 for (PendingFeatures::const_iterator i = m_pending.begin(); | |
161 i != m_pending.end(); ++i) { | |
162 TrackTransformPair tt = i->first; | |
163 Plugin::Feature f = i->second; | |
164 QTextStream *sptr = getOutputStream(tt.first, tt.second); | |
165 if (!sptr) { | |
166 throw FailedToOpenOutputStream(tt.first, tt.second); | |
167 } | |
168 QTextStream &stream = *sptr; | |
169 // final feature has its own time as end time (we can't | |
170 // reliably determine the end of audio file, and because of | |
171 // the nature of block processing, the feature could even | |
172 // start beyond that anyway) | |
173 writeFeature(tt, stream, f, &f, m_pendingSummaryTypes[tt]); | |
174 } | |
175 | |
176 m_pending.clear(); | |
177 } | |
178 | |
179 void | |
180 CSVFeatureWriter::writeFeature(TrackTransformPair tt, | |
181 QTextStream &stream, | |
182 const Plugin::Feature &f, | |
183 const Plugin::Feature *optionalNextFeature, | |
184 std::string summaryType) | |
185 { | |
186 QString trackId = tt.first; | |
187 TransformId transformId = tt.second; | |
188 | |
189 if (m_stdout || m_singleFileName != "") { | |
190 if (trackId != m_prevPrintedTrackId) { | |
191 stream << "\"" << trackId << "\"" << m_separator; | |
192 m_prevPrintedTrackId = trackId; | |
193 } else { | |
194 stream << m_separator; | |
195 } | |
196 } | |
197 | |
198 Vamp::RealTime duration; | |
199 bool haveDuration = true; | |
200 | |
201 if (f.hasDuration) { | |
202 duration = f.duration; | |
203 } else if (optionalNextFeature) { | |
204 duration = optionalNextFeature->timestamp - f.timestamp; | |
205 } else { | |
206 haveDuration = false; | |
207 } | |
208 | |
209 if (m_sampleTiming) { | |
210 | |
211 float rate = m_rates[transformId]; | |
212 | |
213 stream << Vamp::RealTime::realTime2Frame(f.timestamp, rate); | |
214 | |
215 if (haveDuration) { | |
216 stream << m_separator; | |
217 if (m_endTimes) { | |
218 stream << Vamp::RealTime::realTime2Frame | |
219 (f.timestamp + duration, rate); | |
117 } else { | 220 } else { |
118 stream << m_separator; | 221 stream << Vamp::RealTime::realTime2Frame(duration, rate); |
119 } | 222 } |
120 } | 223 } |
121 | 224 |
122 if (m_sampleTiming) { | 225 } else { |
123 | 226 |
124 stream << Vamp::RealTime::realTime2Frame | 227 QString timestamp = f.timestamp.toString().c_str(); |
125 (features[i].timestamp, transform.getSampleRate()); | 228 timestamp.replace(QRegExp("^ +"), ""); |
126 | 229 stream << timestamp; |
127 if (features[i].hasDuration) { | 230 |
128 stream << m_separator; | 231 if (haveDuration) { |
129 if (m_endTimes) { | 232 if (m_endTimes) { |
130 stream << Vamp::RealTime::realTime2Frame | 233 QString endtime = |
131 (features[i].timestamp + features[i].duration, | 234 (f.timestamp + duration).toString().c_str(); |
132 transform.getSampleRate()); | 235 endtime.replace(QRegExp("^ +"), ""); |
133 } else { | 236 stream << m_separator << endtime; |
134 stream << Vamp::RealTime::realTime2Frame | 237 } else { |
135 (features[i].duration, transform.getSampleRate()); | 238 QString d = duration.toString().c_str(); |
136 } | 239 d.replace(QRegExp("^ +"), ""); |
240 stream << m_separator << d; | |
137 } | 241 } |
138 | 242 } |
139 } else { | 243 } |
140 | 244 |
141 QString timestamp = features[i].timestamp.toString().c_str(); | 245 if (summaryType != "") { |
142 timestamp.replace(QRegExp("^ +"), ""); | 246 stream << m_separator << summaryType.c_str(); |
143 stream << timestamp; | 247 } |
144 | 248 |
145 if (features[i].hasDuration) { | 249 for (unsigned int j = 0; j < f.values.size(); ++j) { |
146 if (m_endTimes) { | 250 stream << m_separator << f.values[j]; |
147 QString endtime = | 251 } |
148 (features[i].timestamp + features[i].duration) | 252 |
149 .toString().c_str(); | 253 if (f.label != "") { |
150 endtime.replace(QRegExp("^ +"), ""); | 254 stream << m_separator << "\"" << f.label.c_str() << "\""; |
151 stream << m_separator << endtime; | 255 } |
152 } else { | 256 |
153 QString duration = features[i].duration.toString().c_str(); | 257 stream << "\n"; |
154 duration.replace(QRegExp("^ +"), ""); | 258 } |
155 stream << m_separator << duration; | 259 |
156 } | 260 |
157 } | |
158 } | |
159 | |
160 if (summaryType != "") { | |
161 stream << m_separator << summaryType.c_str(); | |
162 } | |
163 | |
164 for (unsigned int j = 0; j < features[i].values.size(); ++j) { | |
165 stream << m_separator << features[i].values[j]; | |
166 } | |
167 | |
168 if (features[i].label != "") { | |
169 stream << m_separator << "\"" << features[i].label.c_str() << "\""; | |
170 } | |
171 | |
172 stream << "\n"; | |
173 } | |
174 } | |
175 | |
176 |