/libKeyFinder

Musical key detection for digital audio, GPL v3

Primary LanguageC++GNU General Public License v3.0GPL-3.0

libKeyFinder

Build Status

libKeyFinder can be used to estimate the musical key of digital recordings.

It is the basis of the KeyFinder GUI app, which is available as a binary download for Mac OSX and Windows at www.ibrahimshaath.co.uk/keyfinder

Examples

For the most basic use case, do something like this:

// Static because it retains useful resources for repeat use
static KeyFinder::KeyFinder k;

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

// Prepare the object for your audio stream
a.setFrameRate(yourAudioStream.framerate);
a.setChannels(yourAudioStream.channels);
a.addToSampleCount(yourAudioStream.length);

// Copy your audio into the object
for (int i = 0; i < yourAudioStream.length; i++) {
  a.setSample(i, yourAudioStream[i]);
}

// Run the analysis
KeyFinder::KeyDetectionResult r =  k.keyOfAudio(a);

// And do something with the result
doSomethingWith(r.globalKeyEstimate);

Alternatively, you can transform a stream of audio into a chromatic representation, and make progressive estimates of the key:

KeyFinder::AudioData a;
a.setFrameRate(yourAudioStream.framerate);
a.setChannels(yourAudioStream.channels);
a.addToSampleCount(yourAudioStream.packetLength);

static KeyFinder::KeyFinder k;

// the workspace holds the memory allocations for analysis of a single track
KeyFinder::Workspace w;

while (someType yourPacket = newAudioPacket()) {

  for (int i = 0; i < yourPacket.length; i++) {
    a.setSample(i, yourPacket[i]);
  }
  k.progressiveChromagram(a, w);

  // if you want to grab progressive key estimates...
  KeyFinder::KeyDetectionResult r = k.keyOfChromagram(w);
  doSomethingWithMostRecentKeyEstimate(r.globalKeyEstimate);
}

// if you only want a single key estimate, or to squeeze
// every last bit of audio from the working buffer after 
// progressive estimates...
k.finalChromagram(w);

// and finally...
KeyFinder::KeyDetectionResult r = k.keyOfChromagram(w);

doSomethingWithFinalKeyEstimate(r.globalKeyEstimate);

Installation

First, you will need to install libKeyFinder's dependencies:

Once dependencies are installed, build libKeyFinder:

$ mkdir build
$ cmake -DCMAKE_INSTALL_PREFIX /where/you/want/to/install/to -S . -B build
$ cmake --build build --target install --parallel number-of-cpu-cores

If you want to build libKeyFinder statically, add -DBUILD_STATIC_LIBS to the first call to cmake above.

Testing

The tests are built together with the library. Simply run the test executable from the build directory:

$ build/tests/test

If all goes well, you should see something like this:

===============================================================================
All tests passed (1705510 assertions in 65 test cases)

Note that there is a known intermittent failure in the FftAdapterTest/ForwardAndBackward test. Try running the tests a handful of times to determine whether you are hitting the intermittent or have introduced a new bug.