Engineering School, 2nd year
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

480 lines
12 KiB

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fftw3.h>
#include <math.h>
#include "beatDetectSEF.h"
#include "apodization_functions.h"
int main() {
int frame_size = 2 * FRAME_NUM_SAMPLE * sizeof(signed short int);
signed short int * beep_buffer;
signed short int * input_buffer;
SEF_data sef_data;
FILE * peaks_stats = fopen("peaks_stats", "w");
// mean filter should retain the last 300ms (here we use only 100ms)
int peak_detection_length = (int)((0.3 * SAMPLE_RATE) / (FRAME_NUM_SAMPLE - OVERLAP));
Shifted_array peak_detection;
// peaks, retain the last 5s
int peaks_length = (int)((5.0 * SAMPLE_RATE) / (FRAME_NUM_SAMPLE - OVERLAP));
Shifted_array peaks;
// periodicity 1s mean estimation,
//int tempo_length = (int)((1 * SAMPLE_RATE) / (FRAME_NUM_SAMPLE - OVERLAP));
int tempo_length = 1;
Shifted_array tempo;
init_sef(&sef_data);
shifted_array_init(&peak_detection, peak_detection_length);
shifted_array_init(&peaks, peaks_length);
shifted_array_init(&tempo, tempo_length);
// compute min & max shift to detect tempo between 60 and 180 bpm
int min_shift = (int)((0.3 * SAMPLE_RATE) / (FRAME_NUM_SAMPLE - OVERLAP));
int max_shift = (int)((2.0 * SAMPLE_RATE) / (FRAME_NUM_SAMPLE - OVERLAP));
input_buffer = (signed short int *) malloc(4 * FRAME_NUM_SAMPLE * 2 * sizeof(signed short int));
beep_buffer = (signed short int *) malloc(FRAME_NUM_SAMPLE * sizeof(signed short int));
float * impulses = malloc(peaks_length * sizeof(float));
make_beep(beep_buffer, FRAME_NUM_SAMPLE, 1024);
int loop = 10;
int beep = 0;
float last_loop = 0;
while (1) {
float instant_energy;
float mean_energy;
read_buffer(input_buffer, frame_size);
buffer_to_samples(input_buffer, sef_data.in_spl, FRAME_NUM_SAMPLE);
// SEF computation
instant_energy = sef_variation(&sef_data);
shifted_array_push(&peak_detection, instant_energy);
mean_energy = shifted_array_mean(&peak_detection);
float p = instant_energy - (mean_energy * COEF);
if (p < 0) {
p = 0;
} else {
p = instant_energy;
}
shifted_array_push(&peaks, p);
fprintf(peaks_stats, "%f ", p);
// make bip
if (loop <= 0) {
beep = BEEP_LENGTH;
loop = compute_periodicity(&peaks, min_shift, max_shift);
generate_impulses_train(impulses, peaks_length, loop);
int phase = compute_phase(&peaks, impulses, loop);
fprintf(stderr, "tempo: %d (%d bpm), déphasage: %d\n", loop, (int) ((60 * SAMPLE_RATE) / ( loop * (FRAME_NUM_SAMPLE - OVERLAP))), phase);
if (phase > (0.76 * loop)){
// we advance the next beep
loop = phase;
} else if (phase < (0.24 * loop) ){
// we delay the next beep
loop = loop + phase;
}
}
--loop;
if (loop <= 2 || beep > 0) {
add_beep_to_output(input_buffer, beep_buffer, FRAME_NUM_SAMPLE);
--beep;
} else {
int i;
for (i = FRAME_NUM_SAMPLE - 1; i >= 0; --i) {
input_buffer[2 * i] = input_buffer[2 * i] / 2;
input_buffer[(2 * i) + 1] = input_buffer[(2 * i) + 1] / 2;
}
}
write_buffer(input_buffer, frame_size);
}
free(input_buffer);
free(beep_buffer);
destroy_sef(&sef_data);
shifted_array_destroy(&peaks);
shifted_array_destroy(&peak_detection);
shifted_array_destroy(&tempo);
fclose(peaks_stats);
return 0;
}
void init_sef(SEF_data * sef_data) {
// we need: FIR_ORDER spectrogram + hanning window
sef_data->spectrograms_spl = (float *) calloc((FFT_NUM_SAMPLE * FIR_ORDER) + FRAME_NUM_SAMPLE,
sizeof(float));
// init the index of the last spectrogram
sef_data->last_spectrogram = 0;
// compute the hanning window
sef_data->hanning_spl = sef_data->spectrograms_spl + (FFT_NUM_SAMPLE * FIR_ORDER);
hanning(sef_data->hanning_spl, FRAME_NUM_SAMPLE);
// allocate in and out buffer for the fft
sef_data->in_spl = (float * ) fftwf_malloc(FRAME_NUM_SAMPLE * sizeof(float));
sef_data->fft_spl = (fftwf_complex *) fftwf_malloc(FFT_NUM_SAMPLE * sizeof(fftw_complex));
// prepare a real to complex discrete fourier transform
sef_data->plan = fftwf_plan_dft_r2c_1d(FRAME_NUM_SAMPLE, sef_data->in_spl, sef_data->fft_spl, FFTW_MEASURE | FFTW_PRESERVE_INPUT);
}
float sef_variation (SEF_data * sef_data) {
// apply a hanning window to the samples
apply_window(sef_data->in_spl, sef_data->hanning_spl, FRAME_NUM_SAMPLE);
// execute dft
fftwf_execute(sef_data->plan);
// shift the circular buffer one step forward (first element become the second etc...)
sef_data->last_spectrogram = (sef_data->last_spectrogram + (FIR_ORDER - 1)) % FIR_ORDER;
float * buffer = sef_data->spectrograms_spl + (sef_data->last_spectrogram * FFT_NUM_SAMPLE);
//fprintf(stderr,"circular buffer index: %d\n", sef_data->last_spectrogram);
// for each frequency, compute the norm of the complex, reduce
// the high freq coef by multiply by the second half of the hanning
// window (we "forgot" the last complex coef, seem not to be important)
int i; // from 255 to 0
float log10 = log(10.0);
for (i = FFT_NUM_SAMPLE - 2; i >= 0; --i) {
float value, real,imag;
fftwf_complex * cpx;
// todo, before getting the norm, apply a lowpass filter to
// perform energy integration (cf ismir2004.pdf)
cpx = sef_data->fft_spl + i;
real = (*cpx)[0];
imag = (*cpx)[1];
value = sqrt((real * real) + (imag * imag)) * sef_data->hanning_spl[FFT_NUM_SAMPLE + i - 1];
// store the value in the current spectrogram
if(value > 0) {
buffer[i] = 20 * (log(value) / log10);
} else {
buffer[i] = 0;
}
}
// apply differentiator filter (FIR), with respect to time for each freq
// store the result in the in_sample array,
// todo: write a real filter, instead of using the [1;-1]
float * last_buffer = sef_data->spectrograms_spl +
(((sef_data->last_spectrogram + 1) % FIR_ORDER)
* FFT_NUM_SAMPLE);
for (i = FFT_NUM_SAMPLE - 2; i >= 0; --i) {
// first order differentiator filter
sef_data->in_spl[i] = buffer[i] - last_buffer[i];
}
// sum the positive variation
float sum = 0;
for (i = FFT_NUM_SAMPLE - 2; i >= 0; --i) {
float value = sef_data->in_spl[i];
if (value > 0) {
sum += value;
}
}
return sum;
}
void destroy_sef(SEF_data * sef_data) {
free(sef_data->spectrograms_spl);
fftwf_free(sef_data->in_spl);
fftwf_free(sef_data->fft_spl);
}
void write_buffer(signed short int * buffer, int frame_size) {
int rc = fwrite(buffer, 1, frame_size, stdout);
if (rc == -1) {
perror("SEF");
exit(-1);
} else if (rc != frame_size) {
fprintf(stderr, "SEF: short write: write %d bytes\n", rc);
}
}
void read_buffer(signed short int * buffer, int frame_size) {
int rc = fread(buffer, 1, frame_size, stdin);
if (rc == 0) {
perror("SEF");
exit(-1);
} else if (rc != frame_size) {
fprintf(stderr, "SEF: short read: read %d bytes\n", rc);
}
}
void buffer_to_samples(signed short int * buffer, float * samples, int num_samples) {
int i;
for(i = 0; i < num_samples ; ++ i) {
samples[i] = (float) buffer[i];
}
}
void apply_window(float * samples, float * window, int num_samples) {
int i;
for(i = 0; i < num_samples ; ++ i) {
samples[i] *= window[i];
}
}
void cpx_sample_to_spectral_energetic_density(fftwf_complex * fft_cpx_samples, float * fft_abs_sample, int num_samples) {
int i;
for (i = 0; i < num_samples; ++i) {
float real,imag;
fftwf_complex * cpx;
cpx = fft_cpx_samples + i;
real = (*cpx)[0];
imag = (*cpx)[1];
fft_abs_sample[i] = (real * real) + (imag * imag);
}
}
void add_beep_to_output(signed short int * output_buffer, signed short int * bip_buffer, int bip_length) {
int i;
for(i = 0; i < bip_length; ++ i) {
output_buffer[2 * i] = bip_buffer[i];
output_buffer[(2 * i) + 1] = bip_buffer[i];
}
}
void make_beep(signed short int * bip_buffer, int bip_length, int freq) {
int i;
for(i = 0; i < bip_length; ++ i) {
double t ;
signed short int val;
t = 0.000023 * i; // 0.000023 = 1/44100
val = rint(15000 * cos(2 * M_PI * freq * t));
bip_buffer[i] = val;
}
}
void shifted_array_init(Shifted_array * array, int length) {
array->buffer = (float *) calloc(length, sizeof(float));
array->length = length;
array->last_in = array->buffer + length - 1;
array->total = 0;
}
void shifted_array_destroy(Shifted_array * array) {
free(array->buffer);
}
float shifted_array_mean(Shifted_array * array) {
return array->total / array->length;
}
float shifted_array_total(Shifted_array * array) {
return array->total;
}
float shifted_array_push(Shifted_array * array, float value) {
float out;
++ array->last_in;
if ((array->last_in - array->buffer) >= array->length) {
// end of array is reached
array->last_in = array->buffer;
}
out = * array->last_in;
* array->last_in = value;
array->total = array->total - out + value;
return out;
}
float shifted_array_value_at(Shifted_array * array, int index) {
float * p = array->last_in + 1 + index;
if ((p - array->buffer) >= array->length) {
// end of array is reached
p -= array->length;
}
return *p;
}
void test_shifted_array() {
Shifted_array a;
shifted_array_init(&a, 8);
int i;
for(i = 1; i <= 20; ++i) {
float total = shifted_array_total(&a);
float mean = shifted_array_mean(&a);
fprintf(stderr, "in: %d, out: %f, mean: %f, total: %f\n",
i,
shifted_array_push(&a, i),
mean,
total);
}
fprintf(stderr, "%f %f %f %f\n",
shifted_array_value_at(&a, 0),
shifted_array_value_at(&a, 1),
shifted_array_value_at(&a, 6),
shifted_array_value_at(&a, 7));
shifted_array_destroy(&a);
}
int compute_periodicity(Shifted_array * peaks, int min_shift, int max_shift) {
int shift;
int best_shift = min_shift;
float max = 0;
//for(shift = max_shift; shift >= min_shift; -- shift ) {
for(shift = min_shift; shift <= max_shift; ++shift ) {
// compute each step of the auto correlation
float sum = 0;
int i;
for (i = peaks->length - 1; i >= 0; --i) {
sum += shifted_array_value_at(peaks, i) * shifted_array_value_at(peaks, (i + shift) % peaks->length);
}
//fprintf(stderr, "shifs: %d, sum: %f\n", shift, sum);
if (sum > (max * 1.0)) {
// float mul = ((float) shift) / ((float) best_shift);
// float rmul = round(mul);
// if (abs(mul - rmul) > 0.1) {
// not a multiple, store the tmp tempo
max = sum;
best_shift = shift;
// }
}
}
//fprintf(stderr, "%d\n", best_shift);
return best_shift;
}
int compute_phase(Shifted_array * peaks, float * impulses, int shift) {
int phase = 0;
int best_phase = 0;
float max = 0;
for(phase = 0; phase < shift; ++phase ) {
// compute each step of the auto correlation
float sum = 0;
int i;
for (i = peaks->length - 1; i >= 0; --i) {
sum += (impulses[i]) * shifted_array_value_at(peaks, (i + phase) % peaks->length);
}
//fprintf(stderr, "shifs: %d, sum: %f\n", shift, sum);
if (sum > max) {
max = sum;
best_phase = phase;
}
}
return best_phase;
}
void test_compute_phase() {
Shifted_array a;
shifted_array_init(&a, 256);
float * imp = (float * ) malloc(256 * sizeof(float));
generate_impulses_train(imp, 256, 50);
generate_impulses_train(a.buffer + 39, 200, 50);
fprintf(stderr, "test phase: %d\n", compute_phase(&a, imp, 50));
shifted_array_destroy(&a);
free(imp);
}
void generate_impulses_train(float * impulses, int length, int shift) {
int i;
for(i = 0; i < length; ++i) {
if ((i % shift) == 0) {
impulses[i] = 1.0;
} else {
impulses[i] = 0.0;
}
}
}