Added #583: add periodic file generation, plus other options:

- Added a Recording menu,
git commit -m Added
This commit is contained in:
vsonnier 2018-01-13 11:50:08 +01:00
parent 4d0f3a794d
commit 26deefd606
14 changed files with 441 additions and 91 deletions

View File

@ -535,6 +535,24 @@ bool AppConfig::verifyRecordingPath() {
return true;
}
void AppConfig::setRecordingSquelchOption(int enumChoice) {
recordingSquelchOption = enumChoice;
}
int AppConfig::getRecordingSquelchOption() {
return recordingSquelchOption;
}
void AppConfig::setRecordingFileTimeLimit(int nbSeconds) {
recordingFileTimeLimitSeconds = nbSeconds;
}
int AppConfig::getRecordingFileTimeLimit() {
return recordingFileTimeLimitSeconds;
}
void AppConfig::setConfigName(std::string configName) {
this->configName = configName;
}
@ -588,8 +606,11 @@ bool AppConfig::save() {
*window_node->newChild("bookmark_visible") = bookmarksVisible.load();
}
//Recording settings:
DataNode *rec_node = cfg.rootNode()->newChild("recording");
*rec_node->newChild("path") = recordingPath;
*rec_node->newChild("squelch") = recordingSquelchOption;
*rec_node->newChild("file_time_limit") = recordingFileTimeLimitSeconds;
DataNode *devices_node = cfg.rootNode()->newChild("devices");
@ -773,6 +794,7 @@ bool AppConfig::load() {
}
}
//Recording settings:
if (cfg.rootNode()->hasAnother("recording")) {
DataNode *rec_node = cfg.rootNode()->getNext("recording");
@ -780,6 +802,16 @@ bool AppConfig::load() {
DataNode *rec_path = rec_node->getNext("path");
recordingPath = rec_path->element()->toString();
}
if (rec_node->hasAnother("squelch")) {
DataNode *rec_squelch = rec_node->getNext("squelch");
rec_squelch->element()->get(recordingSquelchOption);
}
if (rec_node->hasAnother("file_time_limit")) {
DataNode *rec_file_time_limit = rec_node->getNext("file_time_limit");
rec_file_time_limit->element()->get(recordingFileTimeLimitSeconds);
}
}
if (cfg.rootNode()->hasAnother("devices")) {

View File

@ -138,10 +138,16 @@ public:
void setBookmarksVisible(bool state);
bool getBookmarksVisible();
//Recording settings:
void setRecordingPath(std::string recPath);
std::string getRecordingPath();
bool verifyRecordingPath();
void setRecordingSquelchOption(int enumChoice);
int getRecordingSquelchOption();
bool verifyRecordingPath();
void setRecordingFileTimeLimit(int nbSeconds);
int getRecordingFileTimeLimit();
#if USE_HAMLIB
int getRigModel();
@ -189,7 +195,10 @@ private:
std::atomic_int dbOffset;
std::vector<SDRManualDef> manualDevices;
std::atomic_bool bookmarksVisible;
std::string recordingPath;
std::string recordingPath = "";
int recordingSquelchOption = 0;
int recordingFileTimeLimitSeconds = 0;
#if USE_HAMLIB
std::atomic_int rigModel, rigRate;
std::string rigPort;

View File

@ -18,7 +18,7 @@
#include <vector>
#include <algorithm>
#include "AudioThread.h"
#include "AudioSinkFileThread.h"
#include "CubicSDR.h"
#include "DataTree.h"
#include "ColorTheme.h"
@ -402,10 +402,8 @@ AppFrame::AppFrame() :
// Make a menubar
menuBar = new wxMenuBar;
fileMenu = makeFileMenu();
menuBar->Append(fileMenu, wxT("&File"));
menuBar->Append(makeFileMenu(), wxT("&File"));
settingsMenu = new wxMenu;
@ -494,6 +492,11 @@ AppFrame::AppFrame() :
menuBar->Append(audioSampleRateMenu, wxT("Audio &Sample Rate"));
//Add a Recording menu
menuBar->Append(makeRecordingMenu(), wxT("Recordin&g"));
//
updateRecordingMenu();
//Add Display menu
displayMenu = new wxMenu;
@ -708,14 +711,6 @@ wxMenu *AppFrame::makeFileMenu() {
menu->Append(wxID_SDR_START_STOP, "Stop / Start Device");
menu->AppendSeparator();
std::string recPath = wxGetApp().getConfig()->getRecordingPath();
if (recPath.length() > 32) {
recPath = "..." + recPath.substr(recPath.length() - 32, 32);
}
menu->Append(wxID_RECORDING_PATH, getSettingsLabel("Set Recording Path", recPath.empty() ? "<Not Set>" : recPath));
menu->AppendSeparator();
menu->Append(wxID_OPEN, "&Open Session");
menu->Append(wxID_SAVE, "&Save Session");
menu->Append(wxID_SAVEAS, "Save Session &As..");
@ -741,15 +736,88 @@ wxMenu *AppFrame::makeFileMenu() {
#endif
#endif
fileMenu = menu;
return menu;
}
void AppFrame::updateFileMenu() {
wxMenu *newFileMenu = makeFileMenu();
menuBar->Replace(0, newFileMenu, wxT("&File"));
fileMenu = newFileMenu;
wxMenu *AppFrame::makeRecordingMenu() {
recordingMenuItems.clear();
wxMenu *menu = new wxMenu;
recordingMenuItems[wxID_RECORDING_PATH] = menu->Append(wxID_RECORDING_PATH, getSettingsLabel("Set Recording Path", "<Not Set>"));
menu->AppendSeparator();
//Squelch options as sub-menu:
wxMenu *subMenu = new wxMenu;
recordingMenuItems[wxID_RECORDING_SQUELCH_BASE] = menu->AppendSubMenu(subMenu, "Squelch");
recordingMenuItems[wxID_RECORDING_SQUELCH_SILENCE] = subMenu->AppendRadioItem(wxID_RECORDING_SQUELCH_SILENCE, "Record Silence",
"Record below squelch-break audio as silence, i.e records as the user may hear.");
recordingMenuItems[wxID_RECORDING_SQUELCH_SKIP] = subMenu->AppendRadioItem(wxID_RECORDING_SQUELCH_SKIP, "Skip Silence",
"Do not record below squelch-break audio, i.e squelch-break audio parts are packed together.");
recordingMenuItems[wxID_RECORDING_SQUELCH_ALWAYS] = subMenu->AppendRadioItem(wxID_RECORDING_SQUELCH_ALWAYS, "Record Always",
"Record everything irrespective of the squelch level.");
recordingMenuItems[wxID_RECORDING_FILE_TIME_LIMIT] = menu->Append(wxID_RECORDING_FILE_TIME_LIMIT, getSettingsLabel("File time limit", "<Not Set>"),
"Creates a new file automatically, each time the recording lasts longer than the limit, named according to the current time.");
recordingMenuItems[wxID_RECORDING_SQUELCH_SILENCE]->Check(true);
recordingMenu = menu;
return menu;
}
void AppFrame::updateRecordingMenu() {
// Recording path:
std::string recPath = wxGetApp().getConfig()->getRecordingPath();
if (recPath.length() > 32) {
recPath = "..." + recPath.substr(recPath.length() - 32, 32);
}
recordingMenuItems[wxID_RECORDING_PATH]->SetItemLabel(getSettingsLabel("Set Recording Path", recPath.empty() ? "<Not Set>" : recPath));
//Squelch options:
int squelchEnumValue = wxGetApp().getConfig()->getRecordingSquelchOption();
if (squelchEnumValue == AudioSinkFileThread::SQUELCH_RECORD_SILENCE) {
recordingMenuItems[wxID_RECORDING_SQUELCH_SILENCE]->Check(true);
recordingMenuItems[wxID_RECORDING_SQUELCH_BASE]->SetItemLabel(getSettingsLabel("Squelch", "Record Silence"));
} else if (squelchEnumValue == AudioSinkFileThread::SQUELCH_SKIP_SILENCE) {
recordingMenuItems[wxID_RECORDING_SQUELCH_SKIP]->Check(true);
recordingMenuItems[wxID_RECORDING_SQUELCH_BASE]->SetItemLabel(getSettingsLabel("Squelch", "Skip Silence"));
} else if (squelchEnumValue == AudioSinkFileThread::SQUELCH_RECORD_ALWAYS) {
recordingMenuItems[wxID_RECORDING_SQUELCH_ALWAYS]->Check(true);
recordingMenuItems[wxID_RECORDING_SQUELCH_BASE]->SetItemLabel(getSettingsLabel("Squelch", "Record Always"));
}
else {
recordingMenuItems[wxID_RECORDING_SQUELCH_SILENCE]->Check(true);
recordingMenuItems[wxID_RECORDING_SQUELCH_BASE]->SetItemLabel(getSettingsLabel("Squelch", "Record Silence"));
}
//File time limit:
int fileTimeLimitSeconds = wxGetApp().getConfig()->getRecordingFileTimeLimit();
if (fileTimeLimitSeconds <= 0) {
recordingMenuItems[wxID_RECORDING_FILE_TIME_LIMIT]->SetItemLabel(getSettingsLabel("File time limit","<Not Set>"));
}
else {
recordingMenuItems[wxID_RECORDING_FILE_TIME_LIMIT]->SetItemLabel(getSettingsLabel("File time limit",
std::to_string(fileTimeLimitSeconds), "s"));
}
}
void AppFrame::initDeviceParams(SDRDeviceInfo *devInfo) {
this->devInfo = devInfo;
@ -1512,6 +1580,73 @@ bool AppFrame::actionOnMenuLoadSave(wxCommandEvent& event) {
return false;
}
bool AppFrame::actionOnMenuRecording(wxCommandEvent& event) {
if (event.GetId() == wxID_RECORDING_PATH) {
std::string recPath = wxGetApp().getConfig()->getRecordingPath();
wxDirDialog recPathDialog(this, _("File Path for Recordings"), recPath, wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST);
if (recPathDialog.ShowModal() == wxID_CANCEL) {
return true;
}
wxGetApp().getConfig()->setRecordingPath(recPathDialog.GetPath().ToStdString());
updateRecordingMenu();
return true;
}
else if (event.GetId() == wxID_RECORDING_SQUELCH_SILENCE) {
wxGetApp().getConfig()->setRecordingSquelchOption(AudioSinkFileThread::SQUELCH_RECORD_SILENCE);
updateRecordingMenu();
return true;
}
else if (event.GetId() == wxID_RECORDING_SQUELCH_SKIP) {
wxGetApp().getConfig()->setRecordingSquelchOption(AudioSinkFileThread::SQUELCH_SKIP_SILENCE);
updateRecordingMenu();
return true;
}
else if (event.GetId() == wxID_RECORDING_SQUELCH_ALWAYS) {
wxGetApp().getConfig()->setRecordingSquelchOption(AudioSinkFileThread::SQUELCH_RECORD_ALWAYS);
updateRecordingMenu();
return true;
}
else if (event.GetId() == wxID_RECORDING_FILE_TIME_LIMIT) {
int currentFileLimitSeconds = wxGetApp().getConfig()->getRecordingFileTimeLimit();
long newFileLimit = wxGetNumberFromUser(wxString("\nFile time limit:\n") +
"\nCreates a new file automatically, each time the recording lasts longer than the limit, named according to the current time.\n\n " +
+ "min: 0 s (no limit)"
+ ", max: 36000 s (10 hours)\n",
"Time in seconds",
"File Time Limit",
//If a manual sample rate has already been input, recall this one.
currentFileLimitSeconds > 0 ? currentFileLimitSeconds : 0,
0,
36000,
this);
if (newFileLimit != -1) {
wxGetApp().getConfig()->setRecordingFileTimeLimit((int)newFileLimit);
updateRecordingMenu();
}
return true;
}
return false;
}
bool AppFrame::actionOnMenuRig(wxCommandEvent& event) {
bool bManaged = false;
@ -1667,17 +1802,6 @@ void AppFrame::OnMenu(wxCommandEvent& event) {
}
}
}
else if (event.GetId() == wxID_RECORDING_PATH) {
std::string recPath = wxGetApp().getConfig()->getRecordingPath();
wxDirDialog recPathDialog(this, _("File Path for Recordings"), recPath, wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST);
if (recPathDialog.ShowModal() == wxID_CANCEL) {
return;
}
wxGetApp().getConfig()->setRecordingPath(recPathDialog.GetPath().ToStdString());
updateFileMenu();
}
else if (event.GetId() == wxID_LOW_PERF) {
lowPerfMode = lowPerfMenuItem->IsChecked();
wxGetApp().getConfig()->setLowPerfMode(lowPerfMode);
@ -1742,9 +1866,12 @@ void AppFrame::OnMenu(wxCommandEvent& event) {
else if (actionOnMenuAudioSampleRate(event)) {
return;
}
else if (actionOnMenuDisplay(event)) {
else if (actionOnMenuRecording(event)) {
return;
}
else if (actionOnMenuDisplay(event)) {
return;
}
//Optional : Rig
else if (actionOnMenuRig(event)) {
return;

View File

@ -42,7 +42,6 @@
#define wxID_LOW_PERF 2011
#define wxID_SET_DB_OFFSET 2012
#define wxID_ABOUT_CUBICSDR 2013
#define wxID_RECORDING_PATH 2014
#define wxID_OPEN_BOOKMARKS 2020
#define wxID_SAVE_BOOKMARKS 2021
@ -77,6 +76,13 @@
#define wxID_DEVICE_ID 3500
#define wxID_RECORDING_PATH 8500
#define wxID_RECORDING_SQUELCH_BASE 8501
#define wxID_RECORDING_SQUELCH_SILENCE 8502
#define wxID_RECORDING_SQUELCH_SKIP 8503
#define wxID_RECORDING_SQUELCH_ALWAYS 8504
#define wxID_RECORDING_FILE_TIME_LIMIT 8505
#define wxID_AUDIO_BANDWIDTH_BASE 9000
#define wxID_AUDIO_DEVICE_MULTIPLIER 50
@ -103,7 +109,9 @@ public:
~AppFrame();
wxMenu *makeFileMenu();
void updateFileMenu();
wxMenu *makeRecordingMenu();
void updateRecordingMenu();
void initDeviceParams(SDRDeviceInfo *devInfo);
void updateDeviceParams();
@ -178,6 +186,7 @@ private:
bool actionOnMenuAudioSampleRate(wxCommandEvent& event);
bool actionOnMenuDisplay(wxCommandEvent& event);
bool actionOnMenuLoadSave(wxCommandEvent& event);
bool actionOnMenuRecording(wxCommandEvent& event);
bool actionOnMenuRig(wxCommandEvent& event);
wxString getSettingsLabel(const std::string& settingsName,
@ -221,6 +230,10 @@ private:
std::map<int, wxMenuItem *> settingsMenuItems;
std::map<int, wxMenuItem *> audioSampleRateMenuItems;
//
std::map<int, wxMenuItem *> recordingMenuItems;
std::map<int, wxMenuItem *> directSamplingMenuItems;
wxMenuBar *menuBar;
@ -231,6 +244,7 @@ private:
wxMenuItem *lowPerfMenuItem = nullptr;
wxMenu *fileMenu = nullptr;
wxMenu *settingsMenu = nullptr;
wxMenu *recordingMenu = nullptr;
SoapySDR::ArgInfoList settingArgs;
int settingsIdMax;

View File

@ -3,7 +3,6 @@
#include "AudioFile.h"
#include "CubicSDR.h"
#include <iomanip>
#include <sstream>
AudioFile::AudioFile() {
@ -18,7 +17,7 @@ void AudioFile::setOutputFileName(std::string filename) {
filenameBase = filename;
}
std::string AudioFile::getOutputFileName(int sequenceNumber) {
std::string AudioFile::getOutputFileName() {
std::string recPath = wxGetApp().getConfig()->getRecordingPath();
@ -36,10 +35,6 @@ std::string AudioFile::getOutputFileName(int sequenceNumber) {
std::stringstream outputFileName;
outputFileName << recPath << filePathSeparator << filenameBaseSafe;
if (sequenceNumber > 0) {
outputFileName << "_" << std::setfill('0') << std::setw(3) << sequenceNumber;
}
int idx = 0;
// If the file exists; then find the next non-existing file in sequence.

View File

@ -14,7 +14,7 @@ public:
virtual void setOutputFileName(std::string filename);
virtual std::string getExtension() = 0;
virtual std::string getOutputFileName(int sequenceNumber = 0);
virtual std::string getOutputFileName();
virtual bool writeToFile(AudioThreadInputPtr input) = 0;
virtual bool closeFile() = 0;

View File

@ -2,6 +2,8 @@
// SPDX-License-Identifier: GPL-2.0+
#include "AudioFileWAV.h"
#include "CubicSDR.h"
#include <iomanip>
//limit file size to 2GB (- margin) for maximum compatibility.
#define MAX_WAV_FILE_SIZE (0x7FFFFFFF - 1024)
@ -63,7 +65,7 @@ std::string AudioFileWAV::getExtension()
bool AudioFileWAV::writeToFile(AudioThreadInputPtr input)
{
if (!outputFileStream.is_open()) {
std::string ofName = getOutputFileName(currentSequenceNumber);
std::string ofName = getOutputFileName();
outputFileStream.open(ofName.c_str(), std::ios::binary);
@ -85,7 +87,7 @@ bool AudioFileWAV::writeToFile(AudioThreadInputPtr input)
currentSequenceNumber++;
currentFileSize = 0;
std::string ofName = getOutputFileName(currentSequenceNumber);
std::string ofName = getOutputFileName();
outputFileStream.open(ofName.c_str(), std::ios::binary);
writeHeaderToFileStream(input);
@ -166,3 +168,39 @@ size_t AudioFileWAV::getMaxWritableNumberOfSamples(AudioThreadInputPtr input) {
return (size_t)(remainingBytesInFile / (input->channels * 2));
}
std::string AudioFileWAV::getOutputFileName() {
std::string recPath = wxGetApp().getConfig()->getRecordingPath();
// Strip any invalid characters from the name
std::string stripChars("<>:\"/\\|?*");
std::string filenameBaseSafe = filenameBase;
for (size_t i = 0, iMax = filenameBaseSafe.length(); i < iMax; i++) {
if (stripChars.find(filenameBaseSafe[i]) != std::string::npos) {
filenameBaseSafe.replace(i, 1, "_");
}
}
// Create output file name
std::stringstream outputFileName;
outputFileName << recPath << filePathSeparator << filenameBaseSafe;
//customized part: append a sequence number.
if (currentSequenceNumber > 0) {
outputFileName << "_" << std::setfill('0') << std::setw(3) << currentSequenceNumber;
}
int idx = 0;
// If the file exists; then find the next non-existing file in sequence.
std::string fileNameCandidate = outputFileName.str();
while (FILE *file = fopen((fileNameCandidate + "." + getExtension()).c_str(), "r")) {
fclose(file);
fileNameCandidate = outputFileName.str() + "-" + std::to_string(++idx);
}
return fileNameCandidate + "." + getExtension();
}

View File

@ -13,10 +13,14 @@ public:
AudioFileWAV();
~AudioFileWAV();
std::string getExtension();
//override of the base method to generate multi-part
//WAV to overcome the WAV format size limit.
virtual std::string getOutputFileName();
bool writeToFile(AudioThreadInputPtr input);
bool closeFile();
virtual std::string getExtension();
virtual bool writeToFile(AudioThreadInputPtr input);
virtual bool closeFile();
protected:
std::ofstream outputFileStream;

View File

@ -2,6 +2,9 @@
// SPDX-License-Identifier: GPL-2.0+
#include "AudioSinkFileThread.h"
#include <ctime>
#define HEARTBEAT_CHECK_PERIOD_MICROS (50 * 1000)
AudioSinkFileThread::AudioSinkFileThread() : AudioSinkThread() {
@ -17,6 +20,57 @@ void AudioSinkFileThread::sink(AudioThreadInputPtr input) {
if (!audioFileHandler) {
return;
}
//by default, always write something
bool isSomethingToWrite = true;
if (input->is_squelch_active) {
if (squelchOption == SQUELCH_RECORD_SILENCE) {
//patch with "silence"
input->data.assign(input->data.size(), 0.0f);
input->peak = 0.0f;
}
else if (squelchOption == SQUELCH_SKIP_SILENCE) {
isSomethingToWrite = false;
}
}
//else, nothing to do record as if squelch was not enabled.
if (!isSomethingToWrite) {
return;
}
if (fileTimeLimit > 0) {
durationMeasurement.update();
//duration exeeded, close this file and create another
//with "now" as timestamp.
if (durationMeasurement.getSeconds() > fileTimeLimit) {
audioFileHandler->closeFile();
//initialize the filename of the AudioFile with the current time
time_t t = std::time(nullptr);
tm ltm = *std::localtime(&t);
// GCC 5+
// fileName << "_" << std::put_time(&ltm, "%d-%m-%Y_%H-%M-%S");
char timeStr[512];
//International format: Year.Month.Day, also lexicographically sortable
strftime(timeStr, sizeof(timeStr), "%Y-%m-%d_%H-%M-%S", &ltm);
audioFileHandler->setOutputFileName(fileNameBase + std::string("_") + timeStr);
//reset duration counter
durationMeasurement.start();
//the following writeToFile will take care of creating another file.
}
}
// forward to output file handler
audioFileHandler->writeToFile(input);
}
@ -28,8 +82,64 @@ void AudioSinkFileThread::inputChanged(AudioThreadInput oldProps, AudioThreadInp
}
audioFileHandler->closeFile();
//reset duration counter
durationMeasurement.start();
}
void AudioSinkFileThread::setAudioFileNameBase(const std::string& baseName) {
fileNameBase = baseName;
}
void AudioSinkFileThread::setAudioFileHandler(AudioFile * output) {
audioFileHandler = output;
//initialize the filename of the AudioFile with the current time
time_t t = std::time(nullptr);
tm ltm = *std::localtime(&t);
// GCC 5+
// fileName << "_" << std::put_time(&ltm, "%d-%m-%Y_%H-%M-%S");
char timeStr[512];
//International format: Year.Month.Day, also lexicographically sortable
strftime(timeStr, sizeof(timeStr), "%Y-%m-%d_%H-%M-%S", &ltm);
audioFileHandler->setOutputFileName(fileNameBase + std::string("_") + timeStr);
// reset Timer
durationMeasurement.start();
}
void AudioSinkFileThread::setSquelchOption(int squelchOptEnumValue) {
if (squelchOptEnumValue == AudioSinkFileThread::SQUELCH_RECORD_SILENCE) {
squelchOption = AudioSinkFileThread::SQUELCH_RECORD_SILENCE;
}
else if (squelchOptEnumValue == AudioSinkFileThread::SQUELCH_SKIP_SILENCE) {
squelchOption = AudioSinkFileThread::SQUELCH_SKIP_SILENCE;
}
else if (squelchOptEnumValue == AudioSinkFileThread::SQUELCH_RECORD_ALWAYS) {
squelchOption = AudioSinkFileThread::SQUELCH_RECORD_ALWAYS;
}
else {
squelchOption = AudioSinkFileThread::SQUELCH_SKIP_SILENCE;
}
}
// Time limit
void AudioSinkFileThread::setFileTimeLimit(int nbSeconds) {
if (nbSeconds > 0) {
fileTimeLimit = nbSeconds;
}
else {
fileTimeLimit = 0;
}
}

View File

@ -5,6 +5,7 @@
#include "AudioSinkThread.h"
#include "AudioFile.h"
#include "Timer.h"
class AudioSinkFileThread : public AudioSinkThread {
@ -12,13 +13,38 @@ public:
AudioSinkFileThread();
~AudioSinkFileThread();
void sink(AudioThreadInputPtr input);
void inputChanged(AudioThreadInput oldProps, AudioThreadInputPtr newProps);
enum SquelchOption {
SQUELCH_RECORD_SILENCE = 0, // default value, record as a user would hear it.
SQUELCH_SKIP_SILENCE = 1, // skip below-squelch level.
SQUELCH_RECORD_ALWAYS = 2, // record irrespective of the squelch level.
SQUELCH_RECORD_MAX
};
virtual void sink(AudioThreadInputPtr input);
virtual void inputChanged(AudioThreadInput oldProps, AudioThreadInputPtr newProps);
void setAudioFileHandler(AudioFile *output);
void setAudioFileNameBase(const std::string& baseName);
//Squelch
void setSquelchOption(int squelchOptEnumValue);
// Time limit
void setFileTimeLimit(int nbSeconds);
protected:
std::string fileNameBase;
AudioFile *audioFileHandler = nullptr;
SquelchOption squelchOption = SQUELCH_RECORD_SILENCE;
int fileTimeLimit = 0;
int fileTimeDurationSeconds = -1;
Timer durationMeasurement;
};

View File

@ -12,8 +12,8 @@ public:
AudioSinkThread();
virtual ~AudioSinkThread();
virtual void run();
virtual void terminate();
virtual void run();
virtual void terminate();
virtual void sink(AudioThreadInputPtr input) = 0;
virtual void inputChanged(AudioThreadInput oldProps, AudioThreadInputPtr newProps) = 0;

View File

@ -21,6 +21,8 @@ public:
int channels;
float peak;
int type;
boolean is_squelch_active = false;
std::vector<float> data;
AudioThreadInput() :

View File

@ -2,7 +2,6 @@
// SPDX-License-Identifier: GPL-2.0+
#include <memory>
#include <ctime>
#include <iomanip>
#include "DemodulatorInstance.h"
@ -628,9 +627,6 @@ void DemodulatorInstance::startRecording() {
AudioSinkFileThread *newSinkThread = new AudioSinkFileThread();
AudioFileWAV *afHandler = new AudioFileWAV();
time_t t = std::time(nullptr);
tm ltm = *std::localtime(&t);
std::stringstream fileName;
std::wstring userLabel = getDemodulatorUserLabel();
@ -643,17 +639,13 @@ void DemodulatorInstance::startRecording() {
} else {
fileName << getLabel();
}
// GCC 5+
// fileName << "_" << std::put_time(&ltm, "%d-%m-%Y_%H-%M-%S");
char timeStr[512];
//International format: Year.Month.Day, also lexicographically sortable
strftime(timeStr, sizeof(timeStr), "%Y-%m-%d_%H-%M-%S", &ltm);
fileName << "_" << timeStr;
afHandler->setOutputFileName(fileName.str());
newSinkThread->setAudioFileNameBase(fileName.str());
//attach options:
newSinkThread->setSquelchOption(wxGetApp().getConfig()->getRecordingSquelchOption());
newSinkThread->setFileTimeLimit(wxGetApp().getConfig()->getRecordingFileTimeLimit());
newSinkThread->setAudioFileHandler(afHandler);
audioSinkThread = newSinkThread;

View File

@ -232,32 +232,39 @@ void DemodulatorThread::run() {
localAudioSinkOutputQueue = audioSinkOutputQueue;
}
if (audioOutputQueue != nullptr && ati && ati->data.size() && !squelched) {
ati->peak = 0;
//compute audio peak:
if (audioOutputQueue != nullptr && ati) {
for (auto data_i : ati->data) {
float p = fabs(data_i);
if (p > ati->peak) {
ati->peak = p;
}
}
} else if (ati) {
//squelch situation, but recording is on-going, so record "silence" to AudioSink:
if (localAudioSinkOutputQueue != nullptr) {
ati->peak = 0;
//Zero the ati samples
ati->peak = 0;
ati->data.assign(ati->data.size(), 0.0f);
if (!localAudioSinkOutputQueue->try_push(ati)) {
std::cout << "DemodulatorThread::run() cannot push ati into audioSinkOutputQueue, is full !" << std::endl;
for (auto data_i : ati->data) {
float p = fabs(data_i);
if (p > ati->peak) {
ati->peak = p;
}
}
}
ati = nullptr;
}
//attach squelch flag to samples, to be used by audio sink.
if (ati) {
ati->is_squelch_active = squelched;
}
//Push to audio sink, if any:
if (ati && localAudioSinkOutputQueue != nullptr) {
if (!localAudioSinkOutputQueue->try_push(ati)) {
std::cout << "DemodulatorThread::run() cannot push ati into audioSinkOutputQueue, is full !" << std::endl;
std::this_thread::yield();
}
}
//now we can nullify ati if squelched, to skip the next processing entirely.
if (ati && squelched) {
ati = nullptr;
}
//At that point, capture the current state of audioVisOutputQueue in a local
//variable, and works with it with now on until the next while-turn.
DemodulatorThreadOutputQueuePtr localAudioVisOutputQueue = nullptr;
@ -345,12 +352,6 @@ void DemodulatorThread::run() {
std::this_thread::yield();
}
}
if (localAudioSinkOutputQueue != nullptr) {
if (!localAudioSinkOutputQueue->try_push(ati)) {
std::cout << "DemodulatorThread::run() cannot push ati into audioSinkOutputQueue, is full !" << std::endl;
}
}
}
DemodulatorThreadControlCommand command;