Mercurial > hg > silvet
comparison src/Silvet.cpp @ 336:d25e4aee73d7 livemode
Add onset+offset output; look up shift counts rather than passing them around
author | Chris Cannam |
---|---|
date | Fri, 26 Jun 2015 10:23:54 +0100 |
parents | d861f86f2b17 |
children | 3c6f5d2d33e8 705d807ca2ca |
comparison
equal
deleted
inserted
replaced
335:d861f86f2b17 | 336:d25e4aee73d7 |
---|---|
265 d.isQuantized = false; | 265 d.isQuantized = false; |
266 d.sampleType = OutputDescriptor::VariableSampleRate; | 266 d.sampleType = OutputDescriptor::VariableSampleRate; |
267 d.sampleRate = processingSampleRate / (m_cq ? m_cq->getColumnHop() : 62); | 267 d.sampleRate = processingSampleRate / (m_cq ? m_cq->getColumnHop() : 62); |
268 d.hasDuration = false; | 268 d.hasDuration = false; |
269 m_onsetsOutputNo = list.size(); | 269 m_onsetsOutputNo = list.size(); |
270 list.push_back(d); | |
271 | |
272 d.identifier = "onoffsets"; | |
273 d.name = "Note onsets and offsets"; | |
274 d.description = "Note onsets and offsets as separate events. Each onset event has time, estimated fundamental frequency in Hz, and a synthetic MIDI velocity (1-127) estimated from the strength of the pitch in the mixture. Offsets are represented in the same way but with a velocity of 0."; | |
275 d.unit = "Hz"; | |
276 d.hasFixedBinCount = true; | |
277 d.binCount = 2; | |
278 d.binNames.push_back("Frequency"); | |
279 d.binNames.push_back("Velocity"); | |
280 d.hasKnownExtents = false; | |
281 d.isQuantized = false; | |
282 d.sampleType = OutputDescriptor::VariableSampleRate; | |
283 d.sampleRate = processingSampleRate / (m_cq ? m_cq->getColumnHop() : 62); | |
284 d.hasDuration = false; | |
285 m_onOffsetsOutputNo = list.size(); | |
270 list.push_back(d); | 286 list.push_back(d); |
271 | 287 |
272 d.identifier = "timefreq"; | 288 d.identifier = "timefreq"; |
273 d.name = "Time-frequency distribution"; | 289 d.name = "Time-frequency distribution"; |
274 d.description = "Filtered constant-Q time-frequency distribution as used as input to the expectation-maximisation algorithm."; | 290 d.description = "Filtered constant-Q time-frequency distribution as used as input to the expectation-maximisation algorithm."; |
305 d.hasFixedBinCount = true; | 321 d.hasFixedBinCount = true; |
306 d.binCount = getPack(0).templateNoteCount; | 322 d.binCount = getPack(0).templateNoteCount; |
307 d.binNames.clear(); | 323 d.binNames.clear(); |
308 if (m_cq) { | 324 if (m_cq) { |
309 for (int i = 0; i < getPack(0).templateNoteCount; ++i) { | 325 for (int i = 0; i < getPack(0).templateNoteCount; ++i) { |
310 d.binNames.push_back(getNoteName(i, 0, 1)); | 326 d.binNames.push_back(getNoteName(i, 0)); |
311 } | 327 } |
312 } | 328 } |
313 d.hasKnownExtents = false; | 329 d.hasKnownExtents = false; |
314 d.isQuantized = false; | 330 d.isQuantized = false; |
315 d.sampleType = OutputDescriptor::FixedSampleRate; | 331 d.sampleType = OutputDescriptor::FixedSampleRate; |
379 | 395 |
380 return names[pitch]; | 396 return names[pitch]; |
381 } | 397 } |
382 | 398 |
383 std::string | 399 std::string |
384 Silvet::getNoteName(int note, int shift, int shiftCount) const | 400 Silvet::getNoteName(int note, int shift) const |
385 { | 401 { |
386 string n = getChromaName(note % 12); | 402 string n = getChromaName(note % 12); |
387 | 403 |
388 int oct = (note + 9) / 12; | 404 int oct = (note + 9) / 12; |
389 | 405 |
390 char buf[30]; | 406 char buf[30]; |
391 | 407 |
392 float pshift = 0.f; | 408 float pshift = 0.f; |
409 int shiftCount = getShiftCount(); | |
393 if (shiftCount > 1) { | 410 if (shiftCount > 1) { |
394 // see getNoteFrequency below | 411 // see getNoteFrequency below |
395 pshift = | 412 pshift = |
396 float((shiftCount - shift) - int(shiftCount / 2) - 1) / shiftCount; | 413 float((shiftCount - shift) - int(shiftCount / 2) - 1) / shiftCount; |
397 } | 414 } |
406 | 423 |
407 return buf; | 424 return buf; |
408 } | 425 } |
409 | 426 |
410 float | 427 float |
411 Silvet::getNoteFrequency(int note, int shift, int shiftCount) const | 428 Silvet::getNoteFrequency(int note, int shift) const |
412 { | 429 { |
413 // Convert shift number to a pitch shift. The given shift number | 430 // Convert shift number to a pitch shift. The given shift number |
414 // is an offset into the template array, which starts with some | 431 // is an offset into the template array, which starts with some |
415 // zeros, followed by the template, then some trailing zeros. | 432 // zeros, followed by the template, then some trailing zeros. |
416 // | 433 // |
422 // zeros at the start, which is the low-frequency end), for a | 439 // zeros at the start, which is the low-frequency end), for a |
423 // positive pitch shift; and higher values represent moving it | 440 // positive pitch shift; and higher values represent moving it |
424 // down in pitch, for a negative pitch shift. | 441 // down in pitch, for a negative pitch shift. |
425 | 442 |
426 float pshift = 0.f; | 443 float pshift = 0.f; |
444 int shiftCount = getShiftCount(); | |
427 if (shiftCount > 1) { | 445 if (shiftCount > 1) { |
428 pshift = | 446 pshift = |
429 float((shiftCount - shift) - int(shiftCount / 2) - 1) / shiftCount; | 447 float((shiftCount - shift) - int(shiftCount / 2) - 1) / shiftCount; |
430 } | 448 } |
431 | 449 |
601 Silvet::FeatureSet | 619 Silvet::FeatureSet |
602 Silvet::getRemainingFeatures() | 620 Silvet::getRemainingFeatures() |
603 { | 621 { |
604 Grid cqout = m_cq->getRemainingOutput(); | 622 Grid cqout = m_cq->getRemainingOutput(); |
605 FeatureSet fs; | 623 FeatureSet fs; |
624 | |
606 if (m_columnCount == 0) { | 625 if (m_columnCount == 0) { |
607 // process() was never called, but we still want these | 626 // process() was never called, but we still want these |
608 insertTemplateFeatures(fs); | 627 insertTemplateFeatures(fs); |
609 } else { | 628 } else { |
629 | |
630 // Complete the transcription | |
631 | |
610 transcribe(cqout, fs); | 632 transcribe(cqout, fs); |
611 } | 633 |
634 // And make sure any extant playing notes are finished and returned | |
635 | |
636 m_pianoRoll.push_back({}); | |
637 | |
638 auto events = noteTrack(); | |
639 | |
640 for (const auto &f : events.notes) { | |
641 fs[m_notesOutputNo].push_back(f); | |
642 } | |
643 | |
644 for (const auto &f : events.onsets) { | |
645 fs[m_onsetsOutputNo].push_back(f); | |
646 } | |
647 | |
648 for (const auto &f : events.onOffsets) { | |
649 fs[m_onOffsetsOutputNo].push_back(f); | |
650 } | |
651 } | |
652 | |
612 return fs; | 653 return fs; |
613 } | 654 } |
614 | 655 |
615 void | 656 void |
616 Silvet::insertTemplateFeatures(FeatureSet &fs) | 657 Silvet::insertTemplateFeatures(FeatureSet &fs) |
628 .data[i % pack.templateNoteCount]; | 669 .data[i % pack.templateNoteCount]; |
629 fs[m_templateOutputNo].push_back(f); | 670 fs[m_templateOutputNo].push_back(f); |
630 } | 671 } |
631 } | 672 } |
632 | 673 |
674 int | |
675 Silvet::getShiftCount() const | |
676 { | |
677 bool wantShifts = (m_mode == HighQualityMode) && m_fineTuning; | |
678 int shiftCount = 1; | |
679 if (wantShifts) { | |
680 const InstrumentPack &pack(getPack(m_instrument)); | |
681 shiftCount = pack.templateMaxShift * 2 + 1; | |
682 } | |
683 return shiftCount; | |
684 } | |
685 | |
633 void | 686 void |
634 Silvet::transcribe(const Grid &cqout, Silvet::FeatureSet &fs) | 687 Silvet::transcribe(const Grid &cqout, Silvet::FeatureSet &fs) |
635 { | 688 { |
636 Grid filtered = preProcess(cqout); | 689 Grid filtered = preProcess(cqout); |
637 | 690 |
665 fs[m_fcqOutputNo].push_back(f); | 718 fs[m_fcqOutputNo].push_back(f); |
666 } | 719 } |
667 | 720 |
668 Grid localPitches(width); | 721 Grid localPitches(width); |
669 | 722 |
670 bool wantShifts = (m_mode == HighQualityMode) && m_fineTuning; | 723 int shiftCount = getShiftCount(); |
671 int shiftCount = 1; | 724 bool wantShifts = (shiftCount > 1); |
672 if (wantShifts) { | |
673 shiftCount = pack.templateMaxShift * 2 + 1; | |
674 } | |
675 | 725 |
676 vector<vector<int> > localBestShifts; | 726 vector<vector<int> > localBestShifts; |
677 if (wantShifts) { | 727 if (wantShifts) { |
678 localBestShifts = vector<vector<int> >(width); | 728 localBestShifts = vector<vector<int> >(width); |
679 } | 729 } |
695 vector<EMFuture> results; | 745 vector<EMFuture> results; |
696 for (int j = 0; j < emThreadCount && i + j < width; ++j) { | 746 for (int j = 0; j < emThreadCount && i + j < width; ++j) { |
697 results.push_back | 747 results.push_back |
698 (async(std::launch::async, | 748 (async(std::launch::async, |
699 [&](int index) { | 749 [&](int index) { |
700 return applyEM | 750 return applyEM(pack, filtered.at(index)); |
701 (pack, filtered.at(index), wantShifts); | |
702 }, i + j)); | 751 }, i + j)); |
703 } | 752 } |
704 for (int j = 0; j < emThreadCount && i + j < width; ++j) { | 753 for (int j = 0; j < emThreadCount && i + j < width; ++j) { |
705 auto out = results[j].get(); | 754 auto out = results[j].get(); |
706 localPitches[i+j] = out.first; | 755 localPitches[i+j] = out.first; |
711 } | 760 } |
712 #endif | 761 #endif |
713 | 762 |
714 if (emThreadCount == 1) { | 763 if (emThreadCount == 1) { |
715 for (int i = 0; i < width; ++i) { | 764 for (int i = 0; i < width; ++i) { |
716 auto out = applyEM(pack, filtered.at(i), wantShifts); | 765 auto out = applyEM(pack, filtered.at(i)); |
717 localPitches[i] = out.first; | 766 localPitches[i] = out.first; |
718 if (wantShifts) localBestShifts[i] = out.second; | 767 if (wantShifts) localBestShifts[i] = out.second; |
719 } | 768 } |
720 } | 769 } |
721 | 770 |
746 } | 795 } |
747 fs[m_chromaOutputNo].push_back(f); | 796 fs[m_chromaOutputNo].push_back(f); |
748 | 797 |
749 // This pushes the up-to-max-polyphony activation column to | 798 // This pushes the up-to-max-polyphony activation column to |
750 // m_pianoRoll | 799 // m_pianoRoll |
751 postProcess(filtered, localBestShifts[i], wantShifts); | 800 postProcess(filtered, localBestShifts[i]); |
752 | 801 |
753 auto events = noteTrack(shiftCount); | 802 auto events = noteTrack(); |
754 | 803 |
755 FeatureList noteFeatures = events.first; | 804 for (const auto &f : events.notes) { |
756 for (FeatureList::const_iterator fi = noteFeatures.begin(); | 805 fs[m_notesOutputNo].push_back(f); |
757 fi != noteFeatures.end(); ++fi) { | 806 } |
758 fs[m_notesOutputNo].push_back(*fi); | 807 |
759 } | 808 for (const auto &f : events.onsets) { |
760 | 809 fs[m_onsetsOutputNo].push_back(f); |
761 FeatureList onsetFeatures = events.second; | 810 } |
762 for (FeatureList::const_iterator fi = onsetFeatures.begin(); | 811 |
763 fi != onsetFeatures.end(); ++fi) { | 812 for (const auto &f : events.onOffsets) { |
764 fs[m_onsetsOutputNo].push_back(*fi); | 813 fs[m_onOffsetsOutputNo].push_back(f); |
765 } | 814 } |
766 } | 815 } |
767 } | 816 } |
768 | 817 |
769 pair<vector<double>, vector<int> > | 818 pair<vector<double>, vector<int> > |
770 Silvet::applyEM(const InstrumentPack &pack, | 819 Silvet::applyEM(const InstrumentPack &pack, |
771 const vector<double> &column, | 820 const vector<double> &column) |
772 bool wantShifts) | |
773 { | 821 { |
774 double columnThreshold = 1e-5; | 822 double columnThreshold = 1e-5; |
775 | 823 |
776 if (m_mode == LiveMode) { | 824 if (m_mode == LiveMode) { |
777 columnThreshold /= 15; | 825 columnThreshold /= 15; |
800 } | 848 } |
801 | 849 |
802 const float *pitchDist = em.getPitchDistribution(); | 850 const float *pitchDist = em.getPitchDistribution(); |
803 const float *const *shiftDist = em.getShifts(); | 851 const float *const *shiftDist = em.getShifts(); |
804 | 852 |
805 int shiftCount = 1; | 853 int shiftCount = getShiftCount(); |
806 if (wantShifts) { | |
807 shiftCount = pack.templateMaxShift * 2 + 1; | |
808 } | |
809 | 854 |
810 for (int j = 0; j < pack.templateNoteCount; ++j) { | 855 for (int j = 0; j < pack.templateNoteCount; ++j) { |
811 | 856 |
812 pitches[j] = pitchDist[j] * sum; | 857 pitches[j] = pitchDist[j] * sum; |
813 | 858 |
814 int bestShift = 0; | 859 int bestShift = 0; |
815 float bestShiftValue = 0.0; | 860 float bestShiftValue = 0.0; |
816 if (wantShifts) { | 861 if (shiftCount > 1) { |
817 for (int k = 0; k < shiftCount; ++k) { | 862 for (int k = 0; k < shiftCount; ++k) { |
818 float value = shiftDist[k][j]; | 863 float value = shiftDist[k][j]; |
819 if (k == 0 || value > bestShiftValue) { | 864 if (k == 0 || value > bestShiftValue) { |
820 bestShiftValue = value; | 865 bestShiftValue = value; |
821 bestShift = k; | 866 bestShift = k; |
915 return out; | 960 return out; |
916 } | 961 } |
917 | 962 |
918 void | 963 void |
919 Silvet::postProcess(const vector<double> &pitches, | 964 Silvet::postProcess(const vector<double> &pitches, |
920 const vector<int> &bestShifts, | 965 const vector<int> &bestShifts) |
921 bool wantShifts) | |
922 { | 966 { |
923 const InstrumentPack &pack(getPack(m_instrument)); | 967 const InstrumentPack &pack(getPack(m_instrument)); |
924 | 968 |
925 // Threshold for level and reduce number of candidate pitches | 969 // Threshold for level and reduce number of candidate pitches |
926 | 970 |
958 ValueIndexMap::const_iterator si = strengths.end(); | 1002 ValueIndexMap::const_iterator si = strengths.end(); |
959 | 1003 |
960 map<int, double> active; | 1004 map<int, double> active; |
961 map<int, int> activeShifts; | 1005 map<int, int> activeShifts; |
962 | 1006 |
1007 int shiftCount = getShiftCount(); | |
1008 | |
963 while (int(active.size()) < pack.maxPolyphony && si != strengths.begin()) { | 1009 while (int(active.size()) < pack.maxPolyphony && si != strengths.begin()) { |
964 | 1010 |
965 --si; | 1011 --si; |
966 | 1012 |
967 double strength = si->first; | 1013 double strength = si->first; |
968 int j = si->second; | 1014 int j = si->second; |
969 | 1015 |
970 active[j] = strength; | 1016 active[j] = strength; |
971 | 1017 |
972 if (wantShifts) { | 1018 if (shiftCount > 1) { |
973 activeShifts[j] = bestShifts[j]; | 1019 activeShifts[j] = bestShifts[j]; |
974 } | 1020 } |
975 } | 1021 } |
976 | 1022 |
977 m_pianoRoll.push_back(active); | 1023 m_pianoRoll.push_back(active); |
978 | 1024 |
979 if (wantShifts) { | 1025 if (shiftCount > 1) { |
980 m_pianoRollShifts.push_back(activeShifts); | 1026 m_pianoRollShifts.push_back(activeShifts); |
981 } | 1027 } |
982 | 1028 |
983 return; | 1029 return; |
984 } | 1030 } |
985 | 1031 |
986 pair<Vamp::Plugin::FeatureList, Vamp::Plugin::FeatureList> | 1032 Silvet::FeatureChunk |
987 Silvet::noteTrack(int shiftCount) | 1033 Silvet::noteTrack() |
988 { | 1034 { |
989 // Minimum duration pruning, and conversion to notes. We can only | 1035 // Minimum duration pruning, and conversion to notes. We can only |
990 // report notes that have just ended (i.e. that are absent in the | 1036 // report notes that have just ended (i.e. that are absent in the |
991 // latest active set but present in the prior set in the piano | 1037 // latest active set but present in the prior set in the piano |
992 // roll) -- any notes that ended earlier will have been reported | 1038 // roll) -- any notes that ended earlier will have been reported |
1002 // only keep notes >= 100ms or thereabouts | 1048 // only keep notes >= 100ms or thereabouts |
1003 double durationThrSec = 0.1; | 1049 double durationThrSec = 0.1; |
1004 int durationThreshold = floor(durationThrSec / columnDuration); // in cols | 1050 int durationThreshold = floor(durationThrSec / columnDuration); // in cols |
1005 if (durationThreshold < 1) durationThreshold = 1; | 1051 if (durationThreshold < 1) durationThreshold = 1; |
1006 | 1052 |
1007 FeatureList noteFeatures, onsetFeatures; | 1053 FeatureList noteFeatures, onsetFeatures, onOffsetFeatures; |
1008 | 1054 |
1009 if (width < durationThreshold + 1) { | 1055 if (width < durationThreshold + 1) { |
1010 return { noteFeatures, onsetFeatures }; | 1056 return { noteFeatures, onsetFeatures, onOffsetFeatures }; |
1011 } | 1057 } |
1012 | 1058 |
1013 for (map<int, double>::const_iterator ni = m_pianoRoll[width-1].begin(); | 1059 for (map<int, double>::const_iterator ni = m_pianoRoll[width-1].begin(); |
1014 ni != m_pianoRoll[width-1].end(); ++ni) { | 1060 ni != m_pianoRoll[width-1].end(); ++ni) { |
1015 | 1061 |
1029 continue; | 1075 continue; |
1030 } | 1076 } |
1031 | 1077 |
1032 if (duration == durationThreshold) { | 1078 if (duration == durationThreshold) { |
1033 m_current.insert(note); | 1079 m_current.insert(note); |
1034 emitOnset(start, note, shiftCount, onsetFeatures); | 1080 emitOnset(start, note, onsetFeatures); |
1081 emitOnset(start, note, onOffsetFeatures); | |
1035 } | 1082 } |
1036 | 1083 |
1037 if (active.find(note) == active.end()) { | 1084 if (active.find(note) == active.end()) { |
1038 // the note was playing but just ended | 1085 // the note was playing but just ended |
1039 m_current.erase(note); | 1086 m_current.erase(note); |
1040 emitNote(start, end, note, shiftCount, noteFeatures); | 1087 emitNote(start, end, note, noteFeatures); |
1088 emitOffset(start, end, note, onOffsetFeatures); | |
1041 } else { // still playing | 1089 } else { // still playing |
1042 // repeated note detection: if level is greater than this | 1090 // repeated note detection: if level is greater than this |
1043 // multiple of its previous value, then we end the note and | 1091 // multiple of its previous value, then we end the note and |
1044 // restart it with the same pitch | 1092 // restart it with the same pitch |
1045 double restartFactor = 1.5; | 1093 double restartFactor = 1.5; |
1046 if (duration >= durationThreshold * 2 && | 1094 if (duration >= durationThreshold * 2 && |
1047 (active.find(note)->second > | 1095 (active.find(note)->second > |
1048 restartFactor * m_pianoRoll[width-1][note])) { | 1096 restartFactor * m_pianoRoll[width-1][note])) { |
1049 m_current.erase(note); | 1097 m_current.erase(note); |
1050 emitNote(start, end-1, note, shiftCount, noteFeatures); | 1098 emitNote(start, end-1, note, noteFeatures); |
1099 emitOffset(start, end-1, note, onOffsetFeatures); | |
1051 // and remove this so that we start counting the new | 1100 // and remove this so that we start counting the new |
1052 // note's duration from the current position | 1101 // note's duration from the current position |
1053 m_pianoRoll[width-1].erase(note); | 1102 m_pianoRoll[width-1].erase(note); |
1054 } | 1103 } |
1055 } | 1104 } |
1056 } | 1105 } |
1057 | 1106 |
1058 // cerr << "returning " << noteFeatures.size() << " complete note(s) " << endl; | 1107 // cerr << "returning " << noteFeatures.size() << " complete note(s) " << endl; |
1059 | 1108 |
1060 return { noteFeatures, onsetFeatures }; | 1109 return { noteFeatures, onsetFeatures, onOffsetFeatures }; |
1061 } | 1110 } |
1062 | 1111 |
1063 void | 1112 void |
1064 Silvet::emitNote(int start, int end, int note, int shiftCount, | 1113 Silvet::emitNote(int start, int end, int note, FeatureList ¬eFeatures) |
1065 FeatureList ¬eFeatures) | |
1066 { | 1114 { |
1067 int partStart = start; | 1115 int partStart = start; |
1068 int partShift = 0; | 1116 int partShift = 0; |
1069 double partStrength = 0; | 1117 double partStrength = 0; |
1070 | 1118 |
1074 | 1122 |
1075 double strength = m_pianoRoll[i][note]; | 1123 double strength = m_pianoRoll[i][note]; |
1076 | 1124 |
1077 int shift = 0; | 1125 int shift = 0; |
1078 | 1126 |
1079 if (shiftCount > 1) { | 1127 if (getShiftCount() > 1) { |
1080 | 1128 |
1081 shift = m_pianoRollShifts[i][note]; | 1129 shift = m_pianoRollShifts[i][note]; |
1082 | 1130 |
1083 if (i == partStart) { | 1131 if (i == partStart) { |
1084 partShift = shift; | 1132 partShift = shift; |
1091 // pitch has changed, emit an intermediate note | 1139 // pitch has changed, emit an intermediate note |
1092 noteFeatures.push_back(makeNoteFeature(partStart, | 1140 noteFeatures.push_back(makeNoteFeature(partStart, |
1093 i, | 1141 i, |
1094 note, | 1142 note, |
1095 partShift, | 1143 partShift, |
1096 shiftCount, | |
1097 partStrength)); | 1144 partStrength)); |
1098 partStart = i; | 1145 partStart = i; |
1099 partShift = shift; | 1146 partShift = shift; |
1100 partStrength = 0; | 1147 partStrength = 0; |
1101 } | 1148 } |
1109 if (end >= partStart + partThreshold) { | 1156 if (end >= partStart + partThreshold) { |
1110 noteFeatures.push_back(makeNoteFeature(partStart, | 1157 noteFeatures.push_back(makeNoteFeature(partStart, |
1111 end, | 1158 end, |
1112 note, | 1159 note, |
1113 partShift, | 1160 partShift, |
1114 shiftCount, | |
1115 partStrength)); | 1161 partStrength)); |
1116 } | 1162 } |
1117 } | 1163 } |
1118 | 1164 |
1119 void | 1165 void |
1120 Silvet::emitOnset(int start, int note, int shiftCount, | 1166 Silvet::emitOnset(int start, int note, FeatureList &onOffsetFeatures) |
1121 FeatureList &onsetFeatures) | |
1122 { | 1167 { |
1123 int len = int(m_pianoRoll.size()); | 1168 int len = int(m_pianoRoll.size()); |
1124 | 1169 |
1125 double onsetStrength = 0; | 1170 double onsetStrength = 0; |
1126 | 1171 |
1127 int shift = 0; | 1172 int shift = 0; |
1128 if (shiftCount > 1) { | 1173 if (getShiftCount() > 1) { |
1129 shift = m_pianoRollShifts[start][note]; | 1174 shift = m_pianoRollShifts[start][note]; |
1130 } | 1175 } |
1131 | 1176 |
1132 for (int i = start; i < len; ++i) { | 1177 for (int i = start; i < len; ++i) { |
1133 double strength = m_pianoRoll[i][note]; | 1178 double strength = m_pianoRoll[i][note]; |
1134 if (strength > onsetStrength) { | 1179 if (strength > onsetStrength) { |
1135 onsetStrength = strength; | 1180 onsetStrength = strength; |
1136 } | 1181 } |
1137 } | 1182 } |
1138 | 1183 |
1139 onsetFeatures.push_back(makeOnsetFeature(start, | 1184 if (onsetStrength == 0) return; |
1140 note, | 1185 |
1141 shift, | 1186 onOffsetFeatures.push_back(makeOnsetFeature(start, |
1142 shiftCount, | 1187 note, |
1143 onsetStrength)); | 1188 shift, |
1189 onsetStrength)); | |
1190 } | |
1191 | |
1192 void | |
1193 Silvet::emitOffset(int start, int end, int note, FeatureList &onOffsetFeatures) | |
1194 { | |
1195 int shift = 0; | |
1196 if (getShiftCount() > 1) { | |
1197 shift = m_pianoRollShifts[start][note]; | |
1198 } | |
1199 | |
1200 onOffsetFeatures.push_back(makeOffsetFeature(end, | |
1201 note, | |
1202 shift)); | |
1144 } | 1203 } |
1145 | 1204 |
1146 RealTime | 1205 RealTime |
1147 Silvet::getColumnTimestamp(int column) | 1206 Silvet::getColumnTimestamp(int column) |
1148 { | 1207 { |
1156 Silvet::Feature | 1215 Silvet::Feature |
1157 Silvet::makeNoteFeature(int start, | 1216 Silvet::makeNoteFeature(int start, |
1158 int end, | 1217 int end, |
1159 int note, | 1218 int note, |
1160 int shift, | 1219 int shift, |
1161 int shiftCount, | |
1162 double strength) | 1220 double strength) |
1163 { | 1221 { |
1164 Feature f; | 1222 Feature f; |
1165 | 1223 |
1166 f.hasTimestamp = true; | 1224 f.hasTimestamp = true; |
1168 | 1226 |
1169 f.hasDuration = true; | 1227 f.hasDuration = true; |
1170 f.duration = getColumnTimestamp(end) - f.timestamp; | 1228 f.duration = getColumnTimestamp(end) - f.timestamp; |
1171 | 1229 |
1172 f.values.clear(); | 1230 f.values.clear(); |
1173 f.values.push_back(getNoteFrequency(note, shift, shiftCount)); | 1231 f.values.push_back(getNoteFrequency(note, shift)); |
1174 f.values.push_back(getVelocityFor(strength, start)); | 1232 f.values.push_back(getVelocityFor(strength, start)); |
1175 | 1233 |
1176 f.label = getNoteName(note, shift, shiftCount); | 1234 f.label = getNoteName(note, shift); |
1177 | 1235 |
1178 return f; | 1236 return f; |
1179 } | 1237 } |
1180 | 1238 |
1181 Silvet::Feature | 1239 Silvet::Feature |
1182 Silvet::makeOnsetFeature(int start, | 1240 Silvet::makeOnsetFeature(int start, |
1183 int note, | 1241 int note, |
1184 int shift, | 1242 int shift, |
1185 int shiftCount, | |
1186 double strength) | 1243 double strength) |
1187 { | 1244 { |
1188 Feature f; | 1245 Feature f; |
1189 | 1246 |
1190 f.hasTimestamp = true; | 1247 f.hasTimestamp = true; |
1191 f.timestamp = getColumnTimestamp(start); | 1248 f.timestamp = getColumnTimestamp(start); |
1192 | 1249 |
1193 f.hasDuration = false; | 1250 f.hasDuration = false; |
1194 | 1251 |
1195 f.values.clear(); | 1252 f.values.clear(); |
1196 f.values.push_back(getNoteFrequency(note, shift, shiftCount)); | 1253 f.values.push_back(getNoteFrequency(note, shift)); |
1197 f.values.push_back(getVelocityFor(strength, start)); | 1254 f.values.push_back(getVelocityFor(strength, start)); |
1198 | 1255 |
1199 f.label = getNoteName(note, shift, shiftCount); | 1256 f.label = getNoteName(note, shift); |
1257 | |
1258 return f; | |
1259 } | |
1260 | |
1261 Silvet::Feature | |
1262 Silvet::makeOffsetFeature(int col, | |
1263 int note, | |
1264 int shift) | |
1265 { | |
1266 Feature f; | |
1267 | |
1268 f.hasTimestamp = true; | |
1269 f.timestamp = getColumnTimestamp(col); | |
1270 | |
1271 f.hasDuration = false; | |
1272 | |
1273 f.values.clear(); | |
1274 f.values.push_back(getNoteFrequency(note, shift)); | |
1275 f.values.push_back(0); // velocity 0 for offset | |
1276 | |
1277 f.label = getNoteName(note, shift) + " off"; | |
1200 | 1278 |
1201 return f; | 1279 return f; |
1202 } | 1280 } |
1203 | 1281 |
1204 int | 1282 int |