mirror of
https://github.com/saitohirga/WSJT-X.git
synced 2024-11-16 00:51:56 -05:00
208 lines
5.2 KiB
C++
208 lines
5.2 KiB
C++
#include "soundout.h"
|
|
|
|
#ifdef Q_OS_WIN32
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
#define FRAMES_PER_BUFFER 256
|
|
|
|
#include <portaudio.h>
|
|
|
|
extern float gran(); //Noise generator (for tests only)
|
|
|
|
extern short int iwave[2*60*11025]; //Wave file for Tx audio
|
|
extern int nwave;
|
|
extern bool btxok;
|
|
extern bool bTune;
|
|
extern bool bIQxt;
|
|
extern int iqAmp;
|
|
extern int iqPhase;
|
|
extern int txPower;
|
|
extern double outputLatency;
|
|
|
|
typedef struct //Parameters sent to or received from callback function
|
|
{
|
|
int nTRperiod;
|
|
} paUserData;
|
|
|
|
//--------------------------------------------------------------- d2aCallback
|
|
extern "C" 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;
|
|
unsigned int i;
|
|
static int n;
|
|
static int ic=0;
|
|
static bool btxok0=false;
|
|
static bool bTune0=false;
|
|
static int nStart=0;
|
|
static double phi=0.;
|
|
double tsec,tstart,dphi;
|
|
int nsec;
|
|
int nTRperiod=udata->nTRperiod;
|
|
|
|
// Get System time
|
|
qint64 ms = QDateTime::currentMSecsSinceEpoch() % 86400000;
|
|
tsec = 0.001*ms;
|
|
nsec = ms/1000;
|
|
qreal dPhase=iqPhase/5729.57795131;
|
|
qreal amp=1.0 + 0.0001*iqAmp;
|
|
qreal xAmp=txPower*295.00*qSqrt(2.0 - amp*amp);
|
|
qreal yAmp=txPower*295.00*amp;
|
|
static int nsec0=0;
|
|
|
|
if(bTune) {
|
|
ic=0;
|
|
dphi=6.28318530718*1270.46/11025.0;
|
|
}
|
|
if(bTune0 and !bTune) btxok=false;
|
|
bTune0=bTune;
|
|
|
|
if(nsec!=nsec0) {
|
|
// qDebug() << txPower << iqAmp << iqPhase << amp << xAmp << yAmp << dPhase << bTune;
|
|
// qDebug() << "A" << nsec%60 << bTune << btxok;
|
|
// ic=0;
|
|
nsec0=nsec;
|
|
}
|
|
|
|
if(btxok and !btxok0) { //Start (or re-start) a transmission
|
|
n=nsec/nTRperiod;
|
|
tstart=tsec - n*nTRperiod - 1.0;
|
|
|
|
if(tstart<1.0) {
|
|
ic=0; //Start of Tx cycle, set starting index to 0
|
|
nStart=n;
|
|
} else {
|
|
if(n != nStart) { //Late start in new Tx cycle: compute starting index
|
|
ic=(int)(tstart*11025.0);
|
|
ic=2*ic;
|
|
nStart=n;
|
|
}
|
|
}
|
|
}
|
|
btxok0=btxok;
|
|
|
|
if(btxok) {
|
|
for(i=0 ; i<framesToProcess; i++ ) {
|
|
short int i2a=iwave[ic++];
|
|
short int i2b=iwave[ic++];
|
|
if(ic > nwave) {i2a=0; i2b=0;}
|
|
// i2 = 500.0*(i2/32767.0 + 5.0*gran()); //Add noise (tests only!)
|
|
// if(bIQxt) {
|
|
if(1) {
|
|
if(bTune) {
|
|
phi += dphi;
|
|
} else {
|
|
phi=qAtan2(qreal(i2b),qreal(i2a));
|
|
}
|
|
i2a=xAmp*qCos(phi);
|
|
i2b=yAmp*qSin(phi + dPhase);
|
|
// qDebug() << xAmp << yAmp << phi << i2a << i2b;
|
|
}
|
|
// i2a=0.01*txPower*i2a;
|
|
// i2b=0.01*txPower*i2b;
|
|
*wptr++ = i2b; //left
|
|
*wptr++ = i2a; //right
|
|
}
|
|
} else {
|
|
for(i=0 ; i<framesToProcess; i++ ) {
|
|
*wptr++ = 0;
|
|
*wptr++ = 0;
|
|
ic++; ic++;
|
|
}
|
|
}
|
|
if(ic > nwave) {
|
|
btxok=0;
|
|
ic=0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
namespace
|
|
{
|
|
struct COMWrapper
|
|
{
|
|
explicit COMWrapper ()
|
|
{
|
|
#ifdef Q_OS_WIN32
|
|
// required because Qt only does this for GUI thread
|
|
CoInitializeEx (nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
|
|
#endif
|
|
}
|
|
~COMWrapper ()
|
|
{
|
|
#ifdef Q_OS_WIN32
|
|
CoUninitialize ();
|
|
#endif
|
|
}
|
|
};
|
|
}
|
|
|
|
void SoundOutThread::run()
|
|
{
|
|
COMWrapper c;
|
|
|
|
PaError paerr;
|
|
PaStreamParameters outParam;
|
|
PaStream *outStream;
|
|
paUserData udata;
|
|
quitExecution = false;
|
|
|
|
auto device_info = Pa_GetDeviceInfo (m_nDevOut);
|
|
|
|
outParam.device=m_nDevOut; //Output device number
|
|
outParam.channelCount=2; //Number of analog channels
|
|
outParam.sampleFormat=paInt16; //Send short ints to PortAudio
|
|
outParam.suggestedLatency=device_info->defaultLowOutputLatency;
|
|
outParam.hostApiSpecificStreamInfo=NULL;
|
|
|
|
udata.nTRperiod=m_TRperiod;
|
|
paerr=Pa_IsFormatSupported(NULL,&outParam,11025.0);
|
|
if(paerr<0) {
|
|
qDebug() << "PortAudio says requested output format not supported.";
|
|
qDebug() << paerr;
|
|
return;
|
|
}
|
|
paerr=Pa_OpenStream(&outStream, //Output stream
|
|
NULL, //No input parameters
|
|
&outParam, //Output parameters
|
|
11025.0, //Sample rate
|
|
FRAMES_PER_BUFFER, //Frames per buffer
|
|
paClipOff, //No clipping
|
|
d2aCallback, //output callbeck routine
|
|
&udata); //userdata
|
|
|
|
paerr=Pa_StartStream(outStream);
|
|
if(paerr<0) {
|
|
qDebug() << "Failed to start audio output stream.";
|
|
return;
|
|
}
|
|
const PaStreamInfo* p=Pa_GetStreamInfo(outStream);
|
|
outputLatency = p->outputLatency;
|
|
bool qe = quitExecution;
|
|
|
|
//---------------------------------------------- Soundcard output loop
|
|
while (!qe) {
|
|
qe = quitExecution;
|
|
if (qe) break;
|
|
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 n)
|
|
{
|
|
m_TRperiod=n;
|
|
}
|