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 "AudioFile.h"
|
||||||
#include "CubicSDR.h"
|
#include "CubicSDR.h"
|
||||||
|
#include <iomanip>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
AudioFile::AudioFile() {
|
AudioFile::AudioFile() {
|
||||||
|
|
||||||
@ -16,7 +18,8 @@ void AudioFile::setOutputFileName(std::string filename) {
|
|||||||
filenameBase = filename;
|
filenameBase = filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string AudioFile::getOutputFileName() {
|
std::string AudioFile::getOutputFileName(int sequenceNumber) {
|
||||||
|
|
||||||
std::string recPath = wxGetApp().getConfig()->getRecordingPath();
|
std::string recPath = wxGetApp().getConfig()->getRecordingPath();
|
||||||
|
|
||||||
// Strip any invalid characters from the name
|
// Strip any invalid characters from the name
|
||||||
@ -30,16 +33,23 @@ std::string AudioFile::getOutputFileName() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create output file name
|
// 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;
|
int idx = 0;
|
||||||
|
|
||||||
// If the file exists; then find the next non-existing file in sequence.
|
// 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);
|
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 void setOutputFileName(std::string filename);
|
||||||
virtual std::string getExtension() = 0;
|
virtual std::string getExtension() = 0;
|
||||||
virtual std::string getOutputFileName();
|
virtual std::string getOutputFileName(int sequenceNumber = 0);
|
||||||
|
|
||||||
virtual bool writeToFile(AudioThreadInputPtr input) = 0;
|
virtual bool writeToFile(AudioThreadInputPtr input) = 0;
|
||||||
virtual bool closeFile() = 0;
|
virtual bool closeFile() = 0;
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
#include "AudioFileWAV.h"
|
#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
|
// Simple endian io read/write handling from
|
||||||
// http://www.cplusplus.com/forum/beginner/31584/#msg171056
|
// http://www.cplusplus.com/forum/beginner/31584/#msg171056
|
||||||
@ -61,44 +63,34 @@ std::string AudioFileWAV::getExtension()
|
|||||||
bool AudioFileWAV::writeToFile(AudioThreadInputPtr input)
|
bool AudioFileWAV::writeToFile(AudioThreadInputPtr input)
|
||||||
{
|
{
|
||||||
if (!outputFileStream.is_open()) {
|
if (!outputFileStream.is_open()) {
|
||||||
std::string ofName = getOutputFileName();
|
std::string ofName = getOutputFileName(currentSequenceNumber);
|
||||||
|
|
||||||
outputFileStream.open(ofName.c_str(), std::ios::binary);
|
outputFileStream.open(ofName.c_str(), std::ios::binary);
|
||||||
|
|
||||||
// Based on simple wav file output code from
|
writeHeaderToFileStream(input);
|
||||||
// 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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevent clipping
|
size_t maxRoomInCurrentFileInSamples = getMaxWritableNumberOfSamples(input);
|
||||||
float intScale = (input->peak < 1.0) ? 32767.0f : (32767.0f / input->peak);
|
|
||||||
|
|
||||||
if (input->channels == 1) {
|
if (maxRoomInCurrentFileInSamples >= input->data.size()) {
|
||||||
for (size_t i = 0, iMax = input->data.size(); i < iMax; i++) {
|
writePayloadToFileStream(input, 0, input->data.size());
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
@ -121,3 +113,56 @@ bool AudioFileWAV::closeFile()
|
|||||||
|
|
||||||
return true;
|
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:
|
protected:
|
||||||
std::ofstream outputFileStream;
|
std::ofstream outputFileStream;
|
||||||
size_t dataChunkPos;
|
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");
|
// fileName << "_" << std::put_time(<m, "%d-%m-%Y_%H-%M-%S");
|
||||||
|
|
||||||
char timeStr[512];
|
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;
|
fileName << "_" << timeStr;
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user