/* * lingot, a musical instrument tuner. * * Copyright (C) 2004-2011 Ibán Cereijo Graña, Jairo Chapela Martínez. * * This file is part of lingot. * * lingot is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * lingot is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with lingot; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <stdio.h> #include <math.h> #include <sys/soundcard.h> #include <string.h> #include <errno.h> #include <sys/time.h> #include <time.h> #include <stdlib.h> #include "lingot-complex.h" #include "lingot-fft.h" #include "lingot-signal.h" #include "lingot-core.h" #include "lingot-config.h" #include "lingot-i18n.h" #include "lingot-msg.h" #ifndef timeradd #define timeradd(a, b, result) \ do { \ (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \ (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \ if ((result)->tv_usec >= 1000000) \ { \ ++(result)->tv_sec; \ (result)->tv_usec -= 1000000; \ } \ } while (0) #endif int lingot_core_read_callback(FLT* read_buffer, int read_buffer_size, void *arg); void lingot_core_run_reading_thread(LingotCore* core); void lingot_core_run_computation_thread(LingotCore* core); int decimation_input_index = 0; LingotCore* lingot_core_new(LingotConfig* conf) { char buff[1000]; LingotCore* core = malloc(sizeof(LingotCore)); core->conf = conf; core->running = 0; core->audio = NULL; core->spd_fft = NULL; core->X = NULL; core->spd_dft = NULL; core->diff2_spd_fft = NULL; core->flt_read_buffer = NULL; core->temporal_buffer = NULL; core->windowed_temporal_buffer = NULL; core->windowed_fft_buffer = NULL; core->hamming_window_temporal = NULL; core->hamming_window_fft = NULL; core->antialiasing_filter = NULL; int requested_sample_rate = conf->sample_rate; if (conf->sample_rate <= 0) { conf->sample_rate = 0; } core->audio = lingot_audio_new(conf->audio_system, conf->audio_dev[conf->audio_system], conf->sample_rate, (LingotAudioProcessCallback) lingot_core_read_callback, core); if (core->audio != NULL) { if (requested_sample_rate != core->audio->real_sample_rate) { conf->sample_rate = core->audio->real_sample_rate; lingot_config_update_internal_params(conf); sprintf( buff, _("The requested sample rate is not available, the real sample rate has been set to %d Hz"), core->audio->real_sample_rate); lingot_msg_add_warning(buff); } if (conf->temporal_buffer_size < conf->fft_size) { conf->temporal_window = ((double) conf->fft_size * conf->oversampling) / conf->sample_rate; conf->temporal_buffer_size = conf->fft_size; lingot_config_update_internal_params(conf); sprintf( buff, _( "The temporal buffer is smaller than FFT size. It has been increased to %0.3f seconds"), conf->temporal_window); lingot_msg_add_warning(buff); } // since the SPD is simmetrical, we only store the 1st half. if (core->conf->fft_size > 256) { core->spd_fft = malloc((core->conf->fft_size >> 1) * sizeof(FLT)); core->X = malloc((core->conf->fft_size >> 1) * sizeof(FLT)); memset(core->spd_fft, 0, (core->conf->fft_size >> 1) * sizeof(FLT)); memset(core->X, 0, (core->conf->fft_size >> 1) * sizeof(FLT)); } else { // if the fft size is 256, we store the whole signal for representation. core->spd_fft = malloc((core->conf->fft_size) * sizeof(FLT)); core->X = malloc((core->conf->fft_size) * sizeof(FLT)); memset(core->spd_fft, 0, core->conf->fft_size * sizeof(FLT)); memset(core->X, 0, (core->conf->fft_size) * sizeof(FLT)); } core->spd_dft = malloc((core->conf->dft_size) * sizeof(FLT)); memset(core->spd_dft, 0, core->conf->dft_size * sizeof(FLT)); core->diff2_spd_fft = malloc(core->conf->fft_size * sizeof(FLT)); // 2nd derivative from SPD. memset(core->diff2_spd_fft, 0, core->conf->fft_size * sizeof(FLT)); memset(core->spd_dft, 0, core->conf->dft_size * sizeof(FLT)); lingot_fft_create_phase_factors(conf); // creates the phase factors for FFT. core->fft_out = malloc((core->conf->fft_size) * sizeof(LingotComplex)); // complex signal in freq domain. memset(core->fft_out, 0, core->conf->fft_size * sizeof(LingotComplex)); // audio source read in floating point format. core->flt_read_buffer = malloc(core->audio->read_buffer_size * sizeof(FLT)); memset(core->flt_read_buffer, 0, core->audio->read_buffer_size * sizeof(FLT)); // stored samples. core->temporal_buffer = malloc((core->conf->temporal_buffer_size) * sizeof(FLT)); memset(core->temporal_buffer, 0, core->conf->temporal_buffer_size * sizeof(FLT)); core->hamming_window_temporal = NULL; core->hamming_window_fft = NULL; if (conf->window_type != NONE) { core->hamming_window_temporal = malloc( (core->conf->temporal_buffer_size) * sizeof(FLT)); core->hamming_window_fft = malloc((core->conf->fft_size) * sizeof(FLT)); lingot_signal_window(core->conf->temporal_buffer_size, core->hamming_window_temporal, conf->window_type); lingot_signal_window(core->conf->fft_size, core->hamming_window_fft, conf->window_type); } core->windowed_temporal_buffer = malloc( (core->conf->temporal_buffer_size) * sizeof(FLT)); memset(core->windowed_temporal_buffer, 0, core->conf->temporal_buffer_size * sizeof(FLT)); core->windowed_fft_buffer = malloc((core->conf->fft_size) * sizeof(FLT)); memset(core->windowed_fft_buffer, 0, core->conf->fft_size * sizeof(FLT)); /* * 8 order Chebyshev filters, with wc=0.9/i (normalized respect to * Pi). We take 0.9 instead of 1 to leave a 10% of safety margin, * in order to avoid aliased frequencies near to w=Pi, due to non * ideality of the filter. * * The cut frequencies wc=Pi/i, with i=1..20, correspond with the * oversampling factor, avoiding aliasing at decimation. * * Why Chebyshev filters?, for a given order, those filters yield * abrupt falls than other ones as Butterworth, making the most of * the order. Although Chebyshev filters affects more to the phase, * it doesn't matter due to the analysis is made on the signal * power distribution (only module). */ core->antialiasing_filter = lingot_filter_cheby_design(8, 0.5, 0.9 / core->conf->oversampling); pthread_mutex_init(&core->temporal_buffer_mutex, NULL); // ------------------------------------------------------------ core->running = 1; } core->freq = 0.0; return core; } // ----------------------------------------------------------------------- /* Deallocate resources */ void lingot_core_destroy(LingotCore* core) { if (core->audio != NULL) { lingot_fft_destroy_phase_factors(); // destroy phase factors. free(core->fft_out); lingot_audio_destroy(core->audio); free(core->spd_fft); free(core->X); free(core->spd_dft); free(core->diff2_spd_fft); free(core->flt_read_buffer); free(core->temporal_buffer); if (core->hamming_window_fft != NULL) { free(core->hamming_window_temporal); } if (core->windowed_temporal_buffer != NULL) { free(core->windowed_temporal_buffer); } if (core->hamming_window_fft != NULL) { free(core->hamming_window_fft); } if (core->windowed_fft_buffer != NULL) { free(core->windowed_fft_buffer); } if (core->antialiasing_filter != NULL) lingot_filter_destroy(core->antialiasing_filter); pthread_mutex_destroy(&core->temporal_buffer_mutex); } free(core); } // ----------------------------------------------------------------------- // reads a new piece of signal from audio source, apply filtering and // decimation and appends it to the buffer int lingot_core_read_callback(FLT* read_buffer, int read_buffer_size, void *arg) { unsigned int i, decimation_output_index; // loop variables. int decimation_output_len; FLT* decimation_in; FLT* decimation_out; LingotCore* core = (LingotCore*) arg; LingotConfig* conf = core->conf; memcpy(core->flt_read_buffer, read_buffer, read_buffer_size * sizeof(FLT)); // double freq = 100.0; // for (i = 0; i < read_buffer_size; i++) { // core->flt_read_buffer[i] = 5e2 * cos(2.0 * M_PI * freq * i // / conf->sample_rate); // } if (conf->gain_nu != 1.0) { for (i = 0; i < read_buffer_size; i++) core->flt_read_buffer[i] *= conf->gain_nu; } // // just readed: // // ---------------------------- // |bxxxbxxxbxxxbxxxbxxxbxxxbxxx| // ---------------------------- // // <----------------------------> read_buffer_size*oversampling // decimation_output_len = 1 + (read_buffer_size - (decimation_input_index + 1)) / conf->oversampling; pthread_mutex_lock(&core->temporal_buffer_mutex); /* we shift the temporal window to leave a hollow where place the new piece of data read. The buffer is actually a queue. */ if (conf->temporal_buffer_size > decimation_output_len) { memmove(core->temporal_buffer, &core->temporal_buffer[decimation_output_len], (conf->temporal_buffer_size - decimation_output_len) * sizeof(FLT)); } // // previous buffer situation: // // ------------------------------------------ // | xxxxxxxxxxxxxxxxxxxxxx | yyyyy | aaaaaaa | // ------------------------------------------ // <------> read_buffer_size // <---------------> fft_size // <----------------------------------------> temporal_buffer_size // // new situation: // // ------------------------------------------ // | xxxxxxxxxxxxxxxxyyyyaa | aaaaa | | // ------------------------------------------ // // decimation with lowpass filtering /* we decimate the read signal and put it at the end of the buffer. */ if (conf->oversampling > 1) { decimation_in = core->flt_read_buffer; decimation_out = &core->temporal_buffer[conf->temporal_buffer_size - decimation_output_len]; // low pass filter to avoid aliasing. lingot_filter_filter(core->antialiasing_filter, read_buffer_size, decimation_in, decimation_in); // compression. for (decimation_output_index = 0; decimation_input_index < read_buffer_size; decimation_output_index++, decimation_input_index += conf->oversampling) decimation_out[decimation_output_index] = decimation_in[decimation_input_index]; decimation_input_index -= read_buffer_size; } else memcpy(&core->temporal_buffer[conf->temporal_buffer_size - decimation_output_len], core->flt_read_buffer, decimation_output_len * sizeof(FLT)); // // ------------------------------------------ // | xxxxxxxxxxxxxxxxyyyyaa | aaaaa | bbbbbbb | // ------------------------------------------ // pthread_mutex_unlock(&core->temporal_buffer_mutex); return 0; } void lingot_core_compute_fundamental_fequency(LingotCore* core) { register unsigned int i, k; // loop variables. LingotConfig* conf = core->conf; FLT delta_w_FFT = 2.0 * M_PI / conf->fft_size; // FFT resolution in rads. // ----------------- TRANSFORMATION TO FREQUENCY DOMAIN ---------------- FLT _1_N2 = 1.0 / (conf->fft_size * conf->fft_size); // SPD normalization constant //printf("%d samples transformed of a total of %d\n", conf->fft_size, // conf->temporal_buffer_size); pthread_mutex_lock(&core->temporal_buffer_mutex); // windowing if (conf->window_type != NONE) { for (i = 0; i < conf->fft_size; i++) { core->windowed_fft_buffer[i] = core->temporal_buffer[conf->temporal_buffer_size - conf->fft_size + i] * core->hamming_window_fft[i]; } } else { memmove(core->windowed_fft_buffer, &core->temporal_buffer[conf->temporal_buffer_size - conf->fft_size], conf->fft_size * sizeof(FLT)); } // transformation. lingot_fft_fft(core->windowed_fft_buffer, core->fft_out, conf->fft_size); // esteem of SPD from FFT. (normalized squared module) for (i = 0; i < ((conf->fft_size > 256) ? (conf->fft_size >> 1) : 256); i++) core->spd_fft[i] = (core->fft_out[i].r * core->fft_out[i].r + core->fft_out[i].i * core->fft_out[i].i) * _1_N2; // representable piece memcpy(core->X, core->spd_fft, ((conf->fft_size > 256) ? (conf->fft_size >> 1) : 256) * sizeof(FLT)); // truncated 2nd derivative esteem, to enhance peaks core->diff2_spd_fft[0] = 0.0; for (i = 1; i < (conf->fft_size >> 1) - 1; i++) { core->diff2_spd_fft[i] = 2.0 * core->spd_fft[i] - core->spd_fft[i - 1] - core->spd_fft[i + 1]; // centred 2nd order derivative, to avoid group delay. if (core->diff2_spd_fft[i] < 0.0) core->diff2_spd_fft[i] = 0.0; // truncation } // peaks searching in that signal. int Mi = lingot_signal_get_fundamental_peak(conf, core->spd_fft, core->diff2_spd_fft, (conf->fft_size >> 1)); // take the fundamental peak. if (Mi == (signed) (conf->fft_size >> 1)) { core->freq = 0.0; pthread_mutex_unlock(&core->temporal_buffer_mutex); return; } FLT w = (Mi - 1) * delta_w_FFT; // frequency of sample previous to the peak // Approximation to fundamental frequency by selective DFTs // --------------------------------------------------------- FLT d_w = delta_w_FFT; for (k = 0; k < conf->dft_number; k++) { d_w = 2.0 * d_w / (conf->dft_size - 1); // resolution in rads. if (k == 0) { lingot_fft_spd(core->windowed_fft_buffer, conf->fft_size, w + d_w, d_w, &core->spd_dft[1], conf->dft_size - 2); core->spd_dft[0] = core->spd_fft[Mi - 1]; core->spd_dft[conf->dft_size - 1] = core->spd_fft[Mi + 1]; // 2 samples known. } else lingot_fft_spd(core->windowed_fft_buffer, conf->fft_size, w, d_w, core->spd_dft, conf->dft_size); lingot_signal_get_max(core->spd_dft, conf->dft_size, &Mi); // search the maximum. w += (Mi - 1) * d_w; // previous sample to the peak. } w += d_w; // DFT approximation. // windowing if (conf->window_type != NONE) { for (i = 0; i < conf->temporal_buffer_size; i++) { core->windowed_temporal_buffer[i] = core->temporal_buffer[i] * core->hamming_window_temporal[i]; } } else { memmove(core->windowed_temporal_buffer, core->temporal_buffer, conf->temporal_buffer_size * sizeof(FLT)); } pthread_mutex_unlock(&core->temporal_buffer_mutex); // Maximum finding by Newton-Raphson // ----------------------------------- FLT wk = -1.0e5; FLT wkm1 = w; // first iterator set to the current approximation. FLT d1_SPD, d2_SPD; for (k = 0; (k < conf->max_nr_iter) && (fabs(wk - wkm1) > 1.0e-8); k++) { wk = wkm1; // ! we use the WHOLE temporal window for greater precission. lingot_fft_spd_diffs(core->windowed_temporal_buffer, conf->temporal_buffer_size, wk, &d1_SPD, &d2_SPD); //printf("%f %f %f\n", wk, d1_SPD, d2_SPD); wkm1 = wk - d1_SPD / d2_SPD; } w = wkm1; // frequency in rads. core->freq = (w * conf->sample_rate) / (2.0 * M_PI * conf->oversampling); // analog frequency. } /* start running the core in another thread */ void lingot_core_start(LingotCore* core) { int audio_status = 0; decimation_input_index = 0; if (core->audio != NULL) { audio_status = lingot_audio_start(core->audio); if (audio_status == 0) { pthread_mutex_init(&core->thread_computation_mutex, NULL); pthread_cond_init(&core->thread_computation_cond, NULL); pthread_attr_init(&core->thread_computation_attr); pthread_create(&core->thread_computation, &core->thread_computation_attr, (void* (*)(void*)) lingot_core_run_computation_thread, core); } core->running = 1; } } /* stop running the core */ void lingot_core_stop(LingotCore* core) { void* thread_result; if (core->running == 1) { core->running = 0; // threads cancelation pthread_cancel(core->thread_computation); pthread_join(core->thread_computation, &thread_result); // printf("%p %p %i\n", thread_result, PTHREAD_CANCELED, thread_result // == PTHREAD_CANCELED); pthread_attr_destroy(&core->thread_computation_attr); memset(core->X, 0, ((core->conf->fft_size > 256) ? (core->conf->fft_size >> 1) : core->conf->fft_size) * sizeof(FLT)); core->freq = 0.0; } if (core->audio != NULL) lingot_audio_stop(core->audio); } /* run the core */ void lingot_core_run_computation_thread(LingotCore* core) { struct timeval t, tout; struct timespec tspec; gettimeofday(&tout, NULL); t.tv_sec = 0; t.tv_usec = 1e6 / core->conf->calculation_rate; while (core->running) { lingot_core_compute_fundamental_fequency(core); timeradd(&t, &tout, &tout); tspec.tv_sec = tout.tv_sec; tspec.tv_nsec = 1000 * tout.tv_usec; pthread_cond_timedwait(&core->thread_computation_cond, &core->thread_computation_mutex, &tspec); if (core->audio != NULL) { if (core->audio->interrupted) { memset(core->X, 0, ((core->conf->fft_size > 256) ? (core->conf->fft_size >> 1) : core->conf->fft_size) * sizeof(FLT)); core->freq = 0.0; core->running = 0; } } } }