comparison src/Silvet.cpp @ 309:07ee4ebea57c

Add chromagram output
author Chris Cannam
date Mon, 19 Jan 2015 11:23:07 +0000
parents 04a3c152e590
children 99af9557dfc1
comparison
equal deleted inserted replaced
305:04a3c152e590 309:07ee4ebea57c
86 } 86 }
87 87
88 int 88 int
89 Silvet::getPluginVersion() const 89 Silvet::getPluginVersion() const
90 { 90 {
91 return 2; 91 return 3;
92 } 92 }
93 93
94 string 94 string
95 Silvet::getCopyright() const 95 Silvet::getCopyright() const
96 { 96 {
288 d.sampleRate = m_colsPerSec; 288 d.sampleRate = m_colsPerSec;
289 d.hasDuration = false; 289 d.hasDuration = false;
290 m_pitchOutputNo = list.size(); 290 m_pitchOutputNo = list.size();
291 list.push_back(d); 291 list.push_back(d);
292 292
293 d.identifier = "chroma";
294 d.name = "Pitch chroma distribution";
295 d.description = "Pitch chroma distribution formed by wrapping the un-thresholded pitch activation distribution into a single octave of semitone bins.";
296 d.unit = "";
297 d.hasFixedBinCount = true;
298 d.binCount = 12;
299 d.binNames.clear();
300 if (m_cq) {
301 for (int i = 0; i < 12; ++i) {
302 d.binNames.push_back(chromaName(i));
303 }
304 }
305 d.hasKnownExtents = false;
306 d.isQuantized = false;
307 d.sampleType = OutputDescriptor::FixedSampleRate;
308 d.sampleRate = m_colsPerSec;
309 d.hasDuration = false;
310 m_chromaOutputNo = list.size();
311 list.push_back(d);
312
293 return list; 313 return list;
294 } 314 }
295 315
296 std::string 316 std::string
297 Silvet::noteName(int note, int shift, int shiftCount) const 317 Silvet::chromaName(int pitch) const
298 { 318 {
299 static const char *names[] = { 319 static const char *names[] = {
300 "A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#" 320 "A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#"
301 }; 321 };
302 322
303 const char *n = names[note % 12]; 323 return names[pitch];
324 }
325
326 std::string
327 Silvet::noteName(int note, int shift, int shiftCount) const
328 {
329 string n = chromaName(note % 12);
304 330
305 int oct = (note + 9) / 12; 331 int oct = (note + 9) / 12;
306 332
307 char buf[30]; 333 char buf[30];
308 334
312 pshift = 338 pshift =
313 float((shiftCount - shift) - int(shiftCount / 2) - 1) / shiftCount; 339 float((shiftCount - shift) - int(shiftCount / 2) - 1) / shiftCount;
314 } 340 }
315 341
316 if (pshift > 0.f) { 342 if (pshift > 0.f) {
317 sprintf(buf, "%s%d+%dc", n, oct, int(round(pshift * 100))); 343 sprintf(buf, "%s%d+%dc", n.c_str(), oct, int(round(pshift * 100)));
318 } else if (pshift < 0.f) { 344 } else if (pshift < 0.f) {
319 sprintf(buf, "%s%d-%dc", n, oct, int(round((-pshift) * 100))); 345 sprintf(buf, "%s%d-%dc", n.c_str(), oct, int(round((-pshift) * 100)));
320 } else { 346 } else {
321 sprintf(buf, "%s%d", n, oct); 347 sprintf(buf, "%s%d", n.c_str(), oct);
322 } 348 }
323 349
324 return buf; 350 return buf;
325 } 351 }
326 352
573 } 599 }
574 } 600 }
575 601
576 for (int i = 0; i < width; ++i) { 602 for (int i = 0; i < width; ++i) {
577 603
604 // This returns a filtered column, and pushes the
605 // up-to-max-polyphony activation column to m_pianoRoll
578 vector<double> filtered = postProcess 606 vector<double> filtered = postProcess
579 (localPitches[i], localBestShifts[i], wantShifts); 607 (localPitches[i], localBestShifts[i], wantShifts);
580 608
609 RealTime timestamp = getColumnTimestamp(m_pianoRoll.size() - 1);
610 float inputGain = getInputGainAt(timestamp);
611
581 Feature f; 612 Feature f;
582 for (int j = 0; j < (int)filtered.size(); ++j) { 613 for (int j = 0; j < (int)filtered.size(); ++j) {
583 float v(filtered[j]); 614 float v = filtered[j];
584 if (v < pack.levelThreshold) v = 0.f; 615 if (v < pack.levelThreshold) v = 0.f;
585 f.values.push_back(v); 616 f.values.push_back(v / inputGain);
586 } 617 }
587 fs[m_pitchOutputNo].push_back(f); 618 fs[m_pitchOutputNo].push_back(f);
619
620 f.values.clear();
621 f.values.resize(12);
622 for (int j = 0; j < (int)filtered.size(); ++j) {
623 f.values[j % 12] += filtered[j] / inputGain;
624 }
625 fs[m_chromaOutputNo].push_back(f);
588 626
589 FeatureList noteFeatures = noteTrack(shiftCount); 627 FeatureList noteFeatures = noteTrack(shiftCount);
590 628
591 for (FeatureList::const_iterator fi = noteFeatures.begin(); 629 for (FeatureList::const_iterator fi = noteFeatures.begin();
592 fi != noteFeatures.end(); ++fi) { 630 fi != noteFeatures.end(); ++fi) {
849 shiftCount, 887 shiftCount,
850 partVelocity)); 888 partVelocity));
851 } 889 }
852 } 890 }
853 891
892 RealTime
893 Silvet::getColumnTimestamp(int column)
894 {
895 double columnDuration = 1.0 / m_colsPerSec;
896 int postFilterLatency = int(m_postFilter[0]->getSize() / 2);
897
898 return m_startTime + RealTime::fromSeconds
899 (columnDuration * (column - postFilterLatency) + 0.02);
900 }
901
854 Silvet::Feature 902 Silvet::Feature
855 Silvet::makeNoteFeature(int start, 903 Silvet::makeNoteFeature(int start,
856 int end, 904 int end,
857 int note, 905 int note,
858 int shift, 906 int shift,
859 int shiftCount, 907 int shiftCount,
860 int velocity) 908 int velocity)
861 { 909 {
862 double columnDuration = 1.0 / m_colsPerSec;
863 int postFilterLatency = int(m_postFilter[0]->getSize() / 2);
864
865 Feature f; 910 Feature f;
866 911
867 f.hasTimestamp = true; 912 f.hasTimestamp = true;
868 f.timestamp = m_startTime + RealTime::fromSeconds 913 f.timestamp = getColumnTimestamp(start);
869 (columnDuration * (start - postFilterLatency) + 0.02);
870 914
871 f.hasDuration = true; 915 f.hasDuration = true;
872 f.duration = RealTime::fromSeconds 916 f.duration = getColumnTimestamp(end) - f.timestamp;
873 (columnDuration * (end - start));
874 917
875 f.values.clear(); 918 f.values.clear();
876 919
877 f.values.push_back 920 f.values.push_back
878 (noteFrequency(note, shift, shiftCount)); 921 (noteFrequency(note, shift, shiftCount));