mirror of
https://github.com/cjcliffe/CubicSDR.git
synced 2024-11-23 12:18:37 -05:00
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:
parent
8daadc3603
commit
fc0f20f07d
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
@ -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;
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user