Player: be smooth when play stops

- free pulseaudio playback after a delay, to permit buffered sound to play

- do zero crossing in playing signal to detect the right moment to stop or
  update frequency
This commit is contained in:
Louis-Joseph Fournier 2016-01-14 15:46:36 +01:00
parent f0640e24ff
commit e45423edde
4 changed files with 61 additions and 28 deletions

View file

@ -55,6 +55,7 @@ static void blank_prevent(bool prevent)
} }
TunerWorker::TunerWorker() : TunerWorker::TunerWorker() :
play_stop_counter(0),
running(false), running(false),
playing(false), playing(false),
quit(false), quit(false),
@ -92,8 +93,15 @@ void TunerWorker::SetPlaying(bool p)
cerr << __func__ << " " << p << endl; cerr << __func__ << " " << p << endl;
if (p == playing) return; if (p == playing) return;
mutex.lock(); mutex.lock();
playing = p; if (p) {
if (p) condition.wakeOne(); playing = p;
play_stop_counter = 0;
condition.wakeOne();
}
else {
// stop after a frame
play_stop_counter = 1;
}
mutex.unlock(); mutex.unlock();
} }
@ -167,21 +175,27 @@ void TunerWorker::Entry()
while (1) { while (1) {
mutex.lock(); mutex.lock();
// stop playing after a frame
if (play_stop_counter >= 2) playing = false;
// free pulseaudio if not running // free pulseaudio if not running
if (!running && p_record) { if (!running && p_record) {
pa_simple_free(p_record); pa_simple_free(p_record);
p_record = NULL; p_record = nullptr;
}
if (!playing && p_play) {
pa_simple_free(p_play);
p_play = NULL;
} }
// free playing after a delay of inactivity to avoid clac
// wait for running // wait for running
if (!running && !playing) { if (!running && !playing) {
blank_prevent(false); blank_prevent(false);
bool waked;
while (!running && !playing && !quit) { while (!running && !playing && !quit) {
condition.wait(&mutex); waked = condition.wait(&mutex, p_play ? 1000000 : ULONG_MAX);
// free playing now
if (!waked && p_play) {
pa_simple_free(p_play);
p_play = nullptr;
}
} }
cerr << "wake-up" << endl; cerr << "wake-up" << endl;
nb_sample_running = 0; nb_sample_running = 0;
@ -277,11 +291,15 @@ void TunerWorker::Entry()
player->SetFreq(pitchDetection->GetNoteFreq(result.note, result.octave)); player->SetFreq(pitchDetection->GetNoteFreq(result.note, result.octave));
} }
player->WriteAudio(buffer, nbSampleBuffer); const int stop_counter = play_stop_counter;
player->WriteAudio(buffer, nbSampleBuffer, stop_counter > 0);
if (pa_simple_write(p_play, buffer, nbSampleBuffer << 1, nullptr) < 0) { if (pa_simple_write(p_play, buffer, nbSampleBuffer << 1, nullptr) < 0) {
cerr << "audio write failed" << endl; cerr << "audio write failed" << endl;
} }
//else cout << "audio written" << endl;
if (stop_counter) play_stop_counter = stop_counter + 1;
} // playing } // playing
// prevent screen blanking // prevent screen blanking

View file

@ -45,6 +45,7 @@ class TunerWorker : public QObject {
QMutex mutex; QMutex mutex;
QWaitCondition condition; QWaitCondition condition;
int play_stop_counter;
bool running, playing, quit; bool running, playing, quit;
// to update vars // to update vars

View file

@ -35,6 +35,7 @@ template<typename sample_t> FreqPlayer<sample_t>::FreqPlayer(int _rate):
template<typename sample_t> void FreqPlayer<sample_t>::Reset() template<typename sample_t> void FreqPlayer<sample_t>::Reset()
{ {
n_frame = 0; n_frame = 0;
last_frame = 0;
if (k_update != -1) { if (k_update != -1) {
k = k_update; k = k_update;
k_update = -1; k_update = -1;
@ -62,21 +63,7 @@ template<> double FreqPlayer<double>::max() { return 1; }
template<typename sample_t> double FreqPlayer<sample_t>::radius() template<typename sample_t> double FreqPlayer<sample_t>::radius()
{ {
double ret = (double) (n_frame++) * k; return (double) (n_frame++) * k;
/* to update frequency factor, wait current radius to go to beginning
* in interval [0, 2PI]
*/
if (k_update != -1) {
double a = fmod(ret, M_PI * 1);
double b = fmod(n_frame * k, M_PI * 2);
if (b < a) { // next frame go back to beginning of circle
n_frame = 0;
k = k_update;
k_update = -1;
}
}
return ret;
} }
template<typename sample_t> sample_t FreqPlayer<sample_t>::AudioFrame() template<typename sample_t> sample_t FreqPlayer<sample_t>::AudioFrame()
@ -90,9 +77,34 @@ template<typename sample_t> sample_t FreqPlayer<sample_t>::AudioFrame()
} }
} }
template<typename sample_t> void FreqPlayer<sample_t>::WriteAudio(sample_t *out, int nb_frame) template<typename sample_t> void FreqPlayer<sample_t>::WriteAudio(sample_t *out, int nb_frame, bool stop)
{ {
while (nb_frame--) *out++ = AudioFrame(); sample_t v;
const bool to_update = (k_update != -1) || stop;
while (nb_frame--) {
v = AudioFrame();
if (to_update && (v >= 0 && last_frame < 0)) {
// zero cross. update now.
if (k_update != -1) {
// update frequency
n_frame = 0;
k = k_update;
k_update = -1;
v = AudioFrame();
}
else if (stop) {
std::cerr << "stop audio" << std::endl;
// finish with 0s
*out++ = 0;
while (nb_frame--) *out++ = 0;
last_frame = 0;
return;
}
}
*out++ = v;
last_frame = v;
}
} }
// instanciation for int16 // instanciation for int16

View file

@ -42,6 +42,8 @@ template<typename sample_t> class FreqPlayer {
WAVEFORM waveform; WAVEFORM waveform;
/// pre computed factor /// pre computed factor
double k, k_update; double k, k_update;
// last frame written
sample_t last_frame;
/// return k computed /// return k computed
double K() const; double K() const;
@ -59,7 +61,7 @@ template<typename sample_t> class FreqPlayer {
/// get next audio frame /// get next audio frame
inline sample_t AudioFrame(); inline sample_t AudioFrame();
/// write audio buffer /// write audio buffer
void WriteAudio(sample_t *out, int nb_frame); void WriteAudio(sample_t *out, int nb_frame, bool stop = false);
/// set current frequency /// set current frequency
void SetFreq(double freq); void SetFreq(double freq);
/// set current volume /// set current volume