forked from emilk/emilib
-
Notifications
You must be signed in to change notification settings - Fork 0
/
wav.cpp
128 lines (102 loc) · 3.42 KB
/
wav.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#include "wav.hpp"
#include <cstdint>
#include <cstring>
#include <stdexcept>
#include <string>
#include <loguru.hpp>
namespace emilib {
Wav parse_wav(const void* wav_void_data, size_t wav_size)
{
const uint8_t* data = reinterpret_cast<const uint8_t*>(wav_void_data);
size_t pos = 0;
auto read_uint16 = [&]() -> uint16_t
{
if (pos + sizeof(uint16_t) > wav_size) {
throw std::runtime_error("Premature end of WAV file.");
}
pos += sizeof(uint16_t);
return uint16_t(data[pos - 2])
| uint16_t(uint16_t(data[pos - 1]) << 8);
};
auto read_uint32 = [&]() -> uint32_t
{
if (pos + sizeof(uint32_t) > wav_size) {
throw std::runtime_error("Premature end of WAV file.");
}
pos += sizeof(uint32_t);
return (uint32_t(data[pos - 4]) << 0)
| (uint32_t(data[pos - 3]) << 8)
| (uint32_t(data[pos - 2]) << 16)
| (uint32_t(data[pos - 1]) << 24);
};
auto skip = [&](size_t n)
{
if (pos + n > wav_size) {
throw std::runtime_error("Premature end of WAV file.");
}
pos += n;
};
auto check_four_bytes = [&](const char* expected)
{
if (pos + 4 > wav_size) {
throw std::runtime_error("Premature end of WAV file.");
}
return strncmp(reinterpret_cast<const char*>(data + pos), expected, 4) == 0;
};
auto skip_four_bytes = [&](const char* expected)
{
if (!check_four_bytes(expected)) {
throw std::runtime_error("Not a WAV file: missing '" + std::string(expected) + "' block");
}
skip(4); // skip expected
};
// ------------------------------------------------------------------------
skip_four_bytes("RIFF");
// we had 'RIFF' let's continue
skip(4); // skip file size, which is probably wrong anyway.
skip_four_bytes("WAVE");
// ------------------------------------------------------------------------
if (check_four_bytes("JUNK")) {
skip(4); // skip "JUNK"
uint32_t chunk_size = read_uint32();
chunk_size += (chunk_size % 2);
skip(chunk_size);
}
// ------------------------------------------------------------------------
skip_four_bytes("fmt ");
uint32_t chunk_size = read_uint32();
if (chunk_size != 16) {
throw std::runtime_error("Expected 'fmt ' block to be 16 bytes, was " + std::to_string(chunk_size));
}
int16_t audio_format = read_uint16(); // our 16 values
if (audio_format != 1) {
throw std::runtime_error("Not PCM");
}
uint16_t channels = read_uint16(); // 1 mono, 2 stereo
uint32_t sample_rate = read_uint32();
uint32_t bytes_per_sec = read_uint32();
// uint16_t block_align = read_uint16();
skip(2); // Skip block_align - its probably 2 bytes.
uint16_t bits_per_sample = read_uint16(); // 8 bit or 16 bit file?
// ------------------------------------------------------------------------
skip_four_bytes("data");
uint32_t data_size = read_uint32(); // How many bytes of sound data we have
if (pos + data_size < wav_size) {
// This happens, and I'm not sure why.
auto extra = wav_size - data_size - pos;
LOG_F(WARNING, "%lu bytes of extra data in WAV file", extra);
}
if (pos + data_size > wav_size) {
throw std::runtime_error("Premature end of WAV file.");
}
// ------------------------------------------------------------------------
Wav wav;
wav.duration_sec = data_size / (double)bytes_per_sec;
wav.channels = channels;
wav.bits_per_sample = bits_per_sample;
wav.sample_rate = sample_rate;
wav.data = data + pos;
wav.data_size = data_size;
return wav;
}
} // namespace emilib