/* Copyright 2016 (C) Louis-Joseph Fournier * louisjoseph.fournier@gmail.com * * This file is part of SailTuner. * * SailTuner 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 3 of the License, or * (at your option) any later version. * * SailTuner 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. * */ #include #include #include #include #include "TunerWorker.hpp" using namespace std; // high 10hz / 16k static double a10[] = { 1 , -2.99214602, 2.98432286, -0.99217678 }; static double b10[] = { 0.99608071, -2.98824212, 2.98824212, -0.99608071 }; const char * TunerWorker::filename_record = NULL; /// function to prevent screen blank on Sailfish OS static void blank_prevent(bool prevent) { cerr << __func__ << endl; QDBusConnection system = QDBusConnection::connectToBus(QDBusConnection::SystemBus, "system"); QDBusInterface interface("com.nokia.mce", "/com/nokia/mce/request", "com.nokia.mce.request", system); if (prevent) { interface.call(QLatin1String("req_display_blanking_pause")); } else { interface.call(QLatin1String("req_display_cancel_blanking_pause")); } } TunerWorker::TunerWorker() : high_filter(NULL), cross(NULL), scale(NULL), running(false), quit(false), la_to_update(0), freq_to_update(NULL) { // part of reset found = false; count_found = count_not_found = 0; nb_sample_running = 0; note_found = octave_found = -1; ResetDeviation(); } TunerWorker::~TunerWorker() { if (filename_record && file_record.is_open()) file_record.close(); if (high_filter) delete high_filter; if (cross) delete cross; if (scale) delete scale; } /// reset analyse values void TunerWorker::Reset() { found = false; count_found = count_not_found = 0; nb_sample_running = 0; note_found = octave_found = -1; ResetDeviation(); blank_prevent(true); high_filter->Clear(); cross->Clear(); } void TunerWorker::Start() { cerr << __func__ << endl; mutex.lock(); running = true; condition.wakeOne(); mutex.unlock(); } void TunerWorker::Stop() { mutex.lock(); running = false; mutex.unlock(); } void TunerWorker::Quit() { mutex.lock(); running = false; quit = true; condition.wakeOne(); mutex.unlock(); } void TunerWorker::SetNotesFrequencies(const double *notes_freq) { mutex.lock(); freq_to_update = notes_freq; mutex.unlock(); } void TunerWorker::SetLa(double la) { mutex.lock(); la_to_update = la; mutex.unlock(); } void TunerWorker::ComputeFrame(int16_t v) { v = (*high_filter)(v); (*cross)(v); } void TunerWorker::ResetDeviation() { // reset deviation values nb_deviation = 0; deviation_start = 0; deviation_sum = 0; } void TunerWorker::UpdateDeviation(double d) { if (nb_deviation == nbDeviationValues) { deviation_sum -= deviation_values[deviation_start]; deviation_start = (deviation_start + 1) % nbDeviationValues; nb_deviation--; } deviation_values[(deviation_start + nb_deviation) % nbDeviationValues] = d; nb_deviation++; deviation_sum += d; } void TunerWorker::SetFound(int n, int o, double d) { if (n != note_found || o != octave_found) { note_found = n; octave_found = o; count_found = 0; SetNotFound(); ResetDeviation(); UpdateDeviation(d); } else if (count_found++ >= nbConfirm) { found = true; count_not_found = 0; UpdateDeviation(d); resultFound(n, o, deviation_sum / nb_deviation, cross->Freq()); } else { UpdateDeviation(d); } } void TunerWorker::SetNotFound() { if (count_not_found++ >= nbDefect) { count_found = 0; if (found) { found = false; ResetDeviation(); resultNotFound(cross->Freq()); } } } void TunerWorker::AudioAnalyse(const int16_t *ptr, int nb_frame) { nb_sample_running += nb_frame; // record in file is needed if (filename_record && file_record.is_open()) file_record.write((char*) ptr, nb_frame * sizeof(int16_t)); // compute every audio frame while (nb_frame--) ComputeFrame(*ptr++); // find note, octave, deviation if (cross->Freq()) { int n, o = 0; double d = 0; n = scale->FindNote(cross->Freq(), o, d); SetFound(n, o, d); } else { // no freq SetNotFound(); } // prevent screen blanking if (nb_sample_running >= nbSamplePreventRunning && running) { nb_sample_running = 0; blank_prevent(true); } } void TunerWorker::Entry() { // initialisations if (filename_record) file_record.open(filename_record); high_filter = new LinearFilter(3, a10, b10); ZeroCross::Config cross_config({rate, defaultNbFrame, defaultFreqMin, defaultFreqMax}); cross = new ZeroCross(cross_config); scale = new Scale(); while (1) { // wait for running mutex.lock(); if (!running) { blank_prevent(false); while (!running && !quit) condition.wait(&mutex); // reset operations on start if (!quit) Reset(); } if (quit) { mutex.unlock(); break; } // update config if (la_to_update) { scale->SetLa(la_to_update); la_to_update = 0; } if (freq_to_update) { scale->SetNotesFrequencies(freq_to_update); freq_to_update = NULL; } mutex.unlock(); std::cout << __func__ << " do job" << std::endl; } } /// Set a filename to record raw audio stream void TunerWorker::set_record(const char *f) { filename_record = f; } /// for analyse_file console logs static void display_results(int note, int octave, double deviation, double frequency) { cout << frequency << " " << Scale::NoteName(note) << " " << octave << " " << deviation << endl; } static void display_no_results(double freq) { cout << freq << endl; } /// analyse a file (static function) void TunerWorker::analyse_file(const char *filename) { cout << "analyse file " << filename << endl; ifstream fin; fin.open(filename); const int nb_frame = 1024; TunerWorker *tuner = new TunerWorker(); int16_t buffer[nb_frame]; connect(tuner, &TunerWorker::resultFound, NULL, display_results); connect(tuner, &TunerWorker::resultNotFound, NULL, display_no_results); while (1) { fin.read((char*) buffer, sizeof(buffer)); tuner->AudioAnalyse(buffer, sizeof(buffer) >> 1); // cout << "." << endl; if (fin.eof()) break; } fin.close(); delete tuner; }