Mercurial > hg > silvet
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 |