Browse Source

Remove old hacks and handle end of audio correctly.

The correct way in Qt to find out when audio playback has finished is to
connect to the stateChanged signal of the QAudioOutput and look for a
transition to QAudio::IdleState.

It seems that in the past, this didn't always work correctly on some
platforms. As a workaround, CuteCW was using its own 'generatorDone'
signal as an alternative way to get an event at the end of playback.

However, the generatorDone signal only indicates when the last sample
has been read from the generator, not when it has actually been played
out by the audio device. The audio driver may read samples from the
input to buffer them well before they are actually played. As such,
users have sometimes encountered playback being truncated. There have
been some attempts to fix this by adding padding to the buffer, but this
is not a reliable approach.

This commit simply assumes that the old platform specific bugs are long
gone by now. It uses the correct way to handle end of playback, and gets
rid of the generatorDone signal and other related tricks.

This change fixes truncated playback for me with Qt 5.11 on Linux. It
should work for all other platforms too, as long as Qt works correctly.
pull/1/head
Martin Ling 3 years ago
parent
commit
ae4d04f9b2
  1. 51
      Generator.cpp
  2. 7
      Generator.h
  3. 24
      Morse.cpp
  4. 3
      Morse.h
  5. 6
      modes/MorseMode.cpp
  6. 2
      modes/MorseMode.h

51
Generator.cpp

@ -13,7 +13,7 @@
#define SYSTEM_FREQ 44100
Generator::Generator(float secs, int freq)
:QIODevice( ), isGenerating(false)
: QIODevice()
{
finished = false;
buffer = new char[int(secs * SYSTEM_FREQ * 4) + 3000];
@ -25,7 +25,7 @@ Generator::Generator(float secs, int freq)
}
Generator::Generator(Generator *copyFrom)
: QIODevice(), isGenerating(false)
: QIODevice()
{
buffer = new char[copyFrom->len];
memcpy(buffer, copyFrom->buffer, copyFrom->len);
@ -48,7 +48,6 @@ void Generator::clearBuffer() {
t = buffer;
len = bytes_left = 4;
pos = 0;
isGenerating = false;
}
void Generator::appendDataFrom(const Generator *copyFrom) {
@ -65,13 +64,11 @@ void Generator::appendDataFrom(const Generator *copyFrom) {
void Generator::start()
{
open(QIODevice::ReadOnly);
isGenerating = true;
}
void Generator::stop()
{
close();
isGenerating = false;
}
int Generator::putShort(char *t, unsigned int value)
@ -122,50 +119,10 @@ qint64 Generator::readData(char *data, qint64 maxlen)
//qDebug() << "left: " << bytes_left << " / wanted: " << len;
if (bytes_left == -1 && isGenerating) {
isGenerating = false;
emit generatorDone();
}
#undef FILL_WITH_SPACE_ONCE
#undef ALWAYS_FILL_WITH_SPACE
#ifdef Q_OS_LINUX
// On linux (with Qt 4.7 and 4.7.1) there is a nasty second-long pause/freeze after the audio finishes playing, so
// we continue to emit empty sound endlessly to get around the gui/qt lockup.
#define ALWAYS_FILL_WITH_SPACE 0
#endif
#ifdef ALWAYS_FILL_WITH_SPACE
if (bytes_left <= 0) {
// should really only be needed on linux with 4.7 I suspect
memset(data, 0, maxlen);
bytes_left = -1;
return maxlen;
}
#elif defined(FILL_WITH_SPACE_ONCE)
/* fill with a blank space just once after the starting */
if (bytes_left == 0) {
memset(data, 0, maxlen);
bytes_left = -1;
return maxlen;
}
if (bytes_left == -1)
return -1;
#else
/* this is how it *should* be done, if the Qt output buffers didn't truncate things */
if (bytes_left == 0)
bytes_left = -1;
if (bytes_left <= 0) {
if (isGenerating) {
isGenerating = false;
emit generatorDone();
}
// No data
return -1;
}
#endif
if (len < bytes_left) {
} else if (len < bytes_left) {
// Normal
memcpy(data, t+pos, len);
pos += len;

7
Generator.h

@ -31,9 +31,6 @@ public:
int m_zerocount;
qint64 bytes_left;
signals:
void generatorDone();
public slots:
void restartData();
@ -43,10 +40,6 @@ public slots:
private:
int putShort(char *t, unsigned int value);
int fillData(char *start, int frequency, float seconds);
bool isGenerating;
};
#endif // GENERATOR_H

24
Morse.cpp

@ -226,10 +226,6 @@ void Morse::playButton() {
m_modes[m_gameMode]->playButton();
}
void Morse::generatorDone() {
audioFinished(QAudio::StoppedState); // fixes windows issues
}
void Morse::setAudioMode(AudioMode newmode) {
m_playingMode = newmode;
/* Like in Morse::playSequence we have the MAC OSX bug
@ -260,17 +256,10 @@ Morse::BadLetterWeighting Morse::badLetterWeighting() {
}
void
Morse::audioFinished(QAudio::State newState)
{
#if (defined(Q_WS_MAEMO_5) || defined(MAEMO_CHANGES))
// This triggers a nasty hang on linux with Qt 4.7.1
// this also makes popping sounds on windows
// it also stops audio from working at all on the mac
// but it seems like the right thing to do, and fixes maemo
if (state != QAudio::ActiveState)
m_audioOutput->stop();
#endif
m_modes[m_gameMode]->audioFinished(newState);
Morse::audioStateChanged(QAudio::State newState)
{
if (newState == QAudio::IdleState)
m_modes[m_gameMode]->audioFinished();
}
void Morse::switchMode(int newmode) {
@ -420,12 +409,9 @@ Morse::_createTones()
if (!m_playBuffer) {
m_playBuffer = new Generator(m_pause);
m_playBuffer->start();
// windows Qt fails to generate an audio state change, so we detect
// the end of the sound buffer this way instead
connect(m_playBuffer, SIGNAL(generatorDone()), this, SLOT(generatorDone()), Qt::QueuedConnection);
}
connect(m_audioOutput, SIGNAL(stateChanged(QAudio::State)), this, SLOT(audioFinished(QAudio::State)));
connect(m_audioOutput, SIGNAL(stateChanged(QAudio::State)), this, SLOT(audioStateChanged(QAudio::State)));
}
void

3
Morse.h

@ -119,8 +119,7 @@ public slots:
void playSequence();
QTime maybePlaySequence(bool addPause = false);
void generatorDone();
void audioFinished(QAudio::State newState);
void audioStateChanged(QAudio::State newState);
void keyPressed(QString newtext);
void keyPressed(QChar key);
void keyReleased(QString newtext);

6
modes/MorseMode.cpp

@ -83,11 +83,7 @@ void MorseMode::handleKeyRelease(const QString &letterReleased) {
handleKeyRelease(letterReleased[0]);
}
void MorseMode::audioFinished(QAudio::State state) {
if (state != QAudio::IdleState && state != QAudio::StoppedState)
return;
// qDebug() << "audio state changed: " << state << ", old state = " << m_morse->audioMode();
void MorseMode::audioFinished() {
if (m_morse->audioMode() != Morse::STOPPED) {
audioStopped();

2
modes/MorseMode.h

@ -110,7 +110,7 @@ public slots:
virtual void help(); // opens a help window to show helpText() contents
virtual void audioFinished(QAudio::State state); // changes the internal state and calls:
virtual void audioFinished(); // changes the internal state and calls:
virtual void audioStopped(); // this when audio has stopped playing
virtual void changeWPM(int wpm); // call this to change the WPM rate

Loading…
Cancel
Save