Skip to content

Commit efd3741

Browse files
author
Bri
committed
Add automatic WAV file splitting at 4GB limit
Implements seamless multi-part WAV file splitting to prevent corruption when recordings exceed the 4GB RIFF format limit. Features: - Automatic detection when file approaches 4GB (~3.7GB safety threshold) - Seamless splitting with zero audio data loss - Multi-part file naming: file.wav, file_part2.wav, file_part3.wav, etc. - Each part is a complete, valid WAV file with proper headers - Files can be concatenated in any audio editor for frame-perfect stitching Implementation details: - Added MAX_FILE_SIZE constant (4,000,000,000 bytes) - Track file part number and total data size across parts - Parse and store base filename on Open() for multi-part naming - Check file size before each WriteData() call - If would exceed limit, call SplitToNextFile() first - SplitToNextFile() finalizes current file and opens new part - Reset per-file data size, increment part number - Transparent to CaptureManager - no changes needed upstream Technical notes: - Conservative 3.7GB limit prevents any risk of corruption - Accounts for WAV header overhead in size calculation - Each file independently playable with correct duration - Total recording time unlimited across multiple parts
1 parent ae5645e commit efd3741

2 files changed

Lines changed: 68 additions & 1 deletion

File tree

include/WavWriter.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,16 @@ class WavWriter {
2828
private:
2929
void WriteWavHeader();
3030
void UpdateWavHeader();
31+
bool SplitToNextFile(); // Open next file part seamlessly
32+
33+
static constexpr UINT64 MAX_FILE_SIZE = 4000000000ULL; // ~3.7GB safety limit
3134

3235
std::ofstream m_file;
3336
std::wstring m_filename;
37+
std::wstring m_baseFilename; // Base filename without extension
3438
std::vector<BYTE> m_formatData; // Store full format (WAVEFORMATEX or WAVEFORMATEXTENSIBLE)
35-
UINT32 m_dataSize;
39+
UINT32 m_dataSize; // Data size in current file
40+
UINT64 m_totalDataSize; // Total data written across all parts
41+
UINT32 m_filePartNumber; // Current file part (1, 2, 3...)
3642
std::streampos m_dataStartPos;
3743
};

src/WavWriter.cpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
WavWriter::WavWriter()
55
: m_dataSize(0)
6+
, m_totalDataSize(0)
7+
, m_filePartNumber(1)
68
, m_dataStartPos(0)
79
{
810
}
@@ -19,6 +21,18 @@ bool WavWriter::Open(const std::wstring& filename, const WAVEFORMATEX* format) {
1921
m_filename = filename;
2022
m_dataSize = 0;
2123

24+
// Extract base filename (remove extension) for multi-part file naming
25+
size_t extPos = filename.rfind(L'.');
26+
if (extPos != std::wstring::npos) {
27+
m_baseFilename = filename.substr(0, extPos);
28+
} else {
29+
m_baseFilename = filename;
30+
}
31+
32+
// Reset file part number for new recording
33+
m_filePartNumber = 1;
34+
m_totalDataSize = 0;
35+
2236
// Calculate format size
2337
UINT32 formatSize = sizeof(WAVEFORMATEX);
2438
if (format->wFormatTag == WAVE_FORMAT_EXTENSIBLE && format->cbSize >= 22) {
@@ -47,12 +61,59 @@ bool WavWriter::WriteData(const BYTE* data, UINT32 size) {
4761
return false;
4862
}
4963

64+
// Calculate current file size (header + data)
65+
// WAV header structure: RIFF(4) + size(4) + WAVE(4) + fmt(4) + fmtsize(4) + fmtdata + data(4) + datasize(4) + audiodata
66+
UINT64 currentFileSize = 12 + 8 + static_cast<UINT64>(m_formatData.size()) + 8 + m_dataSize;
67+
68+
// Check if writing this data would exceed the 4GB limit
69+
if (currentFileSize + size > MAX_FILE_SIZE) {
70+
// Split to next file part before writing
71+
if (!SplitToNextFile()) {
72+
return false;
73+
}
74+
}
75+
5076
m_file.write(reinterpret_cast<const char*>(data), size);
5177
m_dataSize += size;
78+
m_totalDataSize += size;
5279

5380
return m_file.good();
5481
}
5582

83+
bool WavWriter::SplitToNextFile() {
84+
// Update current file's header with final sizes
85+
UpdateWavHeader();
86+
87+
// Close current file
88+
m_file.close();
89+
90+
// Increment part number
91+
m_filePartNumber++;
92+
93+
// Generate new filename: baseFilename_part2.wav, baseFilename_part3.wav, etc.
94+
wchar_t partSuffix[32];
95+
swprintf_s(partSuffix, L"_part%u.wav", m_filePartNumber);
96+
std::wstring newFilename = m_baseFilename + partSuffix;
97+
98+
// Reset data size for new file
99+
m_dataSize = 0;
100+
101+
// Open new file
102+
m_file.open(newFilename, std::ios::binary | std::ios::out);
103+
if (!m_file.is_open()) {
104+
return false;
105+
}
106+
107+
// Write header for new file
108+
WriteWavHeader();
109+
m_dataStartPos = m_file.tellp();
110+
111+
// Update current filename
112+
m_filename = newFilename;
113+
114+
return true;
115+
}
116+
56117
void WavWriter::Close() {
57118
if (!m_file.is_open()) {
58119
return;

0 commit comments

Comments
 (0)