comparison src/Silvet.cpp @ 166:7979fa40c9f7 finetune

Start working on returning fine-tuning pitches
author Chris Cannam
date Tue, 20 May 2014 17:34:49 +0100
parents f73be84f5c90
children 416b555df3b2
comparison
equal deleted inserted replaced
165:f73be84f5c90 166:7979fa40c9f7
40 Plugin(inputSampleRate), 40 Plugin(inputSampleRate),
41 m_instruments(InstrumentPack::listInstrumentPacks()), 41 m_instruments(InstrumentPack::listInstrumentPacks()),
42 m_resampler(0), 42 m_resampler(0),
43 m_cq(0), 43 m_cq(0),
44 m_hqMode(true), 44 m_hqMode(true),
45 m_fineTuning(false),
45 m_instrument(0) 46 m_instrument(0)
46 { 47 {
47 } 48 }
48 49
49 Silvet::~Silvet() 50 Silvet::~Silvet()
140 desc.minValue = 0; 141 desc.minValue = 0;
141 desc.maxValue = 1; 142 desc.maxValue = 1;
142 desc.defaultValue = 1; 143 desc.defaultValue = 1;
143 desc.isQuantized = true; 144 desc.isQuantized = true;
144 desc.quantizeStep = 1; 145 desc.quantizeStep = 1;
145 desc.valueNames.push_back("Draft (faster)"); 146 desc.valueNames.push_back("Draft (faster)");
146 desc.valueNames.push_back("Intensive (higher quality)"); 147 desc.valueNames.push_back("Intensive (higher quality)");
147 list.push_back(desc); 148 list.push_back(desc);
148 149
149 desc.identifier = "soloinstrument"; 150 desc.identifier = "soloinstrument";
150 desc.name = "Solo instrument"; 151 desc.name = "Solo instrument";
157 desc.quantizeStep = 1; 158 desc.quantizeStep = 1;
158 desc.valueNames.clear(); 159 desc.valueNames.clear();
159 for (int i = 0; i < int(m_instruments.size()); ++i) { 160 for (int i = 0; i < int(m_instruments.size()); ++i) {
160 desc.valueNames.push_back(m_instruments[i].name); 161 desc.valueNames.push_back(m_instruments[i].name);
161 } 162 }
162 163 list.push_back(desc);
164
165 desc.identifier = "finetune";
166 desc.name = "Return fine pitch estimates";
167 desc.unit = "";
168 desc.description = "Return pitch estimates at finer than semitone resolution (works only in Intensive mode)";
169 desc.minValue = 0;
170 desc.maxValue = 1;
171 desc.defaultValue = 0;
172 desc.isQuantized = true;
173 desc.quantizeStep = 1;
174 desc.valueNames.clear();
163 list.push_back(desc); 175 list.push_back(desc);
164 176
165 return list; 177 return list;
166 } 178 }
167 179
168 float 180 float
169 Silvet::getParameter(string identifier) const 181 Silvet::getParameter(string identifier) const
170 { 182 {
171 if (identifier == "mode") { 183 if (identifier == "mode") {
172 return m_hqMode ? 1.f : 0.f; 184 return m_hqMode ? 1.f : 0.f;
185 } else if (identifier == "finetune") {
186 return m_fineTuning ? 1.f : 0.f;
173 } else if (identifier == "soloinstrument") { 187 } else if (identifier == "soloinstrument") {
174 return m_instrument; 188 return m_instrument;
175 } 189 }
176 return 0; 190 return 0;
177 } 191 }
179 void 193 void
180 Silvet::setParameter(string identifier, float value) 194 Silvet::setParameter(string identifier, float value)
181 { 195 {
182 if (identifier == "mode") { 196 if (identifier == "mode") {
183 m_hqMode = (value > 0.5); 197 m_hqMode = (value > 0.5);
198 } else if (identifier == "finetune") {
199 m_fineTuning = (value > 0.5);
184 } else if (identifier == "soloinstrument") { 200 } else if (identifier == "soloinstrument") {
185 m_instrument = lrintf(value); 201 m_instrument = lrintf(value);
186 } 202 }
187 } 203 }
188 204
353 369
354 int width = filtered.size(); 370 int width = filtered.size();
355 371
356 int iterations = m_hqMode ? 20 : 10; 372 int iterations = m_hqMode ? 20 : 10;
357 373
358 Grid pitchMatrix(width, vector<double>(processingNotes)); 374 vector<EM *> em(width, (EM *)0);
375 vector<double> sums(width, 0.0);
359 376
360 #pragma omp parallel for 377 #pragma omp parallel for
361 for (int i = 0; i < width; ++i) { 378 for (int i = 0; i < width; ++i) {
362 379
363 double sum = 0.0;
364 for (int j = 0; j < processingHeight; ++j) { 380 for (int j = 0; j < processingHeight; ++j) {
365 sum += filtered.at(i).at(j); 381 sums[i] += filtered.at(i).at(j);
366 } 382 }
367 383
368 if (sum < 1e-5) continue; 384 if (sums[i] < 1e-5) continue;
369 385
370 EM em(&m_instruments[m_instrument], m_hqMode); 386 em[i] = new EM(&m_instruments[m_instrument], m_hqMode);
371 387
372 for (int j = 0; j < iterations; ++j) { 388 for (int j = 0; j < iterations; ++j) {
373 em.iterate(filtered.at(i).data()); 389 em[i]->iterate(filtered.at(i).data());
374 } 390 }
391 }
375 392
376 const float *pitches = em.getPitchDistribution();
377
378 for (int j = 0; j < processingNotes; ++j) {
379 pitchMatrix[i][j] = pitches[j] * sum;
380 }
381 }
382
383 for (int i = 0; i < width; ++i) { 393 for (int i = 0; i < width; ++i) {
394
395 if (!em[i]) {
396 noteTrack(map<int, double>());
397 continue;
398 }
399
400 map<int, double> active = postProcess(em[i]->getPitchDistribution(),
401 em[i]->getShifts(),
402 em[i]->getShiftCount(),
403 sums[i]);
384 404
385 FeatureList noteFeatures = postProcess(pitchMatrix[i]); 405 delete em[i];
406
407 FeatureList noteFeatures = noteTrack(active);
386 408
387 for (FeatureList::const_iterator fi = noteFeatures.begin(); 409 for (FeatureList::const_iterator fi = noteFeatures.begin();
388 fi != noteFeatures.end(); ++fi) { 410 fi != noteFeatures.end(); ++fi) {
389 fs[m_notesOutputNo].push_back(*fi); 411 fs[m_notesOutputNo].push_back(*fi);
390 } 412 }
458 } 480 }
459 481
460 return out; 482 return out;
461 } 483 }
462 484
463 Vamp::Plugin::FeatureList 485 map<int, double>
464 Silvet::postProcess(const vector<double> &pitches) 486 Silvet::postProcess(const float *pitches,
465 { 487 const float *const *shifts,
488 int shiftCount,
489 double gain)
490 {
466 vector<double> filtered; 491 vector<double> filtered;
467 492
468 for (int j = 0; j < processingNotes; ++j) { 493 for (int j = 0; j < processingNotes; ++j) {
469 m_postFilter[j]->push(pitches[j]); 494 m_postFilter[j]->push(pitches[j] * gain);
470 filtered.push_back(m_postFilter[j]->get()); 495 filtered.push_back(m_postFilter[j]->get());
471 } 496 }
472
473 int postFilterLatency = int(m_postFilter[0]->getSize() / 2);
474 497
475 // Threshold for level and reduce number of candidate pitches 498 // Threshold for level and reduce number of candidate pitches
476 499
477 int polyphony = 5; 500 int polyphony = 5;
478 501
481 // double threshold = 4.8; 504 // double threshold = 4.8;
482 505
483 typedef std::multimap<double, int> ValueIndexMap; 506 typedef std::multimap<double, int> ValueIndexMap;
484 507
485 ValueIndexMap strengths; 508 ValueIndexMap strengths;
509
486 for (int j = 0; j < processingNotes; ++j) { 510 for (int j = 0; j < processingNotes; ++j) {
487 strengths.insert(ValueIndexMap::value_type(filtered[j], j)); 511
512 double strength = filtered[j];
513 if (strength < threshold) continue;
514
515 // convert note number j to a pitch value p. If we are not using fine tuning or
516
517 strengths.insert(ValueIndexMap::value_type(strength, j));
488 } 518 }
489 519
490 map<int, double> active; 520 map<int, double> active;
491 ValueIndexMap::const_iterator si = strengths.end(); 521 ValueIndexMap::const_iterator si = strengths.end();
492 while (int(active.size()) < polyphony) { 522 while (int(active.size()) < polyphony && si != strengths.begin()) {
493 --si; 523 --si;
494 if (si->first < threshold) break;
495 // cerr << si->second << " : " << si->first << endl; 524 // cerr << si->second << " : " << si->first << endl;
496 active[si->second] = si->first; 525 active[si->second] = si->first;
497 if (si == strengths.begin()) break; 526 if (si == strengths.begin()) break;
498 } 527 }
499 528
529 return active;
530 }
531
532 Vamp::Plugin::FeatureList
533 Silvet::noteTrack(const map<int, double> &active)
534 {
500 // Minimum duration pruning, and conversion to notes. We can only 535 // Minimum duration pruning, and conversion to notes. We can only
501 // report notes that have just ended (i.e. that are absent in the 536 // report notes that have just ended (i.e. that are absent in the
502 // latest active set but present in the last set in the piano 537 // latest active set but present in the last set in the piano
503 // roll) -- any notes that ended earlier will have been reported 538 // roll) -- any notes that ended earlier will have been reported
504 // already, and if they haven't ended, we don't know their 539 // already, and if they haven't ended, we don't know their
505 // duration. 540 // duration.
506 541
542 int postFilterLatency = int(m_postFilter[0]->getSize() / 2);
543
507 int width = m_pianoRoll.size(); 544 int width = m_pianoRoll.size();
508 545
509 double columnDuration = 1.0 / m_colsPerSec; 546 double columnDuration = 1.0 / m_colsPerSec;
510 547
511 // only keep notes >= 100ms or thereabouts 548 // only keep notes >= 100ms or thereabouts
517 if (width < durationThreshold + 1) { 554 if (width < durationThreshold + 1) {
518 m_pianoRoll.push_back(active); 555 m_pianoRoll.push_back(active);
519 return noteFeatures; 556 return noteFeatures;
520 } 557 }
521 558
522 //!!! try: 20ms intervals in intensive mode
523 //!!! try: repeated note detection? (look for change in first derivative of the pitch matrix) 559 //!!! try: repeated note detection? (look for change in first derivative of the pitch matrix)
524 560
525 for (map<int, double>::const_iterator ni = m_pianoRoll[width-1].begin(); 561 for (map<int, double>::const_iterator ni = m_pianoRoll[width-1].begin();
526 ni != m_pianoRoll[width-1].end(); ++ni) { 562 ni != m_pianoRoll[width-1].end(); ++ni) {
527 563