1 // Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
2 //
3 // Permission to use, copy, modify, and distribute this software for any
4 // purpose with or without fee is hereby granted, provided that the above
5 // copyright notice and this permission notice appear in all copies.
6 //
7 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10 // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12 // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13 // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14 //
15 // Aegisub Project http://www.aegisub.org/
16
17 #include "libaegisub/audio/provider.h"
18
19 #include "libaegisub/file_mapping.h"
20 #include "libaegisub/fs.h"
21 #include "libaegisub/make_unique.h"
22
23 #include <array>
24 #include <vector>
25
26 namespace {
27 using namespace agi;
28
29 struct IndexPoint {
30 uint64_t start_byte;
31 uint64_t num_samples;
32 };
33
34 struct file_ended {};
35
36 class PCMAudioProvider : public AudioProvider {
FillBuffer(void * buf,int64_t start,int64_t count) const37 void FillBuffer(void *buf, int64_t start, int64_t count) const override {
38 auto write_buf = static_cast<char *>(buf);
39 auto bps = bytes_per_sample * channels;
40 uint64_t pos = 0;
41
42 for (auto ip : index_points) {
43 if (count == 0) break;
44 if (pos + ip.num_samples <= (uint64_t)start) {
45 pos += ip.num_samples;
46 continue;
47 }
48
49 auto read_offset = start - pos;
50 auto read_count = std::min<size_t>(count, ip.num_samples - read_offset);
51 auto bytes = read_count * bps;
52 memcpy(write_buf, file.read(ip.start_byte + read_offset * bps, bytes), bytes);
53
54 write_buf += bytes;
55 count -= read_count;
56 start += read_count;
57 pos += ip.num_samples;
58 }
59 }
60
61 protected:
62 mutable read_file_mapping file;
63 uint64_t file_pos = 0;
64
PCMAudioProvider(fs::path const & filename)65 PCMAudioProvider(fs::path const& filename) : file(filename) { }
66
67 template<typename T, typename UInt>
Read(UInt * data_left)68 const T *Read(UInt *data_left) {
69 if (*data_left < sizeof(T)) throw file_ended();
70 if (file.size() - file_pos < sizeof(T)) throw file_ended();
71
72 auto data = file.read(file_pos, sizeof(T));
73 file_pos += sizeof(T);
74 *data_left -= sizeof(T);
75 return reinterpret_cast<const T *>(data);
76 }
77
78 std::vector<IndexPoint> index_points;
79 };
80
81 struct FourCC {
82 std::array<char, 4> data;
operator !=__anon9b2fca840111::FourCC83 bool operator!=(const char *cmp) const {
84 return data[0] != cmp[0] || data[1] != cmp[1]
85 || data[2] != cmp[2] || data[3] != cmp[3];
86 }
operator ==__anon9b2fca840111::FourCC87 bool operator==(const char *cmp) const { return !(*this != cmp); }
88 };
89
90 // Overview of RIFF WAV: <http://www.sonicspot.com/guide/wavefiles.html>
91 struct RiffWav {
92 using DataSize = uint32_t;
93 using ChunkId = FourCC;
94
riff_id__anon9b2fca840111::RiffWav95 static const char *riff_id() { return "RIFF"; }
wave_id__anon9b2fca840111::RiffWav96 static const char *wave_id() { return "WAVE"; }
fmt_id__anon9b2fca840111::RiffWav97 static const char *fmt_id() { return "fmt "; }
data_id__anon9b2fca840111::RiffWav98 static const char *data_id() { return "data "; }
99
100 static const int alignment = 1;
101
data_size__anon9b2fca840111::RiffWav102 static uint32_t data_size(uint32_t size) { return size; }
chunk_size__anon9b2fca840111::RiffWav103 static uint32_t chunk_size(uint32_t size) { return size; }
104 };
105
106 typedef std::array<uint8_t, 16> GUID;
107
108 static const GUID w64GuidRIFF = {{
109 // {66666972-912E-11CF-A5D6-28DB04C10000}
110 0x72, 0x69, 0x66, 0x66, 0x2E, 0x91, 0xCF, 0x11, 0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00
111 }};
112
113 static const GUID w64GuidWAVE = {{
114 // {65766177-ACF3-11D3-8CD1-00C04F8EDB8A}
115 0x77, 0x61, 0x76, 0x65, 0xF3, 0xAC, 0xD3, 0x11, 0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A
116 }};
117
118 static const GUID w64Guidfmt = {{
119 // {20746D66-ACF3-11D3-8CD1-00C04F8EDB8A}
120 0x66, 0x6D, 0x74, 0x20, 0xF3, 0xAC, 0xD3, 0x11, 0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A
121 }};
122
123 static const GUID w64Guiddata = {{
124 // {61746164-ACF3-11D3-8CD1-00C04F8EDB8A}
125 0x64, 0x61, 0x74, 0x61, 0xF3, 0xAC, 0xD3, 0x11, 0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A
126 }};
127
128 // http://www.vcs.de/fileadmin/user_upload/MBS/PDF/Whitepaper/Informations_about_Sony_Wave64.pdf
129 struct Wave64 {
130 using DataSize = uint64_t;
131 using ChunkId = GUID;
132
riff_id__anon9b2fca840111::Wave64133 static GUID riff_id() { return w64GuidRIFF; }
wave_id__anon9b2fca840111::Wave64134 static GUID wave_id() { return w64GuidWAVE; }
fmt_id__anon9b2fca840111::Wave64135 static GUID fmt_id() { return w64Guidfmt; }
data_id__anon9b2fca840111::Wave64136 static GUID data_id() { return w64Guiddata; }
137
138 static const uint64_t alignment = 7ULL;
139
140 // Wave 64 includes the size of the header in the chunk sizes
data_size__anon9b2fca840111::Wave64141 static uint64_t data_size(uint64_t size) { return size - 16; }
chunk_size__anon9b2fca840111::Wave64142 static uint64_t chunk_size(uint64_t size) { return size - 24; }
143 };
144
145 template<typename Impl>
146 class WavPCMAudioProvider : public PCMAudioProvider {
147 public:
WavPCMAudioProvider(fs::path const & filename)148 WavPCMAudioProvider(fs::path const& filename)
149 : PCMAudioProvider(filename)
150 {
151 using DataSize = typename Impl::DataSize;
152 using ChunkId = typename Impl::ChunkId;
153
154 try {
155 auto data_left = std::numeric_limits<DataSize>::max();
156 if (*Read<ChunkId>(&data_left) != Impl::riff_id())
157 throw AudioDataNotFound("File is not a RIFF file");
158
159 data_left = Impl::data_size(*Read<DataSize>(&data_left));
160
161 if (*Read<ChunkId>(&data_left) != Impl::wave_id())
162 throw AudioDataNotFound("File is not a RIFF WAV file");
163
164 while (data_left) {
165 auto chunk_fcc = *Read<ChunkId>(&data_left);
166 auto chunk_size = Impl::chunk_size(*Read<DataSize>(&data_left));
167
168 data_left -= chunk_size;
169
170 if (chunk_fcc == Impl::fmt_id()) {
171 if (channels || sample_rate || bytes_per_sample)
172 throw AudioProviderError("Multiple 'fmt ' chunks not supported");
173
174 auto compression = *Read<uint16_t>(&chunk_size);
175 if (compression != 1)
176 throw AudioProviderError("File is not uncompressed PCM");
177
178 channels = *Read<uint16_t>(&chunk_size);
179 sample_rate = *Read<uint32_t>(&chunk_size);
180 Read<uint32_t>(&chunk_size); // Average bytes per sample; meaningless
181 Read<uint16_t>(&chunk_size); // Block align
182 bytes_per_sample = (*Read<uint16_t>(&chunk_size) + 7) / 8;
183 }
184 else if (chunk_fcc == Impl::data_id()) {
185 if (!channels || !sample_rate || !bytes_per_sample)
186 throw AudioProviderError("Found 'data' chunk without format being set.");
187 index_points.emplace_back(IndexPoint{file_pos, chunk_size / bytes_per_sample / channels});
188 num_samples += chunk_size / bytes_per_sample / channels;
189 }
190 // There's a bunch of other chunk types. They're all dumb.
191
192 // blocks are aligned and the padding bytes are not included in
193 // the size of the chunk
194 file_pos += (chunk_size + Impl::alignment) & ~Impl::alignment;
195 }
196
197 }
198 catch (file_ended) {
199 if (!channels || !sample_rate || !bytes_per_sample)
200 throw AudioDataNotFound("File ended before reaching format chunk");
201 // Truncated files are fine otherwise
202 }
203 decoded_samples = num_samples;
204 }
205 };
206 }
207
208 namespace agi {
CreatePCMAudioProvider(fs::path const & filename,BackgroundRunner *)209 std::unique_ptr<AudioProvider> CreatePCMAudioProvider(fs::path const& filename, BackgroundRunner *) {
210 bool wrong_file_type = true;
211 std::string msg;
212
213 try {
214 return make_unique<WavPCMAudioProvider<RiffWav>>(filename);
215 }
216 catch (AudioDataNotFound const& err) {
217 msg = "RIFF PCM WAV audio provider: " + err.GetMessage();
218 }
219 catch (AudioProviderError const& err) {
220 wrong_file_type = false;
221 msg = "RIFF PCM WAV audio provider: " + err.GetMessage();
222 }
223
224 try {
225 return make_unique<WavPCMAudioProvider<Wave64>>(filename);
226 }
227 catch (AudioDataNotFound const& err) {
228 msg += "\nWave64 audio provider: " + err.GetMessage();
229 }
230 catch (AudioProviderError const& err) {
231 wrong_file_type = false;
232 msg += "\nWave64 audio provider: " + err.GetMessage();
233 }
234
235 if (wrong_file_type)
236 throw AudioDataNotFound(msg);
237 else
238 throw AudioProviderError(msg);
239 }
240 }
241