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/fs.h"
20 #include "libaegisub/io.h"
21 #include "libaegisub/log.h"
22 #include "libaegisub/util.h"
23 
24 namespace agi {
GetAudioWithVolume(void * buf,int64_t start,int64_t count,double volume) const25 void AudioProvider::GetAudioWithVolume(void *buf, int64_t start, int64_t count, double volume) const {
26 	GetAudio(buf, start, count);
27 
28 	if (volume == 1.0) return;
29 	if (bytes_per_sample != 2)
30 		throw agi::InternalError("GetAudioWithVolume called on unconverted audio stream");
31 
32 	auto buffer = static_cast<int16_t *>(buf);
33 	for (size_t i = 0; i < (size_t)count; ++i)
34 		buffer[i] = util::mid(-0x8000, static_cast<int>(buffer[i] * volume + 0.5), 0x7FFF);
35 }
36 
ZeroFill(void * buf,int64_t count) const37 void AudioProvider::ZeroFill(void *buf, int64_t count) const {
38 	if (bytes_per_sample == 1)
39 		// 8 bit formats are usually unsigned with bias 127
40 		memset(buf, 127, count * channels);
41 	else // While everything else is signed
42 		memset(buf, 0, count * bytes_per_sample * channels);
43 }
44 
GetAudio(void * buf,int64_t start,int64_t count) const45 void AudioProvider::GetAudio(void *buf, int64_t start, int64_t count) const {
46 	if (start < 0) {
47 		ZeroFill(buf, std::min(-start, count));
48 		buf = static_cast<char *>(buf) + -start * bytes_per_sample * channels;
49 		count += start;
50 		start = 0;
51 	}
52 
53 	if (start + count > num_samples) {
54 		int64_t zero_count = std::min(count, start + count - num_samples);
55 		count -= zero_count;
56 		ZeroFill(static_cast<char *>(buf) + count * bytes_per_sample * channels, zero_count);
57 	}
58 
59 	if (count <= 0) return;
60 
61 	try {
62 		FillBuffer(buf, start, count);
63 	}
64 	catch (AudioDecodeError const& e) {
65 		// We don't have any good way to report errors here, so just log the
66 		// failure and return silence
67 		LOG_E("audio_provider") << e.GetMessage();
68 		ZeroFill(buf, count);
69 		return;
70 	}
71 	catch (...) {
72 		LOG_E("audio_provider") << "Unknown audio decoding error";
73 		ZeroFill(buf, count);
74 		return;
75 	}
76 }
77 
78 namespace {
79 class writer {
80 	io::Save outfile;
81 	std::ostream& out;
82 
83 public:
writer(agi::fs::path const & filename)84 	writer(agi::fs::path const& filename) : outfile(filename, true), out(outfile.Get()) { }
85 
86 	template<int N>
write(const char (& str)[N])87 	void write(const char(&str)[N]) {
88 		out.write(str, N - 1);
89 	}
90 
write(std::vector<char> const & data)91 	void write(std::vector<char> const& data) {
92 		out.write(data.data(), data.size());
93 	}
94 
95 	template<typename Dest, typename Src>
write(Src v)96 	void write(Src v) {
97 		auto converted = static_cast<Dest>(v);
98 		out.write(reinterpret_cast<char *>(&converted), sizeof(Dest));
99 	}
100 };
101 }
102 
SaveAudioClip(AudioProvider * provider,fs::path const & path,int start_time,int end_time)103 void SaveAudioClip(AudioProvider *provider, fs::path const& path, int start_time, int end_time) {
104 	auto start_sample = ((int64_t)start_time * provider->GetSampleRate() + 999) / 1000;
105 	auto end_sample = ((int64_t)end_time * provider->GetSampleRate() + 999) / 1000;
106 	if (start_sample >= provider->GetNumSamples() || start_sample >= end_sample) return;
107 
108 	size_t bytes_per_sample = provider->GetBytesPerSample() * provider->GetChannels();
109 	size_t bufsize = (end_sample - start_sample) * bytes_per_sample;
110 
111 	writer out{path};
112 	out.write("RIFF");
113 	out.write<int32_t>(bufsize + 36);
114 
115 	out.write("WAVEfmt ");
116 	out.write<int32_t>(16); // Size of chunk
117 	out.write<int16_t>(1);  // compression format (PCM)
118 	out.write<int16_t>(provider->GetChannels());
119 	out.write<int32_t>(provider->GetSampleRate());
120 	out.write<int32_t>(provider->GetSampleRate() * provider->GetChannels() * provider->GetBytesPerSample());
121 	out.write<int16_t>(provider->GetChannels() * provider->GetBytesPerSample());
122 	out.write<int16_t>(provider->GetBytesPerSample() * 8);
123 
124 	out.write("data");
125 	out.write<int32_t>(bufsize);
126 
127 	// samples per read
128 	size_t spr = 65536 / bytes_per_sample;
129 	std::vector<char> buf;
130 	for (int64_t i = start_sample; i < end_sample; i += spr) {
131 		spr = std::min<size_t>(spr, end_sample - i);
132 		buf.resize(spr * bytes_per_sample);
133 		provider->GetAudio(&buf[0], i, spr);
134 		out.write(buf);
135 	}
136 }
137 }
138