summaryrefslogtreecommitdiffhomepage
path: root/src/lingot-core.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lingot-core.c')
-rw-r--r--src/lingot-core.c416
1 files changed, 416 insertions, 0 deletions
diff --git a/src/lingot-core.c b/src/lingot-core.c
new file mode 100644
index 0000000..d0cdaec
--- /dev/null
+++ b/src/lingot-core.c
@@ -0,0 +1,416 @@
+//-*- C++ -*-
+/*
+ * lingot, a musical instrument tuner.
+ *
+ * Copyright (C) 2004-2007 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>
+
+#ifndef LIB_FFTW
+# include "lingot-complex.h"
+#endif
+
+#include "lingot-fft.h"
+#include "lingot-signal.h"
+#include "lingot-core.h"
+#include "lingot-config.h"
+
+LingotCore* lingot_core_new(LingotConfig* conf) {
+
+ LingotCore* core = malloc(sizeof(LingotCore));
+
+ core->conf = conf;
+ core->running = 1;
+
+# ifdef LIBSNDOBJ
+# ifdef OSS
+ core->audio = new SndRTIO(1, SND_INPUT, 512, 512, SHORTSAM_LE, NULL,
+ core->conf->read_buffer_size*core->conf->oversampling, core->conf->sample_rate, core->conf->audio_dev);
+# endif
+# ifdef ALSA
+ core->audio = new SndRTIO(1, SND_INPUT, 512, 4, SHORTSAM_LE, NULL,
+ core->conf->read_buffer_size*core->conf->oversampling, core->conf->sample_rate, "plughw:0,0");
+# endif
+# else
+ // creates an audio handler.
+ core->audio = lingot_audio_new(1, core->conf->sample_rate, SAMPLE_FORMAT,
+ core->conf->audio_dev);
+# endif
+
+ // 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));
+
+#ifndef LIB_FFTW
+ 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));
+#else
+ core->fftw_in = new fftw_complex[core->conf->fft_size];
+ memset(core->fftw_in, 0, core->conf->fft_size*sizeof(fftw_complex));
+ core->fftw_out = new fftw_complex[core->conf->fft_size];
+ memset(core->fftw_out, 0, core->conf->fft_size*sizeof(fftw_complex));
+ core->fftwplan = fftw_create_plan(core->conf->fft_size, FFTW_FORWARD, FFTW_ESTIMATE);
+#endif
+
+ // read buffer from soundcard.
+ core->read_buffer= malloc((core->conf->read_buffer_size
+ *core->conf->oversampling)*sizeof(SAMPLE_TYPE));
+ memset(core->read_buffer, 0, (core->conf->read_buffer_size
+ *core->conf->oversampling)*sizeof(SAMPLE_TYPE));
+
+ // read buffer from soundcard in floating point format.
+ core->flt_read_buffer= malloc((core->conf->read_buffer_size
+ *core->conf->oversampling)*sizeof(FLT));
+ memset(core->flt_read_buffer, 0, (core->conf->read_buffer_size
+ *core->conf->oversampling)*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 = malloc((core->conf->temporal_buffer_size)
+ *sizeof(FLT));
+ lingot_signal_hamming_window(core->conf->temporal_buffer_size,
+ core->hamming_window_temporal);
+ core->hamming_window_fft = malloc((core->conf->fft_size) *sizeof(FLT));
+ lingot_signal_hamming_window(core->conf->fft_size, core->hamming_window_fft);
+
+ 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);
+
+ // ------------------------------------------------------------
+
+ core->freq = 0.0;
+ return core;
+}
+
+// -----------------------------------------------------------------------
+
+/* Deallocate resources */
+void lingot_core_destroy(LingotCore* core) {
+ pthread_attr_destroy(&core->attr);
+
+#ifdef LIB_FFTW
+ fftw_destroy_plan(core->fftwplan);
+ free(core->fftw_in);
+ free(core->fftw_out);
+#else
+ lingot_fft_destroy_phase_factors(); // destroy phase factors.
+ free(core->fft_out);
+#endif
+
+ lingot_audio_destroy(core->audio);
+
+ free(core->spd_fft);
+ free(core->X);
+ free(core->spd_dft);
+ free(core->diff2_spd_fft);
+ free(core->read_buffer);
+ free(core->flt_read_buffer);
+ free(core->temporal_buffer);
+
+ free(core->windowed_temporal_buffer);
+ free(core->windowed_fft_buffer);
+
+ free(core->hamming_window_temporal);
+ free(core->hamming_window_fft);
+
+ lingot_filter_destroy(core->antialiasing_filter);
+
+ free(core);
+}
+
+// -----------------------------------------------------------------------
+
+// signal decimation with antialiasing, in & out overlapables.
+void lingot_core_decimate(LingotCore* core, FLT* in, FLT* out) {
+ register unsigned int i, j;
+
+ // low pass filter to avoid aliasing.
+ lingot_filter_filter(core->antialiasing_filter,
+ core->conf->read_buffer_size*core->conf->oversampling, in, in);
+
+ // compression.
+ for (i = j = 0; i < core->conf->read_buffer_size; i++, j
+ += core->conf->oversampling)
+ out[i] = in[j];
+}
+
+// -----------------------------------------------------------------------
+
+void lingot_core_process(LingotCore* core) {
+ register unsigned int i, k; // loop variables.
+ FLT delta_w_FFT = 2.0*M_PI/core->conf->fft_size;
+ // FFT resolution in rads.
+
+# ifdef LIBSNDOBJ
+ audio->Read();
+ for (i = 0; i < core->conf->oversampling*core->conf->read_buffer_size; i++)
+ flt_read_buffer[i] = audio->Output(i);
+# else
+ if ((lingot_audio_read(core->audio, core->read_buffer,
+ core->conf->oversampling*core->conf->read_buffer_size
+ *sizeof(SAMPLE_TYPE)))< 0) {
+ //perror("Error reading samples");
+ return;
+ }
+
+ for (i = 0; i < core->conf->oversampling*core->conf->read_buffer_size; i++)
+ core->flt_read_buffer[i] = core->read_buffer[i];
+# endif
+
+ //
+ // just readed:
+ //
+ // ----------------------------
+ // |bxxxbxxxbxxxbxxxbxxxbxxxbxxx|
+ // ----------------------------
+ //
+ // <----------------------------> read_buffer_size*oversampling
+ //
+
+ /* we shift the temporal window to leave a hollow where place the new piece
+ of data read. The buffer is actually a queue. */
+ if (core->conf->temporal_buffer_size > core->conf->read_buffer_size)
+ memcpy(core->temporal_buffer,
+ &core->temporal_buffer[core->conf->read_buffer_size],
+ (core->conf->temporal_buffer_size
+ - core->conf->read_buffer_size) *sizeof(FLT));
+
+ //
+ // previous buffer situation:
+ //
+ // ------------------------------------------
+ // | xxxxxxxxxxxxxxxxxxxxxx | yyyyy | aaaaaaa |
+ // ------------------------------------------
+ // <------> read_buffer_size
+ // <---------------> fft_size
+ // <----------------------------------------> temporal_buffer_size
+ //
+ // new situation:
+ //
+ // ------------------------------------------
+ // | xxxxxxxxxxxxxxxxyyyyaa | aaaaa | |
+ // ------------------------------------------
+ //
+
+ /* we decimate the read signal and put it at the end of the buffer. */
+ if (core->conf->oversampling > 1)
+ lingot_core_decimate(
+ core,
+ core->flt_read_buffer,
+ &core->temporal_buffer[core->conf->temporal_buffer_size - core->conf->read_buffer_size]);
+ else
+ memcpy(
+ &core->temporal_buffer[core->conf->temporal_buffer_size - core->conf->read_buffer_size],
+ core->flt_read_buffer, core->conf->read_buffer_size*sizeof(FLT));
+ //
+ // ------------------------------------------
+ // | xxxxxxxxxxxxxxxxyyyyaa | aaaaa | bbbbbbb |
+ // ------------------------------------------
+ //
+
+ // ----------------- TRANSFORMATION TO FREQUENCY DOMAIN ----------------
+
+ FLT _1_N2 = 1.0/(core->conf->fft_size*core->conf->fft_size);
+ // SPD normalization constant
+
+ // windowing
+ for (i = 0; i < core->conf->fft_size; i++) {
+ core->windowed_fft_buffer[i]
+ = core->temporal_buffer[core->conf->temporal_buffer_size - core->conf->fft_size + i]
+ *core->hamming_window_fft[i];
+ }
+
+# ifdef LIB_FFTW
+ for (i = 0; i < core->conf->fft_size; i++)
+ fftw_in[i].re = core->windowed_fft_buffer[i];
+
+ // transformation.
+ fftw_one(core->fftwplan, core->fftw_in, core->fftw_out);
+
+ // esteem of SPD from FFT. (normalized squared module)
+ for (i = 0; i < ((core->conf->fft_size > 256) ? (core->conf->fft_size >> 1) : 256); i++)
+ core->spd_fft[i] = (core->fftw_out[i].re*core->fftw_out[i].re +
+ core->fftw_out[i].im*core->fftw_out[i].im)*_1_N2;
+# else
+
+ // transformation.
+ lingot_fft_fft(core->windowed_fft_buffer, core->fft_out,
+ core->conf->fft_size);
+
+ // esteem of SPD from FFT. (normalized squared module)
+ for (i = 0; i < ((core->conf->fft_size > 256) ? (core->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;
+# endif
+
+ // representable piece
+ memcpy(core->X, core->spd_fft,
+ ((core->conf->fft_size > 256) ? (core->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 < (core->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(core->conf, core->spd_fft,
+ core->diff2_spd_fft, (core->conf->fft_size >> 1)); // take the fundamental peak.
+
+ if (Mi == (signed) (core->conf->fft_size >> 1)) {
+ core->freq = 0.0;
+ return;
+ }
+
+ FLT w = (Mi - 1)*delta_w_FFT;
+ // frecuencia de la muestra anterior al pico.
+
+ // Approximation to fundamental frequency by selective DFTs
+ // ---------------------------------------------------------
+
+ FLT d_w = delta_w_FFT;
+ for (k = 0; k < core->conf->dft_number; k++) {
+
+ d_w = 2.0*d_w/(core->conf->dft_size - 1); // resolution in rads.
+
+ if (k == 0) {
+ lingot_fft_spd(core->windowed_fft_buffer, core->conf->fft_size, w
+ + d_w, d_w, &core->spd_dft[1], core->conf->dft_size- 2);
+ core->spd_dft[0] = core->spd_fft[Mi - 1];
+ core->spd_dft[core->conf->dft_size - 1] = core->spd_fft[Mi + 1]; // 2 samples known.
+ } else
+ lingot_fft_spd(core->windowed_fft_buffer, core->conf->fft_size, w,
+ d_w, core->spd_dft, core->conf->dft_size);
+
+ lingot_signal_get_max(core->spd_dft, core->conf->dft_size, &Mi); // search the maximum.
+
+ w += (Mi - 1)*d_w; // previous sample to the peak.
+ }
+
+ w += d_w; // approximation by DFTs.
+
+ // windowing
+ for (i = 0; i < core->conf->temporal_buffer_size; i++) {
+ core->windowed_temporal_buffer[i] = core->temporal_buffer[i]
+ *core->hamming_window_temporal[i];
+ }
+
+ // 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 < core->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,
+ core->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*core->conf->sample_rate) /(2.0*M_PI
+ *core->conf->oversampling); // analog frequency.
+
+}
+
+/* start running the core in another thread */
+void lingot_core_start(LingotCore* core) {
+ pthread_attr_init(&core->attr);
+
+ // detached thread.
+ // pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ pthread_create(&core->thread, &core->attr, (void* (*)(void*)) lingot_core_run, core);
+}
+
+/* stop running the core */
+void lingot_core_stop(LingotCore* core) {
+ void* thread_result;
+
+ core->running = 0;
+
+ // wait for the thread exit
+ pthread_join(core->thread, &thread_result);
+}
+
+/* run the core */
+void lingot_core_run(LingotCore* core) {
+ while (core->running) {
+ lingot_core_process(core); // process new data block.
+ }
+}