2018-10-14 02:25:51 +02:00
///////////////////////////////////////////////////////////////////////////////////
2023-11-18 10:07:41 +01:00
// Copyright (C) 2015-2020, 2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// Copyright (C) 2019 Vort <vvort@yandex.ru> //
// Copyright (C) 2021, 2023 Jon Beniston, M7RCE <jon@beniston.com> //
// Copyright (C) 2022 Jiří Pinkava <jiri.pinkava@rossum.ai> //
2018-10-14 02:25:51 +02:00
// //
// 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 //
2019-04-11 06:57:41 +02:00
// (at your option) any later version. //
2018-10-14 02:25:51 +02:00
// //
// 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 <string.h>
#include <errno.h>
#include <QDebug>
2018-12-26 11:06:37 +01:00
#include <QNetworkReply>
#include <QBuffer>
2018-10-14 02:25:51 +02:00
#include "SWGDeviceSettings.h"
2019-07-08 00:59:04 +02:00
#include "SWGFileInputSettings.h"
2018-10-14 02:25:51 +02:00
#include "SWGDeviceState.h"
#include "SWGDeviceReport.h"
#include "util/simpleserializer.h"
#include "dsp/dspcommands.h"
2019-05-08 22:11:53 +02:00
#include "dsp/dspdevicesourceengine.h"
2018-10-14 02:25:51 +02:00
#include "dsp/filerecord.h"
2021-05-21 10:06:10 +01:00
#include "dsp/wavfilerecord.h"
2019-05-08 22:11:53 +02:00
#include "device/deviceapi.h"
2018-10-14 02:25:51 +02:00
2019-07-08 00:59:04 +02:00
#include "fileinput.h"
2020-07-11 06:45:16 +02:00
#include "fileinputworker.h"
2019-07-08 00:59:04 +02:00
MESSAGE_CLASS_DEFINITION ( FileInput :: MsgConfigureFileInput , Message )
MESSAGE_CLASS_DEFINITION ( FileInput :: MsgConfigureFileSourceName , Message )
MESSAGE_CLASS_DEFINITION ( FileInput :: MsgConfigureFileInputWork , Message )
MESSAGE_CLASS_DEFINITION ( FileInput :: MsgConfigureFileSourceSeek , Message )
MESSAGE_CLASS_DEFINITION ( FileInput :: MsgConfigureFileInputStreamTiming , Message )
MESSAGE_CLASS_DEFINITION ( FileInput :: MsgStartStop , Message )
MESSAGE_CLASS_DEFINITION ( FileInput :: MsgPlayPause , Message )
MESSAGE_CLASS_DEFINITION ( FileInput :: MsgReportFileSourceAcquisition , Message )
MESSAGE_CLASS_DEFINITION ( FileInput :: MsgReportFileInputStreamData , Message )
MESSAGE_CLASS_DEFINITION ( FileInput :: MsgReportFileInputStreamTiming , Message )
MESSAGE_CLASS_DEFINITION ( FileInput :: MsgReportHeaderCRC , Message )
FileInput :: FileInput ( DeviceAPI * deviceAPI ) :
2018-10-14 02:25:51 +02:00
m_deviceAPI ( deviceAPI ),
m_settings (),
2020-07-11 06:45:16 +02:00
m_fileInputWorker ( nullptr ),
2022-03-17 21:10:30 +01:00
m_deviceDescription ( "FileInput" ),
2020-07-11 11:09:58 +02:00
m_sampleRate ( 48000 ),
2018-10-14 02:25:51 +02:00
m_sampleSize ( 0 ),
2020-07-11 11:09:58 +02:00
m_centerFrequency ( 435000000 ),
2024-01-04 16:14:00 +00:00
m_dataStartPos ( 0 ),
2020-08-26 00:12:30 +02:00
m_recordLengthMuSec ( 0 ),
2019-05-21 23:33:36 +02:00
m_startingTimeStamp ( 0 )
2018-10-14 02:25:51 +02:00
{
2022-03-17 21:10:30 +01:00
m_sampleFifo . setLabel ( m_deviceDescription );
2019-05-21 00:27:08 +02:00
m_deviceAPI -> setNbSourceStreams ( 1 );
2019-07-08 00:59:04 +02:00
qDebug ( "FileInput::FileInput: device source engine: %p" , m_deviceAPI -> getDeviceSourceEngine ());
qDebug ( "FileInput::FileInput: device source engine message queue: %p" , m_deviceAPI -> getDeviceEngineInputMessageQueue ());
qDebug ( "FileInput::FileInput: device source: %p" , m_deviceAPI -> getDeviceSourceEngine () -> getSource ());
2018-12-26 11:06:37 +01:00
m_networkManager = new QNetworkAccessManager ();
2022-03-23 22:32:23 +01:00
QObject :: connect (
m_networkManager ,
& QNetworkAccessManager :: finished ,
this ,
& FileInput :: networkManagerFinished
);
2019-05-21 23:33:36 +02:00
m_masterTimer . setTimerType ( Qt :: PreciseTimer );
m_masterTimer . start ( 50 );
2018-10-14 02:25:51 +02:00
}
2019-07-08 00:59:04 +02:00
FileInput ::~ FileInput ()
2018-10-14 02:25:51 +02:00
{
2019-05-21 23:33:36 +02:00
m_masterTimer . stop ();
2022-03-23 22:32:23 +01:00
QObject :: disconnect (
m_networkManager ,
& QNetworkAccessManager :: finished ,
this ,
& FileInput :: networkManagerFinished
);
2018-12-26 11:06:37 +01:00
delete m_networkManager ;
2018-10-14 02:25:51 +02:00
stop ();
}
2019-07-08 00:59:04 +02:00
void FileInput :: destroy ()
2018-10-14 02:25:51 +02:00
{
delete this ;
}
2019-07-08 00:59:04 +02:00
void FileInput :: openFileStream ()
2018-10-14 02:25:51 +02:00
{
//stopInput();
2023-01-02 15:42:34 +00:00
#ifdef ANDROID
if ( m_inputFile . isOpen ()) {
m_inputFile . close ();
}
m_inputFile . setFileName ( m_settings . m_fileName );
m_inputFile . open ( QIODevice :: ReadOnly | QIODevice :: ExistingOnly );
quint64 fileSize = ( quint64 ) m_inputFile . size ();
#else
if ( m_ifstream . is_open ()) {
2018-10-14 02:25:51 +02:00
m_ifstream . close ();
}
2019-05-27 08:26:38 +03:00
#ifdef Q_OS_WIN
2021-03-05 15:55:44 +00:00
m_ifstream . open ( m_settings . m_fileName . toStdWString (). c_str (), std :: ios :: binary | std :: ios :: ate );
2019-05-27 08:26:38 +03:00
#else
2021-03-05 15:55:44 +00:00
m_ifstream . open ( m_settings . m_fileName . toStdString (). c_str (), std :: ios :: binary | std :: ios :: ate );
2019-05-27 08:26:38 +03:00
#endif
2018-10-14 02:25:51 +02:00
quint64 fileSize = m_ifstream . tellg ();
2023-01-02 15:42:34 +00:00
#endif
2018-10-14 02:25:51 +02:00
2021-05-21 10:06:10 +01:00
if ( m_settings . m_fileName . endsWith ( ".wav" ))
{
WavFileRecord :: Header header ;
2023-01-02 15:42:34 +00:00
#ifdef ANDROID
m_inputFile . seek ( 0 );
bool headerOK = WavFileRecord :: readHeader ( m_inputFile , header );
#else
2021-05-21 10:06:10 +01:00
m_ifstream . seekg ( 0 , std :: ios_base :: beg );
bool headerOK = WavFileRecord :: readHeader ( m_ifstream , header );
2023-01-02 15:42:34 +00:00
#endif
2021-05-21 10:06:10 +01:00
m_sampleRate = header . m_sampleRate ;
if ( header . m_auxiHeader . m_size > 0 )
{
// Some WAV files written by SDR tools have auxi header
m_centerFrequency = header . m_auxi . m_centerFreq ;
2021-12-30 13:24:39 +00:00
m_startingTimeStamp = header . getStartTime (). toMSecsSinceEpoch ();
2021-05-21 10:06:10 +01:00
}
else
{
2021-05-21 21:05:23 +01:00
// Attempt to extract start time and frequency from filename
QDateTime startTime ;
if ( WavFileRecord :: getStartTime ( m_settings . m_fileName , startTime )) {
2021-12-30 13:24:39 +00:00
m_startingTimeStamp = startTime . toMSecsSinceEpoch ();
2021-05-21 10:06:10 +01:00
}
2021-05-21 21:05:23 +01:00
WavFileRecord :: getCenterFrequency ( m_settings . m_fileName , m_centerFrequency );
2021-05-21 10:06:10 +01:00
}
m_sampleSize = header . m_bitsPerSample ;
if ( headerOK && ( m_sampleRate > 0 ) && ( m_sampleSize > 0 ))
{
2023-01-02 15:42:34 +00:00
#ifdef ANDROID
2024-01-04 16:14:00 +00:00
m_dataStartPos = m_inputFile . pos ();
2023-01-02 15:42:34 +00:00
#else
2024-01-04 16:14:00 +00:00
m_dataStartPos = m_ifstream . tellg ();
2023-01-02 15:42:34 +00:00
#endif
2024-01-04 16:14:00 +00:00
m_recordLengthMuSec = (( fileSize - m_dataStartPos ) * 1000000UL ) / (( m_sampleSize == 24 ? 8 : 4 ) * m_sampleRate );
2021-05-21 10:06:10 +01:00
}
else
{
2022-09-28 15:26:27 +02:00
qCritical ( "FileInput::openFileStream: broken or unsupported format of .wav file" );
2021-05-21 10:06:10 +01:00
m_recordLengthMuSec = 0 ;
}
if ( getMessageQueueToGUI ())
{
MsgReportHeaderCRC * report = MsgReportHeaderCRC :: create ( headerOK );
getMessageQueueToGUI () -> push ( report );
}
}
else if ( fileSize > sizeof ( FileRecord :: Header ))
2018-10-14 02:25:51 +02:00
{
FileRecord :: Header header ;
2023-01-02 15:42:34 +00:00
#ifdef ANDROID
m_inputFile . seek ( 0 );
bool crcOK = FileRecord :: readHeader ( m_inputFile , header );
#else
2018-10-14 02:25:51 +02:00
m_ifstream . seekg ( 0 , std :: ios_base :: beg );
bool crcOK = FileRecord :: readHeader ( m_ifstream , header );
2023-01-02 15:42:34 +00:00
#endif
2024-01-04 16:14:00 +00:00
m_dataStartPos = sizeof ( FileRecord :: Header );
m_sampleRate = header . sampleRate ;
2018-10-14 02:25:51 +02:00
m_centerFrequency = header . centerFrequency ;
m_startingTimeStamp = header . startTimeStamp ;
m_sampleSize = header . sampleSize ;
QString crcHex = QString ( "%1" ). arg ( header . crc32 , 0 , 16 );
2021-03-08 21:41:46 +00:00
if ( crcOK && ( m_sampleRate > 0 ) && ( m_sampleSize > 0 ))
2018-10-14 02:25:51 +02:00
{
2019-07-08 00:59:04 +02:00
qDebug ( "FileInput::openFileStream: CRC32 OK for header: %s" , qPrintable ( crcHex ));
2024-01-04 16:14:00 +00:00
m_recordLengthMuSec = (( fileSize - m_dataStartPos ) * 1000000UL ) / (( m_sampleSize == 24 ? 8 : 4 ) * m_sampleRate );
2018-10-14 02:25:51 +02:00
}
2021-03-08 21:41:46 +00:00
else if ( ! crcOK )
2018-10-14 02:25:51 +02:00
{
2019-07-08 00:59:04 +02:00
qCritical ( "FileInput::openFileStream: bad CRC32 for header: %s" , qPrintable ( crcHex ));
2020-08-26 00:12:30 +02:00
m_recordLengthMuSec = 0 ;
2018-10-14 02:25:51 +02:00
}
2021-03-08 21:41:46 +00:00
else
{
qCritical ( "FileInput::openFileStream: invalid header" );
m_recordLengthMuSec = 0 ;
}
2018-10-14 02:25:51 +02:00
2020-07-11 06:56:32 +02:00
if ( getMessageQueueToGUI ())
{
2018-10-14 02:25:51 +02:00
MsgReportHeaderCRC * report = MsgReportHeaderCRC :: create ( crcOK );
getMessageQueueToGUI () -> push ( report );
}
}
else
{
2020-08-26 00:12:30 +02:00
m_recordLengthMuSec = 0 ;
2018-10-14 02:25:51 +02:00
}
2021-03-05 15:55:44 +00:00
qDebug () << "FileInput::openFileStream: " << m_settings . m_fileName . toStdString (). c_str ()
2018-10-14 02:25:51 +02:00
<< " fileSize: " << fileSize << " bytes"
2020-08-26 00:12:30 +02:00
<< " length: " << m_recordLengthMuSec << " microseconds"
2018-10-14 02:25:51 +02:00
<< " sample rate: " << m_sampleRate << " S/s"
<< " center frequency: " << m_centerFrequency << " Hz"
<< " sample size: " << m_sampleSize << " bits" ;
2020-07-11 06:56:32 +02:00
if ( getMessageQueueToGUI ())
{
2020-07-11 11:09:58 +02:00
DSPSignalNotification * notif = new DSPSignalNotification ( m_sampleRate , m_centerFrequency );
getMessageQueueToGUI () -> push ( notif );
2019-07-08 00:59:04 +02:00
MsgReportFileInputStreamData * report = MsgReportFileInputStreamData :: create ( m_sampleRate ,
2018-10-14 02:25:51 +02:00
m_sampleSize ,
m_centerFrequency ,
m_startingTimeStamp ,
2020-08-26 00:12:30 +02:00
m_recordLengthMuSec ); // file stream data
2018-10-14 02:25:51 +02:00
getMessageQueueToGUI () -> push ( report );
}
2020-08-26 00:12:30 +02:00
if ( m_recordLengthMuSec == 0 ) {
2023-01-02 15:42:34 +00:00
#ifdef ANDROID
m_inputFile . close ();
#else
2018-10-14 02:25:51 +02:00
m_ifstream . close ();
2023-01-02 15:42:34 +00:00
#endif
2018-10-14 02:25:51 +02:00
}
}
2019-07-08 00:59:04 +02:00
void FileInput :: seekFileStream ( int seekMillis )
2018-10-14 02:25:51 +02:00
{
QMutexLocker mutexLocker ( & m_mutex );
2023-01-02 15:42:34 +00:00
if (
#ifdef ANDROID
m_inputFile . isOpen ()
#else
m_ifstream . is_open ()
#endif
&& m_fileInputWorker && ! m_fileInputWorker -> isRunning ())
2018-10-14 02:25:51 +02:00
{
2020-08-26 00:12:30 +02:00
quint64 seekPoint = (( m_recordLengthMuSec * seekMillis ) / 1000 ) * m_sampleRate ;
seekPoint /= 1000000UL ;
2020-07-11 06:45:16 +02:00
m_fileInputWorker -> setSamplesCount ( seekPoint );
2024-01-04 16:14:00 +00:00
seekPoint *= ( m_sampleSize == 24 ? 8 : 4 );
2023-01-02 15:42:34 +00:00
#ifdef ANDROID
2024-01-04 16:14:00 +00:00
m_inputFile . seek ( seekPoint + m_dataStartPos );
2023-01-02 15:42:34 +00:00
#else
2018-10-14 02:25:51 +02:00
m_ifstream . clear ();
2024-01-04 16:14:00 +00:00
m_ifstream . seekg ( seekPoint + m_dataStartPos , std :: ios :: beg );
2023-01-02 15:42:34 +00:00
#endif
2018-10-14 02:25:51 +02:00
}
}
2019-07-08 00:59:04 +02:00
void FileInput :: init ()
2018-10-14 02:25:51 +02:00
{
2020-07-11 11:09:58 +02:00
DSPSignalNotification * notif = new DSPSignalNotification ( m_sampleRate , m_centerFrequency );
2018-10-14 02:25:51 +02:00
m_deviceAPI -> getDeviceEngineInputMessageQueue () -> push ( notif );
}
2019-07-08 00:59:04 +02:00
bool FileInput :: start ()
2018-10-14 02:25:51 +02:00
{
2023-01-02 15:42:34 +00:00
#ifdef ANDROID
if ( ! m_inputFile . isOpen ())
#else
2018-10-14 02:25:51 +02:00
if ( ! m_ifstream . is_open ())
2023-01-02 15:42:34 +00:00
#endif
2018-10-14 02:25:51 +02:00
{
2019-07-08 00:59:04 +02:00
qWarning ( "FileInput::start: file not open. not starting" );
2018-10-14 02:25:51 +02:00
return false ;
}
QMutexLocker mutexLocker ( & m_mutex );
2019-07-08 00:59:04 +02:00
qDebug () << "FileInput::start" ;
2018-10-14 02:25:51 +02:00
2026-03-05 16:52:12 +01:00
#ifdef ANDROID
m_inputFile . seek ( 0 );
#else
if ( m_ifstream . tellg () != ( std :: streampos ) 0 )
{
m_ifstream . clear ();
m_ifstream . seekg ( m_dataStartPos , std :: ios :: beg );
}
#endif
2018-10-14 02:25:51 +02:00
2020-07-11 06:56:32 +02:00
if ( ! m_sampleFifo . setSize ( m_settings . m_accelerationFactor * m_sampleRate * sizeof ( Sample )))
{
2018-10-14 02:25:51 +02:00
qCritical ( "Could not allocate SampleFifo" );
return false ;
}
2023-01-02 15:42:34 +00:00
#ifdef ANDROID
m_fileInputWorker = new FileInputWorker ( & m_inputFile , & m_sampleFifo , m_masterTimer , & m_inputMessageQueue );
#else
2020-07-11 06:45:16 +02:00
m_fileInputWorker = new FileInputWorker ( & m_ifstream , & m_sampleFifo , m_masterTimer , & m_inputMessageQueue );
2023-01-02 15:42:34 +00:00
#endif
2020-07-11 06:45:16 +02:00
m_fileInputWorker -> moveToThread ( & m_fileInputWorkerThread );
m_fileInputWorker -> setSampleRateAndSize ( m_settings . m_accelerationFactor * m_sampleRate , m_sampleSize ); // Fast Forward: 1 corresponds to live. 1/2 is half speed, 2 is double speed
startWorker ();
2019-07-08 00:59:04 +02:00
m_deviceDescription = "FileInput" ;
2018-10-14 02:25:51 +02:00
mutexLocker . unlock ();
2019-07-08 00:59:04 +02:00
qDebug ( "FileInput::startInput: started" );
2018-10-14 02:25:51 +02:00
2020-07-11 06:56:32 +02:00
if ( getMessageQueueToGUI ())
{
2018-10-14 02:25:51 +02:00
MsgReportFileSourceAcquisition * report = MsgReportFileSourceAcquisition :: create ( true ); // acquisition on
getMessageQueueToGUI () -> push ( report );
}
return true ;
}
2019-07-08 00:59:04 +02:00
void FileInput :: stop ()
2018-10-14 02:25:51 +02:00
{
2019-07-08 00:59:04 +02:00
qDebug () << "FileInput::stop" ;
2018-10-14 02:25:51 +02:00
QMutexLocker mutexLocker ( & m_mutex );
2020-07-11 06:45:16 +02:00
if ( m_fileInputWorker )
2018-10-14 02:25:51 +02:00
{
2020-07-11 06:45:16 +02:00
stopWorker ();
delete m_fileInputWorker ;
m_fileInputWorker = nullptr ;
2018-10-14 02:25:51 +02:00
}
m_deviceDescription . clear ();
2020-07-11 06:56:32 +02:00
if ( getMessageQueueToGUI ())
{
2018-10-14 02:25:51 +02:00
MsgReportFileSourceAcquisition * report = MsgReportFileSourceAcquisition :: create ( false ); // acquisition off
getMessageQueueToGUI () -> push ( report );
}
}
2020-07-11 06:45:16 +02:00
void FileInput :: startWorker ()
{
m_fileInputWorker -> startWork ();
m_fileInputWorkerThread . start ();
}
void FileInput :: stopWorker ()
{
m_fileInputWorker -> stopWork ();
m_fileInputWorkerThread . quit ();
m_fileInputWorkerThread . wait ();
}
2019-07-08 00:59:04 +02:00
QByteArray FileInput :: serialize () const
2018-10-14 02:25:51 +02:00
{
return m_settings . serialize ();
}
2019-07-08 00:59:04 +02:00
bool FileInput :: deserialize ( const QByteArray & data )
2018-10-14 02:25:51 +02:00
{
bool success = true ;
if ( ! m_settings . deserialize ( data ))
{
m_settings . resetToDefaults ();
success = false ;
}
2022-10-23 20:33:51 +02:00
MsgConfigureFileInput * message = MsgConfigureFileInput :: create ( m_settings , QList < QString > (), true );
2018-10-14 02:25:51 +02:00
m_inputMessageQueue . push ( message );
if ( getMessageQueueToGUI ())
{
2022-10-23 20:33:51 +02:00
MsgConfigureFileInput * messageToGUI = MsgConfigureFileInput :: create ( m_settings , QList < QString > (), true );
2018-10-14 02:25:51 +02:00
getMessageQueueToGUI () -> push ( messageToGUI );
}
return success ;
}
2019-07-08 00:59:04 +02:00
const QString & FileInput :: getDeviceDescription () const
2018-10-14 02:25:51 +02:00
{
return m_deviceDescription ;
}
2019-07-08 00:59:04 +02:00
int FileInput :: getSampleRate () const
2018-10-14 02:25:51 +02:00
{
return m_sampleRate ;
}
2019-07-08 00:59:04 +02:00
quint64 FileInput :: getCenterFrequency () const
2018-10-14 02:25:51 +02:00
{
return m_centerFrequency ;
}
2019-07-08 00:59:04 +02:00
void FileInput :: setCenterFrequency ( qint64 centerFrequency )
2018-10-14 02:25:51 +02:00
{
2019-07-08 00:59:04 +02:00
FileInputSettings settings = m_settings ;
2020-07-11 11:09:58 +02:00
m_centerFrequency = centerFrequency ;
2018-10-14 02:25:51 +02:00
2022-10-23 20:33:51 +02:00
MsgConfigureFileInput * message = MsgConfigureFileInput :: create ( m_settings , QList < QString > { "centerFrequency" }, false );
2018-10-14 02:25:51 +02:00
m_inputMessageQueue . push ( message );
if ( getMessageQueueToGUI ())
{
2022-10-23 20:33:51 +02:00
MsgConfigureFileInput * messageToGUI = MsgConfigureFileInput :: create ( m_settings , QList < QString > { "centerFrequency" }, false );
2018-10-14 02:25:51 +02:00
getMessageQueueToGUI () -> push ( messageToGUI );
}
}
2019-07-08 00:59:04 +02:00
quint64 FileInput :: getStartingTimeStamp () const
2018-10-14 02:25:51 +02:00
{
return m_startingTimeStamp ;
}
2019-07-08 00:59:04 +02:00
bool FileInput :: handleMessage ( const Message & message )
2018-10-14 02:25:51 +02:00
{
2019-07-08 00:59:04 +02:00
if ( MsgConfigureFileInput :: match ( message ))
2018-10-14 02:25:51 +02:00
{
2019-07-08 00:59:04 +02:00
MsgConfigureFileInput & conf = ( MsgConfigureFileInput & ) message ;
2022-10-23 20:33:51 +02:00
applySettings ( conf . getSettings (), conf . getSettingsKeys (), conf . getForce ());
2018-10-14 02:25:51 +02:00
return true ;
}
else if ( MsgConfigureFileSourceName :: match ( message ))
{
MsgConfigureFileSourceName & conf = ( MsgConfigureFileSourceName & ) message ;
2021-03-05 15:55:44 +00:00
m_settings . m_fileName = conf . getFileName ();
2018-10-14 02:25:51 +02:00
openFileStream ();
return true ;
}
2019-07-08 00:59:04 +02:00
else if ( MsgConfigureFileInputWork :: match ( message ))
2018-10-14 02:25:51 +02:00
{
2019-07-08 00:59:04 +02:00
MsgConfigureFileInputWork & conf = ( MsgConfigureFileInputWork & ) message ;
2018-10-14 02:25:51 +02:00
bool working = conf . isWorking ();
2020-07-11 06:56:32 +02:00
if ( m_fileInputWorker )
2018-10-14 02:25:51 +02:00
{
2018-10-14 10:38:31 +02:00
if ( working ) {
2020-07-11 06:45:16 +02:00
startWorker ();
2018-10-14 10:38:31 +02:00
} else {
2020-07-11 06:45:16 +02:00
stopWorker ();
2018-10-14 02:25:51 +02:00
}
}
return true ;
}
else if ( MsgConfigureFileSourceSeek :: match ( message ))
{
MsgConfigureFileSourceSeek & conf = ( MsgConfigureFileSourceSeek & ) message ;
2018-10-14 02:38:24 +02:00
int seekMillis = conf . getMillis ();
seekFileStream ( seekMillis );
2018-10-14 02:25:51 +02:00
return true ;
}
2019-07-08 00:59:04 +02:00
else if ( MsgConfigureFileInputStreamTiming :: match ( message ))
2018-10-14 02:25:51 +02:00
{
2019-07-08 00:59:04 +02:00
MsgReportFileInputStreamTiming * report ;
2018-10-14 02:25:51 +02:00
2020-07-11 06:56:32 +02:00
if ( m_fileInputWorker )
2018-10-14 02:25:51 +02:00
{
if ( getMessageQueueToGUI ())
{
2020-07-11 06:45:16 +02:00
report = MsgReportFileInputStreamTiming :: create ( m_fileInputWorker -> getSamplesCount ());
2018-10-14 02:25:51 +02:00
getMessageQueueToGUI () -> push ( report );
}
}
return true ;
}
else if ( MsgStartStop :: match ( message ))
{
MsgStartStop & cmd = ( MsgStartStop & ) message ;
2019-07-08 00:59:04 +02:00
qDebug () << "FileInput::handleMessage: MsgStartStop: " << ( cmd . getStartStop () ? "start" : "stop" );
2018-10-14 02:25:51 +02:00
if ( cmd . getStartStop ())
{
2020-07-11 06:56:32 +02:00
if ( m_deviceAPI -> initDeviceEngine ()) {
2019-05-08 22:11:53 +02:00
m_deviceAPI -> startDeviceEngine ();
2018-10-14 02:25:51 +02:00
}
}
else
{
2019-05-08 22:11:53 +02:00
m_deviceAPI -> stopDeviceEngine ();
2018-10-14 02:25:51 +02:00
}
2018-12-26 11:06:37 +01:00
if ( m_settings . m_useReverseAPI ) {
webapiReverseSendStartStop ( cmd . getStartStop ());
}
2018-10-14 02:25:51 +02:00
return true ;
}
2020-07-11 06:45:16 +02:00
else if ( FileInputWorker :: MsgReportEOF :: match ( message ))
2018-10-14 02:25:51 +02:00
{
2019-07-08 00:59:04 +02:00
qDebug () << "FileInput::handleMessage: MsgReportEOF" ;
2020-07-11 06:45:16 +02:00
stopWorker ();
2018-10-14 02:25:51 +02:00
if ( getMessageQueueToGUI ())
{
2020-07-11 06:45:16 +02:00
MsgReportFileInputStreamTiming * report = MsgReportFileInputStreamTiming :: create ( m_fileInputWorker -> getSamplesCount ());
2018-10-14 02:25:51 +02:00
getMessageQueueToGUI () -> push ( report );
}
if ( m_settings . m_loop )
{
seekFileStream ( 0 );
2020-07-11 06:45:16 +02:00
startWorker ();
2018-10-14 02:25:51 +02:00
}
else
{
if ( getMessageQueueToGUI ())
{
MsgPlayPause * report = MsgPlayPause :: create ( false );
getMessageQueueToGUI () -> push ( report );
}
}
return true ;
}
else
{
return false ;
}
}
2022-10-23 20:33:51 +02:00
bool FileInput :: applySettings ( const FileInputSettings & settings , const QList < QString >& settingsKeys , bool force )
2018-10-14 02:25:51 +02:00
{
2022-10-23 20:33:51 +02:00
qDebug () << "FileInput::applySettings: force: " << force << settings . getDebugString ( settingsKeys , force );
2018-12-26 11:06:37 +01:00
2022-10-23 20:33:51 +02:00
if ( settingsKeys . contains ( "accelerationFactor" ) || force )
2018-10-14 02:25:51 +02:00
{
2020-07-11 06:45:16 +02:00
if ( m_fileInputWorker )
2018-10-14 02:25:51 +02:00
{
QMutexLocker mutexLocker ( & m_mutex );
2018-10-14 02:58:14 +02:00
if ( ! m_sampleFifo . setSize ( m_settings . m_accelerationFactor * m_sampleRate * sizeof ( Sample ))) {
2019-07-08 00:59:04 +02:00
qCritical ( "FileInput::applySettings: could not reallocate sample FIFO size to %lu" ,
2018-10-14 02:58:14 +02:00
m_settings . m_accelerationFactor * m_sampleRate * sizeof ( Sample ));
}
2020-07-11 06:45:16 +02:00
m_fileInputWorker -> setSampleRateAndSize ( settings . m_accelerationFactor * m_sampleRate , m_sampleSize ); // Fast Forward: 1 corresponds to live. 1/2 is half speed, 2 is double speed
2018-10-14 02:25:51 +02:00
}
}
2024-04-14 18:58:12 +02:00
if ( settings . m_useReverseAPI )
2018-12-26 11:06:37 +01:00
{
2022-10-23 20:33:51 +02:00
bool fullUpdate = ( settingsKeys . contains ( "useReverseAPI" ) && settings . m_useReverseAPI ) ||
settingsKeys . contains ( "reverseAPIAddress" ) ||
settingsKeys . contains ( "reverseAPIPort" ) ||
settingsKeys . contains ( "reverseAPIDeviceIndex" );
webapiReverseSendSettings ( settingsKeys , settings , fullUpdate || force );
2018-12-26 11:06:37 +01:00
}
2022-10-23 20:33:51 +02:00
if ( force ) {
m_settings = settings ;
} else {
m_settings . applySettings ( settingsKeys , settings );
}
2021-03-05 15:55:44 +00:00
// Open the file if there isn't a GUI which will open it
2022-10-23 20:33:51 +02:00
if (( m_guiMessageQueue == nullptr ) && settingsKeys . contains ( "fileName" ) && ! m_settings . m_fileName . isEmpty ()) {
2021-03-05 15:55:44 +00:00
openFileStream ();
2022-10-23 20:33:51 +02:00
}
2021-03-05 15:55:44 +00:00
2018-10-14 02:25:51 +02:00
return true ;
}
2019-07-08 00:59:04 +02:00
int FileInput :: webapiSettingsGet (
2018-10-14 02:25:51 +02:00
SWGSDRangel :: SWGDeviceSettings & response ,
2018-11-13 08:51:14 +01:00
QString & errorMessage )
2018-10-14 02:25:51 +02:00
{
2018-11-13 08:51:14 +01:00
( void ) errorMessage ;
2019-07-08 00:59:04 +02:00
response . setFileInputSettings ( new SWGSDRangel :: SWGFileInputSettings ());
response . getFileInputSettings () -> init ();
2018-10-14 10:38:31 +02:00
webapiFormatDeviceSettings ( response , m_settings );
return 200 ;
}
2019-07-08 00:59:04 +02:00
int FileInput :: webapiSettingsPutPatch (
2018-10-14 10:38:31 +02:00
bool force ,
const QStringList & deviceSettingsKeys ,
SWGSDRangel :: SWGDeviceSettings & response , // query + response
2018-11-13 08:51:14 +01:00
QString & errorMessage )
2018-10-14 10:38:31 +02:00
{
2018-11-13 08:51:14 +01:00
( void ) errorMessage ;
2019-07-08 00:59:04 +02:00
FileInputSettings settings = m_settings ;
2019-08-04 20:24:44 +02:00
webapiUpdateDeviceSettings ( settings , deviceSettingsKeys , response );
2018-10-14 10:38:31 +02:00
2022-10-23 20:33:51 +02:00
MsgConfigureFileInput * msg = MsgConfigureFileInput :: create ( settings , deviceSettingsKeys , force );
2019-08-04 20:24:44 +02:00
m_inputMessageQueue . push ( msg );
if ( m_guiMessageQueue ) // forward to GUI if any
{
2022-10-23 20:33:51 +02:00
MsgConfigureFileInput * msgToGUI = MsgConfigureFileInput :: create ( settings , deviceSettingsKeys , force );
2019-08-04 20:24:44 +02:00
m_guiMessageQueue -> push ( msgToGUI );
}
webapiFormatDeviceSettings ( response , settings );
return 200 ;
}
void FileInput :: webapiUpdateDeviceSettings (
FileInputSettings & settings ,
const QStringList & deviceSettingsKeys ,
SWGSDRangel :: SWGDeviceSettings & response )
{
2026-01-28 02:48:42 +01:00
if ( deviceSettingsKeys . contains ( "title" )) {
settings . m_title = * response . getFileInputSettings () -> getTitle ();
}
2018-10-14 10:38:31 +02:00
if ( deviceSettingsKeys . contains ( "fileName" )) {
2019-07-08 00:59:04 +02:00
settings . m_fileName = * response . getFileInputSettings () -> getFileName ();
2018-10-14 10:38:31 +02:00
}
if ( deviceSettingsKeys . contains ( "accelerationFactor" )) {
2019-07-08 00:59:04 +02:00
settings . m_accelerationFactor = response . getFileInputSettings () -> getAccelerationFactor ();
2018-10-14 10:38:31 +02:00
}
if ( deviceSettingsKeys . contains ( "loop" )) {
2019-07-08 00:59:04 +02:00
settings . m_loop = response . getFileInputSettings () -> getLoop () != 0 ;
2018-10-14 10:38:31 +02:00
}
2019-01-11 14:45:00 +01:00
if ( deviceSettingsKeys . contains ( "useReverseAPI" )) {
2019-07-08 00:59:04 +02:00
settings . m_useReverseAPI = response . getFileInputSettings () -> getUseReverseApi () != 0 ;
2019-01-11 14:45:00 +01:00
}
if ( deviceSettingsKeys . contains ( "reverseAPIAddress" )) {
2019-07-08 00:59:04 +02:00
settings . m_reverseAPIAddress = * response . getFileInputSettings () -> getReverseApiAddress ();
2019-01-11 14:45:00 +01:00
}
if ( deviceSettingsKeys . contains ( "reverseAPIPort" )) {
2019-07-08 00:59:04 +02:00
settings . m_reverseAPIPort = response . getFileInputSettings () -> getReverseApiPort ();
2019-01-11 14:45:00 +01:00
}
if ( deviceSettingsKeys . contains ( "reverseAPIDeviceIndex" )) {
2019-07-08 00:59:04 +02:00
settings . m_reverseAPIDeviceIndex = response . getFileInputSettings () -> getReverseApiDeviceIndex ();
2019-01-11 14:45:00 +01:00
}
2018-10-14 02:25:51 +02:00
}
2019-07-08 00:59:04 +02:00
int FileInput :: webapiRunGet (
2018-10-14 02:25:51 +02:00
SWGSDRangel :: SWGDeviceState & response ,
2018-11-13 08:51:14 +01:00
QString & errorMessage )
2018-10-14 02:25:51 +02:00
{
2018-11-13 08:51:14 +01:00
( void ) errorMessage ;
2018-10-14 02:25:51 +02:00
m_deviceAPI -> getDeviceEngineStateStr ( * response . getState ());
return 200 ;
}
2019-07-08 00:59:04 +02:00
int FileInput :: webapiRun (
2018-10-14 02:25:51 +02:00
bool run ,
SWGSDRangel :: SWGDeviceState & response ,
2018-11-13 08:51:14 +01:00
QString & errorMessage )
2018-10-14 02:25:51 +02:00
{
2018-11-13 08:51:14 +01:00
( void ) errorMessage ;
2018-10-14 02:25:51 +02:00
m_deviceAPI -> getDeviceEngineStateStr ( * response . getState ());
MsgStartStop * message = MsgStartStop :: create ( run );
m_inputMessageQueue . push ( message );
if ( getMessageQueueToGUI ()) // forward to GUI if any
{
MsgStartStop * msgToGUI = MsgStartStop :: create ( run );
getMessageQueueToGUI () -> push ( msgToGUI );
}
return 200 ;
}
2019-07-08 00:59:04 +02:00
int FileInput :: webapiReportGet (
2018-10-14 02:25:51 +02:00
SWGSDRangel :: SWGDeviceReport & response ,
2018-11-13 08:51:14 +01:00
QString & errorMessage )
2018-10-14 02:25:51 +02:00
{
2018-11-13 08:51:14 +01:00
( void ) errorMessage ;
2019-07-08 00:59:04 +02:00
response . setFileInputReport ( new SWGSDRangel :: SWGFileInputReport ());
response . getFileInputReport () -> init ();
2018-10-14 02:25:51 +02:00
webapiFormatDeviceReport ( response );
return 200 ;
}
2019-07-08 00:59:04 +02:00
void FileInput :: webapiFormatDeviceSettings ( SWGSDRangel :: SWGDeviceSettings & response , const FileInputSettings & settings )
2018-10-14 10:38:31 +02:00
{
2026-01-28 02:48:42 +01:00
if ( response . getFileInputSettings () -> getTitle ()) {
* response . getFileInputSettings () -> getTitle () = settings . m_title ;
} else {
response . getFileInputSettings () -> setTitle ( new QString ( settings . m_title ));
}
2019-07-08 00:59:04 +02:00
response . getFileInputSettings () -> setFileName ( new QString ( settings . m_fileName ));
response . getFileInputSettings () -> setAccelerationFactor ( settings . m_accelerationFactor );
response . getFileInputSettings () -> setLoop ( settings . m_loop ? 1 : 0 );
2018-10-14 10:38:31 +02:00
2019-07-08 00:59:04 +02:00
response . getFileInputSettings () -> setUseReverseApi ( settings . m_useReverseAPI ? 1 : 0 );
2019-01-11 14:45:00 +01:00
2019-07-08 00:59:04 +02:00
if ( response . getFileInputSettings () -> getReverseApiAddress ()) {
* response . getFileInputSettings () -> getReverseApiAddress () = settings . m_reverseAPIAddress ;
2019-01-11 14:45:00 +01:00
} else {
2019-07-08 00:59:04 +02:00
response . getFileInputSettings () -> setReverseApiAddress ( new QString ( settings . m_reverseAPIAddress ));
2019-01-11 14:45:00 +01:00
}
2019-07-08 00:59:04 +02:00
response . getFileInputSettings () -> setReverseApiPort ( settings . m_reverseAPIPort );
response . getFileInputSettings () -> setReverseApiDeviceIndex ( settings . m_reverseAPIDeviceIndex );
2018-10-14 10:38:31 +02:00
}
2019-07-08 00:59:04 +02:00
void FileInput :: webapiFormatDeviceReport ( SWGSDRangel :: SWGDeviceReport & response )
2018-10-14 02:25:51 +02:00
{
qint64 t_sec = 0 ;
qint64 t_msec = 0 ;
quint64 samplesCount = 0 ;
2020-07-11 06:45:16 +02:00
if ( m_fileInputWorker ) {
samplesCount = m_fileInputWorker -> getSamplesCount ();
2018-10-14 02:25:51 +02:00
}
if ( m_sampleRate > 0 )
{
t_sec = samplesCount / m_sampleRate ;
t_msec = ( samplesCount - ( t_sec * m_sampleRate )) * 1000 / m_sampleRate ;
}
QTime t ( 0 , 0 , 0 , 0 );
t = t . addSecs ( t_sec );
t = t . addMSecs ( t_msec );
2019-07-08 00:59:04 +02:00
response . getFileInputReport () -> setElapsedTime ( new QString ( t . toString ( "HH:mm:ss.zzz" )));
2018-10-14 02:25:51 +02:00
2021-12-30 13:24:39 +00:00
qint64 startingTimeStampMsec = m_startingTimeStamp ;
2018-10-14 02:25:51 +02:00
QDateTime dt = QDateTime :: fromMSecsSinceEpoch ( startingTimeStampMsec );
dt = dt . addSecs ( t_sec );
dt = dt . addMSecs ( t_msec );
2019-07-08 00:59:04 +02:00
response . getFileInputReport () -> setAbsoluteTime ( new QString ( dt . toString ( "yyyy-MM-dd HH:mm:ss.zzz" )));
2018-10-14 02:25:51 +02:00
QTime recordLength ( 0 , 0 , 0 , 0 );
2020-08-26 00:12:30 +02:00
recordLength = recordLength . addMSecs ( m_recordLengthMuSec / 1000UL );
response . getFileInputReport () -> setDurationTime ( new QString ( recordLength . toString ( "HH:mm:ss.zzz" )));
2018-10-14 02:25:51 +02:00
2021-03-05 15:55:44 +00:00
response . getFileInputReport () -> setFileName ( new QString ( m_settings . m_fileName ));
2019-07-08 00:59:04 +02:00
response . getFileInputReport () -> setSampleRate ( m_sampleRate );
response . getFileInputReport () -> setSampleSize ( m_sampleSize );
2018-10-14 02:25:51 +02:00
}
2022-10-23 20:33:51 +02:00
void FileInput :: webapiReverseSendSettings ( const QList < QString >& deviceSettingsKeys , const FileInputSettings & settings , bool force )
2018-12-26 11:06:37 +01:00
{
SWGSDRangel :: SWGDeviceSettings * swgDeviceSettings = new SWGSDRangel :: SWGDeviceSettings ();
2019-05-07 14:43:38 +02:00
swgDeviceSettings -> setDirection ( 0 ); // single Rx
2019-03-25 13:41:38 +01:00
swgDeviceSettings -> setOriginatorIndex ( m_deviceAPI -> getDeviceSetIndex ());
2019-07-08 00:59:04 +02:00
swgDeviceSettings -> setDeviceHwType ( new QString ( "FileInput" ));
swgDeviceSettings -> setFileInputSettings ( new SWGSDRangel :: SWGFileInputSettings ());
SWGSDRangel :: SWGFileInputSettings * swgFileInputSettings = swgDeviceSettings -> getFileInputSettings ();
2018-12-26 11:06:37 +01:00
// transfer data that has been modified. When force is on transfer all data except reverse API data
if ( deviceSettingsKeys . contains ( "accelerationFactor" ) || force ) {
2019-07-08 00:59:04 +02:00
swgFileInputSettings -> setAccelerationFactor ( settings . m_accelerationFactor );
2018-12-26 11:06:37 +01:00
}
if ( deviceSettingsKeys . contains ( "loop" ) || force ) {
2019-07-08 00:59:04 +02:00
swgFileInputSettings -> setLoop ( settings . m_loop );
2018-12-26 11:06:37 +01:00
}
if ( deviceSettingsKeys . contains ( "fileName" ) || force ) {
2019-07-08 00:59:04 +02:00
swgFileInputSettings -> setFileName ( new QString ( settings . m_fileName ));
2018-12-26 11:06:37 +01:00
}
QString deviceSettingsURL = QString ( "http://%1:%2/sdrangel/deviceset/%3/device/settings" )
. arg ( settings . m_reverseAPIAddress )
. arg ( settings . m_reverseAPIPort )
. arg ( settings . m_reverseAPIDeviceIndex );
m_networkRequest . setUrl ( QUrl ( deviceSettingsURL ));
m_networkRequest . setHeader ( QNetworkRequest :: ContentTypeHeader , "application/json" );
2019-11-12 18:46:21 +01:00
QBuffer * buffer = new QBuffer ();
2018-12-26 11:06:37 +01:00
buffer -> open (( QBuffer :: ReadWrite ));
buffer -> write ( swgDeviceSettings -> asJson (). toUtf8 ());
buffer -> seek ( 0 );
// Always use PATCH to avoid passing reverse API settings
2019-11-12 18:46:21 +01:00
QNetworkReply * reply = m_networkManager -> sendCustomRequest ( m_networkRequest , "PATCH" , buffer );
buffer -> setParent ( reply );
2018-12-26 11:06:37 +01:00
delete swgDeviceSettings ;
}
2019-07-08 00:59:04 +02:00
void FileInput :: webapiReverseSendStartStop ( bool start )
2018-12-26 11:06:37 +01:00
{
2019-03-25 15:21:17 +01:00
SWGSDRangel :: SWGDeviceSettings * swgDeviceSettings = new SWGSDRangel :: SWGDeviceSettings ();
2019-05-07 14:43:38 +02:00
swgDeviceSettings -> setDirection ( 0 ); // single Rx
2019-03-25 15:21:17 +01:00
swgDeviceSettings -> setOriginatorIndex ( m_deviceAPI -> getDeviceSetIndex ());
2019-07-08 00:59:04 +02:00
swgDeviceSettings -> setDeviceHwType ( new QString ( "FileInput" ));
2019-03-25 15:21:17 +01:00
2018-12-26 11:06:37 +01:00
QString deviceSettingsURL = QString ( "http://%1:%2/sdrangel/deviceset/%3/device/run" )
. arg ( m_settings . m_reverseAPIAddress )
. arg ( m_settings . m_reverseAPIPort )
. arg ( m_settings . m_reverseAPIDeviceIndex );
m_networkRequest . setUrl ( QUrl ( deviceSettingsURL ));
2019-03-25 15:21:17 +01:00
m_networkRequest . setHeader ( QNetworkRequest :: ContentTypeHeader , "application/json" );
2019-11-12 18:46:21 +01:00
QBuffer * buffer = new QBuffer ();
2019-03-25 15:21:17 +01:00
buffer -> open (( QBuffer :: ReadWrite ));
buffer -> write ( swgDeviceSettings -> asJson (). toUtf8 ());
buffer -> seek ( 0 );
2019-11-12 18:46:21 +01:00
QNetworkReply * reply ;
2018-12-26 11:06:37 +01:00
if ( start ) {
2019-11-12 18:46:21 +01:00
reply = m_networkManager -> sendCustomRequest ( m_networkRequest , "POST" , buffer );
2018-12-26 11:06:37 +01:00
} else {
2019-11-12 18:46:21 +01:00
reply = m_networkManager -> sendCustomRequest ( m_networkRequest , "DELETE" , buffer );
2018-12-26 11:06:37 +01:00
}
2019-06-14 16:58:09 +02:00
2019-11-12 18:46:21 +01:00
buffer -> setParent ( reply );
2019-06-14 16:58:09 +02:00
delete swgDeviceSettings ;
2018-12-26 11:06:37 +01:00
}
2019-07-08 00:59:04 +02:00
void FileInput :: networkManagerFinished ( QNetworkReply * reply )
2018-12-26 11:06:37 +01:00
{
QNetworkReply :: NetworkError replyError = reply -> error ();
if ( replyError )
{
2019-07-08 00:59:04 +02:00
qWarning () << "FileInput::networkManagerFinished:"
2018-12-26 11:06:37 +01:00
<< " error(" << ( int ) replyError
<< "): " << replyError
<< ": " << reply -> errorString ();
2019-11-12 18:46:21 +01:00
}
else
{
QString answer = reply -> readAll ();
answer . chop ( 1 ); // remove last \n
qDebug ( "FileInput::networkManagerFinished: reply: \n %s" , answer . toStdString (). c_str ());
2018-12-26 11:06:37 +01:00
}
2018-10-14 02:25:51 +02:00
2019-11-12 18:46:21 +01:00
reply -> deleteLater ();
2018-12-26 11:06:37 +01:00
}