/* Analyseur de MUsique et ENtraƮnement au CHAnt This file is released under either of the two licenses below, your choice: - LGPL v2.1 or later, https://www.gnu.org The GNU Lesser General Public Licence, version 2.1 or, at your option, any later version. - CeCILL-C, http://www.cecill.info The CeCILL-C license is more adapted to the French laws, but can be converted to the GNU LGPL. You can use, modify and/or redistribute the software under the terms of any of these licences, which should have been provided to you together with this sofware. If that is not the case, you can find a copy of the licences on the indicated web sites. By Nicolas . Brodu @ Inria . fr See http://nicolas.brodu.net/programmation/amuencha/ for more information */ #ifndef AUDIOINPUTTHREAD_H #define AUDIOINPUTTHREAD_H #include #include #include #include #include #include namespace Amuencha { class FrequencyAnalyzer : public QThread { public: FrequencyAnalyzer(QObject *parent = 0); ~FrequencyAnalyzer(); // called by the RT audio thread to feed new data void new_data(float* chunk, int size); void set_sampling_rate(float sampling_rate); void set_min_max_notes(int min_midi_note, int max_midi_note); void set_periods(int periods); void set_max_buffer_duration(int max_buffer_duration); // call to remove all existing chunk references // this may cause signal loss, but this is usually called precisely when the signal is lost... void invalidate_samples(); // Arguments are: frequency bins [f,f+1), and power in each bin // hence the first vector size is 1 more than the second // TODO if useful: make a proper listener API with id, etc typedef std::function&, const std::vector&)> PowerHandler; // sampling rate in Hz // PowerHandler for the callback void setup(float sampling_rate, PowerHandler&& handler, int min_midi_note = 24, int max_midi_note = 72, int periods = 20, int max_buffer_duration = 500); private: void setup(); PowerHandler power_handler; int min_midi_note, max_midi_note; // max_buffer_duration in milliseconds, specifies the largest buffer for computing the frequency content // At lower frequencies, long buffers are needed for accurate frequency separation. // When that max buffer duration is reached, then it is capped and the frequency resolution decreases // Too low buffers also limit the min_freq, duration must be >= period float max_buffer_duration, periods, sampling_rate; void run() override; // Multi-threading related variables static const unsigned long CYCLE_PERIOD = 20; // in milliseconds QMutex mutex, data_mutex; QWaitCondition condition; unsigned long waiting_time = CYCLE_PERIOD; enum Status : std::uint8_t { NO_DATA = 0, HAS_NEW_DATA = 1, QUIT_NOW = 2 }; Status status; // new data chunks arrived since the last periodic processing std::vector> chunks; // The window is the usual Kaiser with alpha=3 static void initialize_window(std::vector& window); static void initialize_window_deriv(std::vector& window); // The filter bank. One filter per frequency // The 4 entries in the v4sf are the real, imaginary parts of the windowed // sine wavelet, and the real, imaginary parts of the derived windowed sine // used for reassigning the power spectrum. // Hopefully, with SIMD, computing all 4 of them is the same price as just one // TODO: v8sf and compute 2 freqs at the same time typedef float v4sf __attribute__ ((vector_size (16))); std::vector> windowed_sines; std::vector frequencies; std::vector power_normalization_factors; float samplerate_div_2pi; std::vector big_buffer; std::vector reassigned_frequencies; std::vector power_spectrum; // caching computations for faster init // on disk for persistence between executions, // in memory for avoiding reloading from disk when changing the spiral size bool read_from_cache(std::vector& window, std::vector& window_deriv); void write_to_cache(std::vector& window, std::vector& window_deriv); std::map> mem_win_cache, mem_winderiv_cache; void compute_frequencies(); }; } // namespace Amuencha #endif // AUDIOINPUTTHREAD_H