Commit 8f69b479 authored by Leander Schulten's avatar Leander Schulten
Browse files

Aubio: Add classes to wrap aubio analyzes

parent 13b36798
Pipeline #187505 passed with stage
in 3 minutes and 24 seconds
......@@ -18,6 +18,9 @@ QML_DESIGNER_IMPORT_PATH =
SOURCES += \
applicationdata.cpp \
audio/aubio/aubiocapi.cpp \
audio/aubio/onsetanalysis.cpp \
audio/aubio/tempoanalysis.cpp \
dmx/channel.cpp \
gui/channelprogrammeditor.cpp \
gui/colorplot.cpp \
......@@ -94,6 +97,9 @@ DEFINES += _USE_MATH_DEFINES
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
HEADERS += \
audio/aubio/aubiocapi.h \
audio/aubio/onsetanalysis.h \
audio/aubio/tempoanalysis.h \
dmx/deviceprototype.h \
dmx/channel.h \
id.h \
......
#include "aubiocapi.h"
#include <QString>
#include <QtGlobal>
namespace Audio::Aubio {
OnsetDetectionFunction toOnsetDetectionFunction(int i) {
Q_ASSERT(i >= 0 && i <= static_cast<int>(OnsetDetectionFunction::Last));
return static_cast<OnsetDetectionFunction>(i);
}
const C_API::char_t *C_API::toName(OnsetDetectionFunction f) {
using E = OnsetDetectionFunction;
switch (f) {
case E::EnergyBased: return "energy";
case E::SpectralDifference: return "specdiff";
case E::HighFrequencyContent: return "hfc";
case E::ComplexDomain: return "complex";
case E::PhaseDeviation: return "phase";
case E::KullbackLiebler: return "kl";
case E::ModifiedKullbackLiebler: return "mkl";
case E::SpectralFlux: return "specflux";
}
}
QString toQString(OnsetDetectionFunction f) {
using E = OnsetDetectionFunction;
switch (f) {
case E::EnergyBased: return QStringLiteral("Energy Based");
case E::SpectralDifference: return QStringLiteral("Spectral Difference");
case E::HighFrequencyContent: return QStringLiteral("High-Frequency Content");
case E::ComplexDomain: return QStringLiteral("Complex Domain");
case E::PhaseDeviation: return QStringLiteral("Phase Deviation");
case E::KullbackLiebler: return QStringLiteral("Kullback-Liebler");
case E::ModifiedKullbackLiebler: return QStringLiteral("Modified Kullback-Liebler");
case E::SpectralFlux: return QStringLiteral("Spectral Flux");
}
}
} // namespace Audio::Aubio
#ifndef AUBIOCAPI_H
#define AUBIOCAPI_H
#include <type_traits>
class QString;
namespace Audio::Aubio {
namespace C_API {
// hide the c api behind the C_API namespace
#include <aubio/aubio.h>
} // namespace C_API
/**
* @brief The OnsetDetectionFunction enum holds all possible onset detection functions
*/
enum class OnsetDetectionFunction {
EnergyBased,
SpectralDifference,
HighFrequencyContent,
ComplexDomain,
PhaseDeviation,
KullbackLiebler,
ModifiedKullbackLiebler,
SpectralFlux,
Last = SpectralFlux,
};
/**
* @brief toOnsetDetectionFunction converts a int to an OnsetDetectionFunction. The function asserts that the int is a valid OnsetDetectionFunction
* @param i The int that should be convertet to a OnsetDetectionFunction
* @return the corresponding OnsetDetectionFunction (the function asserts if there is no corresponsing OnsetDetectionFunction)
*/
OnsetDetectionFunction toOnsetDetectionFunction(int i);
/**
* Converts an enum to the underlying type (mostly a int)
*/
template <typename E>
constexpr auto to_integral(E e) -> typename std::underlying_type<E>::type {
return static_cast<typename std::underlying_type<E>::type>(e);
}
namespace C_API {
/**
* @brief toName Converts the enum to an string that is used by the aubio c lib
* @param f the enum that should be converted
* @return the c string representation of the enum that can be used with the aubio lib
*/
const C_API::char_t *toName(OnsetDetectionFunction f);
} // namespace C_API
/**
* @brief toQString Converts the enum to a human readable string
* @param f the enum that should be converted
* @return a human readable string
*/
QString toQString(OnsetDetectionFunction f);
} // namespace Audio::Aubio
#endif // AUBIOCAPI_H
#include "onsetanalysis.h"
#include <QDebug>
#include <array>
namespace Audio::Aubio {
using namespace C_API;
OnsetAnalysis::OnsetAnalysis(OnsetDetectionFunction onsetDetectionFunction, uint_t fftSize, uint_t stepSize, uint_t sampleRate) : onset(new_aubio_onset(toName(onsetDetectionFunction), fftSize, stepSize, sampleRate), del_aubio_onset), inputData{stepSize, nullptr} {}
bool OnsetAnalysis::processNewSamples(float *newSamples) {
// setup the data structures
smpl_t wasOnset;
fvec_t out{1, &wasOnset};
inputData.data = newSamples;
aubio_onset_do(onset.get(), &inputData, &out);
return wasOnset != 0;
}
unsigned OnsetAnalysis::getLastOnset() { return aubio_onset_get_last(onset.get()); }
float OnsetAnalysis::getOnsetValue() { return aubio_onset_get_descriptor(onset.get()); }
float OnsetAnalysis::getCurrentThreshold() { return aubio_onset_get_thresholded_descriptor(onset.get()); }
} // namespace Audio::Aubio
#ifndef ONSETANALYSIS_H
#define ONSETANALYSIS_H
#include "aubiocapi.h"
#include <memory>
namespace Audio::Aubio {
/**
* @brief The OnsetAnalysis class can detect onsets (music events). They are similar to the beats, but not so uniformly (rather the feeling of the music).
*/
class OnsetAnalysis {
std::unique_ptr<C_API::aubio_onset_t, decltype(&C_API::del_aubio_onset)> onset;
C_API::fvec_t inputData;
public:
/**
* @brief OnsetAnalysis Creates a onset analysis, which can detect onsets (music events)
* @param onsetDetectionFunction The onset detection function that should be used. The results can vary greatly based on the onset detection function
* @param fftSize The size of the fft that should be used. Larger fft sizes are slower but result in better results (must be a power of 2)
* @param stepSize The size of a new block that gets passed to this->processNewSamples().
* @param sampleRate The sample rate of the data
*/
OnsetAnalysis(OnsetDetectionFunction onsetDetectionFunction, C_API::uint_t fftSize, C_API::uint_t stepSize, C_API::uint_t sampleRate);
/**
* @brief processNewSamples process and analyse new samples
* @param newSamples A pointer to the new samples, the length of the array must be minimun the step size from the constructor
* @return true, if there was a onset in the new sample, false otherwise
*/
bool processNewSamples(float *newSamples);
/**
* @brief getLastOnset return the last detected onset
* @return the position of the last detected onset in samples
*/
unsigned getLastOnset();
/**
* @brief getOnsetValue return the last value of the onset detection function that is used
* @return the latest value of the onset detection function, the value range can vary from onset detection function to onset detection function
*/
float getOnsetValue();
/**
* @brief getCurrentThreshold return the current threshold used by the detection function to detect onsets
* @return the current threshold used by the detection function to detect onsets
*/
float getCurrentThreshold();
};
} // namespace Audio::Aubio
#endif // ONSETANALYSIS_H
#include "tempoanalysis.h"
#include <QDebug>
#include <array>
namespace Audio::Aubio {
using namespace C_API;
TempoAnalysis::TempoAnalysis(OnsetDetectionFunction onsetDetectionFunction, uint_t fftSize, uint_t stepSize, uint_t sampleRate) : tempo(new_aubio_tempo(toName(onsetDetectionFunction), fftSize, stepSize, sampleRate), del_aubio_tempo), inputData{stepSize, nullptr} {}
bool TempoAnalysis::processNewSamples(float *newSamples) {
// setup the data structures
smpl_t wasBeat;
fvec_t out{1, &wasBeat};
inputData.data = newSamples;
aubio_tempo_do(tempo.get(), &inputData, &out);
return wasBeat != 0;
}
unsigned TempoAnalysis::getLastBeat() { return aubio_tempo_get_last(tempo.get()); }
float TempoAnalysis::getCurrentBPM() { return aubio_tempo_get_bpm(tempo.get()); }
unsigned TempoAnalysis::getLastTatum() { return static_cast<unsigned>(aubio_tempo_get_last_tatum(tempo.get())); }
} // namespace Audio::Aubio
#ifndef TEMPOANALYSIS_H
#define TEMPOANALYSIS_H
#include "aubiocapi.h"
#include <memory>
namespace Audio::Aubio {
/**
* @brief The TempoAnalysis class can determine the tempo (beats per minute) of the input audio data. Also the class can detect beats.
* The class can use different onset detection methods (onset = "music event"), which leads to different results.
*/
class TempoAnalysis {
std::unique_ptr<C_API::aubio_tempo_t, decltype(&C_API::del_aubio_tempo)> tempo;
C_API::fvec_t inputData;
public:
/**
* @brief TempoAnalysis Creates a tempo analysis, which can detect beats, tatums and the current bpm
* @param onsetDetectionFunction The onset detection function that should be used. The results can vary greatly based on the onset detection function
* @param fftSize The size of the fft that should be used. Larger fft sizes are slower but result in better results (must be a power of 2)
* @param stepSize The size of a new block that gets passed to this->processNewSamples().
* @param sampleRate The sample rate of the data
*/
TempoAnalysis(OnsetDetectionFunction onsetDetectionFunction, C_API::uint_t fftSize, C_API::uint_t stepSize, C_API::uint_t sampleRate);
/**
* @brief processNewSamples process and analyse new samples
* @param newSamples A pointer to the new samples, the length of the array must be minimun the step size from the constructor
* @return true, if there was a beat in the new sample, false otherwise
*/
bool processNewSamples(float *newSamples);
/**
* @brief getLastBeat returns the position of the last detected beat, in samples
* @return the position of the last beat in samples
*/
unsigned getLastBeat();
/**
* @brief getCurrentBPM return the current determined beats per second
* @return the currently determined beats per minute
*/
float getCurrentBPM();
/**
* @brief getLastTatum return the position of the last detected tatum (sub beat) in samples
* @return the position of the last tatum im samples
*/
unsigned getLastTatum();
};
} // namespace Audio::Aubio
#endif // TEMPOANALYSIS_H
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment