Mercurial > hg > silvet
comparison src/Silvet.cpp @ 176:8af9b6cd7451
Add some ensemble instrument packs
author | Chris Cannam |
---|---|
date | Wed, 21 May 2014 15:03:12 +0100 |
parents | abfd19f5cc1a |
children | a53c713b2a4a |
comparison
equal
deleted
inserted
replaced
175:abfd19f5cc1a | 176:8af9b6cd7451 |
---|---|
31 using std::endl; | 31 using std::endl; |
32 using Vamp::RealTime; | 32 using Vamp::RealTime; |
33 | 33 |
34 static int processingSampleRate = 44100; | 34 static int processingSampleRate = 44100; |
35 static int processingBPO = 60; | 35 static int processingBPO = 60; |
36 | |
37 //!!! todo: replace these two with values from instrument pack | |
38 static int processingHeight = 545; | |
39 static int processingNotes = 88; | |
40 | 36 |
41 Silvet::Silvet(float inputSampleRate) : | 37 Silvet::Silvet(float inputSampleRate) : |
42 Plugin(inputSampleRate), | 38 Plugin(inputSampleRate), |
43 m_instruments(InstrumentPack::listInstrumentPacks()), | 39 m_instruments(InstrumentPack::listInstrumentPacks()), |
44 m_resampler(0), | 40 m_resampler(0), |
147 desc.quantizeStep = 1; | 143 desc.quantizeStep = 1; |
148 desc.valueNames.push_back("Draft (faster)"); | 144 desc.valueNames.push_back("Draft (faster)"); |
149 desc.valueNames.push_back("Intensive (higher quality)"); | 145 desc.valueNames.push_back("Intensive (higher quality)"); |
150 list.push_back(desc); | 146 list.push_back(desc); |
151 | 147 |
152 desc.identifier = "soloinstrument"; | 148 desc.identifier = "instrument"; |
153 desc.name = "Solo instrument"; | 149 desc.name = "Instrument"; |
154 desc.unit = ""; | 150 desc.unit = ""; |
155 desc.description = "The instrument known to be present in the recording, if there is only one"; | 151 desc.description = "The instrument known to be present in the recording, if there is only one"; |
156 desc.minValue = 0; | 152 desc.minValue = 0; |
157 desc.maxValue = m_instruments.size()-1; | 153 desc.maxValue = m_instruments.size()-1; |
158 desc.defaultValue = 0; | 154 desc.defaultValue = 0; |
184 { | 180 { |
185 if (identifier == "mode") { | 181 if (identifier == "mode") { |
186 return m_hqMode ? 1.f : 0.f; | 182 return m_hqMode ? 1.f : 0.f; |
187 } else if (identifier == "finetune") { | 183 } else if (identifier == "finetune") { |
188 return m_fineTuning ? 1.f : 0.f; | 184 return m_fineTuning ? 1.f : 0.f; |
189 } else if (identifier == "soloinstrument") { | 185 } else if (identifier == "instrument") { |
190 return m_instrument; | 186 return m_instrument; |
191 } | 187 } |
192 return 0; | 188 return 0; |
193 } | 189 } |
194 | 190 |
197 { | 193 { |
198 if (identifier == "mode") { | 194 if (identifier == "mode") { |
199 m_hqMode = (value > 0.5); | 195 m_hqMode = (value > 0.5); |
200 } else if (identifier == "finetune") { | 196 } else if (identifier == "finetune") { |
201 m_fineTuning = (value > 0.5); | 197 m_fineTuning = (value > 0.5); |
202 } else if (identifier == "soloinstrument") { | 198 } else if (identifier == "instrument") { |
203 m_instrument = lrintf(value); | 199 m_instrument = lrintf(value); |
204 } | 200 } |
205 } | 201 } |
206 | 202 |
207 Silvet::ProgramList | 203 Silvet::ProgramList |
362 | 358 |
363 for (int i = 0; i < (int)m_postFilter.size(); ++i) { | 359 for (int i = 0; i < (int)m_postFilter.size(); ++i) { |
364 delete m_postFilter[i]; | 360 delete m_postFilter[i]; |
365 } | 361 } |
366 m_postFilter.clear(); | 362 m_postFilter.clear(); |
367 for (int i = 0; i < processingNotes; ++i) { | 363 for (int i = 0; i < m_instruments[0].templateNoteCount; ++i) { |
368 m_postFilter.push_back(new MedianFilter<double>(3)); | 364 m_postFilter.push_back(new MedianFilter<double>(3)); |
369 } | 365 } |
370 m_pianoRoll.clear(); | 366 m_pianoRoll.clear(); |
371 m_columnCount = 0; | 367 m_columnCount = 0; |
372 m_startTime = RealTime::zeroTime; | 368 m_startTime = RealTime::zeroTime; |
425 int width = filtered.size(); | 421 int width = filtered.size(); |
426 | 422 |
427 int iterations = m_hqMode ? 20 : 10; | 423 int iterations = m_hqMode ? 20 : 10; |
428 | 424 |
429 //!!! pitches or notes? [terminology] | 425 //!!! pitches or notes? [terminology] |
430 Grid localPitches(width, vector<double>(processingNotes, 0.0)); | 426 Grid localPitches(width, vector<double>(pack.templateNoteCount, 0.0)); |
431 | 427 |
432 bool wantShifts = m_hqMode && m_fineTuning; | 428 bool wantShifts = m_hqMode && m_fineTuning; |
433 int shiftCount = 1; | 429 int shiftCount = 1; |
434 if (wantShifts) { | 430 if (wantShifts) { |
435 shiftCount = pack.templateMaxShift * 2 + 1; | 431 shiftCount = pack.templateMaxShift * 2 + 1; |
436 } | 432 } |
437 | 433 |
438 vector<vector<int> > localBestShifts; | 434 vector<vector<int> > localBestShifts; |
439 if (wantShifts) { | 435 if (wantShifts) { |
440 localBestShifts = | 436 localBestShifts = |
441 vector<vector<int> >(width, vector<int>(processingNotes, 0)); | 437 vector<vector<int> >(width, vector<int>(pack.templateNoteCount, 0)); |
442 } | 438 } |
443 | 439 |
444 vector<bool> present(width, false); | 440 vector<bool> present(width, false); |
445 | 441 |
446 #pragma omp parallel for | 442 #pragma omp parallel for |
447 for (int i = 0; i < width; ++i) { | 443 for (int i = 0; i < width; ++i) { |
448 | 444 |
449 double sum = 0.0; | 445 double sum = 0.0; |
450 for (int j = 0; j < processingHeight; ++j) { | 446 for (int j = 0; j < pack.templateHeight; ++j) { |
451 sum += filtered.at(i).at(j); | 447 sum += filtered.at(i).at(j); |
452 } | 448 } |
453 if (sum < 1e-5) continue; | 449 if (sum < 1e-5) continue; |
454 | 450 |
455 present[i] = true; | 451 present[i] = true; |
461 } | 457 } |
462 | 458 |
463 const float *pitchDist = em.getPitchDistribution(); | 459 const float *pitchDist = em.getPitchDistribution(); |
464 const float *const *shiftDist = em.getShifts(); | 460 const float *const *shiftDist = em.getShifts(); |
465 | 461 |
466 for (int j = 0; j < processingNotes; ++j) { | 462 for (int j = 0; j < pack.templateNoteCount; ++j) { |
467 | 463 |
468 localPitches[i][j] = pitchDist[j] * sum; | 464 localPitches[i][j] = pitchDist[j] * sum; |
469 | 465 |
470 int bestShift = 0; | 466 int bestShift = 0; |
471 int bestShiftValue = 0.0; | 467 int bestShiftValue = 0.0; |
483 | 479 |
484 for (int i = 0; i < width; ++i) { | 480 for (int i = 0; i < width; ++i) { |
485 | 481 |
486 if (!present[i]) { | 482 if (!present[i]) { |
487 // silent column | 483 // silent column |
488 for (int j = 0; j < processingNotes; ++j) { | 484 for (int j = 0; j < pack.templateNoteCount; ++j) { |
489 m_postFilter[j]->push(0.0); | 485 m_postFilter[j]->push(0.0); |
490 } | 486 } |
491 m_pianoRoll.push_back(map<int, double>()); | 487 m_pianoRoll.push_back(map<int, double>()); |
492 if (wantShifts) { | 488 if (wantShifts) { |
493 m_pianoRollShifts.push_back(map<int, int>()); | 489 m_pianoRollShifts.push_back(map<int, int>()); |
525 // isn't quite accurate. But the small constant offset is | 521 // isn't quite accurate. But the small constant offset is |
526 // practically irrelevant compared to the jitter from the frame | 522 // practically irrelevant compared to the jitter from the frame |
527 // size we reduce to in a moment | 523 // size we reduce to in a moment |
528 int latentColumns = m_cq->getLatency() / m_cq->getColumnHop(); | 524 int latentColumns = m_cq->getLatency() / m_cq->getColumnHop(); |
529 | 525 |
526 const InstrumentPack &pack = m_instruments[m_instrument]; | |
527 | |
530 for (int i = 0; i < width; ++i) { | 528 for (int i = 0; i < width; ++i) { |
531 | 529 |
532 if (m_columnCount < latentColumns) { | 530 if (m_columnCount < latentColumns) { |
533 ++m_columnCount; | 531 ++m_columnCount; |
534 continue; | 532 continue; |
539 | 537 |
540 bool select = (sampleNo / spacing != prevSampleNo / spacing); | 538 bool select = (sampleNo / spacing != prevSampleNo / spacing); |
541 | 539 |
542 if (select) { | 540 if (select) { |
543 vector<double> inCol = in[i]; | 541 vector<double> inCol = in[i]; |
544 vector<double> outCol(processingHeight); | 542 vector<double> outCol(pack.templateHeight); |
545 | 543 |
546 // we reverse the column as we go (the CQ output is | 544 // we reverse the column as we go (the CQ output is |
547 // "upside-down", with high frequencies at the start of | 545 // "upside-down", with high frequencies at the start of |
548 // each column, and we want it the other way around) and | 546 // each column, and we want it the other way around) and |
549 // then ignore the first 55 (lowest-frequency) bins, | 547 // then ignore the first 55 (lowest-frequency) bins, |
550 // giving us 545 bins instead of 600 | 548 // giving us 545 bins instead of 600 |
551 | 549 |
552 for (int j = 0; j < processingHeight; ++j) { | 550 for (int j = 0; j < pack.templateHeight; ++j) { |
553 int ix = inCol.size() - j - 55; | 551 int ix = inCol.size() - j - 55; |
554 outCol[j] = inCol[ix]; | 552 outCol[j] = inCol[ix]; |
555 } | 553 } |
556 | 554 |
557 vector<double> noiseLevel1 = | 555 vector<double> noiseLevel1 = |
558 MedianFilter<double>::filter(40, outCol); | 556 MedianFilter<double>::filter(40, outCol); |
559 for (int j = 0; j < processingHeight; ++j) { | 557 for (int j = 0; j < pack.templateHeight; ++j) { |
560 noiseLevel1[j] = std::min(outCol[j], noiseLevel1[j]); | 558 noiseLevel1[j] = std::min(outCol[j], noiseLevel1[j]); |
561 } | 559 } |
562 | 560 |
563 vector<double> noiseLevel2 = | 561 vector<double> noiseLevel2 = |
564 MedianFilter<double>::filter(40, noiseLevel1); | 562 MedianFilter<double>::filter(40, noiseLevel1); |
565 for (int j = 0; j < processingHeight; ++j) { | 563 for (int j = 0; j < pack.templateHeight; ++j) { |
566 outCol[j] = std::max(outCol[j] - noiseLevel2[j], 0.0); | 564 outCol[j] = std::max(outCol[j] - noiseLevel2[j], 0.0); |
567 } | 565 } |
568 | 566 |
569 out.push_back(outCol); | 567 out.push_back(outCol); |
570 } | 568 } |
578 void | 576 void |
579 Silvet::postProcess(const vector<double> &pitches, | 577 Silvet::postProcess(const vector<double> &pitches, |
580 const vector<int> &bestShifts, | 578 const vector<int> &bestShifts, |
581 bool wantShifts) | 579 bool wantShifts) |
582 { | 580 { |
581 const InstrumentPack &pack = m_instruments[m_instrument]; | |
582 | |
583 vector<double> filtered; | 583 vector<double> filtered; |
584 | 584 |
585 for (int j = 0; j < processingNotes; ++j) { | 585 for (int j = 0; j < pack.templateNoteCount; ++j) { |
586 m_postFilter[j]->push(pitches[j]); | 586 m_postFilter[j]->push(pitches[j]); |
587 filtered.push_back(m_postFilter[j]->get()); | 587 filtered.push_back(m_postFilter[j]->get()); |
588 } | 588 } |
589 | 589 |
590 // Threshold for level and reduce number of candidate pitches | 590 // Threshold for level and reduce number of candidate pitches |
597 | 597 |
598 typedef std::multimap<double, int> ValueIndexMap; | 598 typedef std::multimap<double, int> ValueIndexMap; |
599 | 599 |
600 ValueIndexMap strengths; | 600 ValueIndexMap strengths; |
601 | 601 |
602 for (int j = 0; j < processingNotes; ++j) { | 602 for (int j = 0; j < pack.templateNoteCount; ++j) { |
603 double strength = filtered[j]; | 603 double strength = filtered[j]; |
604 if (strength < threshold) continue; | 604 if (strength < threshold) continue; |
605 strengths.insert(ValueIndexMap::value_type(strength, j)); | 605 strengths.insert(ValueIndexMap::value_type(strength, j)); |
606 } | 606 } |
607 | 607 |