diff --git a/mainwindow.cpp b/mainwindow.cpp index 2c2c5eedd..036471627 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -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) diff --git a/mainwindow.h b/mainwindow.h index 1f1400b9a..f42d982f9 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -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; diff --git a/soundin.cpp b/soundin.cpp index 899535f29..7e943b2bd 100644 --- a/soundin.cpp +++ b/soundin.cpp @@ -1,6 +1,8 @@ #ifndef QAUDIO_INPUT #include "soundin.h" -#include + +#include +#include #define FRAMES_PER_BUFFER 1024 #define NSMAX 6827 @@ -48,29 +50,28 @@ int a2dCallback( const void *inputBuffer, void * /* outputBuffer */, SoundInput::CallbackData * udata = reinterpret_cast(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 + nbytes=2*framesToProcess; //Bytes per frame k=udata->kin; 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 - } + 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 + +#include #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 diff --git a/soundin.h b/soundin.h index bff938d1a..270c9a5aa 100644 --- a/soundin.h +++ b/soundin.h @@ -4,9 +4,9 @@ #include -#include -#include -#include +#include +#include +#include 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 -#include -#include +#include +#include #include #include @@ -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 diff --git a/soundout.cpp b/soundout.cpp index 8693237c4..88c143ba6 100644 --- a/soundout.cpp +++ b/soundout.cpp @@ -1,42 +1,28 @@ #include "soundout.h" -//#define FRAMES_PER_BUFFER 1024 +#include +#include -extern "C" { -#include -} +#include +#include + +//#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(userData); + short * wptr = reinterpret_cast(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 ; itwopi) 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 ; idnsps); //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; } diff --git a/soundout.h b/soundout.h index a289afc99..4b428a77b 100644 --- a/soundout.h +++ b/soundout.h @@ -1,53 +1,76 @@ #ifndef SOUNDOUT_H #define SOUNDOUT_H -#include -#include -// An instance of this thread sends audio data to a specified soundcard. +#include + +#include +#include + +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