Skip to content

Commit 7a4c549

Browse files
committed
move wave file handler to separate module
1 parent 6f43edb commit 7a4c549

File tree

5 files changed

+189
-151
lines changed

5 files changed

+189
-151
lines changed

CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@ project(audio)
33

44
set(CMAKE_CXX_STANDARD 17)
55

6-
add_executable(audio main.cpp FileHandler.cpp FileHandler.h)
6+
add_executable(audio main.cpp FileHandler.cpp FileHandler.h WaveFileHandler.cpp WaveFileHandler.h)

WaveFileHandler.cpp

+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
//
2+
// Created by hmaurya on 21/02/20.
3+
//
4+
5+
#include "WaveFileHandler.h"
6+
7+
8+
void WaveFileHandler::writeRiffChunkDescriptor() {
9+
// Contains the letters "RIFF" in ASCII form
10+
// (0x52494646 big-endian form).
11+
const string chunkID = "RIFF";
12+
13+
// 36 + SubChunk2Size, or more precisely:
14+
// 4 + (8 + SubChunk1Size) + (8 + SubChunk2Size)
15+
// This is the size of the rest of the chunk
16+
// following this number. This is the size of the
17+
// entire file in bytes minus 8 bytes for the
18+
// two fields not included in this count:
19+
// ChunkID and ChunkSize.
20+
// Initialized to 0.
21+
const unsigned int chunkSize = 0;
22+
23+
// Contains the letters "WAVE"
24+
// (0x57415645 big-endian form).
25+
const string format = "WAVE";
26+
27+
// Dumping the header to file with appropriate endianness.
28+
this->rawFileHandler.writeString(chunkID);
29+
this->rawFileHandler.writeBytes(chunkSize, sizeof(chunkSize));
30+
this->rawFileHandler.writeString(format);
31+
}
32+
33+
void WaveFileHandler::setChunkSize() {
34+
size_t currentFileSize = this->rawFileHandler.currentSize();
35+
unsigned int sizeToWrite = currentFileSize - 8;
36+
this->rawFileHandler.modifyBytes(sizeToWrite, 4, 4);
37+
}
38+
39+
void WaveFileHandler::writeFmtSubChunk() {
40+
// Bytes per sample (=2 for 16 bit resolution)
41+
const unsigned bytesPerSample = bitsPerSample / 8;
42+
43+
// Contains the letters "fmt "
44+
// Notice the extra space after fmt to complete 4 bytes.
45+
// (0x666d7420 big-endian form).
46+
const string subchunk1ID = "fmt ";
47+
48+
// 16 for PCM. This is the size of the
49+
// rest of the Subchunk which follows this number.
50+
const unsigned int subchunk1Size = 16;
51+
52+
// PCM = 1 (i.e. Linear quantization)
53+
// Values other than 1 indicate some form of compression.
54+
const unsigned short audioFormat = 1;
55+
56+
// Mono = 1, Stereo = 2, etc.
57+
const unsigned short numChannels = 1;
58+
59+
// Sample rate 8000, 44100, etc.
60+
const unsigned int sampleRate = 44100;
61+
62+
// ByteRate == SampleRate * NumChannels * BytesPerSample
63+
const unsigned int byteRate = sampleRate * numChannels * bytesPerSample;
64+
65+
// BlockAlign == NumChannels * BytesPerSample
66+
// The number of bytes for one sample including all channels.
67+
const unsigned short blockAlign = numChannels * bytesPerSample;
68+
69+
// TODO: Add extra params for non-PCM sounds.
70+
71+
// Finally dumping the header to file.
72+
this->rawFileHandler.writeString(subchunk1ID);
73+
this->rawFileHandler.writeBytes(subchunk1Size, sizeof(subchunk1Size));
74+
this->rawFileHandler.writeBytes(audioFormat, sizeof(audioFormat));
75+
this->rawFileHandler.writeBytes(numChannels, sizeof(numChannels));
76+
this->rawFileHandler.writeBytes(sampleRate, sizeof(sampleRate));
77+
this->rawFileHandler.writeBytes(byteRate, sizeof(byteRate));
78+
this->rawFileHandler.writeBytes(blockAlign, sizeof(blockAlign));
79+
this->rawFileHandler.writeBytes(bitsPerSample, sizeof(bitsPerSample));
80+
}
81+
82+
void WaveFileHandler::writeDataSubChunkHeader() {
83+
// Contains the letters "data"
84+
// (0x64617461 big-endian form).
85+
const string subChunk2ID = "data";
86+
87+
// Subchunk2Size == NumSamples * NumChannels * BitsPerSample/8
88+
// This is the number of bytes in the data.
89+
// You can also think of this as the size
90+
// of the rest of the sub-chunk following this number.
91+
// Initialize to 0 since there is no data initially.
92+
const int subChunk2Size = 0;
93+
94+
this->rawFileHandler.writeString(subChunk2ID);
95+
this->setDataSubChunkSizeLocation(this->rawFileHandler.currentWriteHeadPosition());
96+
this->rawFileHandler.writeBytes(subChunk2Size, sizeof(subChunk2Size));
97+
this->setDataStartLocation(this->rawFileHandler.currentWriteHeadPosition());
98+
}
99+
100+
void WaveFileHandler::writeDataSubChunkSize() {
101+
size_t currentFileSize = this->rawFileHandler.currentSize();
102+
unsigned int sizeToWrite = currentFileSize - this->dataStartLocation;
103+
this->rawFileHandler.modifyBytes(sizeToWrite, this->dataSubChunkSizeLocation, 4);
104+
}
105+
106+
void WaveFileHandler::close() {
107+
// Set Chunk size at the end of all writes (headers and data)
108+
this->setChunkSize();
109+
// Set data sub-chunk size
110+
this->writeDataSubChunkSize();
111+
// Finally close the file
112+
this->rawFileHandler.close();
113+
}

WaveFileHandler.h

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//
2+
// Created by hmaurya on 21/02/20.
3+
//
4+
5+
#ifndef AUDIO_WAVEFILEHANDLER_H
6+
#define AUDIO_WAVEFILEHANDLER_H
7+
#include "FileHandler.h"
8+
#include <string>
9+
10+
using namespace std;
11+
12+
13+
class WaveFileHandler {
14+
private:
15+
FileHandler rawFileHandler;
16+
unsigned int dataSubChunkSizeLocation{};
17+
unsigned int dataStartLocation{};
18+
// Size of a single sample/frame in bits
19+
// 8 bits = 8, 16 bits = 16, etc.
20+
unsigned short bitsPerSample;
21+
22+
void setDataSubChunkSizeLocation(unsigned int locationBytes) {
23+
dataSubChunkSizeLocation = locationBytes;
24+
}
25+
void setDataStartLocation(unsigned int locationBytes) {
26+
dataStartLocation = locationBytes;
27+
}
28+
29+
/**
30+
* Write RIFF header
31+
*/
32+
void writeRiffChunkDescriptor();
33+
34+
/**
35+
* Set chunk size when all headers and data have been written
36+
*/
37+
void setChunkSize();
38+
39+
/**
40+
* Write fmt sub-chunk header
41+
* Currently assuming PCM mode.
42+
* TODO: Make this function generic to write other formats other than PCM as well.
43+
*/
44+
void writeFmtSubChunk();
45+
46+
/**
47+
* Write only the header of data sub-chunk
48+
* Actual data to be written by separate function
49+
*/
50+
void writeDataSubChunkHeader();
51+
52+
void writeDataSubChunkSize();
53+
public:
54+
explicit WaveFileHandler(string filename) {
55+
this->bitsPerSample = 16;
56+
this->rawFileHandler.initializeFile(std::move(filename));
57+
this->writeRiffChunkDescriptor();
58+
this->writeFmtSubChunk();
59+
this->writeDataSubChunkHeader();
60+
}
61+
62+
template <typename T>
63+
void writeSample(T sample_data, size_t sample_size);
64+
65+
void close();
66+
};
67+
68+
#include "WaveFileHandler.tpp"
69+
70+
#endif //AUDIO_WAVEFILEHANDLER_H

WaveFileHandler.tpp

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
template <typename T>
2+
void WaveFileHandler::writeSample(T sample_data, size_t sample_size) {
3+
this->rawFileHandler.writeBytes(sample_data, sample_size);
4+
}

main.cpp

+1-150
Original file line numberDiff line numberDiff line change
@@ -4,163 +4,14 @@
44
#include <utility>
55
#include <math.h>
66
#include "FileHandler.h"
7+
#include "WaveFileHandler.h"
78

89
using namespace std;
910

1011
// The default byte-write order is little-endian
1112
// It changes only for writing strings, i.e. big-endian (the natural order) is used.
1213

13-
class WaveFileHandler {
14-
private:
15-
FileHandler rawFileHandler;
16-
unsigned int dataSubChunkSizeLocation{};
17-
unsigned int dataStartLocation{};
18-
// Size of a single sample/frame in bits
19-
// 8 bits = 8, 16 bits = 16, etc.
20-
unsigned short bitsPerSample;
2114

22-
void setDataSubChunkSizeLocation(unsigned int locationBytes) {
23-
dataSubChunkSizeLocation = locationBytes;
24-
}
25-
void setDataStartLocation(unsigned int locationBytes) {
26-
dataStartLocation = locationBytes;
27-
}
28-
29-
/**
30-
* Write RIFF header
31-
*/
32-
void writeRiffChunkDescriptor() {
33-
// Contains the letters "RIFF" in ASCII form
34-
// (0x52494646 big-endian form).
35-
const string chunkID = "RIFF";
36-
37-
// 36 + SubChunk2Size, or more precisely:
38-
// 4 + (8 + SubChunk1Size) + (8 + SubChunk2Size)
39-
// This is the size of the rest of the chunk
40-
// following this number. This is the size of the
41-
// entire file in bytes minus 8 bytes for the
42-
// two fields not included in this count:
43-
// ChunkID and ChunkSize.
44-
// Initialized to 0.
45-
const unsigned int chunkSize = 0;
46-
47-
// Contains the letters "WAVE"
48-
// (0x57415645 big-endian form).
49-
const string format = "WAVE";
50-
51-
// Dumping the header to file with appropriate endianness.
52-
this->rawFileHandler.writeString(chunkID);
53-
this->rawFileHandler.writeBytes(chunkSize, sizeof(chunkSize));
54-
this->rawFileHandler.writeString(format);
55-
}
56-
57-
/**
58-
* Set chunk size when all headers and data have been written
59-
*/
60-
void setChunkSize() {
61-
size_t currentFileSize = this->rawFileHandler.currentSize();
62-
unsigned int sizeToWrite = currentFileSize - 8;
63-
this->rawFileHandler.modifyBytes(sizeToWrite, 4, 4);
64-
}
65-
66-
/**
67-
* Write fmt sub-chunk header
68-
* Currently assuming PCM mode.
69-
* TODO: Make this function generic to write other formats other than PCM as well.
70-
*/
71-
void writeFmtSubChunk() {
72-
// Bytes per sample (=2 for 16 bit resolution)
73-
const unsigned bytesPerSample = bitsPerSample / 8;
74-
75-
// Contains the letters "fmt "
76-
// Notice the extra space after fmt to complete 4 bytes.
77-
// (0x666d7420 big-endian form).
78-
const string subchunk1ID = "fmt ";
79-
80-
// 16 for PCM. This is the size of the
81-
// rest of the Subchunk which follows this number.
82-
const unsigned int subchunk1Size = 16;
83-
84-
// PCM = 1 (i.e. Linear quantization)
85-
// Values other than 1 indicate some form of compression.
86-
const unsigned short audioFormat = 1;
87-
88-
// Mono = 1, Stereo = 2, etc.
89-
const unsigned short numChannels = 1;
90-
91-
// Sample rate 8000, 44100, etc.
92-
const unsigned int sampleRate = 44100;
93-
94-
// ByteRate == SampleRate * NumChannels * BytesPerSample
95-
const unsigned int byteRate = sampleRate * numChannels * bytesPerSample;
96-
97-
// BlockAlign == NumChannels * BytesPerSample
98-
// The number of bytes for one sample including all channels.
99-
const unsigned short blockAlign = numChannels * bytesPerSample;
100-
101-
// TODO: Add extra params for non-PCM sounds.
102-
103-
// Finally dumping the header to file.
104-
this->rawFileHandler.writeString(subchunk1ID);
105-
this->rawFileHandler.writeBytes(subchunk1Size, sizeof(subchunk1Size));
106-
this->rawFileHandler.writeBytes(audioFormat, sizeof(audioFormat));
107-
this->rawFileHandler.writeBytes(numChannels, sizeof(numChannels));
108-
this->rawFileHandler.writeBytes(sampleRate, sizeof(sampleRate));
109-
this->rawFileHandler.writeBytes(byteRate, sizeof(byteRate));
110-
this->rawFileHandler.writeBytes(blockAlign, sizeof(blockAlign));
111-
this->rawFileHandler.writeBytes(bitsPerSample, sizeof(bitsPerSample));
112-
}
113-
114-
/**
115-
* Write only the header of data sub-chunk
116-
* Actual data to be written by separate function
117-
*/
118-
void writeDataSubChunkHeader() {
119-
// Contains the letters "data"
120-
// (0x64617461 big-endian form).
121-
const string subChunk2ID = "data";
122-
123-
// Subchunk2Size == NumSamples * NumChannels * BitsPerSample/8
124-
// This is the number of bytes in the data.
125-
// You can also think of this as the size
126-
// of the rest of the sub-chunk following this number.
127-
// Initialize to 0 since there is no data initially.
128-
const int subChunk2Size = 0;
129-
130-
this->rawFileHandler.writeString(subChunk2ID);
131-
this->setDataSubChunkSizeLocation(this->rawFileHandler.currentWriteHeadPosition());
132-
this->rawFileHandler.writeBytes(subChunk2Size, sizeof(subChunk2Size));
133-
this->setDataStartLocation(this->rawFileHandler.currentWriteHeadPosition());
134-
}
135-
136-
void writeDataSubChunkSize() {
137-
size_t currentFileSize = this->rawFileHandler.currentSize();
138-
unsigned int sizeToWrite = currentFileSize - this->dataStartLocation;
139-
this->rawFileHandler.modifyBytes(sizeToWrite, this->dataSubChunkSizeLocation, 4);
140-
}
141-
public:
142-
explicit WaveFileHandler(string filename) {
143-
this->bitsPerSample = 16;
144-
this->rawFileHandler.initializeFile(std::move(filename));
145-
this->writeRiffChunkDescriptor();
146-
this->writeFmtSubChunk();
147-
this->writeDataSubChunkHeader();
148-
}
149-
150-
template <typename T>
151-
void writeSample(T sample_data, size_t sample_size) {
152-
this->rawFileHandler.writeBytes(sample_data, sample_size);
153-
}
154-
155-
void close() {
156-
// Set Chunk size at the end of all writes (headers and data)
157-
this->setChunkSize();
158-
// Set data sub-chunk size
159-
this->writeDataSubChunkSize();
160-
// Finally close the file
161-
this->rawFileHandler.close();
162-
}
163-
};
16415

16516
int main() {
16617
WaveFileHandler wave_file("sample.wav");

0 commit comments

Comments
 (0)