void KeyAnalysisThread::analyzeSingleKey()
{
	DBG("Thread: " << this->getThreadName() << " analyzing single key of: " << fileToAnalyze->getUUID());
	//skip already-analyzed files
	if (fileToAnalyze->isFileAnalyzed() == "Yes")
	{
		DBG("Skipping analysis of " << fileToAnalyze->getFileName() << " : Already analyzed.");
		return;
	}
	//DBG("File being analyzed: " << fileToAnalyze.getFileToBeAnalyzed().getFileName());
//	r> audioFormatReader = audioFormatManager.createReaderFor(fileToAnalyze.getFileToBeAnalyzed());

	size_t numSamples = audioFormatReader->lengthInSamples;
	size_t numChannels = audioFormatReader->numChannels;
	size_t sampleRate = audioFormatReader->sampleRate;
	//size_t numFrames = numSamples / numChannels;
	//DBG("Samples:" + String(numSamples) + " Channels:" + String(numChannels) + " Frames:" + String(numFrames));
	audioData.addToSampleCount(numSamples*numChannels);
	audioData.setChannels(numChannels);
	audioData.setFrameRate(sampleRate);

	AudioBuffer<float> audioBuffer(numChannels, numSamples);

	// #TODO(Casey): Fix either audiobuffer or audioData to allow direct move of sample array (Performance)
	// #TODO(Casey): integrate libsndfile as primary choice

	//DBG("Reading wav to buffer");
	audioFormatReader->read(&audioBuffer, 0, numSamples, 0, true, true);


	//DBG("Copying buffer samples to audioData");
	for (size_t i = 0; i < numSamples; ++i)
	{
		//DBG("On sample " + String(i) + " of " + String(numSamples));
		for (size_t currChannel = 0; currChannel < numChannels; ++currChannel)
		{
			audioData.setSampleByFrame(i, currChannel, audioBuffer.getSample(currChannel, i));
		}
		//audioData.setSampleByFrame(i, 1, audioBuffer.getSample(1, i));
	}

	//DBG("Detecting key...");

	static KeyFinder::KeyFinder keyFinder;

	KeyFinder::key_t key = keyFinder.keyOfAudio(audioData);

	//KeyFinder::key_t key = KeyFinder::key_t::C_MAJOR;

	fileToAnalyze->setDetectedKey(key);

	String foundKey(fileToAnalyze->getDetectedKeyAsString());

	DBG("Detected key in analyzeAudioFiles: " + foundKey);
}
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;
}
const char* kfinder_get_key(short signed int   *samples,
                            unsigned int        nb_samples,
                            short unsigned int  frame_rate,
                            short unsigned int  nb_channels)
{
    // Check input parameter.
    if ((samples == NULL) || (nb_samples == 0) || (frame_rate == 0) || (nb_channels == 0))
    {
        return "";
    }

    // Build the main computing object.
    KeyFinder::KeyFinder k;

    // Build an empty audio object
    KeyFinder::AudioData a;

    // Prepare the object for your audio stream
    a.setFrameRate(frame_rate);
    a.setChannels(nb_channels);
    a.addToSampleCount(nb_samples);

    // Copy your audio into the object (as float).
    for (unsigned int i = 0; i < nb_samples; i++)
    {
        a.setSample(i, (float)samples[i]);
    }

    // Run the analysis
    KeyFinder::key_t r;
    try
    {
        r =  k.keyOfAudio(a);
    }
    catch(const std::exception& e)
    {
        cerr << "libKeyFinder: exception: " << e.what() << endl;
        return "";
    }
    catch(...)
    {
        cerr << "libKeyFinder: unknown exception" << endl;
        return "";
    }


    // And do something with the result!
    switch(r)
    {
        case KeyFinder::A_MAJOR:      return "AM";
        case KeyFinder::A_MINOR:      return "Am";
        case KeyFinder::B_FLAT_MAJOR: return "BbM";
        case KeyFinder::B_FLAT_MINOR: return "Bbm";
        case KeyFinder::B_MAJOR:      return "BM";
        case KeyFinder::B_MINOR:      return "Bm";
        case KeyFinder::C_MAJOR:      return "CM";
        case KeyFinder::C_MINOR:      return "Cm";
        case KeyFinder::D_FLAT_MAJOR: return "DbM";
        case KeyFinder::D_FLAT_MINOR: return "Dbm";
        case KeyFinder::D_MAJOR:      return "DM";
        case KeyFinder::D_MINOR:      return "Dm";
        case KeyFinder::E_FLAT_MAJOR: return "EbM";
        case KeyFinder::E_FLAT_MINOR: return "Ebm";
        case KeyFinder::E_MAJOR:      return "EM";
        case KeyFinder::E_MINOR:      return "Em";
        case KeyFinder::F_MAJOR:      return "FM";
        case KeyFinder::F_MINOR:      return "Fm";
        case KeyFinder::G_FLAT_MAJOR: return "GbM";
        case KeyFinder::G_FLAT_MINOR: return "Gbm";
        case KeyFinder::G_MAJOR:      return "GM";
        case KeyFinder::G_MINOR:      return "Gm";
        case KeyFinder::A_FLAT_MAJOR: return "AbM";
        case KeyFinder::A_FLAT_MINOR: return "Abm";
        case KeyFinder::SILENCE:      return "";
        default:                      return "";
    }
}