1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-15 12:51:49 -05:00

FCD Pro+ plugin: non ALSA classes to handle FCD audio for Windows build

This commit is contained in:
f4exb 2016-03-29 03:17:59 +02:00
parent bf9e5c6d15
commit 989feb9108
7 changed files with 616 additions and 4 deletions

View File

@ -23,6 +23,7 @@ struct fcd_traits
static const uint16_t productId = 0x0;
static const int sampleRate = 48000;
static const int convBufSize = (1<<11);
static const int fcdBufSize = (1<<12);
static const char *alsaDeviceName;
static const char *interfaceIID;
static const char *displayedName;
@ -37,6 +38,7 @@ struct fcd_traits<Pro>
static const uint16_t productId = 0xFB56;
static const int sampleRate = 96000;
static const int convBufSize = (1<<11);
static const int fcdBufSize = (1<<12);
static const char *alsaDeviceName;
static const char *interfaceIID;
static const char *displayedName;
@ -51,6 +53,7 @@ struct fcd_traits<ProPlus>
static const uint16_t productId = 0xFB31;
static const int sampleRate = 192000;
static const int convBufSize = (1<<12);
static const int fcdBufSize = (1<<18);
static const char *alsaDeviceName;
static const char *interfaceIID;
static const char *displayedName;

View File

@ -1,6 +1,5 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
// written by Christian Daniel //
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //

View File

@ -1,6 +1,5 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
// written by Christian Daniel //
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //

View File

@ -0,0 +1,318 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
// //
// This is a pure Qt (non ALSA) FCD sound card reader for Windows build //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QDebug>
#include <string.h>
#include <errno.h>
#include "dsp/dspcommands.h"
#include "dsp/dspengine.h"
#include "fcdproplusinputqt.h"
#include "fcdproplusgui.h"
#include "fcdproplusreader.h"
#include "fcdtraits.h"
#include "fcdproplusconst.h"
MESSAGE_CLASS_DEFINITION(FCDProPlusInput::MsgConfigureFCD, Message)
FCDProPlusInput::FCDProPlusInput() :
m_dev(0),
m_settings(),
m_deviceDescription(fcd_traits<ProPlus>::displayedName)
{
m_FCDReader = new FCDProPlusReader(&m_sampleFifo);
}
FCDProPlusInput::~FCDProPlusInput()
{
stop();
delete m_FCDReader;
}
bool FCDProPlusInput::init(const Message& cmd)
{
return false;
}
bool FCDProPlusInput::start(int device)
{
qDebug() << "FCDProPlusInput::start with device #" << device;
QMutexLocker mutexLocker(&m_mutex);
m_dev = fcdOpen(fcd_traits<ProPlus>::vendorId, fcd_traits<ProPlus>::productId, device);
if (m_dev == 0)
{
qCritical("FCDProPlusInput::start: could not open FCD");
return false;
}
/* Apply settings before streaming to avoid bus contention;
* there is very little spare bandwidth on a full speed USB device.
* Failure is harmless if no device is found
* ... This is rubbish...*/
//applySettings(m_settings, true);
if(!m_sampleFifo.setSize(96000*4))
{
qCritical("Could not allocate SampleFifo");
return false;
}
m_FCDReader->startWork();
mutexLocker.unlock();
applySettings(m_settings, true);
qDebug("FCDProPlusInput::started");
return true;
}
void FCDProPlusInput::stop()
{
QMutexLocker mutexLocker(&m_mutex);
m_FCDReader->stopWork();
fcdClose(m_dev);
m_dev = 0;
}
const QString& FCDProPlusInput::getDeviceDescription() const
{
return m_deviceDescription;
}
int FCDProPlusInput::getSampleRate() const
{
return fcd_traits<ProPlus>::sampleRate;
}
quint64 FCDProPlusInput::getCenterFrequency() const
{
return m_settings.m_centerFrequency;
}
bool FCDProPlusInput::handleMessage(const Message& message)
{
if(MsgConfigureFCD::match(message))
{
qDebug() << "FCDProPlusInput::handleMessage: MsgConfigureFCD";
MsgConfigureFCD& conf = (MsgConfigureFCD&) message;
applySettings(conf.getSettings(), false);
return true;
}
else
{
return false;
}
}
void FCDProPlusInput::applySettings(const FCDProPlusSettings& settings, bool force)
{
bool signalChange = false;
if ((m_settings.m_centerFrequency != settings.m_centerFrequency) || force)
{
qDebug() << "FCDProPlusInput::applySettings: fc: " << settings.m_centerFrequency;
m_settings.m_centerFrequency = settings.m_centerFrequency;
if (m_dev != 0)
{
set_center_freq((double) m_settings.m_centerFrequency);
}
signalChange = true;
}
if ((m_settings.m_lnaGain != settings.m_lnaGain) || force)
{
m_settings.m_lnaGain = settings.m_lnaGain;
if (m_dev != 0)
{
set_lna_gain(settings.m_lnaGain);
}
}
if ((m_settings.m_biasT != settings.m_biasT) || force)
{
m_settings.m_biasT = settings.m_biasT;
if (m_dev != 0)
{
set_bias_t(settings.m_biasT);
}
}
if ((m_settings.m_mixGain != settings.m_mixGain) || force)
{
m_settings.m_mixGain = settings.m_mixGain;
if (m_dev != 0)
{
set_mixer_gain(settings.m_mixGain);
}
}
if ((m_settings.m_ifGain != settings.m_ifGain) || force)
{
m_settings.m_ifGain = settings.m_ifGain;
if (m_dev != 0)
{
set_if_gain(settings.m_ifGain);
}
}
if ((m_settings.m_ifFilterIndex != settings.m_ifFilterIndex) || force)
{
m_settings.m_ifFilterIndex = settings.m_ifFilterIndex;
if (m_dev != 0)
{
set_if_filter(settings.m_ifFilterIndex);
}
}
if ((m_settings.m_rfFilterIndex != settings.m_rfFilterIndex) || force)
{
m_settings.m_rfFilterIndex = settings.m_rfFilterIndex;
if (m_dev != 0)
{
set_rf_filter(settings.m_rfFilterIndex);
}
}
if ((m_settings.m_LOppmTenths != settings.m_LOppmTenths) || force)
{
m_settings.m_LOppmTenths = settings.m_LOppmTenths;
if (m_dev != 0)
{
set_lo_ppm();
}
}
if ((m_settings.m_dcBlock != settings.m_dcBlock) || force)
{
m_settings.m_dcBlock = settings.m_dcBlock;
DSPEngine::instance()->configureCorrections(m_settings.m_dcBlock, m_settings.m_iqImbalance);
}
if ((m_settings.m_iqImbalance != settings.m_iqImbalance) || force)
{
m_settings.m_iqImbalance = settings.m_iqImbalance;
DSPEngine::instance()->configureCorrections(m_settings.m_dcBlock, m_settings.m_iqImbalance);
}
if (signalChange)
{
DSPSignalNotification *notif = new DSPSignalNotification(fcd_traits<ProPlus>::sampleRate, m_settings.m_centerFrequency);
DSPEngine::instance()->getInputMessageQueue()->push(notif);
}
}
void FCDProPlusInput::set_center_freq(double freq)
{
freq += freq*(((double) m_settings.m_LOppmTenths)/10000000.0);
if (fcdAppSetFreq(m_dev, freq) == FCD_MODE_NONE)
{
qDebug("No FCD HID found for frquency change");
}
}
void FCDProPlusInput::set_bias_t(bool on)
{
quint8 cmd = on ? 1 : 0;
fcdAppSetParam(m_dev, FCDPROPLUS_HID_CMD_SET_BIAS_TEE, &cmd, 1);
}
void FCDProPlusInput::set_lna_gain(bool on)
{
quint8 cmd = on ? 1 : 0;
fcdAppSetParam(m_dev, FCDPROPLUS_HID_CMD_SET_LNA_GAIN, &cmd, 1);
}
void FCDProPlusInput::set_mixer_gain(bool on)
{
quint8 cmd = on ? 1 : 0;
fcdAppSetParam(m_dev, FCDPROPLUS_HID_CMD_SET_MIXER_GAIN, &cmd, 1);
}
void FCDProPlusInput::set_if_gain(int gain)
{
if (gain < 0)
{
return;
}
quint8 cmd_value = gain;
if (fcdAppSetParam(m_dev, FCDPROPLUS_HID_CMD_SET_IF_GAIN, &cmd_value, 1) != FCD_MODE_APP)
{
qWarning() << "FCDProPlusInput::set_if_gain: failed to set at " << cmd_value;
}
}
void FCDProPlusInput::set_if_filter(int filterIndex)
{
if ((filterIndex < 0) || (filterIndex >= FCDProPlusConstants::fcdproplus_if_filter_nb_values()))
{
return;
}
quint8 cmd_value = FCDProPlusConstants::if_filters[filterIndex].value;
if (fcdAppSetParam(m_dev, FCDPROPLUS_HID_CMD_SET_IF_FILTER, &cmd_value, 1) != FCD_MODE_APP)
{
qWarning() << "FCDProPlusInput::set_if_filter: failed to set at " << cmd_value;
}
}
void FCDProPlusInput::set_rf_filter(int filterIndex)
{
if ((filterIndex < 0) || (filterIndex >= FCDProPlusConstants::fcdproplus_rf_filter_nb_values()))
{
return;
}
quint8 cmd_value = FCDProPlusConstants::rf_filters[filterIndex].value;
if (fcdAppSetParam(m_dev, FCDPROPLUS_HID_CMD_SET_RF_FILTER, &cmd_value, 1) != FCD_MODE_APP)
{
qWarning() << "FCDProPlusInput::set_rf_filter: failed to set at " << cmd_value;
}
}
void FCDProPlusInput::set_lo_ppm()
{
set_center_freq((double) m_settings.m_centerFrequency);
}

View File

@ -0,0 +1,89 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
// //
// This is a pure Qt (non ALSA) FCD sound card reader for Windows build //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_FCDINPUT_H
#define INCLUDE_FCDINPUT_H
#include "fcdproplussettings.h"
#include "dsp/samplesource.h"
#include "fcdhid.h"
#include <QString>
#include <inttypes.h>
struct fcd_buffer {
void *start;
std::size_t length;
};
class FCDProPlusReader;
class FCDProPlusInput : public SampleSource {
public:
class MsgConfigureFCD : public Message {
MESSAGE_CLASS_DECLARATION
public:
const FCDProPlusSettings& getSettings() const { return m_settings; }
static MsgConfigureFCD* create(const FCDProPlusSettings& settings)
{
return new MsgConfigureFCD(settings);
}
private:
FCDProPlusSettings m_settings;
MsgConfigureFCD(const FCDProPlusSettings& settings) :
Message(),
m_settings(settings)
{ }
};
FCDProPlusInput();
virtual ~FCDProPlusInput();
virtual bool init(const Message& cmd);
virtual bool start(int device);
virtual void stop();
virtual const QString& getDeviceDescription() const;
virtual int getSampleRate() const;
virtual quint64 getCenterFrequency() const;
virtual bool handleMessage(const Message& message);
void set_center_freq(double freq);
void set_bias_t(bool on);
void set_lna_gain(bool on);
void set_mixer_gain(bool on);
void set_if_gain(int gain);
void set_rf_filter(int filterIndex);
void set_if_filter(int filterIndex);
void set_lo_ppm();
private:
void applySettings(const FCDProPlusSettings& settings, bool force);
hid_device *m_dev;
QMutex m_mutex;
FCDProPlusSettings m_settings;
FCDProPlusReader* m_FCDReader;
QString m_deviceDescription;
};
#endif // INCLUDE_FCD_H

View File

@ -0,0 +1,145 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QDebug>
#include <QAudio>
#include <QAudioDeviceInfo>
#include <QAudioInput>
#include <QAudioFormat>
#include <QIODevice>
#include <QList>
#include <stdio.h>
#include <errno.h>
#include "dsp/samplefifo.h"
#include "fcdproplusreader.h"
#include "fcdtraits.h"
FCDProPlusReader::FCDProPlusReader(SampleFifo* sampleFifo, QObject* parent) :
QObject(parent),
m_fcdAudioInput(0),
m_fcdInput(0),
m_running(false),
m_convertBuffer(fcd_traits<ProPlus>::convBufSize),
m_fcdBuffer(fcd_traits<ProPlus>::fcdBufSize, 0),
m_sampleFifo(sampleFifo)
{
}
FCDProPlusReader::~FCDProPlusReader()
{
if (m_fcdAudioInput) {
delete m_fcdAudioInput;
}
}
void FCDProPlusReader::startWork()
{
QList<QAudioDeviceInfo> audioDevices = QAudioDeviceInfo::availableDevices(QAudio::AudioInput);
QList<QAudioDeviceInfo>::iterator audioDeviceIt, fcdDeviceIt = audioDevices.end();
for (audioDeviceIt = audioDevices.begin(); audioDeviceIt != audioDevices.end(); ++audioDeviceIt)
{
QAudioFormat fcdAudioFormat = audioDeviceIt->preferredFormat();
int sampleRate = fcdAudioFormat.sampleRate();
int sampleBits = fcdAudioFormat.sampleSize();
if ((sampleRate == 192000) && (sampleBits == 16))
{
qDebug() << "FCDProPlusReader::startWork: found: " << audioDeviceIt->deviceName()
<< " sampleRate: " << fcdAudioFormat.sampleRate()
<< " sampleBits: " << fcdAudioFormat.sampleSize();
fcdDeviceIt = audioDeviceIt;
break;
}
}
if (fcdDeviceIt == audioDevices.end())
{
qCritical() << "FCDProPlusReader::startWork: FCD Pro+ sound card not found";
return;
}
openFcdAudio(*fcdDeviceIt);
if (!m_fcdAudioInput)
{
qCritical() << "FCDProPlusReader::startWork: cannot open FCD Pro+ sound card";
return;
}
m_fcdAudioInput->stop();
m_fcdInput = m_fcdAudioInput->start();
if (!m_fcdInput)
{
qCritical() << "FCDProPlusReader::startWork: cannot start FCD Pro+ sound card";
return;
}
connect(m_fcdInput, SIGNAL(readyRead()), this, SLOT(readFcdAudio()));
m_running = true;
qDebug() << "FCDProPlusReader::startWork: started";
}
void FCDProPlusReader::stopWork()
{
m_running = false;
disconnect(m_fcdInput, SIGNAL(readyRead()), this, SLOT(readFcdAudio()));
m_fcdAudioInput->stop();
qDebug() << "FCDProPlusReader::stopWork: stopped";
}
void FCDProPlusReader::openFcdAudio(const QAudioDeviceInfo& fcdAudioDeviceInfo)
{
QAudioFormat fcdAudioFormat = fcdAudioDeviceInfo.preferredFormat();
qDebug() << "FCDProPlusReader::openFcdAudio: device: " << fcdAudioDeviceInfo.deviceName()
<< " sampleRate: " << fcdAudioFormat.sampleRate()
<< " sampleBits: " << fcdAudioFormat.sampleSize();
m_fcdAudioInput = new QAudioInput(fcdAudioDeviceInfo, fcdAudioFormat, this);
//m_fcdAudioInput->setBufferSize(1<<14);
//m_fcdAudioInput->setNotifyInterval(50);
}
void FCDProPlusReader::readFcdAudio()
{
if (!m_fcdAudioInput) {
return;
}
int len = m_fcdAudioInput->bytesReady();
// qDebug() << "FCDProPlusReader::readFcdAudio:"
// << " buffer size: " << m_fcdAudioInput->bufferSize()
// << " interval: " << m_fcdAudioInput->notifyInterval()
// << " len: " << len;
if (len > fcd_traits<ProPlus>::fcdBufSize) {
len = fcd_traits<ProPlus>::fcdBufSize;
}
int readLen = m_fcdInput->read(m_fcdBuffer.data(), len);
if (readLen > 0) {
m_sampleFifo->write((const quint8*) m_fcdBuffer.constData(), (uint) readLen);
}
}

View File

@ -0,0 +1,59 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_FCDPROPLUSREADER_H
#define INCLUDE_FCDPROPLUSREADER_H
#include <QObject>
#include <QMutex>
#include <QWaitCondition>
#include <QByteArray>
#include "dsp/samplefifo.h"
#include "dsp/inthalfbandfilter.h"
class QAudioInput;
class QIODevice;
class QAudioDeviceInfo;
class FCDProPlusReader : public QObject {
Q_OBJECT
public:
FCDProPlusReader(SampleFifo* sampleFifo, QObject* parent = NULL);
~FCDProPlusReader();
void startWork();
void stopWork();
private:
QAudioInput *m_fcdAudioInput;
QIODevice *m_fcdInput;
QMutex m_startWaitMutex;
QWaitCondition m_startWaiter;
bool m_running;
SampleVector m_convertBuffer;
QByteArray m_fcdBuffer;
SampleFifo* m_sampleFifo;
void openFcdAudio(const QAudioDeviceInfo& fcdAudioDeviceInfo);
private slots:
void readFcdAudio();
};
#endif // INCLUDE_FCDPROPLUSREADER_H