2013-07-30 16:36:14 -04:00
|
|
|
#ifndef QAUDIO_INPUT
|
2012-05-22 13:09:48 -04:00
|
|
|
#include "soundin.h"
|
2013-07-31 20:49:58 -04:00
|
|
|
|
|
|
|
#include <QDateTime>
|
|
|
|
#include <QDebug>
|
2012-05-22 13:09:48 -04:00
|
|
|
|
|
|
|
#define FRAMES_PER_BUFFER 1024
|
2013-07-08 09:17:22 -04:00
|
|
|
#define NSMAX 6827
|
2013-05-20 10:09:02 -04:00
|
|
|
#define NTMAX 120
|
2012-05-22 13:09:48 -04:00
|
|
|
|
|
|
|
extern "C" {
|
|
|
|
#include <portaudio.h>
|
|
|
|
extern struct {
|
2012-10-16 19:32:15 -04:00
|
|
|
float ss[184*NSMAX]; //This is "common/jt9com/..." in fortran
|
2012-09-28 19:59:50 -04:00
|
|
|
float savg[NSMAX];
|
2013-07-08 09:17:22 -04:00
|
|
|
// float c0[2*NTMAX*1500];
|
2013-05-20 10:09:02 -04:00
|
|
|
short int d2[NTMAX*12000];
|
2012-09-25 12:04:38 -04:00
|
|
|
int nutc; //UTC as integer, HHMM
|
2012-10-16 19:32:15 -04:00
|
|
|
int ndiskdat; //1 ==> data read from *.wav file
|
2012-09-25 16:26:12 -04:00
|
|
|
int ntrperiod; //TR period (seconds)
|
2012-09-25 12:04:38 -04:00
|
|
|
int mousefqso; //User-selected QSO freq (kHz)
|
|
|
|
int newdat; //1 ==> new data, must do long FFT
|
2012-10-19 15:26:07 -04:00
|
|
|
int npts8; //npts in c0() array
|
2012-11-21 12:42:53 -05:00
|
|
|
int nfa; //Low decode limit (Hz)
|
|
|
|
int nfb; //High decode limit (Hz)
|
2012-09-25 12:04:38 -04:00
|
|
|
int ntol; //+/- decoding range around fQSO (Hz)
|
2012-07-04 12:27:57 -04:00
|
|
|
int kin;
|
2012-11-19 13:23:39 -05:00
|
|
|
int nzhsym;
|
2012-11-21 12:42:53 -05:00
|
|
|
int nsave;
|
|
|
|
int nagain;
|
|
|
|
int ndepth;
|
2013-07-08 09:17:22 -04:00
|
|
|
int ntxmode;
|
|
|
|
int nmode;
|
2012-11-21 12:42:53 -05:00
|
|
|
char datetime[20];
|
2012-10-03 11:42:13 -04:00
|
|
|
} jt9com_;
|
2012-05-22 13:09:48 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
//--------------------------------------------------------------- a2dCallback
|
2013-07-29 20:51:42 -04:00
|
|
|
int a2dCallback( const void *inputBuffer, void * /* outputBuffer */,
|
|
|
|
unsigned long framesToProcess,
|
|
|
|
const PaStreamCallbackTimeInfo * /* timeInfo */,
|
|
|
|
PaStreamCallbackFlags statusFlags,
|
|
|
|
void *userData )
|
2012-05-22 13:09:48 -04:00
|
|
|
|
|
|
|
// This routine called by the PortAudio engine when samples are available.
|
|
|
|
// It may be called at interrupt level, so don't do anything
|
|
|
|
// that could mess up the system like calling malloc() or free().
|
|
|
|
|
|
|
|
{
|
2013-07-29 20:51:42 -04:00
|
|
|
SoundInput::CallbackData * udata = reinterpret_cast<SoundInput::CallbackData *>(userData);
|
2012-07-04 13:48:57 -04:00
|
|
|
int nbytes,k;
|
2012-05-22 13:09:48 -04:00
|
|
|
|
|
|
|
if( (statusFlags&paInputOverflow) != 0) {
|
2013-07-08 09:17:22 -04:00
|
|
|
qDebug() << "Input Overflow in a2dCallback";
|
2012-05-22 13:09:48 -04:00
|
|
|
}
|
2013-07-31 20:49:58 -04:00
|
|
|
if(udata->bzero)
|
|
|
|
{ //Start of a new Rx sequence
|
|
|
|
udata->kin = 0; //Reset buffer pointer
|
|
|
|
udata->bzero = false;
|
|
|
|
}
|
2012-05-22 13:09:48 -04:00
|
|
|
|
2013-07-31 20:49:58 -04:00
|
|
|
nbytes=2*framesToProcess; //Bytes per frame
|
2012-07-04 12:27:57 -04:00
|
|
|
k=udata->kin;
|
2012-11-01 15:54:40 -04:00
|
|
|
if(udata->monitoring) {
|
|
|
|
memcpy(&jt9com_.d2[k],inputBuffer,nbytes); //Copy all samples to d2
|
|
|
|
}
|
2013-07-31 20:49:58 -04:00
|
|
|
udata->kin+=framesToProcess;
|
|
|
|
jt9com_.kin=udata->kin; // we are the only writer to jt9com_ so no MT issue here
|
2012-07-04 12:27:57 -04:00
|
|
|
|
2012-05-22 13:09:48 -04:00
|
|
|
return paContinue;
|
|
|
|
}
|
|
|
|
|
2013-07-29 20:51:42 -04:00
|
|
|
SoundInput::SoundInput()
|
|
|
|
: m_inStream(0),
|
|
|
|
m_TRperiod(60),
|
|
|
|
m_nsps(6912),
|
|
|
|
m_monitoring(false),
|
|
|
|
m_intervalTimer(this)
|
|
|
|
{
|
2013-07-30 20:03:26 -04:00
|
|
|
connect(&m_intervalTimer, SIGNAL(timeout()), this,SLOT(intervalNotify()));
|
2013-07-29 20:51:42 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void SoundInput::start(qint32 device)
|
2012-05-22 13:09:48 -04:00
|
|
|
{
|
2013-07-29 20:51:42 -04:00
|
|
|
stop();
|
2012-05-22 13:09:48 -04:00
|
|
|
|
|
|
|
//---------------------------------------------------- Soundcard Setup
|
|
|
|
PaError paerr;
|
|
|
|
PaStreamParameters inParam;
|
|
|
|
|
2013-07-31 20:49:58 -04:00
|
|
|
m_callbackData.kin = 0; //Buffer pointer
|
|
|
|
m_callbackData.bzero = false; //Flag to request reset of kin
|
|
|
|
m_callbackData.monitoring = m_monitoring;
|
2012-05-22 13:09:48 -04:00
|
|
|
|
2013-07-29 20:51:42 -04:00
|
|
|
inParam.device=device; //### Input Device Number ###
|
2012-07-04 12:27:57 -04:00
|
|
|
inParam.channelCount=1; //Number of analog channels
|
2012-07-05 12:16:03 -04:00
|
|
|
inParam.sampleFormat=paInt16; //Get i*2 from Portaudio
|
2012-05-22 13:09:48 -04:00
|
|
|
inParam.suggestedLatency=0.05;
|
|
|
|
inParam.hostApiSpecificStreamInfo=NULL;
|
|
|
|
|
2012-09-24 19:36:38 -04:00
|
|
|
paerr=Pa_IsFormatSupported(&inParam,NULL,12000.0);
|
2012-05-22 13:09:48 -04:00
|
|
|
if(paerr<0) {
|
|
|
|
emit error("PortAudio says requested soundcard format not supported.");
|
|
|
|
}
|
2013-07-29 20:51:42 -04:00
|
|
|
paerr=Pa_OpenStream(&m_inStream, //Input stream
|
|
|
|
&inParam, //Input parameters
|
|
|
|
NULL, //No output parameters
|
|
|
|
12000.0, //Sample rate
|
|
|
|
FRAMES_PER_BUFFER, //Frames per buffer
|
2012-07-05 12:16:03 -04:00
|
|
|
// paClipOff+paDitherOff, //No clipping or dithering
|
2013-07-29 20:51:42 -04:00
|
|
|
paClipOff, //No clipping
|
|
|
|
a2dCallback, //Input callback routine
|
|
|
|
&m_callbackData); //userdata
|
|
|
|
paerr=Pa_StartStream(m_inStream);
|
2012-05-22 13:09:48 -04:00
|
|
|
if(paerr<0) {
|
|
|
|
emit error("Failed to start audio input stream.");
|
|
|
|
return;
|
|
|
|
}
|
2013-07-29 20:51:42 -04:00
|
|
|
m_ntr0 = 99; // initial value higher than any expected
|
|
|
|
m_intervalTimer.start(100);
|
|
|
|
m_ms0 = QDateTime::currentMSecsSinceEpoch();
|
|
|
|
m_nsps0 = 0;
|
|
|
|
}
|
2012-05-22 13:09:48 -04:00
|
|
|
|
2013-07-29 20:51:42 -04:00
|
|
|
void SoundInput::intervalNotify()
|
|
|
|
{
|
2013-07-31 20:49:58 -04:00
|
|
|
m_callbackData.monitoring = m_monitoring; // update monitoring
|
|
|
|
// status
|
|
|
|
|
2013-07-29 20:51:42 -04:00
|
|
|
qint64 ms = QDateTime::currentMSecsSinceEpoch();
|
|
|
|
ms=ms % 86400000;
|
|
|
|
int nsec = ms/1000; // Time according to this computer
|
|
|
|
int ntr = nsec % m_TRperiod;
|
|
|
|
|
2013-07-31 20:49:58 -04:00
|
|
|
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
|
|
|
|
|
2013-07-29 20:51:42 -04:00
|
|
|
// 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;
|
2013-07-31 20:49:58 -04:00
|
|
|
m_callbackData.bzero = true; // request callback to reset buffer pointer
|
2013-07-29 20:51:42 -04:00
|
|
|
}
|
2013-07-31 20:49:58 -04:00
|
|
|
|
2013-07-29 20:51:42 -04:00
|
|
|
if(m_monitoring) {
|
|
|
|
int kstep=m_nsps/2;
|
|
|
|
// m_step=k/kstep;
|
|
|
|
m_step=(k-1)/kstep;
|
|
|
|
if(m_step != m_nstep0) {
|
2013-07-31 20:49:58 -04:00
|
|
|
emit readyForFFT(k-1); //Signal to compute new FFTs
|
2013-07-29 20:51:42 -04:00
|
|
|
m_nstep0=m_step;
|
2012-05-22 13:09:48 -04:00
|
|
|
}
|
|
|
|
}
|
2013-07-29 20:51:42 -04:00
|
|
|
m_ntr0=ntr;
|
2012-05-22 13:09:48 -04:00
|
|
|
}
|
|
|
|
|
2013-07-29 20:51:42 -04:00
|
|
|
SoundInput::~SoundInput()
|
2012-05-22 13:09:48 -04:00
|
|
|
{
|
2013-07-29 20:51:42 -04:00
|
|
|
if (m_inStream)
|
|
|
|
{
|
|
|
|
Pa_CloseStream(m_inStream), m_inStream = 0;
|
|
|
|
}
|
2012-05-22 13:09:48 -04:00
|
|
|
}
|
|
|
|
|
2013-07-29 20:51:42 -04:00
|
|
|
void SoundInput::stop()
|
2012-05-22 13:09:48 -04:00
|
|
|
{
|
2013-07-29 20:51:42 -04:00
|
|
|
m_intervalTimer.stop();
|
|
|
|
if (m_inStream)
|
|
|
|
{
|
|
|
|
Pa_StopStream(m_inStream);
|
|
|
|
Pa_CloseStream(m_inStream), m_inStream = 0;
|
|
|
|
}
|
2012-05-22 13:09:48 -04:00
|
|
|
}
|
|
|
|
|
2013-07-30 16:36:14 -04:00
|
|
|
#else // QAUDIO_INPUT
|
|
|
|
|
|
|
|
#include "soundin.h"
|
2013-07-31 20:49:58 -04:00
|
|
|
|
|
|
|
#include <QDateTime>
|
2013-07-30 16:36:14 -04:00
|
|
|
|
|
|
|
#define FRAMES_PER_BUFFER 1024
|
|
|
|
#define NSMAX 6827
|
|
|
|
#define NTMAX 120
|
|
|
|
|
|
|
|
extern "C" {
|
|
|
|
#include <portaudio.h>
|
|
|
|
extern struct {
|
|
|
|
float ss[184*NSMAX]; //This is "common/jt9com/..." in fortran
|
|
|
|
float savg[NSMAX];
|
|
|
|
// float c0[2*NTMAX*1500];
|
|
|
|
short int d2[NTMAX*12000];
|
|
|
|
int nutc; //UTC as integer, HHMM
|
|
|
|
int ndiskdat; //1 ==> data read from *.wav file
|
|
|
|
int ntrperiod; //TR period (seconds)
|
|
|
|
int mousefqso; //User-selected QSO freq (kHz)
|
|
|
|
int newdat; //1 ==> new data, must do long FFT
|
|
|
|
int npts8; //npts in c0() array
|
|
|
|
int nfa; //Low decode limit (Hz)
|
|
|
|
int nfb; //High decode limit (Hz)
|
|
|
|
int ntol; //+/- decoding range around fQSO (Hz)
|
|
|
|
int kin;
|
|
|
|
int nzhsym;
|
|
|
|
int nsave;
|
|
|
|
int nagain;
|
|
|
|
int ndepth;
|
2013-07-31 11:46:28 -04:00
|
|
|
int ntxmode;
|
2013-07-30 16:36:14 -04:00
|
|
|
int nmode;
|
|
|
|
char datetime[20];
|
|
|
|
} jt9com_;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString reportAudioError(QAudio::Error audioError)
|
|
|
|
{
|
|
|
|
switch (audioError) {
|
|
|
|
case QAudio::NoError: Q_ASSERT(false);
|
|
|
|
case QAudio::OpenError: return QObject::tr(
|
|
|
|
"An error opening the audio device has occurred.");
|
|
|
|
case QAudio::IOError: return QObject::tr(
|
|
|
|
"An error occurred during read/write of audio device.");
|
|
|
|
case QAudio::UnderrunError: return QObject::tr(
|
|
|
|
"Audio data not being fed to the audio device fast enough.");
|
|
|
|
case QAudio::FatalError: return QObject::tr(
|
|
|
|
"Non-recoverable error, audio device not usable at this time.");
|
|
|
|
}
|
|
|
|
Q_ASSERT(false);
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
SoundInput::SoundInput()
|
|
|
|
: m_dataSinkBusy(false),
|
|
|
|
m_TRperiod(60),
|
|
|
|
m_nsps(6912),
|
|
|
|
m_monitoring(false),
|
|
|
|
m_intervalTimer(this)
|
|
|
|
{
|
2013-07-31 11:46:28 -04:00
|
|
|
// qDebug() << "A";
|
|
|
|
connect(&m_intervalTimer, SIGNAL(timeout()), this,SLOT(intervalNotify()));
|
2013-07-30 16:36:14 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void SoundInput::start(qint32 device)
|
|
|
|
{
|
|
|
|
stop();
|
|
|
|
|
|
|
|
//---------------------------------------------------- Soundcard Setup
|
|
|
|
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;
|
|
|
|
|
|
|
|
//### Temporary: hardwired device selection
|
|
|
|
QAudioDeviceInfo DeviceInfo;
|
|
|
|
QList<QAudioDeviceInfo> m_InDevices;
|
|
|
|
QAudioDeviceInfo m_InDeviceInfo;
|
|
|
|
m_InDevices = DeviceInfo.availableDevices(QAudio::AudioInput);
|
|
|
|
inputDevice = m_InDevices.at(0);
|
|
|
|
//###
|
2013-07-31 11:46:28 -04:00
|
|
|
// qDebug() << "B" << m_InDevices.length() << inputDevice.deviceName();
|
2013-07-30 16:36:14 -04:00
|
|
|
|
|
|
|
const char* pcmCodec = "audio/pcm";
|
|
|
|
QAudioFormat audioFormat = inputDevice.preferredFormat();
|
|
|
|
audioFormat.setChannelCount(1);
|
|
|
|
audioFormat.setCodec(pcmCodec);
|
|
|
|
audioFormat.setSampleRate(12000);
|
|
|
|
audioFormat.setSampleType(QAudioFormat::SignedInt);
|
|
|
|
audioFormat.setSampleSize(16);
|
|
|
|
|
2013-07-31 11:46:28 -04:00
|
|
|
// qDebug() << "C" << audioFormat << audioFormat.isValid();
|
|
|
|
|
2013-07-30 16:36:14 -04:00
|
|
|
if (!audioFormat.isValid()) {
|
|
|
|
emit error(tr("Requested audio format is not available."));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
audioInput = new QAudioInput(inputDevice, audioFormat);
|
2013-07-31 11:46:28 -04:00
|
|
|
// qDebug() << "D" << audioInput->error() << QAudio::NoError;
|
|
|
|
if (audioInput->error() != QAudio::NoError) {
|
2013-07-30 16:36:14 -04:00
|
|
|
emit error(reportAudioError(audioInput->error()));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
stream = audioInput->start();
|
2013-07-31 11:46:28 -04:00
|
|
|
// qDebug() << "E" << stream->errorString();
|
2013-07-30 16:36:14 -04:00
|
|
|
|
|
|
|
m_ntr0 = 99; // initial value higher than any expected
|
|
|
|
m_nBusy = 0;
|
|
|
|
m_intervalTimer.start(100);
|
|
|
|
m_ms0 = QDateTime::currentMSecsSinceEpoch();
|
|
|
|
m_nsps0 = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SoundInput::intervalNotify()
|
|
|
|
{
|
|
|
|
m_callbackData.monitoring=m_monitoring;
|
|
|
|
qint64 ms = QDateTime::currentMSecsSinceEpoch();
|
|
|
|
ms=ms % 86400000;
|
|
|
|
int nsec = ms/1000; // Time according to this computer
|
|
|
|
int ntr = nsec % m_TRperiod;
|
|
|
|
static int k=0;
|
|
|
|
|
2013-07-31 11:46:28 -04:00
|
|
|
// qDebug() << "a" << ms << nsec;
|
|
|
|
// Reset buffer pointer and symbol number at start of minute
|
2013-07-30 16:36:14 -04:00
|
|
|
if(ntr < m_ntr0 or !m_monitoring or m_nsps!=m_nsps0) {
|
|
|
|
m_nstep0=0;
|
|
|
|
m_nsps0=m_nsps;
|
|
|
|
m_callbackData.bzero=true;
|
|
|
|
k=0;
|
|
|
|
}
|
|
|
|
// int k=m_callbackData.kin;
|
|
|
|
|
2013-07-31 11:46:28 -04:00
|
|
|
// How many new samples are available?
|
2013-07-30 16:36:14 -04:00
|
|
|
const qint32 bytesReady = audioInput->bytesReady();
|
2013-07-31 11:46:28 -04:00
|
|
|
// qDebug() << "b" << bytesReady;
|
|
|
|
Q_ASSERT(bytesReady >= 0);
|
2013-07-30 16:36:14 -04:00
|
|
|
Q_ASSERT(bytesReady % 2 == 0);
|
|
|
|
if (bytesReady == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
qint32 bytesRead;
|
2013-07-31 11:46:28 -04:00
|
|
|
bytesRead = stream->read((char*)&jt9com_.d2[k], bytesReady); // Get the new samples
|
|
|
|
k += bytesRead/2;
|
|
|
|
// qDebug() << "c" << bytesReady << bytesRead;
|
|
|
|
Q_ASSERT(bytesRead <= bytesReady);
|
2013-07-30 16:36:14 -04:00
|
|
|
if (bytesRead < 0) {
|
|
|
|
emit error(tr("audio stream QIODevice::read returned -1."));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
Q_ASSERT(bytesRead % 2 == 0);
|
|
|
|
|
|
|
|
if(m_monitoring) {
|
|
|
|
int kstep=m_nsps/2;
|
|
|
|
m_step=(k-1)/kstep;
|
|
|
|
if(m_step != m_nstep0) {
|
|
|
|
if(m_dataSinkBusy) {
|
|
|
|
m_nBusy++;
|
|
|
|
} else {
|
|
|
|
emit readyForFFT(k-1); //Signal to compute new FFTs
|
|
|
|
}
|
|
|
|
m_nstep0=m_step;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_ntr0=ntr;
|
|
|
|
}
|
|
|
|
|
|
|
|
SoundInput::~SoundInput()
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
if (m_inStream)
|
|
|
|
{
|
|
|
|
Pa_CloseStream(m_inStream), m_inStream = 0;
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
}
|
2013-07-31 11:46:28 -04:00
|
|
|
/*
|
|
|
|
// memcpy(jt9com_.d2[k],buf0,bytesRead);
|
|
|
|
// k+=bytesRead/2;
|
|
|
|
for(int i=0; i<bytesRead/2; i++) {
|
|
|
|
jt9com_.d2[k++]=buf0[i];
|
|
|
|
}
|
|
|
|
*/
|
2013-07-30 16:36:14 -04:00
|
|
|
void SoundInput::stop()
|
|
|
|
{
|
|
|
|
m_intervalTimer.stop();
|
|
|
|
/*
|
|
|
|
if (m_inStream)
|
|
|
|
{
|
|
|
|
Pa_StopStream(m_inStream);
|
|
|
|
Pa_CloseStream(m_inStream), m_inStream = 0;
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // QAUDIO_INPUT
|