diff --git a/Tuner.pro b/Tuner.pro index 7e34ae3..24f983b 100644 --- a/Tuner.pro +++ b/Tuner.pro @@ -13,6 +13,7 @@ SOURCES += \ src/PitchDetection.cpp \ src/Tuner.cpp \ src/TunerWorker.cpp \ + src/audio/FreqPlayer.cpp \ src/audio/LinearFilter.cpp \ src/audio/ZeroCross.cpp \ src/scale/Scale.cpp \ @@ -23,6 +24,7 @@ HEADERS += \ src/ObjectSaver.hpp \ src/Tuner.hpp \ src/TunerWorker.hpp \ + src/audio/FreqPlayer.hpp \ src/audio/LinearFilter.hpp \ src/audio/ZeroCross.hpp \ src/scale/Scale.hpp \ diff --git a/harbour-sailtuner.pro b/harbour-sailtuner.pro index 12ec1ea..6a1b22b 100644 --- a/harbour-sailtuner.pro +++ b/harbour-sailtuner.pro @@ -17,6 +17,7 @@ SOURCES += \ src/ObjectSaver.cpp \ src/Tuner.cpp \ src/TunerWorker.cpp \ + src/audio/FreqPlayer.cpp \ src/audio/LinearFilter.cpp \ src/audio/ZeroCross.cpp \ src/scale/Scale.cpp \ @@ -28,6 +29,7 @@ HEADERS += \ src/Tuner.cpp \ src/Tuner.hpp \ src/TunerWorker.hpp \ + src/audio/FreqPlayer.hpp \ src/audio/LinearFilter.hpp \ src/audio/ZeroCross.hpp \ src/scale/Scale.hpp \ diff --git a/src/PitchDetection.cpp b/src/PitchDetection.cpp index 8f15bb1..83c2412 100644 --- a/src/PitchDetection.cpp +++ b/src/PitchDetection.cpp @@ -198,6 +198,11 @@ QStringList PitchDetection::GetTemperamentList() const return temperaments->GetNames(); } +double PitchDetection::GetNoteFreq(int note, int octave) const +{ + return scale->GetNoteFreq(note, octave); +} + /// for analyse_file console logs static void display_results(const PitchDetection::PitchResult &res) { diff --git a/src/PitchDetection.hpp b/src/PitchDetection.hpp index f2818d6..4f2be4f 100644 --- a/src/PitchDetection.hpp +++ b/src/PitchDetection.hpp @@ -92,6 +92,8 @@ class PitchDetection { void SetTemperament(int idx); /// Get temperament list QStringList GetTemperamentList() const; + /// Get the note frequency from Scale + double GetNoteFreq(int note, int octave) const; /// analyse a file for debug static void analyse_file(const char *filename); diff --git a/src/Tuner.cpp b/src/Tuner.cpp index cdd3398..10d6407 100644 --- a/src/Tuner.cpp +++ b/src/Tuner.cpp @@ -66,6 +66,7 @@ void Tuner::SetPlaying(bool p) { if (p == playing) return; playing = p; + worker->SetPlaying(p); emit playingChanged(); } @@ -95,6 +96,14 @@ int Tuner::GetNote() return result.note; } +void Tuner::SetNote(int note) +{ + if (result.note == note) return; + result.note = note; + worker->SetNote(note); + emit resultChanged(); +} + double Tuner::GetDeviation() { return result.deviation; @@ -105,6 +114,14 @@ int Tuner::GetOctave() return result.octave; } +void Tuner::SetOctave(int octave) +{ + if (result.octave == octave) return; + result.octave = octave; + worker->SetOctave(octave); + emit resultChanged(); +} + bool Tuner::GetFound() { return result.found; diff --git a/src/Tuner.hpp b/src/Tuner.hpp index a29b8e1..689beeb 100644 --- a/src/Tuner.hpp +++ b/src/Tuner.hpp @@ -29,8 +29,8 @@ class Tuner : public QObject { Q_PROPERTY(bool running READ GetRunning WRITE SetRunning NOTIFY runningChanged) Q_PROPERTY(double freq READ GetFreq NOTIFY resultChanged) Q_PROPERTY(double deviation READ GetDeviation NOTIFY resultChanged) - Q_PROPERTY(int note READ GetNote NOTIFY resultChanged) - Q_PROPERTY(int octave READ GetOctave NOTIFY resultChanged) + Q_PROPERTY(int note READ GetNote WRITE SetNote NOTIFY resultChanged) + Q_PROPERTY(int octave READ GetOctave WRITE SetOctave NOTIFY resultChanged) Q_PROPERTY(bool found READ GetFound NOTIFY foundChanged) Q_PROPERTY(int temperament_idx READ GetTemperamentIndex WRITE SetTemperamentIndex NOTIFY temperamentChanged) Q_PROPERTY(QStringList temperament_list READ GetTemperamentList NOTIFY temperamentListChanged) @@ -56,7 +56,9 @@ class Tuner : public QObject { void SetRunning(bool r); double GetFreq(); int GetNote(); + void SetNote(int note); int GetOctave(); + void SetOctave(int octave); double GetDeviation(); bool GetFound(); unsigned int GetTemperamentIndex(); diff --git a/src/TunerWorker.cpp b/src/TunerWorker.cpp index a27caa2..00064d1 100644 --- a/src/TunerWorker.cpp +++ b/src/TunerWorker.cpp @@ -26,6 +26,7 @@ extern "C" { #include } +#include "audio/FreqPlayer.hpp" #include "TunerWorker.hpp" #define name_(x) #x @@ -57,7 +58,9 @@ TunerWorker::TunerWorker() : running(false), quit(false), la_to_update(0), - temperament_to_update(0) // update the first time in every cases + temperament_to_update(0), // update the first time in every cases + note_to_update(-1), + octave_to_update(-1) { //qRegisterMetaType("PitchDetection::PitchResult"); } @@ -115,6 +118,21 @@ void TunerWorker::SetTemperamentIndex(int idx) mutex.unlock(); } +void TunerWorker::SetNote(int note) +{ + mutex.lock(); + note_to_update = note; + mutex.unlock(); +} + + +void TunerWorker::SetOctave(int octave) +{ + mutex.lock(); + octave_to_update = octave; + mutex.unlock(); +} + void TunerWorker::Entry() { cerr << __func__ << endl; @@ -132,6 +150,8 @@ void TunerWorker::Entry() PitchDetection *pitchDetection = new PitchDetection(); emit temperamentListUpdated(pitchDetection->GetTemperamentList()); + FreqPlayer *player = new FreqPlayer(PitchDetection::rate); + // pulseaudio pa_simple *p_record = NULL, *p_play = NULL; pa_sample_spec p_spec; @@ -179,6 +199,16 @@ void TunerWorker::Entry() pitchDetection->SetTemperament(temperament_to_update); temperament_to_update = -1; } + if (note_to_update != -1) { + result.note = note_to_update; + note_to_update = -1; + player->SetFreq(pitchDetection->GetNoteFreq(result.note, result.octave)); + } + if (octave_to_update != -1) { + result.octave = octave_to_update; + octave_to_update = -1; + player->SetFreq(pitchDetection->GetNoteFreq(result.note, result.octave)); + } mutex.unlock(); if (running ) { @@ -220,14 +250,13 @@ void TunerWorker::Entry() if (result.found) cout << Scale::NoteName(result.note) << " " << result.frequency << endl; emit resultUpdated(result); } - - } + } // running if (playing) { // play if (!p_play) { // start pulseaudio if stopped - p_record = pa_simple_new( + p_play = pa_simple_new( NULL, NAME, PA_STREAM_PLAYBACK, @@ -238,8 +267,16 @@ void TunerWorker::Entry() NULL, NULL ); + + // update frequency to update from previous tuner results + player->SetFreq(pitchDetection->GetNoteFreq(result.note, result.octave)); } - } + + player->WriteAudio(buffer, nbSampleBuffer); + if (pa_simple_write(p_play, buffer, nbSampleBuffer << 1, nullptr) < 0) { + cerr << "audio write failed" << endl; + } + } // playing // prevent screen blanking nb_sample_running += nbSampleBuffer; @@ -252,6 +289,7 @@ void TunerWorker::Entry() if (p_record) pa_simple_free(p_record); if (p_play) pa_simple_free(p_play); + delete player; delete pitchDetection; delete buffer; diff --git a/src/TunerWorker.hpp b/src/TunerWorker.hpp index 2e5e352..c3abf35 100644 --- a/src/TunerWorker.hpp +++ b/src/TunerWorker.hpp @@ -49,7 +49,7 @@ class TunerWorker : public QObject { // to update vars double la_to_update; - int temperament_to_update; + int temperament_to_update, note_to_update, octave_to_update; public: /// constructor @@ -62,6 +62,8 @@ class TunerWorker : public QObject { void SetPlaying(bool p); void SetTemperamentIndex(int idx); void SetLa(double la); + void SetNote(int note); + void SetOctave(int octave); void Entry(); void Quit(); diff --git a/src/audio/FreqPlayer.cpp b/src/audio/FreqPlayer.cpp index 42692f8..57f6520 100644 --- a/src/audio/FreqPlayer.cpp +++ b/src/audio/FreqPlayer.cpp @@ -20,42 +20,53 @@ #include "FreqPlayer.hpp" -template typename FreqPlayer::FreqPlayer(int _rate): - rate(_rate), +template FreqPlayer::FreqPlayer(int _rate): freq(440), volume(0.2), + rate(_rate), n_frame(0), waveform(W_SINUS) { } -template typename void FreqPlayer::Reset() +template void FreqPlayer::Reset() { n_frame = 0; } -template typename void FreqPlayer::SetFreq(double freq) +template void FreqPlayer::SetFreq(double freq) { this->freq = freq; } -template typename void FreqPlayer::SetVolume(double volume) +template void FreqPlayer::SetVolume(double volume) { this->volume = volume; } -int16_t FreqPlayer::max() { return INT16_MAX; } -double FreqPlayer::max() { return 1; } +template<> int16_t FreqPlayer::max() { return INT16_MAX; } +template<> double FreqPlayer::max() { return 1; } -template typename double FreqPlayer::radius() +template double FreqPlayer::radius() { - return (double) (n_sample++) * freq / rate * M_PI * 2; + return (double) (n_frame++) * freq / rate * M_PI * 2; } -template typename sample_t FreqPlayer::AudioFrame() +template sample_t FreqPlayer::AudioFrame() { -// return (n_sample++) + switch (waveform) { + case W_SINUS: + return (double) sin(radius()) * max(); + + default: + return 0; + } +} + +template void FreqPlayer::WriteAudio(sample_t *out, int nb_frame) +{ + while (nb_frame--) *out++ = AudioFrame(); } // instanciation for int16 -template class LinearFilter; +template class FreqPlayer; diff --git a/src/audio/FreqPlayer.hpp b/src/audio/FreqPlayer.hpp index 1dc247e..61adf69 100644 --- a/src/audio/FreqPlayer.hpp +++ b/src/audio/FreqPlayer.hpp @@ -53,7 +53,9 @@ template class FreqPlayer { /// reset current sound void Reset(); /// get next audio frame - sample_t AudioFrame(); + inline sample_t AudioFrame(); + /// write audio buffer + void WriteAudio(sample_t *out, int nb_frame); /// set current frequency void SetFreq(double freq); /// set current volume diff --git a/src/scale/Scale.cpp b/src/scale/Scale.cpp index 1d3bbe5..5959d73 100644 --- a/src/scale/Scale.cpp +++ b/src/scale/Scale.cpp @@ -139,3 +139,14 @@ void Scale::SetLa(double la) actualLa = la; if (freq_setted) updateScale(); } + +double Scale::GetNoteFreq(int note, int octave) +{ + assert(note >= 0 && note < nbNote); + double f = actualNoteFreq[note]; + octave -= 4; + if (octave < 0) f /= 1 << octave; + else if (octave > 0) f *= 1 << octave; + + return f; +} diff --git a/src/scale/Scale.hpp b/src/scale/Scale.hpp index ba2c263..b704128 100644 --- a/src/scale/Scale.hpp +++ b/src/scale/Scale.hpp @@ -56,9 +56,15 @@ class Scale { /// Set notes frequencies from a temperament void SetNotesFrequencies(const double freq[nbNote]); + /// Get ref freq for la4 double GetLa(); + + /// Set ref freq for la4 void SetLa(double la); + /// get note frequency + double GetNoteFreq(int note, int octave); + /** * Find nearest note, octave, and deviation */