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() :
|
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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue