KeyFinderResultWrapper keyDetectionProcess(const AsyncFileObject& object){ KeyFinderResultWrapper result; result.batchRow = object.batchRow; // initialise stream and decode file into it. KeyFinder::AudioData* audio = NULL; AudioFileDecoder* decoder = NULL; try{ decoder = AudioFileDecoder::getDecoder(); }catch(KeyFinder::Exception& e){ delete decoder; result.errorMessage = QString(e.what().c_str()); return result; } try{ audio = decoder->decodeFile(object.filePath); delete decoder; }catch(KeyFinder::Exception& e){ delete audio; delete decoder; result.errorMessage = QString(e.what().c_str()); return result; } // make audio stream monaural ahead of downsample to reduce load audio->reduceToMono(); // downsample if necessary if(object.prefs.getDFactor() > 1){ Downsampler* ds = Downsampler::getDownsampler(object.prefs.getDFactor(),audio->getFrameRate(),object.prefs.getLastFreq()); try{ audio = ds->downsample(audio,object.prefs.getDFactor()); }catch(KeyFinder::Exception& e){ delete audio; delete ds; result.errorMessage = QString(e.what().c_str()); return result; } delete ds; } KeyFinder::KeyFinder* kf = LibKeyFinderSingleton::getInstance()->getKeyFinder(); result.core = kf->findKey(*audio, object.prefs.core); delete audio; return result; }
KeyFinderResultWrapper keyDetectionProcess(const AsyncFileObject& object) { KeyFinderResultWrapper result; result.batchRow = object.batchRow; AudioFileDecoder* decoder = NULL; try { decoder = new AudioFileDecoder(object.filePath, object.prefs.getMaxDuration()); } catch (std::exception& e) { delete decoder; result.errorMessage = QString(e.what()); return result; } catch (...) { delete decoder; result.errorMessage = "Unknown exception initialising decoder"; return result; } KeyFinder::Workspace workspace; static KeyFinder::KeyFinder kf; try { while (true) { KeyFinder::AudioData* tempAudio = decoder->decodeNextAudioPacket(); if (tempAudio == NULL) break; kf.progressiveChromagram(*tempAudio, workspace); delete tempAudio; } delete decoder; decoder = NULL; kf.finalChromagram(workspace); result.fullChromagram = KeyFinder::Chromagram(*workspace.chromagram); result.core = kf.keyOfChromagram(workspace); } catch (std::exception& e) { if (decoder != NULL) delete decoder; result.errorMessage = QString(e.what()); return result; } catch (...) { if (decoder != NULL) { delete decoder; result.errorMessage = "Unknown exception while decoding"; } else { result.errorMessage = "Unknown exception while analysing"; } return result; } return result; }
KeyFinderResultWrapper keyDetectionProcess(const AsyncFileObject& object) { KeyFinderResultWrapper result; result.batchRow = object.batchRow; AudioFileDecoder* decoder = NULL; try { decoder = new AudioFileDecoder(object.filePath, object.prefs.getMaxDuration()); } catch (std::exception& e) { delete decoder; result.errorMessage = QString(e.what()); return result; } catch (...) { delete decoder; result.errorMessage = "Unknown exception initialising decoder"; return result; } KeyFinder::Workspace workspace; static KeyFinder::KeyFinder kf; try { while (true) { KeyFinder::AudioData* tempAudio = decoder->decodeNextAudioPacket(); if (tempAudio == NULL) break; kf.progressiveChromagram(*tempAudio, workspace, object.prefs.core); delete tempAudio; } delete decoder; decoder = NULL; kf.finalChromagram(workspace, object.prefs.core); result.fullChromagram = KeyFinder::Chromagram(*workspace.chromagram); result.core = kf.keyOfChromagram(workspace, object.prefs.core); result.oneOctaveChromagram = result.fullChromagram; result.oneOctaveChromagram.reduceToOneOctave(); } catch (std::exception& e) { if (decoder != NULL) delete decoder; result.errorMessage = QString(e.what()); return result; } catch (...) { if (decoder != NULL) { delete decoder; result.errorMessage = "Unknown exception while decoding"; } else { result.errorMessage = "Unknown exception while analysing"; } return result; } for (unsigned int i = 0; i < result.core.segments.size(); i++) { qDebug( "Chroma vector for segment %d of file %s: [%.0f, %.0f, %.0f, %.0f, %.0f, %.0f, %.0f, %.0f, %.0f, %.0f, %.0f, %.0f]", i, object.filePath.toUtf8().constData(), result.core.segments[i].chromaVector[0], result.core.segments[i].chromaVector[1], result.core.segments[i].chromaVector[2], result.core.segments[i].chromaVector[3], result.core.segments[i].chromaVector[4], result.core.segments[i].chromaVector[5], result.core.segments[i].chromaVector[6], result.core.segments[i].chromaVector[7], result.core.segments[i].chromaVector[8], result.core.segments[i].chromaVector[9], result.core.segments[i].chromaVector[10], result.core.segments[i].chromaVector[11] ); } return result; }
void KeyFinderWorkerThread::run(){ if(!haveParams){ emit failed("No parameters."); return; } // initialise stream and decode file into it AudioStream* astrm = NULL; AudioFileDecoder* dec = AudioFileDecoder::getDecoder(filePath.toUtf8().data()); try{ astrm = dec->decodeFile(filePath.toUtf8().data()); }catch(Exception){ delete astrm; delete dec; emit failed("Could not decode file."); return; } delete dec; emit decoded(); // make audio stream monaural astrm->reduceToMono(); emit madeMono(); // downsample if necessary if(prefs.getDFactor() > 1){ Downsampler* ds = Downsampler::getDownsampler(prefs.getDFactor(),astrm->getFrameRate(),prefs.getLastFreq()); try{ astrm = ds->downsample(astrm,prefs.getDFactor()); }catch(Exception){ delete astrm; delete ds; emit failed("Downsampler failed."); return; } delete ds; emit downsampled(); } // start spectrum analysis SpectrumAnalyser* sa = NULL; Chromagram* ch = NULL; sa = SpectrumAnalyserFactory::getInstance()->getSpectrumAnalyser(astrm->getFrameRate(),prefs); ch = sa->chromagram(astrm); delete astrm; // note we don't delete the spectrum analyser; it stays in the centralised factory for reuse. ch->reduceTuningBins(prefs); emit producedFullChromagram(*ch); // reduce chromagram ch->reduceToOneOctave(prefs); emit producedOneOctaveChromagram(*ch); // get energy level across track to weight segments std::vector<float> loudness(ch->getHops()); for(int h=0; h<ch->getHops(); h++) for(int b=0; b<ch->getBins(); b++) loudness[h] += ch->getMagnitude(h,b); // get harmonic change signal Segmentation* hcdf = Segmentation::getSegmentation(prefs); std::vector<double> harmonicChangeSignal = hcdf->getRateOfChange(ch,prefs); emit producedHarmonicChangeSignal(harmonicChangeSignal); // get track segmentation std::vector<int> changes = hcdf->getSegments(harmonicChangeSignal,prefs); changes.push_back(ch->getHops()); // It used to be getHops()-1. But this doesn't crash. So we like it. // batch output of keychange locations for Beatles experiment //for(int i=1; i<changes.size(); i++) // don't want the leading zero // std::cout << filePath.substr(53) << "\t" << std::fixed << std::setprecision(2) << changes[i]*(prefs.getHopSize()/(44100.0/prefs.getDFactor())) << std::endl; // end experiment output // get key estimates for segments KeyClassifier hc(prefs); std::vector<int> keys(0); std::vector<float> keyWeights(24); for(int i=0; i<(signed)changes.size()-1; i++){ std::vector<double> chroma(ch->getBins()); for(int j=changes[i]; j<changes[i+1]; j++) for(int k=0; k<ch->getBins(); k++) chroma[k] += ch->getMagnitude(j,k); int key = hc.classify(chroma); for(int j=changes[i]; j<changes[i+1]; j++){ keys.push_back(key); if(key < 24) // ignore parts that were classified as silent keyWeights[key] += loudness[j]; } } keys.push_back(keys[keys.size()-1]); // put last key on again to match length of track delete ch; emit producedKeyEstimates(keys); // get global key int mostCommonKey = 24; float mostCommonKeyWeight = 0.0; for(int i=0; i<(signed)keyWeights.size(); i++){ if(keyWeights[i] > mostCommonKeyWeight){ mostCommonKeyWeight = keyWeights[i]; mostCommonKey = i; } } emit producedGlobalKeyEstimate(mostCommonKey); return; }