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