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