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 :)
This commit is contained in:
vsonnier 2018-01-07 08:55:38 +01:00
parent 8daadc3603
commit fc0f20f07d
5 changed files with 106 additions and 39 deletions

View File

@ -3,6 +3,8 @@
#include "AudioFile.h"
#include "CubicSDR.h"
#include <iomanip>
#include <sstream>
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();
}

View File

@ -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;

View File

@ -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));
}

View File

@ -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);
};

View File

@ -648,7 +648,8 @@ void DemodulatorInstance::startRecording() {
// fileName << "_" << std::put_time(&ltm, "%d-%m-%Y_%H-%M-%S");
char timeStr[512];
strftime(timeStr, sizeof(timeStr), "%d-%m-%Y_%H-%M-%S", &ltm);
//International format: Year.Month.Day, also lexicographically sortable
strftime(timeStr, sizeof(timeStr), "%Y-%m-%d_%H-%M-%S", &ltm);
fileName << "_" << timeStr;