mirror of
				https://github.com/f4exb/sdrangel.git
				synced 2025-11-03 13:11:20 -05:00 
			
		
		
		
	DemodAnalyzer: implemented record audio. Part of #1330
This commit is contained in:
		
							parent
							
								
									93a238503c
								
							
						
					
					
						commit
						2c02a9bcf1
					
				
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 15 KiB  | 
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 92 KiB  | 
										
											Binary file not shown.
										
									
								
							@ -220,6 +220,10 @@ bool DemodAnalyzer::handleMessage(const Message& cmd)
 | 
			
		||||
            m_sampleRate = report.getSampleRate();
 | 
			
		||||
            m_scopeVis.setLiveRate(m_sampleRate);
 | 
			
		||||
 | 
			
		||||
            if (m_running) {
 | 
			
		||||
                m_worker->applySampleRate(m_sampleRate);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            DSPSignalNotification *msg = new DSPSignalNotification(0, m_sampleRate);
 | 
			
		||||
            m_spectrumVis.getInputMessageQueue()->push(msg);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -16,6 +16,7 @@
 | 
			
		||||
///////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
#include <QMessageBox>
 | 
			
		||||
#include <QFileDialog>
 | 
			
		||||
 | 
			
		||||
#include "feature/featureuiset.h"
 | 
			
		||||
#include "dsp/spectrumvis.h"
 | 
			
		||||
@ -206,6 +207,11 @@ void DemodAnalyzerGUI::displaySettings()
 | 
			
		||||
    setTitle(m_settings.m_title);
 | 
			
		||||
    blockApplySettings(true);
 | 
			
		||||
    ui->log2Decim->setCurrentIndex(m_settings.m_log2Decim);
 | 
			
		||||
    ui->record->setChecked(m_settings.m_recordToFile);
 | 
			
		||||
    ui->fileNameText->setText(m_settings.m_fileRecordName);
 | 
			
		||||
    ui->showFileDialog->setEnabled(!m_settings.m_recordToFile);
 | 
			
		||||
    ui->recordSilenceTime->setValue(m_settings.m_recordSilenceTime);
 | 
			
		||||
    ui->recordSilenceText->setText(tr("%1").arg(m_settings.m_recordSilenceTime / 10.0, 0, 'f', 1));
 | 
			
		||||
    getRollupContents()->restoreState(m_rollupState);
 | 
			
		||||
    blockApplySettings(false);
 | 
			
		||||
}
 | 
			
		||||
@ -327,6 +333,47 @@ void DemodAnalyzerGUI::on_log2Decim_currentIndexChanged(int index)
 | 
			
		||||
    applySettings();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DemodAnalyzerGUI::on_record_toggled(bool checked)
 | 
			
		||||
{
 | 
			
		||||
    ui->showFileDialog->setEnabled(!checked);
 | 
			
		||||
    m_settings.m_recordToFile = checked;
 | 
			
		||||
    applySettings();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DemodAnalyzerGUI::on_showFileDialog_clicked(bool checked)
 | 
			
		||||
{
 | 
			
		||||
    (void) checked;
 | 
			
		||||
    QFileDialog fileDialog(
 | 
			
		||||
        this,
 | 
			
		||||
        tr("Save record file"),
 | 
			
		||||
        m_settings.m_fileRecordName,
 | 
			
		||||
        tr("WAV Files (*.wav)")
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    fileDialog.setOptions(QFileDialog::DontUseNativeDialog);
 | 
			
		||||
    fileDialog.setFileMode(QFileDialog::AnyFile);
 | 
			
		||||
    QStringList fileNames;
 | 
			
		||||
 | 
			
		||||
    if (fileDialog.exec())
 | 
			
		||||
    {
 | 
			
		||||
        fileNames = fileDialog.selectedFiles();
 | 
			
		||||
 | 
			
		||||
        if (fileNames.size() > 0)
 | 
			
		||||
        {
 | 
			
		||||
            m_settings.m_fileRecordName = fileNames.at(0);
 | 
			
		||||
		    ui->fileNameText->setText(m_settings.m_fileRecordName);
 | 
			
		||||
            applySettings();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DemodAnalyzerGUI::on_recordSilenceTime_valueChanged(int value)
 | 
			
		||||
{
 | 
			
		||||
    m_settings.m_recordSilenceTime = value;
 | 
			
		||||
    ui->recordSilenceText->setText(tr("%1").arg(value / 10.0, 0, 'f', 1));
 | 
			
		||||
    applySettings();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DemodAnalyzerGUI::tick()
 | 
			
		||||
{
 | 
			
		||||
	m_channelPowerAvg(m_demodAnalyzer->getMagSqAvg());
 | 
			
		||||
@ -379,4 +426,7 @@ void DemodAnalyzerGUI::makeUIConnections()
 | 
			
		||||
	QObject::connect(ui->channels, qOverload<int>(&QComboBox::currentIndexChanged), this, &DemodAnalyzerGUI::on_channels_currentIndexChanged);
 | 
			
		||||
	QObject::connect(ui->channelApply, &QPushButton::clicked, this, &DemodAnalyzerGUI::on_channelApply_clicked);
 | 
			
		||||
	QObject::connect(ui->log2Decim, qOverload<int>(&QComboBox::currentIndexChanged), this, &DemodAnalyzerGUI::on_log2Decim_currentIndexChanged);
 | 
			
		||||
    QObject::connect(ui->record, &ButtonSwitch::toggled, this, &DemodAnalyzerGUI::on_record_toggled);
 | 
			
		||||
    QObject::connect(ui->showFileDialog, &QPushButton::clicked, this, &DemodAnalyzerGUI::on_showFileDialog_clicked);
 | 
			
		||||
    QObject::connect(ui->recordSilenceTime, &QSlider::valueChanged, this, &DemodAnalyzerGUI::on_recordSilenceTime_valueChanged);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -93,6 +93,9 @@ private slots:
 | 
			
		||||
	void on_channels_currentIndexChanged(int index);
 | 
			
		||||
	void on_channelApply_clicked();
 | 
			
		||||
	void on_log2Decim_currentIndexChanged(int index);
 | 
			
		||||
	void on_record_toggled(bool checked);
 | 
			
		||||
    void on_showFileDialog_clicked(bool checked);
 | 
			
		||||
    void on_recordSilenceTime_valueChanged(int value);
 | 
			
		||||
	void updateStatus();
 | 
			
		||||
	void tick();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -7,7 +7,7 @@
 | 
			
		||||
    <x>0</x>
 | 
			
		||||
    <y>0</y>
 | 
			
		||||
    <width>739</width>
 | 
			
		||||
    <height>778</height>
 | 
			
		||||
    <height>785</height>
 | 
			
		||||
   </rect>
 | 
			
		||||
  </property>
 | 
			
		||||
  <property name="sizePolicy">
 | 
			
		||||
@ -37,7 +37,7 @@
 | 
			
		||||
     <x>0</x>
 | 
			
		||||
     <y>10</y>
 | 
			
		||||
     <width>718</width>
 | 
			
		||||
     <height>41</height>
 | 
			
		||||
     <height>70</height>
 | 
			
		||||
    </rect>
 | 
			
		||||
   </property>
 | 
			
		||||
   <property name="minimumSize">
 | 
			
		||||
@ -72,6 +72,12 @@
 | 
			
		||||
      </property>
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="ButtonSwitch" name="startStop">
 | 
			
		||||
        <property name="minimumSize">
 | 
			
		||||
         <size>
 | 
			
		||||
          <width>0</width>
 | 
			
		||||
          <height>22</height>
 | 
			
		||||
         </size>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="toolTip">
 | 
			
		||||
         <string>start/stop acquisition</string>
 | 
			
		||||
        </property>
 | 
			
		||||
@ -195,12 +201,6 @@
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="QLabel" name="sinkSampleRateText">
 | 
			
		||||
        <property name="minimumSize">
 | 
			
		||||
         <size>
 | 
			
		||||
          <width>80</width>
 | 
			
		||||
          <height>0</height>
 | 
			
		||||
         </size>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="toolTip">
 | 
			
		||||
         <string>Analyzer (sink) sample rate</string>
 | 
			
		||||
        </property>
 | 
			
		||||
@ -249,6 +249,119 @@
 | 
			
		||||
      </item>
 | 
			
		||||
     </layout>
 | 
			
		||||
    </item>
 | 
			
		||||
    <item>
 | 
			
		||||
     <layout class="QHBoxLayout" name="fileNameLayout">
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="ButtonSwitch" name="record">
 | 
			
		||||
        <property name="maximumSize">
 | 
			
		||||
         <size>
 | 
			
		||||
          <width>24</width>
 | 
			
		||||
          <height>16777215</height>
 | 
			
		||||
         </size>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="toolTip">
 | 
			
		||||
         <string>Start/stop recording</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string/>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="icon">
 | 
			
		||||
         <iconset resource="../../../sdrgui/resources/res.qrc">
 | 
			
		||||
          <normaloff>:/record_off.png</normaloff>:/record_off.png</iconset>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="QPushButton" name="showFileDialog">
 | 
			
		||||
        <property name="minimumSize">
 | 
			
		||||
         <size>
 | 
			
		||||
          <width>24</width>
 | 
			
		||||
          <height>24</height>
 | 
			
		||||
         </size>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="maximumSize">
 | 
			
		||||
         <size>
 | 
			
		||||
          <width>24</width>
 | 
			
		||||
          <height>24</height>
 | 
			
		||||
         </size>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="toolTip">
 | 
			
		||||
         <string>Open file</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string/>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="icon">
 | 
			
		||||
         <iconset resource="../../../sdrgui/resources/res.qrc">
 | 
			
		||||
          <normaloff>:/preset-load.png</normaloff>:/preset-load.png</iconset>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="QLabel" name="fileNameText">
 | 
			
		||||
        <property name="enabled">
 | 
			
		||||
         <bool>true</bool>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="toolTip">
 | 
			
		||||
         <string>Current recording file</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string>...</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="alignment">
 | 
			
		||||
         <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="QDial" name="recordSilenceTime">
 | 
			
		||||
        <property name="maximumSize">
 | 
			
		||||
         <size>
 | 
			
		||||
          <width>24</width>
 | 
			
		||||
          <height>24</height>
 | 
			
		||||
         </size>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="toolTip">
 | 
			
		||||
         <string>Silence time (s) before recording is stopoed. 0 for continuous recording.</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="minimum">
 | 
			
		||||
         <number>0</number>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="maximum">
 | 
			
		||||
         <number>100</number>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="singleStep">
 | 
			
		||||
         <number>1</number>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="pageStep">
 | 
			
		||||
         <number>1</number>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="value">
 | 
			
		||||
         <number>0</number>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="QLabel" name="recordSilenceText">
 | 
			
		||||
        <property name="maximumSize">
 | 
			
		||||
         <size>
 | 
			
		||||
          <width>30</width>
 | 
			
		||||
          <height>16777215</height>
 | 
			
		||||
         </size>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="toolTip">
 | 
			
		||||
         <string>Silence time (s) before recording is stopoed</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string>10.0</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="alignment">
 | 
			
		||||
         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
     </layout>
 | 
			
		||||
    </item>
 | 
			
		||||
   </layout>
 | 
			
		||||
  </widget>
 | 
			
		||||
  <widget class="QWidget" name="spectrumContainer" native="true">
 | 
			
		||||
 | 
			
		||||
@ -83,6 +83,9 @@ void DemodAnalyzerSettings::resetToDefaults()
 | 
			
		||||
    m_reverseAPIFeatureSetIndex = 0;
 | 
			
		||||
    m_reverseAPIFeatureIndex = 0;
 | 
			
		||||
    m_workspaceIndex = 0;
 | 
			
		||||
    m_fileRecordName.clear();
 | 
			
		||||
    m_recordToFile = false;
 | 
			
		||||
    m_recordSilenceTime = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QByteArray DemodAnalyzerSettings::serialize() const
 | 
			
		||||
@ -112,6 +115,9 @@ QByteArray DemodAnalyzerSettings::serialize() const
 | 
			
		||||
 | 
			
		||||
    s.writeS32(13, m_workspaceIndex);
 | 
			
		||||
    s.writeBlob(14, m_geometryBytes);
 | 
			
		||||
    s.writeString(15, m_fileRecordName);
 | 
			
		||||
    s.writeBool(16, m_recordToFile);
 | 
			
		||||
    s.writeS32(17, m_recordSilenceTime);
 | 
			
		||||
 | 
			
		||||
    return s.final();
 | 
			
		||||
}
 | 
			
		||||
@ -170,6 +176,9 @@ bool DemodAnalyzerSettings::deserialize(const QByteArray& data)
 | 
			
		||||
 | 
			
		||||
        d.readS32(13, &m_workspaceIndex, 0);
 | 
			
		||||
        d.readBlob(14, &m_geometryBytes);
 | 
			
		||||
        d.readString(15, &m_fileRecordName);
 | 
			
		||||
        d.readBool(16, &m_recordToFile, false);
 | 
			
		||||
        d.readS32(17, &m_recordSilenceTime, 0);
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -49,6 +49,9 @@ struct DemodAnalyzerSettings
 | 
			
		||||
    uint16_t m_reverseAPIPort;
 | 
			
		||||
    uint16_t m_reverseAPIFeatureSetIndex;
 | 
			
		||||
    uint16_t m_reverseAPIFeatureIndex;
 | 
			
		||||
    QString m_fileRecordName;
 | 
			
		||||
    bool m_recordToFile;
 | 
			
		||||
    int m_recordSilenceTime; //!< 100's ms
 | 
			
		||||
    Serializable *m_spectrumGUI;
 | 
			
		||||
    Serializable *m_scopeGUI;
 | 
			
		||||
    Serializable *m_rollupState;
 | 
			
		||||
 | 
			
		||||
@ -19,6 +19,7 @@
 | 
			
		||||
 | 
			
		||||
#include "dsp/scopevis.h"
 | 
			
		||||
#include "dsp/datafifo.h"
 | 
			
		||||
#include "dsp/wavfilerecord.h"
 | 
			
		||||
 | 
			
		||||
#include "demodanalyzerworker.h"
 | 
			
		||||
 | 
			
		||||
@ -28,7 +29,11 @@ MESSAGE_CLASS_DEFINITION(DemodAnalyzerWorker::MsgConnectFifo, Message)
 | 
			
		||||
DemodAnalyzerWorker::DemodAnalyzerWorker() :
 | 
			
		||||
    m_dataFifo(nullptr),
 | 
			
		||||
    m_msgQueueToFeature(nullptr),
 | 
			
		||||
    m_sampleBufferSize(0)
 | 
			
		||||
    m_sampleBufferSize(0),
 | 
			
		||||
    m_wavFileRecord(nullptr),
 | 
			
		||||
    m_recordSilenceNbSamples(0),
 | 
			
		||||
    m_recordSilenceCount(0),
 | 
			
		||||
    m_nbBytes(0)
 | 
			
		||||
{
 | 
			
		||||
    qDebug("DemodAnalyzerWorker::DemodAnalyzerWorker");
 | 
			
		||||
}
 | 
			
		||||
@ -47,12 +52,15 @@ void DemodAnalyzerWorker::reset()
 | 
			
		||||
void DemodAnalyzerWorker::startWork()
 | 
			
		||||
{
 | 
			
		||||
    QMutexLocker mutexLocker(&m_mutex);
 | 
			
		||||
    m_wavFileRecord = new WavFileRecord(m_sinkSampleRate);
 | 
			
		||||
    connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DemodAnalyzerWorker::stopWork()
 | 
			
		||||
{
 | 
			
		||||
    QMutexLocker mutexLocker(&m_mutex);
 | 
			
		||||
    delete m_wavFileRecord;
 | 
			
		||||
    m_wavFileRecord = nullptr;
 | 
			
		||||
    disconnect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -74,6 +82,13 @@ void DemodAnalyzerWorker::feedPart(
 | 
			
		||||
        nbBytes = 2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ((nbBytes != m_nbBytes) && m_wavFileRecord)
 | 
			
		||||
    {
 | 
			
		||||
        m_wavFileRecord->stopRecording();
 | 
			
		||||
        m_wavFileRecord->setMono(nbBytes == 2);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    m_nbBytes = nbBytes;
 | 
			
		||||
    int countSamples = (end - begin) / nbBytes;
 | 
			
		||||
 | 
			
		||||
    if (countSamples > m_sampleBufferSize)
 | 
			
		||||
@ -93,6 +108,62 @@ void DemodAnalyzerWorker::feedPart(
 | 
			
		||||
        vbegin.push_back(m_sampleBuffer.begin());
 | 
			
		||||
        m_scopeVis->feed(vbegin, countSamples/(1<<m_settings.m_log2Decim));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    if (m_settings.m_recordToFile && m_wavFileRecord)
 | 
			
		||||
    {
 | 
			
		||||
        for (int is = 0; is < countSamples/(1<<m_settings.m_log2Decim); is++)
 | 
			
		||||
        {
 | 
			
		||||
            const Sample& sample = m_sampleBuffer[is];
 | 
			
		||||
 | 
			
		||||
            if ((sample.m_real == 0) && (sample.m_imag == 0))
 | 
			
		||||
            {
 | 
			
		||||
                if (m_recordSilenceNbSamples <= 0)
 | 
			
		||||
                {
 | 
			
		||||
                    writeSampleToFile(sample);
 | 
			
		||||
                    m_recordSilenceCount = 0;
 | 
			
		||||
                }
 | 
			
		||||
                else if (m_recordSilenceCount < m_recordSilenceNbSamples)
 | 
			
		||||
                {
 | 
			
		||||
                    writeSampleToFile(sample);
 | 
			
		||||
                    m_recordSilenceCount++;
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    if (m_wavFileRecord->isRecording()) {
 | 
			
		||||
                        m_wavFileRecord->stopRecording();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                if (!m_wavFileRecord->isRecording()) {
 | 
			
		||||
                    m_wavFileRecord->startRecording();
 | 
			
		||||
                }
 | 
			
		||||
                writeSampleToFile(sample);
 | 
			
		||||
                m_recordSilenceCount = 0;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DemodAnalyzerWorker::writeSampleToFile(const Sample& sample)
 | 
			
		||||
{
 | 
			
		||||
    if (SDR_RX_SAMP_SZ == 16)
 | 
			
		||||
    {
 | 
			
		||||
        if (m_nbBytes == 2) {
 | 
			
		||||
            m_wavFileRecord->writeMono(sample.m_real);
 | 
			
		||||
        } else {
 | 
			
		||||
            m_wavFileRecord->write(sample.m_real, sample.m_imag);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        if (m_nbBytes == 2) {
 | 
			
		||||
            m_wavFileRecord->writeMono(sample.m_real >> 8);
 | 
			
		||||
        } else {
 | 
			
		||||
            m_wavFileRecord->write(sample.m_real >> 8, sample.m_imag >> 8);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DemodAnalyzerWorker::decimate(int countSamples)
 | 
			
		||||
@ -190,11 +261,95 @@ void DemodAnalyzerWorker::applySettings(const DemodAnalyzerSettings& settings, b
 | 
			
		||||
            << " m_title: " << settings.m_title
 | 
			
		||||
            << " m_rgbColor: " << settings.m_rgbColor
 | 
			
		||||
            << " m_log2Decim: " << settings.m_log2Decim
 | 
			
		||||
            << " m_fileRecordName: " << settings.m_fileRecordName
 | 
			
		||||
            << " m_recordToFile: " << settings.m_recordToFile
 | 
			
		||||
            << " m_recordSilenceTime: " << settings.m_recordSilenceTime
 | 
			
		||||
            << " force: " << force;
 | 
			
		||||
 | 
			
		||||
    if ((m_settings.m_fileRecordName != settings.m_fileRecordName) || force)
 | 
			
		||||
    {
 | 
			
		||||
        if (m_wavFileRecord)
 | 
			
		||||
        {
 | 
			
		||||
            QStringList dotBreakout = settings.m_fileRecordName.split(QLatin1Char('.'));
 | 
			
		||||
 | 
			
		||||
            if (dotBreakout.size() > 1)
 | 
			
		||||
            {
 | 
			
		||||
                QString extension = dotBreakout.last();
 | 
			
		||||
 | 
			
		||||
                if (extension != "wav") {
 | 
			
		||||
                    dotBreakout.last() = "wav";
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                dotBreakout.append("wav");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            QString newFileRecordName = dotBreakout.join(QLatin1Char('.'));
 | 
			
		||||
            QString fileBase;
 | 
			
		||||
            FileRecordInterface::guessTypeFromFileName(newFileRecordName, fileBase);
 | 
			
		||||
            qDebug("DemodAnalyzerWorker::applySettings: newFileRecordName: %s fileBase: %s", qPrintable(newFileRecordName), qPrintable(fileBase));
 | 
			
		||||
            m_wavFileRecord->setFileName(fileBase);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ((m_settings.m_recordToFile != settings.m_recordToFile) || force)
 | 
			
		||||
    {
 | 
			
		||||
        if (m_wavFileRecord)
 | 
			
		||||
        {
 | 
			
		||||
            if (settings.m_recordToFile)
 | 
			
		||||
            {
 | 
			
		||||
                if (!m_wavFileRecord->isRecording()) {
 | 
			
		||||
                    m_wavFileRecord->startRecording();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                if (m_wavFileRecord->isRecording()) {
 | 
			
		||||
                    m_wavFileRecord->stopRecording();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            m_recordSilenceCount = 0;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ((m_settings.m_recordSilenceTime != settings.m_recordSilenceTime)
 | 
			
		||||
     || (m_settings.m_log2Decim != settings.m_log2Decim) || force)
 | 
			
		||||
    {
 | 
			
		||||
        m_recordSilenceNbSamples = (settings.m_recordSilenceTime * (m_sinkSampleRate / (1<<settings.m_log2Decim))) / 10; // time in 100'ś ms
 | 
			
		||||
        m_recordSilenceCount = 0;
 | 
			
		||||
 | 
			
		||||
        if (m_wavFileRecord)
 | 
			
		||||
        {
 | 
			
		||||
            if (m_wavFileRecord->isRecording()) {
 | 
			
		||||
                m_wavFileRecord->stopRecording();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            m_wavFileRecord->setSampleRate(m_sinkSampleRate / (1<<settings.m_log2Decim));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    m_settings = settings;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DemodAnalyzerWorker::applySampleRate(int sampleRate)
 | 
			
		||||
{
 | 
			
		||||
    m_sinkSampleRate = sampleRate;
 | 
			
		||||
 | 
			
		||||
    m_recordSilenceNbSamples = (m_settings.m_recordSilenceTime * (m_sinkSampleRate / (1<<m_settings.m_log2Decim))) / 10; // time in 100'ś ms
 | 
			
		||||
    m_recordSilenceCount = 0;
 | 
			
		||||
 | 
			
		||||
    if (m_wavFileRecord)
 | 
			
		||||
    {
 | 
			
		||||
        if (m_wavFileRecord->isRecording()) {
 | 
			
		||||
            m_wavFileRecord->stopRecording();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        m_wavFileRecord->setSampleRate(m_sinkSampleRate / (1<<m_settings.m_log2Decim));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DemodAnalyzerWorker::handleData()
 | 
			
		||||
{
 | 
			
		||||
    QMutexLocker mutexLocker(&m_mutex);
 | 
			
		||||
 | 
			
		||||
@ -37,6 +37,8 @@ class BasebandSampleSink;
 | 
			
		||||
class ScopeVis;
 | 
			
		||||
class ChannelAPI;
 | 
			
		||||
class Feature;
 | 
			
		||||
class WavFileRecord;
 | 
			
		||||
 | 
			
		||||
class DemodAnalyzerWorker : public QObject {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
public:
 | 
			
		||||
@ -91,12 +93,6 @@ public:
 | 
			
		||||
    MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
 | 
			
		||||
    void setMessageQueueToFeature(MessageQueue *messageQueue) { m_msgQueueToFeature = messageQueue; }
 | 
			
		||||
 | 
			
		||||
    void feedPart(
 | 
			
		||||
        const QByteArray::const_iterator& begin,
 | 
			
		||||
        const QByteArray::const_iterator& end,
 | 
			
		||||
        DataFifo::DataType dataType
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    void applySampleRate(int sampleRate);
 | 
			
		||||
	void applySettings(const DemodAnalyzerSettings& settings, bool force = false);
 | 
			
		||||
 | 
			
		||||
@ -121,10 +117,21 @@ private:
 | 
			
		||||
    int m_sampleBufferSize;
 | 
			
		||||
	MovingAverageUtil<double, double, 480> m_channelPowerAvg;
 | 
			
		||||
    ScopeVis* m_scopeVis;
 | 
			
		||||
    WavFileRecord* m_wavFileRecord;
 | 
			
		||||
    int m_recordSilenceNbSamples;
 | 
			
		||||
    int m_recordSilenceCount;
 | 
			
		||||
    int m_nbBytes;
 | 
			
		||||
    QRecursiveMutex m_mutex;
 | 
			
		||||
 | 
			
		||||
    void feedPart(
 | 
			
		||||
        const QByteArray::const_iterator& begin,
 | 
			
		||||
        const QByteArray::const_iterator& end,
 | 
			
		||||
        DataFifo::DataType dataType
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    bool handleMessage(const Message& cmd);
 | 
			
		||||
    void decimate(int countSamples);
 | 
			
		||||
    void writeSampleToFile(const Sample& sample);
 | 
			
		||||
 | 
			
		||||
    inline void processSample(
 | 
			
		||||
        DataFifo::DataType dataType,
 | 
			
		||||
 | 
			
		||||
@ -73,6 +73,35 @@ This is the resulting sample rate after possible decimation that is used by the
 | 
			
		||||
 | 
			
		||||
Average total power in dB relative to a +/- 1.0 amplitude signal received in the pass band.
 | 
			
		||||
 | 
			
		||||
<h3>A.8. Record as .wav file</h3>
 | 
			
		||||
 | 
			
		||||
Use this button to toggle recording. Start or stop recording
 | 
			
		||||
 | 
			
		||||
Format is always 16 bit little-endian and can be mono (1 channel) or stereo (2 channels) depending on data type.
 | 
			
		||||
 | 
			
		||||
<h3>A.9. Select recording output file</h3>
 | 
			
		||||
 | 
			
		||||
Click on this icon to open a file selection dialog that lets you specify the location and name of the output files.
 | 
			
		||||
 | 
			
		||||
Each recording is written in a new file with the starting timestamp before the `.wav` extension in `yyyy-MM-ddTHH_mm_ss_zzz` format. It keeps the first dot limited groups of the filename before the `.wav` extension if there are two such groups or before the two last groups if there are more than two groups. Examples:
 | 
			
		||||
 | 
			
		||||
  - Given file name: `test.wav` then a recording file will be like: `test.2020-08-05T21_39_07_974.wav`
 | 
			
		||||
  - Given file name: `test.2020-08-05T20_36_15_974.wav` then a recording file will be like (with timestamp updated): `test.2020-08-05T21_41_21_173.wav`
 | 
			
		||||
  - Given file name: `test.first.wav` then a recording file will be like: `test.2020-08-05T22_00_07_974.wav`
 | 
			
		||||
  - Given file name: `record.test.first.eav` then a recording file will be like: `reocrd.test.2020-08-05T21_39_52_974.wav`
 | 
			
		||||
 | 
			
		||||
If a filename is given without `.wav` extension then the `.wav` extension is appended automatically before the above algorithm is applied. If a filename is given with an extension different of `.wav` then the extension is replaced by `.wav` automatically before the above algorithm is applied.
 | 
			
		||||
 | 
			
		||||
The file path currently being written (or last closed) appears at the right of the button (A.1.10).
 | 
			
		||||
 | 
			
		||||
<h3>A.1.10. Recording file path</h3>
 | 
			
		||||
 | 
			
		||||
This is the file path currently being written (or last closed).
 | 
			
		||||
 | 
			
		||||
<h3>A.1.11 Record silence time</h3>
 | 
			
		||||
 | 
			
		||||
This is the time in seconds (between 0.1 and 10.0) of silence (null samples) before recording stops. When non null samples come again this will start a new recording. Set the value to 0 to record continuously.
 | 
			
		||||
 | 
			
		||||
<h2>B. Spectrum view</h2>
 | 
			
		||||
 | 
			
		||||
This is the same display as with the channel analyzer spectrum view. This is the spectrum of a real signal so it is symmetrical around zero frequency. Details on the spectrum view and controls can be found [here](../../../sdrgui/gui/spectrum.md)
 | 
			
		||||
 | 
			
		||||
@ -82,8 +82,9 @@ void WavFileRecord::feed(const SampleVector::const_iterator& begin, const Sample
 | 
			
		||||
{
 | 
			
		||||
    (void) positiveOnly;
 | 
			
		||||
 | 
			
		||||
    if(!m_recordOn)
 | 
			
		||||
    if (!m_recordOn) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (begin < end) // if there is something to put out
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
@ -94,6 +94,7 @@ public:
 | 
			
		||||
    virtual int getBytesPerSample() override { return 4; };
 | 
			
		||||
    const QString& getCurrentFileName() override { return m_currentFileName; }
 | 
			
		||||
    void setMono(bool mono) { m_nbChannels = mono ? 1 : 2; }
 | 
			
		||||
    void setSampleRate(quint32 sampleRate) { m_sampleRate = sampleRate; }
 | 
			
		||||
 | 
			
		||||
    void genUniqueFileName(uint deviceUID, int istream = -1);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user