mirror of
https://github.com/saitohirga/WSJT-X.git
synced 2024-11-27 06:38:44 -05:00
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:
parent
0db0574ad3
commit
c5b46973f2
107
mainwindow.cpp
107
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)
|
||||
|
@ -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;
|
||||
|
67
soundin.cpp
67
soundin.cpp
@ -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,29 +50,28 @@ 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
|
||||
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 <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
|
||||
|
42
soundin.h
42
soundin.h
@ -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
|
||||
|
193
soundout.cpp
193
soundout.cpp
@ -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;
|
||||
}
|
||||
|
91
soundout.h
91
soundout.h
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user