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 0db0574ad3
commit c5b46973f2
6 changed files with 230 additions and 275 deletions

View File

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

View File

@ -183,6 +183,7 @@ private:
qint32 m_timeout; qint32 m_timeout;
qint32 m_rxFreq; qint32 m_rxFreq;
qint32 m_txFreq; qint32 m_txFreq;
int m_XIT;
qint32 m_setftx; qint32 m_setftx;
qint32 m_ndepth; qint32 m_ndepth;
qint32 m_sec0; qint32 m_sec0;
@ -219,6 +220,8 @@ private:
qint32 m_bad; qint32 m_bad;
bool m_monitoring; 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_transmitting;
bool m_diskData; bool m_diskData;
bool m_loopall; bool m_loopall;
@ -341,7 +344,7 @@ private:
QDateTime m_dateTimeQSO; QDateTime m_dateTimeQSO;
SoundInput m_soundInput; //Instantiate the audio objects SoundInput m_soundInput; //Instantiate the audio objects
SoundOutThread soundOutThread; SoundOutput m_soundOutput;
QSharedMemory *mem_jt9; QSharedMemory *mem_jt9;
// Multiple instances: // Multiple instances:
QString *mykey_jt9; QString *mykey_jt9;

View File

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

View File

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

View File

@ -1,42 +1,28 @@
#include "soundout.h" #include "soundout.h"
//#define FRAMES_PER_BUFFER 1024 #include <cmath>
#include <cstring>
extern "C" { #include <QDateTime>
#include <portaudio.h> #include <QDebug>
}
//#define FRAMES_PER_BUFFER 1024
extern float gran(); //Noise generator (for tests only) extern float gran(); //Noise generator (for tests only)
extern int itone[126]; //Audio tones for all Tx symbols extern int itone[126]; //Audio tones for all Tx symbols
extern int icw[250]; //Dits for CW ID extern int icw[250]; //Dits for CW ID
extern int outBufSize; 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 //--------------------------------------------------------------- d2aCallback
extern "C" int d2aCallback(const void *inputBuffer, void *outputBuffer, int d2aCallback(const void *inputBuffer, void *outputBuffer,
unsigned long framesToProcess, unsigned long framesToProcess,
const PaStreamCallbackTimeInfo* timeInfo, const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags, PaStreamCallbackFlags statusFlags,
void *userData ) void *userData )
{ {
paUserData *udata=(paUserData*)userData; SoundOutput::CallbackData * udata = reinterpret_cast<SoundOutput::CallbackData *>(userData);
short *wptr = (short*)outputBuffer; short * wptr = reinterpret_cast<short *>(outputBuffer);
static double twopi=2.0*3.141592653589793238462; static double twopi=2.0*3.141592653589793238462;
static double baud; static double baud;
@ -56,15 +42,19 @@ extern "C" int d2aCallback(const void *inputBuffer, void *outputBuffer,
// Time according to this computer // Time according to this computer
qint64 ms = QDateTime::currentMSecsSinceEpoch() % 86400000; qint64 ms = QDateTime::currentMSecsSinceEpoch() % 86400000;
int mstr = ms % (1000*udata->ntrperiod ); 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; ic=(mstr-1000)*48;
udata->bRestart=false; udata->bRestart=false;
srand(mstr); //Initialize random seed srand(mstr); //Initialize random seed
} }
isym=ic/(4.0*udata->dnsps); //Actual fsample=48000 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) { 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; fac=3000.0;
if(snr>1.0) fac=3000.0/snr; 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++ ) { for(uint i=0 ; i<framesToProcess; i++ ) {
phi += dphi; phi += dphi;
if(phi>twopi) phi -= twopi; if(phi>twopi) phi -= twopi;
i2=32767.0*sin(phi); i2=32767.0*std::sin(phi);
j=(ic-ic0)/nspd + 1; j=(ic-ic0)/nspd + 1;
if(icw[j]==0) i2=0; if(icw[j]==0) i2=0;
if(udata->txsnrdb < 0.0) { if(udata->txsnrdb < 0.0) {
@ -90,7 +80,7 @@ extern "C" int d2aCallback(const void *inputBuffer, void *outputBuffer,
if(i4<-32767) i4=-32767; if(i4<-32767) i4=-32767;
i2=i4; i2=i4;
} }
if(!btxok or btxMute) i2=0; if(udata->mute) i2=0;
*wptr++ = i2; //left *wptr++ = i2; //left
#ifdef UNIX #ifdef UNIX
*wptr++ = i2; //right *wptr++ = i2; //right
@ -107,13 +97,14 @@ extern "C" int d2aCallback(const void *inputBuffer, void *outputBuffer,
amp=32767.0; amp=32767.0;
int i0=(udata->nsym-0.017)*4.0*udata->dnsps; int i0=(udata->nsym-0.017)*4.0*udata->dnsps;
int i1=udata->nsym*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; i0=999*udata->dnsps;
i1=i0; i1=i0;
} }
for(uint i=0 ; i<framesToProcess; i++ ) { for(uint i=0 ; i<framesToProcess; i++ ) {
isym=ic/(4.0*udata->dnsps); //Actual fsample=48000 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) { if(isym!=isym0) {
freq=udata->ntxfreq + itone[isym]*baud - udata->xit; freq=udata->ntxfreq + itone[isym]*baud - udata->xit;
dphi=twopi*freq/48000.0; dphi=twopi*freq/48000.0;
@ -123,14 +114,14 @@ extern "C" int d2aCallback(const void *inputBuffer, void *outputBuffer,
if(phi>twopi) phi -= twopi; if(phi>twopi) phi -= twopi;
if(ic>i0) amp=0.98*amp; if(ic>i0) amp=0.98*amp;
if(ic>i1) amp=0.0; if(ic>i1) amp=0.0;
i2=amp*sin(phi); i2=amp*std::sin(phi);
if(udata->txsnrdb < 0.0) { if(udata->txsnrdb < 0.0) {
int i4=fac*(gran() + i2*snr/32768.0); int i4=fac*(gran() + i2*snr/32768.0);
if(i4>32767) i4=32767; if(i4>32767) i4=32767;
if(i4<-32767) i4=-32767; if(i4<-32767) i4=-32767;
i2=i4; i2=i4;
} }
if(!btxok or btxMute) i2=0; if(udata->mute) i2=0;
*wptr++ = i2; //left *wptr++ = i2; //left
#ifdef UNIX #ifdef UNIX
*wptr++ = i2; //right *wptr++ = i2; //right
@ -144,15 +135,21 @@ extern "C" int d2aCallback(const void *inputBuffer, void *outputBuffer,
return paContinue; 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 outParam.channelCount=1; //Number of analog channels
#ifdef UNIX #ifdef UNIX
outParam.channelCount=2; //Number of analog channels outParam.channelCount=2; //Number of analog channels
@ -161,110 +158,60 @@ void SoundOutThread::run()
outParam.suggestedLatency=0.05; outParam.suggestedLatency=0.05;
outParam.hostApiSpecificStreamInfo=NULL; outParam.hostApiSpecificStreamInfo=NULL;
paerr=Pa_IsFormatSupported(NULL,&outParam,48000.0); PaError paerr = Pa_IsFormatSupported(NULL,&outParam,48000.0);
if(paerr<0) { if(paerr<0) {
qDebug() << "PortAudio says requested output format not supported."; qDebug() << "PortAudio says requested output format not supported.";
qDebug() << paerr << m_nDevOut; qDebug() << paerr << deviceNumber;
return; return;
} }
udata.txsnrdb=99.0; m_callbackData.txsnrdb=txsnrdb;
udata.dnsps=m_nsps; m_callbackData.dnsps=nsps;
udata.nsym=85; m_callbackData.nsym=85;
if(m_modeTx=="JT65") { if(mode=="JT65") {
udata.dnsps=4096.0*12000.0/11025.0; m_callbackData.dnsps=4096.0*12000.0/11025.0;
udata.nsym=126; m_callbackData.nsym=126;
} }
udata.ntrperiod=m_TRperiod; m_callbackData.ntrperiod=TRPeriod;
udata.ntxfreq=m_txFreq; m_callbackData.ntxfreq=txFreq;
udata.xit=m_xit; m_callbackData.xit=xit;
udata.ncall=0; m_callbackData.ncall=0;
udata.txMute=m_txMute; m_callbackData.bRestart=true;
udata.bRestart=true;
udata.btune=m_tune;
paerr=Pa_OpenStream(&outStream, //Output stream paerr=Pa_OpenStream(&m_stream, //Output stream
NULL, //No input parameters NULL, //No input parameters
&outParam, //Output parameters &outParam, //Output parameters
48000.0, //Sample rate 48000.0, //Sample rate
outBufSize, //Frames per buffer outBufSize, //Frames per buffer
paClipOff, //No clipping paClipOff, //No clipping
d2aCallback, //output callbeck routine d2aCallback, //output callbeck routine
&udata); //userdata &m_callbackData); //userdata
paerr=Pa_StartStream(outStream); paerr=Pa_StartStream(m_stream);
if(paerr<0) { if(paerr<0) {
qDebug() << "Failed to start audio output stream."; qDebug() << "Failed to start audio output stream.";
return; return;
} }
const PaStreamInfo* p=Pa_GetStreamInfo(outStream); const PaStreamInfo* p=Pa_GetStreamInfo(m_stream);
outputLatency = p->outputLatency; m_outputLatency = p->outputLatency;
bool qe = quitExecution; m_ms0 = QDateTime::currentMSecsSinceEpoch();
qint64 ms0 = QDateTime::currentMSecsSinceEpoch(); m_active = true;
}
//---------------------------------------------- Soundcard output loop void SoundOutput::stop()
while (!qe) { {
qe = quitExecution; if (m_stream)
if (qe) break; {
Pa_StopStream(m_stream);
udata.txsnrdb=m_txsnrdb; Pa_CloseStream(m_stream), m_stream = 0;
udata.dnsps=m_nsps;
udata.nsym=85;
if(m_modeTx=="JT65") {
udata.dnsps=4096.0*12000.0/11025.0;
udata.nsym=126;
} }
udata.ntrperiod=m_TRperiod; m_active = false;
udata.ntxfreq=m_txFreq; }
udata.xit=m_xit;
udata.txMute=m_txMute;
udata.btune=m_tune;
m_SamFacOut=1.0; SoundOutput::~SoundOutput()
if(udata.ncall>400) { {
qint64 ms = QDateTime::currentMSecsSinceEpoch(); if (m_stream)
m_SamFacOut=udata.ncall*outBufSize*1000.0/(48000.0*(ms-ms0-50)); {
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 #ifndef SOUNDOUT_H
#define 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 // Output can be muted while underway, preserving waveform timing when
// transmission is resumed. // transmission is resumed.
class SoundOutThread : public QThread class SoundOutput : public QObject
{ {
Q_OBJECT Q_OBJECT;
protected: Q_PROPERTY(bool running READ isRunning);
virtual void run(); Q_PROPERTY(bool mute READ isMuted WRITE mute);
Q_PROPERTY(bool tune READ isTuning WRITE tune);
public: public:
// Constructs (but does not start) a SoundOutThread SoundOutput();
SoundOutThread() ~SoundOutput();
: quitExecution(false) // Initialize some private members
, m_txOK(false)
, m_txMute(false)
{
}
public: bool isRunning() const {return m_active;}
void setOutputDevice(qint32 n); bool isMuted() const {return m_callbackData.mute;}
void setPeriod(int ntrperiod, int nsps); bool isTuning() const {return m_callbackData.tune;}
void setTxFreq(int n); double outputLatency() const {return m_outputLatency;}
void setXIT(int n);
void setTxSNR(double snr); // the following can be called while the stream is running
void setTune(bool b); void setTxFreq(int n) {m_callbackData.ntxfreq = n;}
double samFacOut(); void setXIT(int n) {m_callbackData.xit = n;}
bool quitExecution; //If true, thread exits gracefully void mute(bool b = true) {m_callbackData.mute = b;}
QString m_modeTx; 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 members
private: private:
double m_txsnrdb; //if < 0, add noise to Tx audio PaStream * m_stream;
double m_SamFacOut; //(Output sample rate)/48000.0 PaTime m_outputLatency;
qint32 m_nDevOut; //Output device number struct CallbackData
qint32 m_TRperiod; //T/R period (s) {
qint32 m_nsps; //Samples per symbol (at 12000 Hz) //Parameters sent to or received from callback function
qint32 m_txFreq; double volatile txsnrdb;
qint32 m_xit; 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 qint64 m_ms0;
bool m_txMute; //Mute temporarily bool m_active;
bool m_tune;
friend int d2aCallback(const void *, void *,
unsigned long,
PaStreamCallbackTimeInfo const *,
PaStreamCallbackFlags,
void *);
}; };
#endif #endif