jamie@233
|
1 #include "WaveFile.h"
|
jamie@233
|
2
|
jamie@233
|
3 #include <fstream>
|
jamie@233
|
4 #include <iostream>
|
jamie@233
|
5 #include <cstdint>
|
jamie@233
|
6 #include <cassert>
|
jamie@233
|
7 #include <cstring>
|
jamie@233
|
8
|
jamie@233
|
9 namespace
|
jamie@233
|
10 {
|
jamie@233
|
11 struct RIFFChunk
|
jamie@233
|
12 {
|
jamie@233
|
13 std::uint32_t chunkID;
|
jamie@233
|
14 std::uint32_t chunkSize;
|
jamie@233
|
15 std::uint32_t format;
|
jamie@233
|
16 };
|
jamie@233
|
17 struct fmtChunk
|
jamie@233
|
18 {
|
jamie@233
|
19 std::uint32_t chunkID;
|
jamie@233
|
20 std::uint32_t chunkSize;
|
jamie@233
|
21 std::uint16_t audioFormat;
|
jamie@233
|
22 std::uint16_t numChannels;
|
jamie@233
|
23 std::uint32_t sampleRate;
|
jamie@233
|
24 std::uint32_t byteRate;
|
jamie@233
|
25 std::uint16_t blockAlign;
|
jamie@233
|
26 std::uint16_t bitsPerSample;
|
jamie@233
|
27 };
|
jamie@233
|
28 struct WaveHeader
|
jamie@233
|
29 {
|
jamie@233
|
30 RIFFChunk riff;
|
jamie@233
|
31 fmtChunk fmt;
|
jamie@233
|
32 };
|
jamie@233
|
33 }
|
jamie@233
|
34
|
jamie@233
|
35 WaveFile::WaveFile() : data(nullptr), size(0)
|
jamie@233
|
36 {
|
jamie@233
|
37 }
|
jamie@233
|
38 WaveFile::WaveFile(const std::string &filename) : data(nullptr), size(0)
|
jamie@233
|
39 {
|
jamie@233
|
40 Load(filename);
|
jamie@233
|
41 }
|
jamie@233
|
42 WaveFile::~WaveFile()
|
jamie@233
|
43 {
|
jamie@233
|
44 Unload();
|
jamie@233
|
45 }
|
jamie@233
|
46
|
jamie@233
|
47 bool WaveFile::Load(const std::string &filename)
|
jamie@233
|
48 {
|
jamie@233
|
49 if (IsLoaded())
|
jamie@233
|
50 {
|
jamie@233
|
51 Unload();
|
jamie@233
|
52 }
|
jamie@233
|
53
|
jamie@233
|
54 std::fstream file(filename, std::ios::in | std::ios::binary);
|
jamie@233
|
55
|
jamie@233
|
56 if (!file.is_open())
|
jamie@233
|
57 {
|
jamie@233
|
58 std::cerr << "Error: Could not open file." << std::endl;
|
jamie@233
|
59 return false;
|
jamie@233
|
60 }
|
jamie@233
|
61
|
jamie@233
|
62 WaveHeader header;
|
jamie@233
|
63 std::memset(&header, 0, sizeof(WaveHeader));
|
jamie@233
|
64
|
jamie@233
|
65 while (file.peek() != std::char_traits<char>::eof())
|
jamie@233
|
66 {
|
jamie@233
|
67 std::uint32_t chunkID;
|
jamie@233
|
68 std::uint32_t chunkSize;
|
jamie@233
|
69
|
jamie@233
|
70 file.read(reinterpret_cast<char*>(&chunkID), sizeof(std::uint32_t));
|
jamie@233
|
71 file.read(reinterpret_cast<char*>(&chunkSize), sizeof(std::uint32_t));
|
jamie@233
|
72
|
jamie@233
|
73 switch (chunkID)
|
jamie@233
|
74 {
|
jamie@233
|
75 case 'FFIR':
|
jamie@233
|
76 {
|
jamie@233
|
77 header.riff.chunkID = chunkID;
|
jamie@233
|
78 header.riff.chunkSize = chunkSize;
|
jamie@233
|
79 file.read(reinterpret_cast<char*>(&header.riff.format), sizeof(std::uint32_t));
|
jamie@233
|
80
|
jamie@233
|
81 if (header.riff.format != 'EVAW')
|
jamie@233
|
82 {
|
jamie@233
|
83 std::cerr << "Error: Not a valid WAVE file." << std::endl;
|
jamie@233
|
84 return false;
|
jamie@233
|
85 }
|
jamie@233
|
86
|
jamie@233
|
87 break;
|
jamie@233
|
88 }
|
jamie@233
|
89 case ' tmf':
|
jamie@233
|
90 {
|
jamie@233
|
91 header.fmt.chunkID = chunkID;
|
jamie@233
|
92 header.fmt.chunkSize = chunkSize;
|
jamie@233
|
93 file.read(reinterpret_cast<char*>(&header.fmt.audioFormat), sizeof(std::uint16_t));
|
jamie@233
|
94 file.read(reinterpret_cast<char*>(&header.fmt.numChannels), sizeof(std::uint16_t));
|
jamie@233
|
95 file.read(reinterpret_cast<char*>(&header.fmt.sampleRate), sizeof(std::uint32_t));
|
jamie@233
|
96 file.read(reinterpret_cast<char*>(&header.fmt.byteRate), sizeof(std::uint32_t));
|
jamie@233
|
97 file.read(reinterpret_cast<char*>(&header.fmt.blockAlign), sizeof(std::uint16_t));
|
jamie@233
|
98 file.read(reinterpret_cast<char*>(&header.fmt.bitsPerSample), sizeof(std::uint16_t));
|
jamie@233
|
99
|
jamie@233
|
100 if (header.fmt.audioFormat != PCM &&
|
jamie@233
|
101 header.fmt.audioFormat != WAVE_FORMAT_IEEE_FLOAT)
|
jamie@233
|
102 {
|
jamie@233
|
103 std::cerr << "Error: Not in valid format" << std::endl;
|
jamie@233
|
104 return false;
|
jamie@233
|
105 }
|
jamie@233
|
106 if (header.fmt.bitsPerSample % 2 != 0)
|
jamie@233
|
107 {
|
jamie@233
|
108 std::cerr << "Error: Invalid number of bits per sample" << std::endl;
|
jamie@233
|
109 return false;
|
jamie@233
|
110 }
|
jamie@233
|
111 if (header.fmt.byteRate != (header.fmt.sampleRate * header.fmt.numChannels * header.fmt.bitsPerSample / 8))
|
jamie@233
|
112 {
|
jamie@233
|
113 std::cerr << "Error: Invalid byte rate" << std::endl;
|
jamie@233
|
114 return false;
|
jamie@233
|
115 }
|
jamie@233
|
116 if (header.fmt.blockAlign != (header.fmt.numChannels * header.fmt.bitsPerSample / 8))
|
jamie@233
|
117 {
|
jamie@233
|
118 std::cerr << "Error: Invalid block align" << std::endl;
|
jamie@233
|
119 return false;
|
jamie@233
|
120 }
|
jamie@233
|
121
|
jamie@233
|
122 break;
|
jamie@233
|
123 }
|
jamie@233
|
124 case 'atad':
|
jamie@233
|
125 {
|
jamie@233
|
126 assert(data == nullptr);
|
jamie@233
|
127 size = chunkSize;
|
jamie@233
|
128 data = new char[size];
|
jamie@233
|
129 file.read(data, chunkSize);
|
jamie@233
|
130
|
jamie@233
|
131 break;
|
jamie@233
|
132 }
|
jamie@233
|
133 default:
|
jamie@233
|
134 {
|
jamie@233
|
135 file.ignore(chunkSize);
|
jamie@233
|
136
|
jamie@233
|
137 break;
|
jamie@233
|
138 }
|
jamie@233
|
139 }
|
jamie@233
|
140 }
|
jamie@233
|
141
|
jamie@233
|
142 // Check that we got all chunks
|
jamie@233
|
143 if (header.riff.chunkID != 'FFIR')
|
jamie@233
|
144 {
|
jamie@233
|
145 std::cerr << "Error: Missing RIFF chunk." << std::endl;
|
jamie@233
|
146 return false;
|
jamie@233
|
147 }
|
jamie@233
|
148 if (header.fmt.chunkID != ' tmf')
|
jamie@233
|
149 {
|
jamie@233
|
150 std::cerr << "Error: Missing fmt chunk." << std::endl;
|
jamie@233
|
151 return false;
|
jamie@233
|
152 }
|
jamie@233
|
153 if (data == nullptr || size == 0)
|
jamie@233
|
154 {
|
jamie@233
|
155 std::cerr << "Error: Missing data chunk." << std::endl;
|
jamie@233
|
156 return false;
|
jamie@233
|
157 }
|
jamie@233
|
158
|
jamie@233
|
159 // Fill meta struct
|
jamie@233
|
160 meta.audioFormat = static_cast<AudioFormat>(header.fmt.audioFormat);
|
jamie@233
|
161 meta.numChannels = header.fmt.numChannels;
|
jamie@233
|
162 meta.sampleRate = header.fmt.sampleRate;
|
jamie@233
|
163 meta.bitsPerSample = header.fmt.bitsPerSample;
|
jamie@233
|
164
|
jamie@233
|
165 return true;
|
jamie@233
|
166 }
|
jamie@233
|
167 void WaveFile::Unload()
|
jamie@233
|
168 {
|
jamie@233
|
169 delete[] data;
|
jamie@233
|
170 data = nullptr;
|
jamie@233
|
171 size = 0;
|
jamie@233
|
172 }
|