New callback style API

This commit is contained in:
DataHoarder 2022-01-29 05:58:25 +01:00
parent d70702bf2c
commit 636476a226
4 changed files with 173 additions and 115 deletions

View file

@ -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)

View file

@ -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;
}

View file

@ -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
}

View file

@ -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);