comparison src/Silvet.cpp @ 55:384338fa460d preshift

Support shifts as an additional dimension (as in the original model). Also return velocity as well.
author Chris Cannam
date Tue, 08 Apr 2014 13:30:32 +0100
parents 22553e7b2a63
children 49c0a139f165
comparison
equal deleted inserted replaced
54:a54df67e607e 55:384338fa460d
15 15
16 #include "Silvet.h" 16 #include "Silvet.h"
17 #include "EM.h" 17 #include "EM.h"
18 18
19 #include "maths/MedianFilter.h" 19 #include "maths/MedianFilter.h"
20 #include "maths/MathUtilities.h"
20 #include "dsp/rateconversion/Resampler.h" 21 #include "dsp/rateconversion/Resampler.h"
21 22
22 #include "constant-q-cpp/cpp-qm-dsp/CQInterpolated.h" 23 #include "constant-q-cpp/cpp-qm-dsp/CQInterpolated.h"
23 24
24 #include <vector> 25 #include <vector>
33 34
34 static int processingSampleRate = 44100; 35 static int processingSampleRate = 44100;
35 static int processingBPO = 60; 36 static int processingBPO = 60;
36 static int processingHeight = 545; 37 static int processingHeight = 545;
37 static int processingNotes = 88; 38 static int processingNotes = 88;
38 static int processingShifts = 5;
39 static int processingPitches = processingNotes * processingShifts;
40 39
41 Silvet::Silvet(float inputSampleRate) : 40 Silvet::Silvet(float inputSampleRate) :
42 Plugin(inputSampleRate), 41 Plugin(inputSampleRate),
43 m_resampler(0), 42 m_resampler(0),
44 m_cq(0) 43 m_cq(0)
233 d.identifier = "pitches"; 232 d.identifier = "pitches";
234 d.name = "Pitch activation"; 233 d.name = "Pitch activation";
235 d.description = "Estimated pitch activation matrix"; 234 d.description = "Estimated pitch activation matrix";
236 d.unit = ""; 235 d.unit = "";
237 d.hasFixedBinCount = true; 236 d.hasFixedBinCount = true;
238 d.binCount = processingPitches; 237 d.binCount = processingNotes;
239 d.binNames.clear(); 238 d.binNames.clear();
240 for (int i = 0; i < processingPitches; ++i) { 239 for (int i = 0; i < processingNotes; ++i) {
241 d.binNames.push_back(noteName(i)); 240 d.binNames.push_back(noteName(i));
242 } 241 }
243 d.hasKnownExtents = false; 242 d.hasKnownExtents = false;
244 d.isQuantized = false; 243 d.isQuantized = false;
245 d.sampleType = OutputDescriptor::FixedSampleRate; 244 d.sampleType = OutputDescriptor::FixedSampleRate;
402 em.iterate(filtered[i]); 401 em.iterate(filtered[i]);
403 } 402 }
404 403
405 vector<double> pitches = em.getPitchDistribution(); 404 vector<double> pitches = em.getPitchDistribution();
406 405
407 for (int j = 0; j < processingPitches; ++j) { 406 for (int j = 0; j < processingNotes; ++j) {
408 pitches[j] *= sum; 407 pitches[j] *= sum;
409 } 408 }
410 409
411 Feature f; 410 Feature f;
412 for (int j = 0; j < processingPitches; ++j) { 411 for (int j = 0; j < processingNotes; ++j) {
413 f.values.push_back(float(pitches[j])); 412 f.values.push_back(float(pitches[j]));
414 } 413 }
415 fs[m_pitchOutputNo].push_back(f); 414 fs[m_pitchOutputNo].push_back(f);
416 415
417 FeatureList noteFeatures = postProcess(pitches); 416 FeatureList noteFeatures = postProcess(pitches);
502 Silvet::postProcess(const vector<double> &pitches) 501 Silvet::postProcess(const vector<double> &pitches)
503 { 502 {
504 vector<double> filtered; 503 vector<double> filtered;
505 504
506 for (int j = 0; j < processingNotes; ++j) { 505 for (int j = 0; j < processingNotes; ++j) {
507 double noteSum = 0.0; 506 m_postFilter[j]->push(pitches[j]);
508 for (int s = 0; s < processingShifts; ++s) {
509 double val = pitches[j * processingShifts + s];
510 noteSum += val;
511 }
512 m_postFilter[j]->push(noteSum);
513 filtered.push_back(m_postFilter[j]->get()); 507 filtered.push_back(m_postFilter[j]->get());
514 } 508 }
515 509
516 // Threshold for level and reduce number of candidate pitches 510 // Threshold for level and reduce number of candidate pitches
517 511
523 ValueIndexMap strengths; 517 ValueIndexMap strengths;
524 for (int j = 0; j < processingNotes; ++j) { 518 for (int j = 0; j < processingNotes; ++j) {
525 strengths.insert(ValueIndexMap::value_type(filtered[j], j)); 519 strengths.insert(ValueIndexMap::value_type(filtered[j], j));
526 } 520 }
527 521
528 set<int> active; 522 map<int, double> active;
529 ValueIndexMap::const_iterator si = strengths.end(); 523 ValueIndexMap::const_iterator si = strengths.end();
530 while (int(active.size()) < polyphony) { 524 while (int(active.size()) < polyphony) {
531 --si; 525 --si;
532 if (si->first < threshold) break; 526 if (si->first < threshold) break;
533 cerr << si->second << " : " << si->first << endl; 527 cerr << si->second << " : " << si->first << endl;
534 active.insert(si->second); 528 active[si->second] = si->first;
535 if (si == strengths.begin()) break; 529 if (si == strengths.begin()) break;
536 } 530 }
537 531
538 // Minimum duration pruning, and conversion to notes. We can only 532 // Minimum duration pruning, and conversion to notes. We can only
539 // report notes that have just ended (i.e. that are absent in the 533 // report notes that have just ended (i.e. that are absent in the
554 } 548 }
555 549
556 // we have 25 columns per second 550 // we have 25 columns per second
557 double columnDuration = 1.0 / 25.0; 551 double columnDuration = 1.0 / 25.0;
558 552
559 for (set<int>::const_iterator ni = m_pianoRoll[width-1].begin(); 553 for (map<int, double>::const_iterator ni = m_pianoRoll[width-1].begin();
560 ni != m_pianoRoll[width-1].end(); ++ni) { 554 ni != m_pianoRoll[width-1].end(); ++ni) {
561 555
562 int note = *ni; 556 int note = ni->first;
563 557
564 if (active.find(note) != active.end()) { 558 if (active.find(note) != active.end()) {
565 // the note is still playing 559 // the note is still playing
566 continue; 560 continue;
567 } 561 }
568 562
569 // the note was playing but just ended 563 // the note was playing but just ended
570 int end = width; 564 int end = width;
571 int start = end-1; 565 int start = end-1;
572 566
567 vector<double> strengths;
568
573 while (m_pianoRoll[start].find(note) != m_pianoRoll[start].end()) { 569 while (m_pianoRoll[start].find(note) != m_pianoRoll[start].end()) {
570 strengths.push_back(m_pianoRoll[start][note]);
574 --start; 571 --start;
575 } 572 }
576 ++start; 573 ++start;
577 574
578 int duration = width - start; 575 int duration = width - start;
579 cerr << "duration " << duration << " for just-ended note " << note << endl; 576 cerr << "duration " << duration << " for just-ended note " << note << endl;
580 if (duration < durationThreshold) { 577 if (duration < durationThreshold) {
581 // spurious 578 // spurious
582 continue; 579 continue;
583 } 580 }
581
582 double medianStrength = MathUtilities::median
583 (strengths.data(), strengths.size());
584 int velocity = medianStrength * 2;
585 if (velocity > 127) velocity = 127;
584 586
585 Feature nf; 587 Feature nf;
586 nf.hasTimestamp = true; 588 nf.hasTimestamp = true;
587 nf.timestamp = RealTime::fromSeconds(columnDuration * start); 589 nf.timestamp = RealTime::fromSeconds(columnDuration * start);
588 nf.hasDuration = true; 590 nf.hasDuration = true;
589 nf.duration = RealTime::fromSeconds(columnDuration * duration); 591 nf.duration = RealTime::fromSeconds(columnDuration * duration);
590 nf.values.push_back(noteFrequency(note)); 592 nf.values.push_back(noteFrequency(note));
591 nf.values.push_back(80.f); //!!! todo: calculate velocity 593 nf.values.push_back(velocity);
592 nf.label = noteName(note); 594 nf.label = noteName(note);
593 noteFeatures.push_back(nf); 595 noteFeatures.push_back(nf);
594 } 596 }
595 597
596 m_pianoRoll.push_back(active); 598 m_pianoRoll.push_back(active);