From fc0f20f07daac6d3b6d7c9c382770588aa091377 Mon Sep 17 00:00:00 2001 From: vsonnier Date: Sun, 7 Jan 2018 08:55:38 +0100 Subject: [PATCH] Removed recording duration limitation: - Limit WAV size to 2GB for maximum compatibility, - Continue recording on another file when size gets too big (XXX_001.wav, then XXX_002.wav and so on) - The sequence assure up to 2000GB worth of recording which should be enough - Changed file pattern to international Year.Month.Day so its recognizable whether you are English of French or whatever :) --- src/audio/AudioFile.cpp | 22 ++++-- src/audio/AudioFile.h | 2 +- src/audio/AudioFileWAV.cpp | 107 +++++++++++++++++++++--------- src/audio/AudioFileWAV.h | 11 +++ src/demod/DemodulatorInstance.cpp | 3 +- 5 files changed, 106 insertions(+), 39 deletions(-) diff --git a/src/audio/AudioFile.cpp b/src/audio/AudioFile.cpp index f3e1a34..1fefed3 100644 --- a/src/audio/AudioFile.cpp +++ b/src/audio/AudioFile.cpp @@ -3,6 +3,8 @@ #include "AudioFile.h" #include "CubicSDR.h" +#include +#include AudioFile::AudioFile() { @@ -16,7 +18,8 @@ void AudioFile::setOutputFileName(std::string filename) { filenameBase = filename; } -std::string AudioFile::getOutputFileName() { +std::string AudioFile::getOutputFileName(int sequenceNumber) { + std::string recPath = wxGetApp().getConfig()->getRecordingPath(); // Strip any invalid characters from the name @@ -30,16 +33,23 @@ std::string AudioFile::getOutputFileName() { } // Create output file name - std::string outputFileName = recPath + filePathSeparator + filenameBaseSafe + "." + getExtension(); + 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. - while (FILE *file = fopen(outputFileName.c_str(), "r")) { + std::string fileNameCandidate = outputFileName.str(); + + while (FILE *file = fopen((fileNameCandidate + "." + getExtension()).c_str(), "r")) { fclose(file); - outputFileName = recPath + filePathSeparator + filenameBaseSafe + "-" + std::to_string(++idx) + "." + getExtension(); + fileNameCandidate = outputFileName.str() + "-" + std::to_string(++idx); } - - return outputFileName; + + return fileNameCandidate + "." + getExtension(); } diff --git a/src/audio/AudioFile.h b/src/audio/AudioFile.h index c8636b8..46fc1bd 100644 --- a/src/audio/AudioFile.h +++ b/src/audio/AudioFile.h @@ -14,7 +14,7 @@ public: virtual void setOutputFileName(std::string filename); virtual std::string getExtension() = 0; - virtual std::string getOutputFileName(); + virtual std::string getOutputFileName(int sequenceNumber = 0); virtual bool writeToFile(AudioThreadInputPtr input) = 0; virtual bool closeFile() = 0; diff --git a/src/audio/AudioFileWAV.cpp b/src/audio/AudioFileWAV.cpp index a8d5fd2..d60cfee 100644 --- a/src/audio/AudioFileWAV.cpp +++ b/src/audio/AudioFileWAV.cpp @@ -3,6 +3,8 @@ #include "AudioFileWAV.h" +//limit file size to 2GB (- margin) for maximum compatibility. +#define MAX_WAV_FILE_SIZE (0x7FFFFFFF - 1024) // Simple endian io read/write handling from // http://www.cplusplus.com/forum/beginner/31584/#msg171056 @@ -61,44 +63,34 @@ std::string AudioFileWAV::getExtension() bool AudioFileWAV::writeToFile(AudioThreadInputPtr input) { if (!outputFileStream.is_open()) { - std::string ofName = getOutputFileName(); + std::string ofName = getOutputFileName(currentSequenceNumber); outputFileStream.open(ofName.c_str(), std::ios::binary); - // Based on simple wav file output code from - // http://www.cplusplus.com/forum/beginner/166954/ - - // Write the wav file headers - outputFileStream << "RIFF----WAVEfmt "; // (chunk size to be filled in later) - write_word(outputFileStream, 16, 4); // no extension data - write_word(outputFileStream, 1, 2); // PCM - integer samples - write_word(outputFileStream, input->channels, 2); // channels - write_word(outputFileStream, input->sampleRate, 4); // samples per second (Hz) - write_word(outputFileStream, (input->sampleRate * 16 * input->channels) / 8, 4); // (Sample Rate * BitsPerSample * Channels) / 8 - write_word(outputFileStream, input->channels * 2, 2); // data block size (size of integer samples, one for each channel, in bytes) - write_word(outputFileStream, 16, 2); // number of bits per sample (use a multiple of 8) - - // Write the data chunk header - dataChunkPos = outputFileStream.tellp(); - outputFileStream << "data----"; // (chunk size to be filled in later) + writeHeaderToFileStream(input); } - // Prevent clipping - float intScale = (input->peak < 1.0) ? 32767.0f : (32767.0f / input->peak); + size_t maxRoomInCurrentFileInSamples = getMaxWritableNumberOfSamples(input); - if (input->channels == 1) { - for (size_t i = 0, iMax = input->data.size(); i < iMax; i++) { - write_word(outputFileStream, int(input->data[i] * intScale), 2); - } - } - else if (input->channels == 2) { - for (size_t i = 0, iMax = input->data.size() / 2; i < iMax; i++) { - write_word(outputFileStream, int(input->data[i * 2] * intScale), 2); - write_word(outputFileStream, int(input->data[i * 2 + 1] * intScale), 2); - } - } + if (maxRoomInCurrentFileInSamples >= input->data.size()) { + writePayloadToFileStream(input, 0, input->data.size()); + } + else { + //we complete the current file and open another: + writePayloadToFileStream(input, 0, maxRoomInCurrentFileInSamples); - // TODO: Periodically update the RIFF/data chunk size in case of crash? + closeFile(); + + // Open a new file with the next sequence number, and dump the rest of samples in it. + currentSequenceNumber++; + currentFileSize = 0; + + std::string ofName = getOutputFileName(currentSequenceNumber); + outputFileStream.open(ofName.c_str(), std::ios::binary); + + writeHeaderToFileStream(input); + writePayloadToFileStream(input, maxRoomInCurrentFileInSamples, input->data.size()); + } return true; } @@ -121,3 +113,56 @@ bool AudioFileWAV::closeFile() return true; } + +void AudioFileWAV::writeHeaderToFileStream(AudioThreadInputPtr input) { + + // Based on simple wav file output code from + // http://www.cplusplus.com/forum/beginner/166954/ + + // Write the wav file headers + outputFileStream << "RIFF----WAVEfmt "; // (chunk size to be filled in later) + write_word(outputFileStream, 16, 4); // no extension data + write_word(outputFileStream, 1, 2); // PCM - integer samples + write_word(outputFileStream, input->channels, 2); // channels + write_word(outputFileStream, input->sampleRate, 4); // samples per second (Hz) + write_word(outputFileStream, (input->sampleRate * 16 * input->channels) / 8, 4); // (Sample Rate * BitsPerSample * Channels) / 8 + write_word(outputFileStream, input->channels * 2, 2); // data block size (size of integer samples, one for each channel, in bytes) + write_word(outputFileStream, 16, 2); // number of bits per sample (use a multiple of 8) + + // Write the data chunk header + dataChunkPos = outputFileStream.tellp(); + currentFileSize = dataChunkPos; + outputFileStream << "data----"; // (chunk size to be filled in later) +} + +void AudioFileWAV::writePayloadToFileStream(AudioThreadInputPtr input, size_t startInputPosition, size_t endInputPosition) { + + // Prevent clipping + float intScale = (input->peak < 1.0) ? 32767.0f : (32767.0f / input->peak); + + if (input->channels == 1) { + for (size_t i = startInputPosition, iMax = endInputPosition; i < iMax; i++) { + + write_word(outputFileStream, int(input->data[i] * intScale), 2); + + currentFileSize += 2; + } + } + else if (input->channels == 2) { + for (size_t i = startInputPosition, iMax = endInputPosition / 2; i < iMax; i++) { + + write_word(outputFileStream, int(input->data[i * 2] * intScale), 2); + write_word(outputFileStream, int(input->data[i * 2 + 1] * intScale), 2); + + currentFileSize += 4; + } + } +} + +size_t AudioFileWAV::getMaxWritableNumberOfSamples(AudioThreadInputPtr input) { + + long long remainingBytesInFile = (long long)(MAX_WAV_FILE_SIZE) - currentFileSize; + + return (size_t)(remainingBytesInFile / (input->channels * 2)); + +} diff --git a/src/audio/AudioFileWAV.h b/src/audio/AudioFileWAV.h index d64f764..883b893 100644 --- a/src/audio/AudioFileWAV.h +++ b/src/audio/AudioFileWAV.h @@ -21,4 +21,15 @@ public: protected: std::ofstream outputFileStream; size_t dataChunkPos; + long long currentFileSize = 0; + int currentSequenceNumber = 0; + +private: + + size_t getMaxWritableNumberOfSamples(AudioThreadInputPtr input); + + void writeHeaderToFileStream(AudioThreadInputPtr input); + + //write [startInputPosition; endInputPosition[ samples from input into the file. + void writePayloadToFileStream(AudioThreadInputPtr input, size_t startInputPosition, size_t endInputPosition); }; \ No newline at end of file diff --git a/src/demod/DemodulatorInstance.cpp b/src/demod/DemodulatorInstance.cpp index 067745b..743c12a 100644 --- a/src/demod/DemodulatorInstance.cpp +++ b/src/demod/DemodulatorInstance.cpp @@ -648,7 +648,8 @@ void DemodulatorInstance::startRecording() { // fileName << "_" << std::put_time(<m, "%d-%m-%Y_%H-%M-%S"); char timeStr[512]; - strftime(timeStr, sizeof(timeStr), "%d-%m-%Y_%H-%M-%S", <m); + //International format: Year.Month.Day, also lexicographically sortable + strftime(timeStr, sizeof(timeStr), "%Y-%m-%d_%H-%M-%S", <m); fileName << "_" << timeStr;