Thanks to G4WJS: SoundOut functions have been moved into the GUI thread.

git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx@3517 ab8295b8-cf94-4d9e-aec4-7959e3be5d79
This commit is contained in:
Joe Taylor 2013-08-01 00:49:58 +00:00
parent c0e80753f2
commit 9fa4270b57
6 changed files with 230 additions and 275 deletions

View File

@ -23,9 +23,6 @@ qint32 g_COMportOpen;
qint32 g_iptt;
static int nc1=1;
wchar_t buffer[256];
bool btxok; //True if OK to transmit
bool btxMute;
double outputLatency; //Latency in seconds
WideGraph* g_pWideGraph = NULL;
@ -149,8 +146,9 @@ MainWindow::MainWindow(QSharedMemory *shdmem, QString *thekey, \
m_auto=false;
m_waterfallAvg = 1;
m_txFirst=false;
btxMute=false;
btxok=false;
m_soundOutput.mute(false);
m_btxMute=false;
m_btxok=false;
m_restart=false;
m_transmitting=false;
m_killAll=false;
@ -304,9 +302,8 @@ MainWindow::MainWindow(QSharedMemory *shdmem, QString *thekey, \
connect(watcher2, SIGNAL(finished()),this,SLOT(diskWriteFinished()));
m_soundInput.start(m_paInDevice);
soundOutThread.setOutputDevice(m_paOutDevice);
soundOutThread.setTxFreq(m_txFreq);
soundOutThread.setTune(false);
m_soundOutput.setTxFreq(m_txFreq);
m_soundOutput.tune(false);
m_monitoring=!m_monitorStartOFF; // Start with Monitoring ON/OFF
m_soundInput.setMonitoring(m_monitoring);
m_diskData=false;
@ -375,10 +372,7 @@ MainWindow::MainWindow(QSharedMemory *shdmem, QString *thekey, \
MainWindow::~MainWindow()
{
writeSettings();
if (soundOutThread.isRunning()) {
soundOutThread.quitExecution=true;
soundOutThread.wait(3000);
}
m_soundOutput.stop();
if(!m_decoderBusy) {
QFile lockFile(m_appDir + "/.lock");
lockFile.remove();
@ -489,6 +483,7 @@ void MainWindow::writeSettings()
settings.setValue("Fmin",m_fMin);
settings.setValue("TxSplit",m_bSplit);
settings.setValue("UseXIT",m_bXIT);
settings.setValue("XIT",m_XIT);
settings.setValue("Plus2kHz",m_plus2kHz);
settings.endGroup();
}
@ -570,7 +565,7 @@ void MainWindow::readSettings()
ui->RxFreqSpinBox->setValue(m_rxFreq);
m_txFreq=settings.value("TxFreq",1500).toInt();
ui->TxFreqSpinBox->setValue(m_txFreq);
soundOutThread.setTxFreq(m_txFreq);
m_soundOutput.setTxFreq(m_txFreq);
m_saveDecoded=ui->actionSave_decoded->isChecked();
m_saveAll=ui->actionSave_all->isChecked();
m_ndepth=settings.value("NDepth",3).toInt();
@ -642,7 +637,8 @@ void MainWindow::readSettings()
m_logComments=settings.value("LogComments","").toString();
m_fMin=settings.value("fMin",2500).toInt();
m_bSplit=settings.value("TxSplit",false).toBool();
m_bXIT=settings.value("UseXit",false).toBool();
m_bXIT=settings.value("UseXIT",false).toBool();
m_XIT=settings.value("XIT",0).toInt();
m_plus2kHz=settings.value("Plus2kHz",false).toBool();
ui->cbPlus2kHz->setChecked(m_plus2kHz);
settings.endGroup();
@ -713,7 +709,6 @@ void MainWindow::dataSink(int k)
watcher2->setFuture(*future2);
}
}
// m_soundInput.m_dataSinkBusy=false;
}
void MainWindow::showSoundInError(const QString& errorMsg)
@ -833,14 +828,11 @@ void MainWindow::on_actionDeviceSetup_triggered() //Setup Dialog
m_After73=dlg.m_After73;
if(dlg.m_restartSoundIn) {
m_soundInput.stop();
m_soundInput.start(m_paInDevice);
}
if(dlg.m_restartSoundOut) {
soundOutThread.quitExecution=true;
soundOutThread.wait(1000);
soundOutThread.setOutputDevice(m_paOutDevice);
m_soundOutput.start(m_paOutDevice,m_modeTx,m_TRperiod,m_nsps,m_txFreq,m_bSplit || m_bXIT ? m_XIT : 0);
}
}
m_catEnabled=dlg.m_catEnabled;
@ -910,7 +902,8 @@ void MainWindow::on_autoButton_clicked() //Auto
if(m_auto) {
ui->autoButton->setStyleSheet(m_pbAutoOn_style);
} else {
btxok=false;
m_btxok=false;
m_soundOutput.mute();
ui->autoButton->setStyleSheet("");
on_monitorButton_clicked();
m_repeatMsg=0;
@ -1702,7 +1695,7 @@ void MainWindow::guiUpdate()
}
float fTR=float((nsec%m_TRperiod))/m_TRperiod;
if(g_iptt==0 and ((bTxTime and !btxMute and fTR<0.4) or m_tune )) {
if(g_iptt==0 and ((bTxTime and !m_btxMute and fTR<0.4) or m_tune )) {
icw[0]=m_ncw;
//Raise PTT
@ -1725,8 +1718,9 @@ void MainWindow::guiUpdate()
}
ptt1Timer->start(200); //Sequencer delay
}
if(!bTxTime || btxMute) {
btxok=false;
if(!bTxTime || m_btxMute) {
m_btxok=false;
m_soundOutput.mute();
}
}
@ -1824,7 +1818,8 @@ void MainWindow::guiUpdate()
signalMeter->setValue(0);
m_monitoring=false;
m_soundInput.setMonitoring(false);
btxok=true;
m_btxok=true;
m_soundOutput.mute(false);
m_transmitting=true;
ui->pbTxMode->setEnabled(false);
if(!m_tune) {
@ -1839,11 +1834,11 @@ void MainWindow::guiUpdate()
if(m_tx2QSO and !m_tune) displayTxMsg(t);
}
if(!btxok && btxok0 && g_iptt==1) stopTx();
if(!m_btxok && btxok0 && g_iptt==1) stopTx();
/*
// If btxok was just lowered, start a countdown for lowering PTT
if(!btxok && btxok0 && g_iptt==1) nc0=-11; //RxDelay = 1.0 s
// If m_btxok was just lowered, start a countdown for lowering PTT
if(!m_btxok && btxok0 && g_iptt==1) nc0=-11; //RxDelay = 1.0 s
if(nc0 <= 0) {
nc0++;
}
@ -1917,7 +1912,7 @@ void MainWindow::guiUpdate()
}
iptt0=g_iptt;
btxok0=btxok;
btxok0=m_btxok;
} //End of GUIupdate
void MainWindow::displayTxMsg(QString t)
@ -1943,17 +1938,16 @@ void MainWindow::displayTxMsg(QString t)
void MainWindow::startTx2()
{
if(!soundOutThread.isRunning()) {
if(!m_soundOutput.isRunning()) {
QString t=ui->tx6->text();
double snr=t.mid(1,5).toDouble();
if(snr>0.0 or snr < -50.0) snr=99.0;
soundOutThread.setTxSNR(snr);
soundOutThread.m_modeTx=m_modeTx;
soundOutThread.start(QThread::HighestPriority);
m_soundOutput.start(m_paOutDevice,m_modeTx,m_TRperiod,m_nsps,m_txFreq,m_bSplit || m_bXIT ? m_XIT : 0,snr);
signalMeter->setValue(0);
m_monitoring=false;
m_soundInput.setMonitoring(false);
btxok=true;
m_btxok=true;
m_soundOutput.mute(false);
m_transmitting=true;
ui->pbTxMode->setEnabled(false);
}
@ -1961,10 +1955,7 @@ void MainWindow::startTx2()
void MainWindow::stopTx()
{
if (soundOutThread.isRunning()) {
soundOutThread.quitExecution=true;
soundOutThread.wait(3000);
}
m_soundOutput.stop();
m_transmitting=false;
ui->pbTxMode->setEnabled(true);
g_iptt=0;
@ -2482,9 +2473,13 @@ void MainWindow::on_tx6_editingFinished() //tx6 edited
{
QString t=ui->tx6->text();
msgtype(t, ui->tx6);
double snr=t.mid(1,5).toDouble();
if(snr>0.0 or snr < -50.0) snr=99.0;
soundOutThread.setTxSNR(snr);
// G4WJS: disabled setting of snr from msg 6 on live edit, will
// still generate noise on next full tx period
// double snr=t.mid(1,5).toDouble();
// if(snr>0.0 or snr < -50.0) snr=99.0;
// m_soundOutput.setTxSNR(snr);
}
void MainWindow::on_dxCallEntry_textChanged(const QString &t) //dxCall changed
@ -2581,7 +2576,6 @@ void MainWindow::on_actionJT9_1_triggered()
m_nsps=6912;
m_hsymStop=173;
m_soundInput.setPeriod(m_TRperiod,m_nsps);
soundOutThread.setPeriod(m_TRperiod,m_nsps);
lab3->setStyleSheet("QLabel{background-color: #ff6ec7}");
lab3->setText(m_mode);
ui->actionJT9_1->setChecked(true);
@ -2600,7 +2594,6 @@ void MainWindow::on_actionJT65_triggered()
m_nsps=6912; //For symspec only
m_hsymStop=173;
m_soundInput.setPeriod(m_TRperiod,m_nsps);
soundOutThread.setPeriod(m_TRperiod,m_nsps);
lab3->setStyleSheet("QLabel{background-color: #ffff00}");
lab3->setText(m_mode);
ui->actionJT65->setChecked(true);
@ -2619,7 +2612,6 @@ void MainWindow::on_actionJT9_JT65_triggered()
m_nsps=6912;
m_hsymStop=173;
m_soundInput.setPeriod(m_TRperiod,m_nsps);
soundOutThread.setPeriod(m_TRperiod,m_nsps);
lab3->setStyleSheet("QLabel{background-color: #ffa500}");
lab3->setText(m_mode);
ui->actionJT9_JT65->setChecked(true);
@ -2634,7 +2626,7 @@ void MainWindow::on_TxFreqSpinBox_valueChanged(int n)
m_txFreq=n;
if(g_pWideGraph!=NULL) g_pWideGraph->setTxFreq(n);
if(m_lockTxFreq) ui->RxFreqSpinBox->setValue(n);
soundOutThread.setTxFreq(n);
m_soundOutput.setTxFreq(n);
}
void MainWindow::on_RxFreqSpinBox_valueChanged(int n)
@ -2964,7 +2956,7 @@ void MainWindow::on_tuneButton_clicked()
} else {
m_tune=true;
m_sent73=false;
soundOutThread.setTune(m_tune);
m_soundOutput.tune(m_tune);
m_repeatMsg=0;
ui->tuneButton->setStyleSheet(m_pbTune_style);
}
@ -2974,10 +2966,11 @@ void MainWindow::on_stopTxButton_clicked() //Stop Tx
{
if(m_tune) {
m_tune=false;
soundOutThread.setTune(m_tune);
m_soundOutput.tune(m_tune);
}
if(m_auto) on_autoButton_clicked();
btxok=false;
m_btxok=false;
m_soundOutput.mute();
m_repeatMsg=0;
ui->tuneButton->setStyleSheet("");
}
@ -3094,15 +3087,15 @@ void MainWindow::on_pbTxMode_clicked()
void MainWindow::setXIT(int n)
{
int ret;
int xit=0;
m_XIT = 0;
if(m_bRigOpen) {
xit=-1000;
if(n>1000) xit=0;
if(n>2000) xit=1000;
if(n>3000) xit=2000;
if(n>4000) xit=3000;
m_XIT=-1000;
if(n>1000) m_XIT=0;
if(n>2000) m_XIT=1000;
if(n>3000) m_XIT=2000;
if(n>4000) m_XIT=3000;
if(m_bXIT) {
ret=rig->setXit((shortfreq_t)xit,RIG_VFO_TX);
ret=rig->setXit((shortfreq_t)m_XIT,RIG_VFO_TX);
if(ret!=RIG_OK) {
QString rt;
rt.sprintf("Setting RIG_VFO_TX failed: %d",ret);
@ -3110,11 +3103,11 @@ void MainWindow::setXIT(int n)
}
}
if(m_bSplit) {
ret=rig->setSplitFreq(MHz(m_dialFreq)+xit,RIG_VFO_B);
ret=rig->setSplitFreq(MHz(m_dialFreq)+m_XIT,RIG_VFO_B);
}
}
if(m_bSplit) soundOutThread.setXIT(xit);
if(!m_bSplit) soundOutThread.setXIT(0);
if(m_bSplit) m_soundOutput.setXIT(m_XIT);
if(!m_bSplit) m_soundOutput.setXIT(0);
}
void MainWindow::setFreq4(int rxFreq, int txFreq)

View File

@ -183,6 +183,7 @@ private:
qint32 m_timeout;
qint32 m_rxFreq;
qint32 m_txFreq;
int m_XIT;
qint32 m_setftx;
qint32 m_ndepth;
qint32 m_sec0;
@ -219,6 +220,8 @@ private:
qint32 m_bad;
bool m_monitoring;
bool m_btxok; //True if OK to transmit
bool m_btxMute; //True if transmit should be muted
bool m_transmitting;
bool m_diskData;
bool m_loopall;
@ -341,7 +344,7 @@ private:
QDateTime m_dateTimeQSO;
SoundInput m_soundInput; //Instantiate the audio objects
SoundOutThread soundOutThread;
SoundOutput m_soundOutput;
QSharedMemory *mem_jt9;
// Multiple instances:
QString *mykey_jt9;

View File

@ -1,6 +1,8 @@
#ifndef QAUDIO_INPUT
#include "soundin.h"
#include <stdexcept>
#include <QDateTime>
#include <QDebug>
#define FRAMES_PER_BUFFER 1024
#define NSMAX 6827
@ -48,13 +50,13 @@ int a2dCallback( const void *inputBuffer, void * /* outputBuffer */,
SoundInput::CallbackData * udata = reinterpret_cast<SoundInput::CallbackData *>(userData);
int nbytes,k;
udata->ncall++;
if( (statusFlags&paInputOverflow) != 0) {
qDebug() << "Input Overflow in a2dCallback";
}
if(udata->bzero) { //Start of a new Rx sequence
udata->kin=0; //Reset buffer pointer
udata->bzero=false;
if(udata->bzero)
{ //Start of a new Rx sequence
udata->kin = 0; //Reset buffer pointer
udata->bzero = false;
}
nbytes=2*framesToProcess; //Bytes per frame
@ -62,15 +64,14 @@ int a2dCallback( const void *inputBuffer, void * /* outputBuffer */,
if(udata->monitoring) {
memcpy(&jt9com_.d2[k],inputBuffer,nbytes); //Copy all samples to d2
}
udata->kin += framesToProcess;
jt9com_.kin=udata->kin;
udata->kin+=framesToProcess;
jt9com_.kin=udata->kin; // we are the only writer to jt9com_ so no MT issue here
return paContinue;
}
SoundInput::SoundInput()
: m_inStream(0),
m_dataSinkBusy(false),
m_TRperiod(60),
m_nsps(6912),
m_monitoring(false),
@ -87,10 +88,9 @@ void SoundInput::start(qint32 device)
PaError paerr;
PaStreamParameters inParam;
m_callbackData.kin=0; //Buffer pointer
m_callbackData.ncall=0; //Number of callbacks
m_callbackData.bzero=false; //Flag to request reset of kin
m_callbackData.monitoring=m_monitoring;
m_callbackData.kin = 0; //Buffer pointer
m_callbackData.bzero = false; //Flag to request reset of kin
m_callbackData.monitoring = m_monitoring;
inParam.device=device; //### Input Device Number ###
inParam.channelCount=1; //Number of analog channels
@ -101,9 +101,7 @@ void SoundInput::start(qint32 device)
paerr=Pa_IsFormatSupported(&inParam,NULL,12000.0);
if(paerr<0) {
emit error("PortAudio says requested soundcard format not supported.");
// return;
}
qDebug() << "";
paerr=Pa_OpenStream(&m_inStream, //Input stream
&inParam, //Input parameters
NULL, //No output parameters
@ -119,7 +117,6 @@ void SoundInput::start(qint32 device)
return;
}
m_ntr0 = 99; // initial value higher than any expected
m_nBusy = 0;
m_intervalTimer.start(100);
m_ms0 = QDateTime::currentMSecsSinceEpoch();
m_nsps0 = 0;
@ -127,35 +124,32 @@ void SoundInput::start(qint32 device)
void SoundInput::intervalNotify()
{
m_callbackData.monitoring=m_monitoring;
m_callbackData.monitoring = m_monitoring; // update monitoring
// status
qint64 ms = QDateTime::currentMSecsSinceEpoch();
m_SamFacIn=1.0;
if(m_callbackData.ncall>100) {
m_SamFacIn=m_callbackData.ncall*FRAMES_PER_BUFFER*1000.0/(12000.0*(ms-m_ms0-50));
}
ms=ms % 86400000;
int nsec = ms/1000; // Time according to this computer
int ntr = nsec % m_TRperiod;
int k=m_callbackData.kin; // get a copy of kin to mitigate the
// potential race condition with the
// callback handler when a buffer
// reset is requested below
// Reset buffer pointer and symbol number at start of minute
if(ntr < m_ntr0 or !m_monitoring or m_nsps!=m_nsps0) {
m_nstep0=0;
m_nsps0=m_nsps;
m_callbackData.bzero=true;
m_callbackData.bzero = true; // request callback to reset buffer pointer
}
int k=m_callbackData.kin;
if(m_monitoring) {
int kstep=m_nsps/2;
// m_step=k/kstep;
m_step=(k-1)/kstep;
if(m_step != m_nstep0) {
if(m_dataSinkBusy) {
m_nBusy++;
} else {
// m_dataSinkBusy=true;
// emit readyForFFT(k); //Signal to compute new FFTs
emit readyForFFT(k-1); //Signal to compute new FFTs
}
m_nstep0=m_step;
}
}
@ -180,14 +174,11 @@ void SoundInput::stop()
}
}
void SoundInput::setMonitoring(bool b)
{
m_monitoring = b;
}
#else // QAUDIO_INPUT
#include "soundin.h"
#include <stdexcept>
#include <QDateTime>
#define FRAMES_PER_BUFFER 1024
#define NSMAX 6827
@ -381,8 +372,4 @@ void SoundInput::stop()
*/
}
void SoundInput::setMonitoring(bool b)
{
m_monitoring = b;
}
#endif // QAUDIO_INPUT

View File

@ -4,9 +4,9 @@
#include <portaudio.h>
#include <QtCore>
#include <QScopedPointer>
#include <QDebug>
#include <QObject>
#include <QTimer>
#include <QString>
extern "C" int a2dCallback( const void *, void *, unsigned long, PaStreamCallbackTimeInfo const *, PaStreamCallbackFlags, void *);
@ -20,14 +20,15 @@ public:
SoundInput();
~SoundInput();
void setMonitoring(bool b);
void setPeriod(int ntrperiod, int nsps) /* this can be called while processing samples */
int mstep() const {return m_step;}
/* these can be called while processing samples */
void setMonitoring(bool b) {m_monitoring = b;}
void setPeriod(int ntrperiod, int nsps)
{
m_TRperiod=ntrperiod;
m_nsps=nsps;
}
int mstep() const {return m_step;}
double samFacIn() const {return m_SamFacIn;}
signals:
void readyForFFT(int k);
@ -36,13 +37,10 @@ signals:
public slots:
void start(qint32 device);
void intervalNotify();
void stop();
private:
PaStream * m_inStream;
bool m_dataSinkBusy;
double m_SamFacIn; //(Input sample rate)/12000.0
qint32 m_step;
qint32 m_TRperiod;
qint32 m_TRperiod0;
@ -50,7 +48,6 @@ private:
bool m_monitoring;
qint64 m_ms0;
int m_ntr0;
int m_nBusy;
int m_nstep0;
int m_nsps0;
@ -58,23 +55,26 @@ private:
struct CallbackData
{
int kin; //Parameters sent to/from the portaudio callback function
int ncall;
bool bzero;
bool monitoring;
//Parameters sent to/from the portaudio callback function
int volatile kin;
bool volatile bzero;
bool volatile monitoring;
} m_callbackData;
private slots:
void intervalNotify();
friend int a2dCallback(void const *, void *, unsigned long, PaStreamCallbackTimeInfo const *, PaStreamCallbackFlags, void *);
};
#endif // SOUNDIN_H
#else // QAUDIO_INPUT
#ifndef SOUNDIN_H
#define SOUNDIN_H
#include <QtCore>
#include <QScopedPointer>
#include <QDebug>
#include <QObject>
#include <QTimer>
#include <QAudioDeviceInfo>
#include <QAudioInput>
@ -88,7 +88,7 @@ public:
SoundInput();
~SoundInput();
void setMonitoring(bool b);
void setMonitoring(bool b) {m_monitoring = b;}
void setPeriod(int ntrperiod, int nsps) /* this can be called while processing samples */
{
m_TRperiod=ntrperiod;
@ -104,7 +104,6 @@ signals:
public slots:
void start(qint32 device);
void intervalNotify();
void stop();
private:
@ -133,6 +132,9 @@ private:
bool bzero;
bool monitoring;
} m_callbackData; //Parameters sent to/from the Notify function
private slots:
void intervalNotify();
};
#endif // SOUNDIN_H
#endif // QAUDIO_INPUT

View File

@ -1,42 +1,28 @@
#include "soundout.h"
//#define FRAMES_PER_BUFFER 1024
#include <cmath>
#include <cstring>
extern "C" {
#include <portaudio.h>
}
#include <QDateTime>
#include <QDebug>
//#define FRAMES_PER_BUFFER 1024
extern float gran(); //Noise generator (for tests only)
extern int itone[126]; //Audio tones for all Tx symbols
extern int icw[250]; //Dits for CW ID
extern int outBufSize;
extern bool btxok;
extern bool btxMute;
extern double outputLatency;
typedef struct //Parameters sent to or received from callback function
{
double txsnrdb;
double dnsps;
int ntrperiod;
int ntxfreq;
int xit;
int ncall;
int nsym;
bool txMute;
bool bRestart;
bool btune;
} paUserData;
//--------------------------------------------------------------- d2aCallback
extern "C" int d2aCallback(const void *inputBuffer, void *outputBuffer,
int d2aCallback(const void *inputBuffer, void *outputBuffer,
unsigned long framesToProcess,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
paUserData *udata=(paUserData*)userData;
short *wptr = (short*)outputBuffer;
SoundOutput::CallbackData * udata = reinterpret_cast<SoundOutput::CallbackData *>(userData);
short * wptr = reinterpret_cast<short *>(outputBuffer);
static double twopi=2.0*3.141592653589793238462;
static double baud;
@ -56,15 +42,19 @@ extern "C" int d2aCallback(const void *inputBuffer, void *outputBuffer,
// Time according to this computer
qint64 ms = QDateTime::currentMSecsSinceEpoch() % 86400000;
int mstr = ms % (1000*udata->ntrperiod );
if(mstr<1000) return paContinue;
if(mstr<1000)
{
std::memset(wptr, 0, framesToProcess * sizeof(*wptr)); // output silence
return paContinue;
}
ic=(mstr-1000)*48;
udata->bRestart=false;
srand(mstr); //Initialize random seed
}
isym=ic/(4.0*udata->dnsps); //Actual fsample=48000
if(udata->btune) isym=0; //If tuning, send pure tone
if(udata->tune) isym=0; //If tuning, send pure tone
if(udata->txsnrdb < 0.0) {
snr=pow(10.0,0.05*(udata->txsnrdb-6.0));
snr=std::pow(10.0,0.05*(udata->txsnrdb-6.0));
fac=3000.0;
if(snr>1.0) fac=3000.0/snr;
}
@ -81,7 +71,7 @@ extern "C" int d2aCallback(const void *inputBuffer, void *outputBuffer,
for(uint i=0 ; i<framesToProcess; i++ ) {
phi += dphi;
if(phi>twopi) phi -= twopi;
i2=32767.0*sin(phi);
i2=32767.0*std::sin(phi);
j=(ic-ic0)/nspd + 1;
if(icw[j]==0) i2=0;
if(udata->txsnrdb < 0.0) {
@ -90,7 +80,7 @@ extern "C" int d2aCallback(const void *inputBuffer, void *outputBuffer,
if(i4<-32767) i4=-32767;
i2=i4;
}
if(!btxok or btxMute) i2=0;
if(udata->mute) i2=0;
*wptr++ = i2; //left
#ifdef UNIX
*wptr++ = i2; //right
@ -107,13 +97,14 @@ extern "C" int d2aCallback(const void *inputBuffer, void *outputBuffer,
amp=32767.0;
int i0=(udata->nsym-0.017)*4.0*udata->dnsps;
int i1=udata->nsym*4.0*udata->dnsps;
if(udata->btune) { //If tuning, no ramp down
bool tune = udata->tune;
if(tune) { //If tuning, no ramp down
i0=999*udata->dnsps;
i1=i0;
}
for(uint i=0 ; i<framesToProcess; i++ ) {
isym=ic/(4.0*udata->dnsps); //Actual fsample=48000
if(udata->btune) isym=0; //If tuning, send pure tone
if(tune) isym=0; //If tuning, send pure tone
if(isym!=isym0) {
freq=udata->ntxfreq + itone[isym]*baud - udata->xit;
dphi=twopi*freq/48000.0;
@ -123,14 +114,14 @@ extern "C" int d2aCallback(const void *inputBuffer, void *outputBuffer,
if(phi>twopi) phi -= twopi;
if(ic>i0) amp=0.98*amp;
if(ic>i1) amp=0.0;
i2=amp*sin(phi);
i2=amp*std::sin(phi);
if(udata->txsnrdb < 0.0) {
int i4=fac*(gran() + i2*snr/32768.0);
if(i4>32767) i4=32767;
if(i4<-32767) i4=-32767;
i2=i4;
}
if(!btxok or btxMute) i2=0;
if(udata->mute) i2=0;
*wptr++ = i2; //left
#ifdef UNIX
*wptr++ = i2; //right
@ -144,15 +135,21 @@ extern "C" int d2aCallback(const void *inputBuffer, void *outputBuffer,
return paContinue;
}
void SoundOutThread::run()
SoundOutput::SoundOutput()
: m_stream(0)
, m_outputLatency(0.)
, m_active(false)
{
PaError paerr;
PaStreamParameters outParam;
PaStream *outStream;
paUserData udata;
quitExecution = false;
}
outParam.device=m_nDevOut; //Output device number
void SoundOutput::start(qint32 deviceNumber,QString const& mode,int TRPeriod
,int nsps,int txFreq,int xit,double txsnrdb)
{
stop();
PaStreamParameters outParam;
outParam.device=deviceNumber; //Output device number
outParam.channelCount=1; //Number of analog channels
#ifdef UNIX
outParam.channelCount=2; //Number of analog channels
@ -161,110 +158,60 @@ void SoundOutThread::run()
outParam.suggestedLatency=0.05;
outParam.hostApiSpecificStreamInfo=NULL;
paerr=Pa_IsFormatSupported(NULL,&outParam,48000.0);
PaError paerr = Pa_IsFormatSupported(NULL,&outParam,48000.0);
if(paerr<0) {
qDebug() << "PortAudio says requested output format not supported.";
qDebug() << paerr << m_nDevOut;
qDebug() << paerr << deviceNumber;
return;
}
udata.txsnrdb=99.0;
udata.dnsps=m_nsps;
udata.nsym=85;
if(m_modeTx=="JT65") {
udata.dnsps=4096.0*12000.0/11025.0;
udata.nsym=126;
m_callbackData.txsnrdb=txsnrdb;
m_callbackData.dnsps=nsps;
m_callbackData.nsym=85;
if(mode=="JT65") {
m_callbackData.dnsps=4096.0*12000.0/11025.0;
m_callbackData.nsym=126;
}
udata.ntrperiod=m_TRperiod;
udata.ntxfreq=m_txFreq;
udata.xit=m_xit;
udata.ncall=0;
udata.txMute=m_txMute;
udata.bRestart=true;
udata.btune=m_tune;
m_callbackData.ntrperiod=TRPeriod;
m_callbackData.ntxfreq=txFreq;
m_callbackData.xit=xit;
m_callbackData.ncall=0;
m_callbackData.bRestart=true;
paerr=Pa_OpenStream(&outStream, //Output stream
paerr=Pa_OpenStream(&m_stream, //Output stream
NULL, //No input parameters
&outParam, //Output parameters
48000.0, //Sample rate
outBufSize, //Frames per buffer
paClipOff, //No clipping
d2aCallback, //output callbeck routine
&udata); //userdata
&m_callbackData); //userdata
paerr=Pa_StartStream(outStream);
paerr=Pa_StartStream(m_stream);
if(paerr<0) {
qDebug() << "Failed to start audio output stream.";
return;
}
const PaStreamInfo* p=Pa_GetStreamInfo(outStream);
outputLatency = p->outputLatency;
bool qe = quitExecution;
qint64 ms0 = QDateTime::currentMSecsSinceEpoch();
const PaStreamInfo* p=Pa_GetStreamInfo(m_stream);
m_outputLatency = p->outputLatency;
m_ms0 = QDateTime::currentMSecsSinceEpoch();
m_active = true;
}
//---------------------------------------------- Soundcard output loop
while (!qe) {
qe = quitExecution;
if (qe) break;
udata.txsnrdb=m_txsnrdb;
udata.dnsps=m_nsps;
udata.nsym=85;
if(m_modeTx=="JT65") {
udata.dnsps=4096.0*12000.0/11025.0;
udata.nsym=126;
void SoundOutput::stop()
{
if (m_stream)
{
Pa_StopStream(m_stream);
Pa_CloseStream(m_stream), m_stream = 0;
}
udata.ntrperiod=m_TRperiod;
udata.ntxfreq=m_txFreq;
udata.xit=m_xit;
udata.txMute=m_txMute;
udata.btune=m_tune;
m_active = false;
}
m_SamFacOut=1.0;
if(udata.ncall>400) {
qint64 ms = QDateTime::currentMSecsSinceEpoch();
m_SamFacOut=udata.ncall*outBufSize*1000.0/(48000.0*(ms-ms0-50));
SoundOutput::~SoundOutput()
{
if (m_stream)
{
Pa_CloseStream(m_stream), m_stream = 0;
}
msleep(100);
}
Pa_StopStream(outStream);
Pa_CloseStream(outStream);
}
void SoundOutThread::setOutputDevice(int n) //setOutputDevice()
{
if (isRunning()) return;
this->m_nDevOut=n;
}
void SoundOutThread::setPeriod(int ntrperiod, int nsps)
{
m_TRperiod=ntrperiod;
m_nsps=nsps;
}
void SoundOutThread::setTxFreq(int n)
{
m_txFreq=n;
}
void SoundOutThread::setXIT(int n)
{
m_xit=n;
}
void SoundOutThread::setTxSNR(double snr)
{
m_txsnrdb=snr;
}
void SoundOutThread::setTune(bool b)
{
m_tune=b;
}
double SoundOutThread::samFacOut()
{
return m_SamFacOut;
}

View File

@ -1,53 +1,76 @@
#ifndef SOUNDOUT_H
#define SOUNDOUT_H
#include <QtCore>
#include <QDebug>
// An instance of this thread sends audio data to a specified soundcard.
#include <portaudio.h>
#include <QObject>
#include <QString>
extern "C" int d2aCallback(const void *, void *,
unsigned long,
PaStreamCallbackTimeInfo const *,
PaStreamCallbackFlags,
void *);
// An instance of this sends audio data to a specified soundcard.
// Output can be muted while underway, preserving waveform timing when
// transmission is resumed.
class SoundOutThread : public QThread
class SoundOutput : public QObject
{
Q_OBJECT
Q_OBJECT;
protected:
virtual void run();
Q_PROPERTY(bool running READ isRunning);
Q_PROPERTY(bool mute READ isMuted WRITE mute);
Q_PROPERTY(bool tune READ isTuning WRITE tune);
public:
// Constructs (but does not start) a SoundOutThread
SoundOutThread()
: quitExecution(false) // Initialize some private members
, m_txOK(false)
, m_txMute(false)
{
}
SoundOutput();
~SoundOutput();
public:
void setOutputDevice(qint32 n);
void setPeriod(int ntrperiod, int nsps);
void setTxFreq(int n);
void setXIT(int n);
void setTxSNR(double snr);
void setTune(bool b);
double samFacOut();
bool quitExecution; //If true, thread exits gracefully
QString m_modeTx;
bool isRunning() const {return m_active;}
bool isMuted() const {return m_callbackData.mute;}
bool isTuning() const {return m_callbackData.tune;}
double outputLatency() const {return m_outputLatency;}
// the following can be called while the stream is running
void setTxFreq(int n) {m_callbackData.ntxfreq = n;}
void setXIT(int n) {m_callbackData.xit = n;}
void mute(bool b = true) {m_callbackData.mute = b;}
void tune(bool b = true) {m_callbackData.tune = b;}
public slots:
void start(qint32 deviceNumber, QString const& mode,int TRPeriod,int nsps,int txFreq,int xit,double txsnrdb = 99.);
void stop();
// Private members
private:
double m_txsnrdb; //if < 0, add noise to Tx audio
double m_SamFacOut; //(Output sample rate)/48000.0
PaStream * m_stream;
PaTime m_outputLatency;
qint32 m_nDevOut; //Output device number
qint32 m_TRperiod; //T/R period (s)
qint32 m_nsps; //Samples per symbol (at 12000 Hz)
qint32 m_txFreq;
qint32 m_xit;
struct CallbackData
{
//Parameters sent to or received from callback function
double volatile txsnrdb;
double volatile dnsps; //Samples per symbol (at 12000 Hz)
int volatile ntrperiod; //T/R period (s)
int volatile ntxfreq;
int volatile xit;
int volatile ncall;
int volatile nsym;
bool volatile mute;
bool volatile bRestart;
bool volatile tune;
} m_callbackData;
bool m_txOK; //Enable Tx audio
bool m_txMute; //Mute temporarily
bool m_tune;
qint64 m_ms0;
bool m_active;
friend int d2aCallback(const void *, void *,
unsigned long,
PaStreamCallbackTimeInfo const *,
PaStreamCallbackFlags,
void *);
};
#endif