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:
parent
f0640e24ff
commit
e45423edde
4 changed files with 61 additions and 28 deletions
|
@ -55,6 +55,7 @@ static void blank_prevent(bool prevent)
|
|||
}
|
||||
|
||||
TunerWorker::TunerWorker() :
|
||||
play_stop_counter(0),
|
||||
running(false),
|
||||
playing(false),
|
||||
quit(false),
|
||||
|
@ -92,8 +93,15 @@ void TunerWorker::SetPlaying(bool p)
|
|||
cerr << __func__ << " " << p << endl;
|
||||
if (p == playing) return;
|
||||
mutex.lock();
|
||||
if (p) {
|
||||
playing = p;
|
||||
if (p) condition.wakeOne();
|
||||
play_stop_counter = 0;
|
||||
condition.wakeOne();
|
||||
}
|
||||
else {
|
||||
// stop after a frame
|
||||
play_stop_counter = 1;
|
||||
}
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
|
@ -167,21 +175,27 @@ void TunerWorker::Entry()
|
|||
while (1) {
|
||||
|
||||
mutex.lock();
|
||||
// stop playing after a frame
|
||||
if (play_stop_counter >= 2) playing = false;
|
||||
|
||||
// free pulseaudio if not running
|
||||
if (!running && p_record) {
|
||||
pa_simple_free(p_record);
|
||||
p_record = NULL;
|
||||
}
|
||||
if (!playing && p_play) {
|
||||
pa_simple_free(p_play);
|
||||
p_play = NULL;
|
||||
p_record = nullptr;
|
||||
}
|
||||
// free playing after a delay of inactivity to avoid clac
|
||||
|
||||
// wait for running
|
||||
if (!running && !playing) {
|
||||
blank_prevent(false);
|
||||
bool waked;
|
||||
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;
|
||||
nb_sample_running = 0;
|
||||
|
@ -277,11 +291,15 @@ void TunerWorker::Entry()
|
|||
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) {
|
||||
cerr << "audio write failed" << endl;
|
||||
}
|
||||
//else cout << "audio written" << endl;
|
||||
|
||||
if (stop_counter) play_stop_counter = stop_counter + 1;
|
||||
|
||||
} // playing
|
||||
|
||||
// prevent screen blanking
|
||||
|
|
|
@ -45,6 +45,7 @@ class TunerWorker : public QObject {
|
|||
QMutex mutex;
|
||||
QWaitCondition condition;
|
||||
|
||||
int play_stop_counter;
|
||||
bool running, playing, quit;
|
||||
|
||||
// to update vars
|
||||
|
|
|
@ -35,6 +35,7 @@ template<typename sample_t> FreqPlayer<sample_t>::FreqPlayer(int _rate):
|
|||
template<typename sample_t> void FreqPlayer<sample_t>::Reset()
|
||||
{
|
||||
n_frame = 0;
|
||||
last_frame = 0;
|
||||
if (k_update != -1) {
|
||||
k = k_update;
|
||||
k_update = -1;
|
||||
|
@ -62,21 +63,7 @@ template<> double FreqPlayer<double>::max() { return 1; }
|
|||
|
||||
template<typename sample_t> double FreqPlayer<sample_t>::radius()
|
||||
{
|
||||
double ret = (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;
|
||||
return (double) (n_frame++) * k;
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
@ -42,6 +42,8 @@ template<typename sample_t> class FreqPlayer {
|
|||
WAVEFORM waveform;
|
||||
/// pre computed factor
|
||||
double k, k_update;
|
||||
// last frame written
|
||||
sample_t last_frame;
|
||||
|
||||
/// return k computed
|
||||
double K() const;
|
||||
|
@ -59,7 +61,7 @@ template<typename sample_t> class FreqPlayer {
|
|||
/// get next audio frame
|
||||
inline sample_t AudioFrame();
|
||||
/// 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
|
||||
void SetFreq(double freq);
|
||||
/// set current volume
|
||||
|
|
Loading…
Reference in a new issue