Fix audio input rate issue on Windows Vista

Windows Vista has a broken rate converter which gets invoked when an
input audio stream at 48kHz sampel rate is requested. I've no idea why
our application can't get exclusive access to the audio input device
and have a unconverted stream direct at 48kHz.

To get around this our down sampling filter for audio input from 48kHz
to 12kHz is disaabled by default on Windows Vista, instead we request
a 12kHz stream and process it directly.

This default behviour can be overriden by specifying the following
settings value:

[Tune]
Audio\DisableInputResampling=false

This settings value defaults to true on Windows Vista and false
everywhere else so normally needn't be present.



git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx@3588 ab8295b8-cf94-4d9e-aec4-7959e3be5d79
This commit is contained in:
Bill Somerville 2013-10-04 19:00:29 +00:00
parent f858ff7038
commit 688943e779
7 changed files with 62 additions and 35 deletions

View File

@ -10,19 +10,15 @@ extern "C" {
void fil4_(qint16*, qint32*, qint16*, qint32*); void fil4_(qint16*, qint32*, qint16*, qint32*);
} }
namespace Detector::Detector (unsigned frameRate, unsigned periodLengthInSeconds, unsigned framesPerSignal, unsigned downSampleFactor, QObject * parent)
{
unsigned const downsampleFactor = 4;
}
Detector::Detector (unsigned frameRate, unsigned periodLengthInSeconds, unsigned framesPerSignal, QObject * parent)
: AudioDevice (parent) : AudioDevice (parent)
, m_frameRate (frameRate) , m_frameRate (frameRate)
, m_period (periodLengthInSeconds) , m_period (periodLengthInSeconds)
, m_downSampleFactor (downSampleFactor)
, m_framesPerSignal (framesPerSignal) , m_framesPerSignal (framesPerSignal)
, m_monitoring (false) , m_monitoring (false)
, m_starting (false) , m_starting (false)
, m_buffer (new short [framesPerSignal * downsampleFactor]) , m_buffer ((downSampleFactor > 1) ? new short [framesPerSignal * downSampleFactor] : 0)
, m_bufferPos (0) , m_bufferPos (0)
{ {
clear (); clear ();
@ -53,7 +49,7 @@ qint64 Detector::writeData (char const * data, qint64 maxSize)
Q_ASSERT (!(maxSize % static_cast<qint64> (bytesPerFrame ()))); // no torn frames Q_ASSERT (!(maxSize % static_cast<qint64> (bytesPerFrame ()))); // no torn frames
// these are in terms of input frames (not down sampled) // these are in terms of input frames (not down sampled)
size_t framesAcceptable ((sizeof (jt9com_.d2) / sizeof (jt9com_.d2[0]) - jt9com_.kin) * downsampleFactor); size_t framesAcceptable ((sizeof (jt9com_.d2) / sizeof (jt9com_.d2[0]) - jt9com_.kin) * m_downSampleFactor);
size_t framesAccepted (qMin (static_cast<size_t> (maxSize / bytesPerFrame ()), framesAcceptable)); size_t framesAccepted (qMin (static_cast<size_t> (maxSize / bytesPerFrame ()), framesAcceptable));
if (framesAccepted < static_cast<size_t> (maxSize / bytesPerFrame ())) if (framesAccepted < static_cast<size_t> (maxSize / bytesPerFrame ()))
@ -63,18 +59,33 @@ qint64 Detector::writeData (char const * data, qint64 maxSize)
for (unsigned remaining = framesAccepted; remaining; ) for (unsigned remaining = framesAccepted; remaining; )
{ {
size_t numFramesProcessed (qMin (m_framesPerSignal * downsampleFactor - m_bufferPos, remaining)); size_t numFramesProcessed (qMin (m_framesPerSignal * m_downSampleFactor - m_bufferPos, remaining));
store (&data[(framesAccepted - remaining) * bytesPerFrame ()], numFramesProcessed, &m_buffer[m_bufferPos]);
m_bufferPos += numFramesProcessed;
if (m_bufferPos == m_framesPerSignal * downsampleFactor && m_monitoring)
{
qint32 framesToProcess (m_framesPerSignal * downsampleFactor);
qint32 framesAfterDownSample;
fil4_(&m_buffer[0], &framesToProcess, &jt9com_.d2[jt9com_.kin], &framesAfterDownSample);
m_bufferPos = 0;
jt9com_.kin += framesAfterDownSample; if (m_downSampleFactor > 1)
Q_EMIT framesWritten (jt9com_.kin); {
store (&data[(framesAccepted - remaining) * bytesPerFrame ()], numFramesProcessed, &m_buffer[m_bufferPos]);
m_bufferPos += numFramesProcessed;
if (m_bufferPos == m_framesPerSignal * m_downSampleFactor && m_monitoring)
{
qint32 framesToProcess (m_framesPerSignal * m_downSampleFactor);
qint32 framesAfterDownSample;
fil4_(&m_buffer[0], &framesToProcess, &jt9com_.d2[jt9com_.kin], &framesAfterDownSample);
jt9com_.kin += framesAfterDownSample;
Q_EMIT framesWritten (jt9com_.kin);
m_bufferPos = 0;
}
}
else
{
store (&data[(framesAccepted - remaining) * bytesPerFrame ()], numFramesProcessed, &jt9com_.d2[jt9com_.kin]);
m_bufferPos += numFramesProcessed;
jt9com_.kin += numFramesProcessed;
if (m_bufferPos == static_cast<unsigned> (m_framesPerSignal) && m_monitoring)
{
Q_EMIT framesWritten (jt9com_.kin);
m_bufferPos = 0;
}
} }
if (!secondInPeriod ()) if (!secondInPeriod ())

View File

@ -26,7 +26,7 @@ public:
// //
// the framesPerSignal argument is the number after down sampling // the framesPerSignal argument is the number after down sampling
// //
Detector (unsigned frameRate, unsigned periodLengthInSeconds, unsigned framesPerSignal, QObject * parent = 0); Detector (unsigned frameRate, unsigned periodLengthInSeconds, unsigned framesPerSignal, unsigned downSampleFactor = 4u, QObject * parent = 0);
bool isMonitoring () const {return m_monitoring;} bool isMonitoring () const {return m_monitoring;}
@ -52,7 +52,8 @@ private:
unsigned m_frameRate; unsigned m_frameRate;
unsigned m_period; unsigned m_period;
qint32 m_framesPerSignal; // after down sampling unsigned m_downSampleFactor;
qint32 m_framesPerSignal; // after any down sampling
bool volatile m_monitoring; bool volatile m_monitoring;
bool m_starting; bool m_starting;
QScopedArrayPointer<short> m_buffer; // de-interleaved sample buffer QScopedArrayPointer<short> m_buffer; // de-interleaved sample buffer

View File

@ -6,6 +6,7 @@
#include <QApplication> #include <QApplication>
#include <QObject> #include <QObject>
#include <QSettings> #include <QSettings>
#include <QSysInfo>
#include "mainwindow.h" #include "mainwindow.h"
@ -55,8 +56,20 @@ int main(int argc, char *argv[])
} }
memset(to,0,size); //Zero all decoding params in shared memory memset(to,0,size); //Zero all decoding params in shared memory
settings.beginGroup ("Tune");
// deal with Windows Vista input audio rate converter problems
unsigned downSampleFactor = settings.value ("Audio/DisableInputResampling",
#if defined (Q_OS_WIN)
QSysInfo::WV_VISTA == QSysInfo::WindowsVersion ? true : false
#else
false
#endif
).toBool () ? 1u : 4u;
settings.endGroup ();
// Multiple instances: Call MainWindow() with the UUID key // Multiple instances: Call MainWindow() with the UUID key
MainWindow w(&settings, &mem_jt9, &my_key, fontSize2, fontWeight2); MainWindow w(&settings, &mem_jt9, &my_key, fontSize2, fontWeight2, downSampleFactor);
w.show(); w.show();
QObject::connect (&a, SIGNAL (lastWindowClosed()), &a, SLOT (quit())); QObject::connect (&a, SIGNAL (lastWindowClosed()), &a, SLOT (quit()));

View File

@ -42,14 +42,14 @@ QString Program_Title_Version=" WSJT-X v1.2.1, r" + rev.mid(6,4) +
//-------------------------------------------------- MainWindow constructor //-------------------------------------------------- MainWindow constructor
// Multiple instances: new arg *thekey // Multiple instances: new arg *thekey
MainWindow::MainWindow(QSettings * settings, QSharedMemory *shdmem, QString *thekey, MainWindow::MainWindow(QSettings * settings, QSharedMemory *shdmem, QString *thekey,
qint32 fontSize2, qint32 fontWeight2, qint32 fontSize2, qint32 fontWeight2, unsigned downSampleFactor,
QWidget *parent) : QWidget *parent) :
QMainWindow(parent), QMainWindow(parent),
m_settings (settings), m_settings (settings),
ui(new Ui::MainWindow), ui(new Ui::MainWindow),
m_wideGraph (new WideGraph (settings)), m_wideGraph (new WideGraph (settings)),
m_logDlg (new LogQSO (settings, this)), m_logDlg (new LogQSO (settings, this)),
m_detector (RX_SAMPLE_RATE, NTMAX / 2, 6912 / 2), m_detector (RX_SAMPLE_RATE, NTMAX / 2, 6912 / 2, downSampleFactor),
m_audioInputDevice (QAudioDeviceInfo::defaultInputDevice ()), // start with default m_audioInputDevice (QAudioDeviceInfo::defaultInputDevice ()), // start with default
m_modulator (TX_SAMPLE_RATE, NTMAX / 2), m_modulator (TX_SAMPLE_RATE, NTMAX / 2),
m_audioOutputDevice (QAudioDeviceInfo::defaultOutputDevice ()), // start with default m_audioOutputDevice (QAudioDeviceInfo::defaultOutputDevice ()), // start with default
@ -57,6 +57,7 @@ MainWindow::MainWindow(QSettings * settings, QSharedMemory *shdmem, QString *the
psk_Reporter (new PSK_Reporter (this)), psk_Reporter (new PSK_Reporter (this)),
m_msAudioOutputBuffered (0u), m_msAudioOutputBuffered (0u),
m_framesAudioInputBuffered (RX_SAMPLE_RATE / 10), m_framesAudioInputBuffered (RX_SAMPLE_RATE / 10),
m_downSampleFactor (downSampleFactor),
m_audioThreadPriority (QThread::HighPriority) m_audioThreadPriority (QThread::HighPriority)
{ {
ui->setupUi(this); ui->setupUi(this);
@ -91,8 +92,8 @@ MainWindow::MainWindow(QSettings * settings, QSharedMemory *shdmem, QString *the
, &m_modulator, SLOT (open (unsigned, double, unsigned, AudioDevice::Channel, bool, double))); , &m_modulator, SLOT (open (unsigned, double, unsigned, AudioDevice::Channel, bool, double)));
// hook up the audio input stream // hook up the audio input stream
connect (this, SIGNAL (startAudioInputStream (QAudioDeviceInfo const&, unsigned, int, QIODevice *)) connect (this, SIGNAL (startAudioInputStream (QAudioDeviceInfo const&, unsigned, int, QIODevice *, unsigned))
, &m_soundInput, SLOT (start (QAudioDeviceInfo const&, unsigned, int, QIODevice *))); , &m_soundInput, SLOT (start (QAudioDeviceInfo const&, unsigned, int, QIODevice *, unsigned)));
connect (this, SIGNAL (stopAudioInputStream ()), &m_soundInput, SLOT (stop ())); connect (this, SIGNAL (stopAudioInputStream ()), &m_soundInput, SLOT (stop ()));
connect(&m_soundInput, SIGNAL (error (QString)), this, SLOT (showSoundInError (QString))); connect(&m_soundInput, SIGNAL (error (QString)), this, SLOT (showSoundInError (QString)));
@ -394,7 +395,7 @@ MainWindow::MainWindow(QSettings * settings, QSharedMemory *shdmem, QString *the
connect(watcher2, SIGNAL(finished()),this,SLOT(diskWriteFinished())); connect(watcher2, SIGNAL(finished()),this,SLOT(diskWriteFinished()));
Q_EMIT startDetector (m_audioInputChannel); Q_EMIT startDetector (m_audioInputChannel);
Q_EMIT startAudioInputStream (m_audioInputDevice, AudioDevice::Mono == m_audioInputChannel ? 1 : 2, m_framesAudioInputBuffered, &m_detector); Q_EMIT startAudioInputStream (m_audioInputDevice, AudioDevice::Mono == m_audioInputChannel ? 1 : 2, m_framesAudioInputBuffered, &m_detector, m_downSampleFactor);
Q_EMIT transmitFrequency (m_txFreq - (m_bSplit || m_bXIT ? m_XIT : 0)); Q_EMIT transmitFrequency (m_txFreq - (m_bSplit || m_bXIT ? m_XIT : 0));
Q_EMIT muteAudioOutput (false); Q_EMIT muteAudioOutput (false);
@ -855,7 +856,7 @@ void MainWindow::on_actionDeviceSetup_triggered() //Setup Dialog
Q_EMIT stopAudioInputStream (); Q_EMIT stopAudioInputStream ();
Q_EMIT detectorClose (); Q_EMIT detectorClose ();
Q_EMIT startDetector (m_audioInputChannel); Q_EMIT startDetector (m_audioInputChannel);
Q_EMIT startAudioInputStream (m_audioInputDevice, AudioDevice::Mono == m_audioInputChannel ? 1 : 2, m_framesAudioInputBuffered, &m_detector); Q_EMIT startAudioInputStream (m_audioInputDevice, AudioDevice::Mono == m_audioInputChannel ? 1 : 2, m_framesAudioInputBuffered, &m_detector, m_downSampleFactor);
} }
if(dlg.m_restartSoundOut) { if(dlg.m_restartSoundOut) {
@ -892,7 +893,7 @@ void MainWindow::on_monitorButton_clicked() //Monitor
{ {
m_monitoring=true; m_monitoring=true;
Q_EMIT detectorSetMonitoring (true); Q_EMIT detectorSetMonitoring (true);
// Q_EMIT startAudioInputStream (m_audioInputDevice, AudioDevice::Mono == m_audioInputChannel ? 1 : 2, m_framesAudioInputBuffered, &m_detector); // Q_EMIT startAudioInputStream (m_audioInputDevice, AudioDevice::Mono == m_audioInputChannel ? 1 : 2, m_framesAudioInputBuffered, &m_detector, m_downSampleFactor);
m_diskData=false; m_diskData=false;
} }

View File

@ -48,8 +48,8 @@ class MainWindow : public QMainWindow
// Multiple instances: call MainWindow() with *thekey // Multiple instances: call MainWindow() with *thekey
public: public:
explicit MainWindow(QSettings *, QSharedMemory *shdmem, QString *thekey, \ explicit MainWindow(QSettings *, QSharedMemory *shdmem, QString *thekey,
qint32 fontSize2, qint32 fontWeight2, \ qint32 fontSize2, qint32 fontWeight2, unsigned downSampleFactor,
QWidget *parent = 0); QWidget *parent = 0);
~MainWindow(); ~MainWindow();
@ -184,7 +184,7 @@ private slots:
Q_SIGNAL void startAudioOutputStream (QAudioDeviceInfo, unsigned channels, unsigned msBuffered); Q_SIGNAL void startAudioOutputStream (QAudioDeviceInfo, unsigned channels, unsigned msBuffered);
Q_SIGNAL void stopAudioOutputStream (); Q_SIGNAL void stopAudioOutputStream ();
Q_SIGNAL void startAudioInputStream (QAudioDeviceInfo const&, unsigned channels, int framesPerBuffer, QIODevice * sink); Q_SIGNAL void startAudioInputStream (QAudioDeviceInfo const&, unsigned channels, int framesPerBuffer, QIODevice * sink, unsigned downSampleFactor);
Q_SIGNAL void stopAudioInputStream (); Q_SIGNAL void stopAudioInputStream ();
Q_SIGNAL void startDetector (AudioDevice::Channel); Q_SIGNAL void startDetector (AudioDevice::Channel);
@ -391,6 +391,7 @@ private:
DecodedText m_QSOText; DecodedText m_QSOText;
unsigned m_msAudioOutputBuffered; unsigned m_msAudioOutputBuffered;
unsigned m_framesAudioInputBuffered; unsigned m_framesAudioInputBuffered;
unsigned m_downSampleFactor;
QThread::Priority m_audioThreadPriority; QThread::Priority m_audioThreadPriority;

View File

@ -38,7 +38,7 @@ bool SoundInput::audioError () const
return result; return result;
} }
void SoundInput::start(QAudioDeviceInfo const& device, unsigned channels, int framesPerBuffer, QIODevice * sink) void SoundInput::start(QAudioDeviceInfo const& device, unsigned channels, int framesPerBuffer, QIODevice * sink, unsigned downSampleFactor)
{ {
Q_ASSERT (0 < channels && channels < 3); Q_ASSERT (0 < channels && channels < 3);
Q_ASSERT (sink); Q_ASSERT (sink);
@ -48,7 +48,7 @@ void SoundInput::start(QAudioDeviceInfo const& device, unsigned channels, int fr
QAudioFormat format (device.preferredFormat()); QAudioFormat format (device.preferredFormat());
format.setChannelCount (channels); format.setChannelCount (channels);
format.setCodec ("audio/pcm"); format.setCodec ("audio/pcm");
format.setSampleRate (48000); format.setSampleRate (12000 * downSampleFactor);
format.setSampleType (QAudioFormat::SignedInt); format.setSampleType (QAudioFormat::SignedInt);
format.setSampleSize (16); format.setSampleSize (16);

View File

@ -31,7 +31,7 @@ class SoundInput : public QObject
Q_SIGNAL void status (QString message) const; Q_SIGNAL void status (QString message) const;
// sink must exist from the start call to any following stop () call // sink must exist from the start call to any following stop () call
Q_SLOT void start(QAudioDeviceInfo const&, unsigned channels, int framesPerBuffer, QIODevice * sink); Q_SLOT void start(QAudioDeviceInfo const&, unsigned channels, int framesPerBuffer, QIODevice * sink, unsigned downSampleFactor);
Q_SLOT void stop(); Q_SLOT void stop();
// used internally // used internally