New callback style API
This commit is contained in:
parent
d70702bf2c
commit
636476a226
|
@ -3,7 +3,7 @@ project(c_gaborator
|
|||
DESCRIPTION "C++ to C simple bridge for Gaborator"
|
||||
VERSION 1.7)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
set(CMAKE_INSTALL_RPATH "$ORIGIN")
|
||||
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
|
||||
|
|
253
cgaborator.cpp
253
cgaborator.cpp
|
@ -3,117 +3,178 @@
|
|||
#include "include/cgaborator.h"
|
||||
#include "gaborator/gaborator.h"
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
|
||||
const int C_ARRAY_SIZE = 100000 * 2; //TODO: why this size
|
||||
class Gaborator {
|
||||
|
||||
struct GaboratorState {
|
||||
gaborator::parameters* paramsRef;
|
||||
gaborator::analyzer<float>* analyzerRef;
|
||||
gaborator::coefs<float>* coefsRef;
|
||||
public:
|
||||
Gaborator(int blockSize, double sampleRate, int bandsPerOctave, double minimumFrequency, double referenceFrequency, double maximumFrequency, int stepSize) :
|
||||
parameters(bandsPerOctave, minimumFrequency / sampleRate, referenceFrequency / sampleRate, 1.0, 1e-5),
|
||||
analyzer(parameters),
|
||||
coefs(analyzer),
|
||||
frequencyBinTimeStepSize(stepSize)
|
||||
{
|
||||
|
||||
//converts frequency (ff_max) in hertz to the number of bands above the min frequency
|
||||
//the ceil is used to end up at a full band
|
||||
int interesting_bands = ceil(bandsPerOctave * log(maximumFrequency/minimumFrequency)/log(2.0f));
|
||||
|
||||
//since bands are ordered from high to low we are only interested in lower bands:
|
||||
//fs/2.0 is the nyquist frequency
|
||||
int total_bands = ceil(bandsPerOctave * log(sampleRate/2.0/minimumFrequency)/log(2.0f));
|
||||
|
||||
latency = (int64_t) ceil(analyzer.analysis_support());
|
||||
min_band = total_bands - interesting_bands;
|
||||
sample_rate = (int) sampleRate;
|
||||
t_in = 0;
|
||||
|
||||
int max_band = analyzer.bandpass_bands_end();
|
||||
|
||||
std::vector<float> bandcenterCache(max_band);
|
||||
|
||||
for(int i = 0 ; i < max_band ; i++){
|
||||
if(i < min_band){
|
||||
bandcenterCache[i] = -1;
|
||||
}else{
|
||||
bandcenterCache[i] = analyzer.band_ff(i) * sample_rate;
|
||||
}
|
||||
|
||||
if (bandcenterCache[i] > 0) {
|
||||
++numberOfBandsCache;
|
||||
if (firstBandCache == -1) {
|
||||
firstBandCache = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
coefficientSize = (latency + 2*blockSize) / frequencyBinTimeStepSize;
|
||||
|
||||
coefficients.resize(coefficientSize);
|
||||
for (int i = 0; i < coefficients.size(); ++i){
|
||||
coefficients[i] = std::make_unique<std::vector<float>>(numberOfBandsCache);
|
||||
}
|
||||
|
||||
assert(t_in == 0);
|
||||
|
||||
}
|
||||
|
||||
void analyze(float* audio_block, int audio_block_length){
|
||||
if (audio_block == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
analyzer.analyze(audio_block, t_in, t_in + audio_block_length, coefs);
|
||||
|
||||
int64_t st0 = t_in - latency;
|
||||
int64_t st1 = t_in - latency + audio_block_length;
|
||||
|
||||
|
||||
analysisVector.clear();
|
||||
apply(
|
||||
analyzer,
|
||||
coefs,
|
||||
[&](std::complex<float> coef, int band, int64_t audioSampleIndex ) {
|
||||
//ignores everything above the max_band
|
||||
if(band >= min_band){
|
||||
analysisVector.push_back({band, static_cast<int>(audioSampleIndex), std::abs(coef)});
|
||||
}
|
||||
},st0,
|
||||
st1);
|
||||
|
||||
t_in += (int64_t) audio_block_length;
|
||||
|
||||
int64_t t_out = t_in - latency;
|
||||
|
||||
forget_before(analyzer, coefs, t_out - audio_block_length);
|
||||
}
|
||||
|
||||
void gabor_transform(float* audio_block, int audio_block_length, gaborator_transform_callback callback, uintptr_t callback_data) {
|
||||
analyze(audio_block, audio_block_length);
|
||||
|
||||
for (auto& entry : analysisVector) {
|
||||
int coefficientIndex = entry.sampleIndex / frequencyBinTimeStepSize;
|
||||
int bandIndex = entry.bandIndex - firstBandCache;
|
||||
|
||||
int circularIndex = coefficientIndex % int(coefficients.size());
|
||||
|
||||
// The first results have a negative audio sample index
|
||||
// ignore these
|
||||
if (coefficientIndex > 0 && bandIndex < coefficients[circularIndex]->size()) {
|
||||
|
||||
// If a new index is reached, save the old (fixed) coefficients in the history
|
||||
// Fill the array with zeros to get the max
|
||||
if (coefficientIndex > mostRecentCoefficentIndex && coefficientIndex > coefficients.size()) {
|
||||
// keep the new maximum
|
||||
mostRecentCoefficentIndex = coefficientIndex;
|
||||
// "copy" the oldest data to the history
|
||||
// the slice can be reused thanks to the oldest being filled with zeros just after
|
||||
callback(callback_data, coefficients[circularIndex]->data(), coefficients[circularIndex]->size());
|
||||
// fill the oldest with zeros
|
||||
//coefficients[circularIndex] = std::make_unique<std::vector<float>>(numberOfBandsCache)
|
||||
std::fill(coefficients[circularIndex]->begin(), coefficients[circularIndex]->end(), 0);
|
||||
}
|
||||
// due to reduction in precision (from audio sample accuracy to steps) multiple
|
||||
// magnitudes could be placed in the same stepIndex, bandIndex pair.
|
||||
// We take the maximum magnitudes value.
|
||||
(*coefficients[circularIndex])[bandIndex] = std::max((*coefficients[circularIndex])[bandIndex], entry.magnitude);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int64_t analysis_support() const {
|
||||
return latency;
|
||||
}
|
||||
|
||||
int number_of_bands() const {
|
||||
return numberOfBandsCache;
|
||||
}
|
||||
|
||||
~Gaborator()= default;
|
||||
|
||||
private:
|
||||
struct gaborator_analyze_entry {
|
||||
int bandIndex;
|
||||
int sampleIndex;
|
||||
float magnitude;
|
||||
};
|
||||
|
||||
std::vector<struct gaborator_analyze_entry> analysisVector;
|
||||
std::vector<std::unique_ptr<std::vector<float>>> coefficients;
|
||||
int firstBandCache = -1;
|
||||
int numberOfBandsCache = 0;
|
||||
int mostRecentCoefficentIndex = 0;
|
||||
|
||||
int frequencyBinTimeStepSize;
|
||||
int64_t t_in;
|
||||
int min_band;
|
||||
int sample_rate;
|
||||
int64_t anal_support;
|
||||
int64_t latency;
|
||||
int64_t coefficientSize;
|
||||
|
||||
gaborator_analyze_entry cArray[C_ARRAY_SIZE];
|
||||
private:
|
||||
gaborator::parameters parameters;
|
||||
gaborator::analyzer<float> analyzer;
|
||||
gaborator::coefs<float> coefs;
|
||||
};
|
||||
|
||||
void* gaborator_initialize(double sampleRate, int bandsPerOctave, double minimumFrequency, double referenceFrequency, double maximumFrequency){
|
||||
|
||||
auto state = new GaboratorState();
|
||||
|
||||
state->paramsRef = new gaborator::parameters(bandsPerOctave, minimumFrequency / sampleRate, referenceFrequency / sampleRate, 1.0, 1e-5);
|
||||
state->analyzerRef = new gaborator::analyzer<float>(*(state->paramsRef));
|
||||
state->coefsRef = new gaborator::coefs<float>(*(state->analyzerRef));
|
||||
|
||||
//converts frequency (ff_max) in hertz to the number of bands above the min frequency
|
||||
//the ceil is used to end up at a full band
|
||||
int interesting_bands = ceil(bandsPerOctave * log(maximumFrequency/minimumFrequency)/log(2.0f));
|
||||
|
||||
//since bands are ordered from high to low we are only interested in lower bands:
|
||||
//fs/2.0 is the nyquist frequency
|
||||
int total_bands = ceil(bandsPerOctave * log(sampleRate/2.0/minimumFrequency)/log(2.0f));
|
||||
|
||||
state->anal_support = (int64_t) ceil(state->analyzerRef->analysis_support());
|
||||
state->min_band = total_bands - interesting_bands;
|
||||
state->sample_rate = (int) sampleRate;
|
||||
state->t_in = 0;
|
||||
|
||||
assert(state->t_in == 0);
|
||||
|
||||
return state;
|
||||
uintptr_t gaborator_initialize(int blockSize, double sampleRate, int bandsPerOctave, double minimumFrequency, double referenceFrequency, double maximumFrequency, int stepSize){
|
||||
return reinterpret_cast<uintptr_t>(new Gaborator(blockSize, sampleRate, bandsPerOctave, minimumFrequency, referenceFrequency,
|
||||
maximumFrequency, stepSize));
|
||||
}
|
||||
|
||||
long gaborator_get_anal_support(void* ptr) {
|
||||
return reinterpret_cast<GaboratorState*>(ptr)->anal_support;
|
||||
long gaborator_analysis_support(uintptr_t ptr) {
|
||||
return reinterpret_cast<Gaborator*>(ptr)->analysis_support();
|
||||
}
|
||||
|
||||
struct gaborator_analyze_entry* gaborator_analyze(void* ptr, float* audio_block, int audio_block_length, int* length) {
|
||||
auto state = reinterpret_cast<GaboratorState*>(ptr);
|
||||
|
||||
if (audio_block == nullptr) {
|
||||
return &state->cArray[0];
|
||||
}
|
||||
|
||||
std::vector<float> buf(audio_block,audio_block + audio_block_length);
|
||||
|
||||
int output_index = 0;
|
||||
|
||||
state->analyzerRef->analyze(buf.data(), state->t_in, state->t_in + audio_block_length, *(state->coefsRef));
|
||||
|
||||
int64_t st0 = state->t_in - state->anal_support;
|
||||
int64_t st1 = state->t_in - state->anal_support + audio_block_length;
|
||||
|
||||
apply(
|
||||
*state->analyzerRef,
|
||||
*state->coefsRef,
|
||||
[&](std::complex<float> coef, int band, int64_t audioSampleIndex ) {
|
||||
//ignores everything above the max_band
|
||||
if(band >= state->min_band){
|
||||
//printf("%f %d %ld\n",std::abs(coef),band,audioSampleIndex);
|
||||
state->cArray[output_index++] = {band, static_cast<int>(audioSampleIndex), std::abs(coef)};
|
||||
//printf("output_index: %d\n", output_index++);
|
||||
//output_index++;
|
||||
}
|
||||
},st0,
|
||||
st1);
|
||||
|
||||
state->t_in += (int64_t) audio_block_length;
|
||||
|
||||
int64_t t_out = state->t_in - state->anal_support;
|
||||
|
||||
forget_before(*state->analyzerRef, *state->coefsRef, t_out - audio_block_length);
|
||||
|
||||
*length = output_index;
|
||||
return &state->cArray[0];
|
||||
int gaborator_number_of_bands(uintptr_t ptr) {
|
||||
return reinterpret_cast<Gaborator*>(ptr)->number_of_bands();
|
||||
}
|
||||
|
||||
int gaborator_bandcenters_array_length(void* ptr) {
|
||||
auto state = reinterpret_cast<GaboratorState*>(ptr);
|
||||
int max_band = state->analyzerRef->bandpass_bands_end();
|
||||
return max_band;
|
||||
void gaborator_transform(uintptr_t ptr, float* audio_block, int audio_block_length, gaborator_transform_callback callback, uintptr_t callback_data){
|
||||
reinterpret_cast<Gaborator*>(ptr)->gabor_transform(audio_block, audio_block_length, callback, callback_data);
|
||||
}
|
||||
|
||||
void gaborator_bandcenters(void* ptr, float* band_centers) {
|
||||
auto state = reinterpret_cast<GaboratorState*>(ptr);
|
||||
int max_band = state->analyzerRef->bandpass_bands_end();
|
||||
|
||||
for(int i = 0 ; i < max_band ; i++){
|
||||
if(i<state->min_band){
|
||||
band_centers[i]=-1;
|
||||
}else{
|
||||
band_centers[i]=state->analyzerRef->band_ff(i) * state->sample_rate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gaborator_release(void* ptr) {
|
||||
auto state = reinterpret_cast<GaboratorState*>(ptr);
|
||||
|
||||
//cleanup memory
|
||||
delete state->analyzerRef;
|
||||
delete state->coefsRef;
|
||||
delete state->paramsRef;
|
||||
delete state;
|
||||
void gaborator_release(uintptr_t ptr) {
|
||||
auto g = reinterpret_cast<Gaborator*>(ptr);
|
||||
delete g;
|
||||
}
|
|
@ -1,24 +1,22 @@
|
|||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#include <cstdint>
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
struct gaborator_analyze_entry {
|
||||
int bandIndex;
|
||||
int sampleIndex;
|
||||
float magnitude;
|
||||
};
|
||||
typedef void (*gaborator_transform_callback)(uintptr_t callback_data, float* data, int size);
|
||||
|
||||
void* gaborator_initialize(double sampleRate, int bandsPerOctave, double minimumFrequency, double referenceFrequency, double maximumFrequency);
|
||||
long gaborator_get_anal_support(void* ptr);
|
||||
uintptr_t gaborator_initialize(int blockSize, double sampleRate, int bandsPerOctave, double minimumFrequency, double referenceFrequency, double maximumFrequency, int stepSize);
|
||||
void gaborator_release(uintptr_t ptr);
|
||||
|
||||
struct gaborator_analyze_entry* gaborator_analyze(void* ptr, float* audio_block, int audio_block_length, int* length);
|
||||
long gaborator_analysis_support(uintptr_t ptr);
|
||||
int gaborator_number_of_bands(uintptr_t ptr);
|
||||
|
||||
int gaborator_bandcenters_array_length(void* ptr);
|
||||
void gaborator_transform(uintptr_t ptr, float* audio_block, int audio_block_length, gaborator_transform_callback callback, uintptr_t callback_data);
|
||||
|
||||
void gaborator_bandcenters(void* ptr, float* band_centers);
|
||||
|
||||
void gaborator_release(void* ptr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
11
test.cpp
11
test.cpp
|
@ -1,5 +1,6 @@
|
|||
#include "cgaborator.h"
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
|
||||
#define BLOCK_SIZE 8192
|
||||
|
||||
|
@ -11,10 +12,7 @@ int main() {
|
|||
return 1;
|
||||
}
|
||||
|
||||
auto ptr = gaborator_initialize(16000, 85, 110, 7040, 440);
|
||||
if (ptr == nullptr){
|
||||
return 1;
|
||||
}
|
||||
auto ptr = gaborator_initialize(8192, 16000, 85, 110, 440, 7040, 128);
|
||||
|
||||
float audioData[BLOCK_SIZE];
|
||||
|
||||
|
@ -23,8 +21,9 @@ int main() {
|
|||
if (read < sizeof(audioData) / sizeof(audioData[0])) { //EOF
|
||||
break;
|
||||
}
|
||||
int len;
|
||||
gaborator_analyze(ptr, &audioData[0], sizeof(audioData) / sizeof(audioData[0]), &len);
|
||||
gaborator_transform(ptr, &audioData[0], sizeof(audioData) / sizeof(audioData[0]), [](uintptr_t, float*, int){
|
||||
|
||||
}, 0);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
|
Loading…
Reference in a new issue