/
sound_data.cpp
235 lines (189 loc) · 7.58 KB
/
sound_data.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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
/*
Minetest
Copyright (C) 2022 DS
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
OpenAL support based on work by:
Copyright (C) 2011 Sebastian 'Bahamada' Rühl
Copyright (C) 2011 Cyriaque 'Cisoun' Skrapits <cysoun@gmail.com>
Copyright (C) 2011 Giuseppe Bilotta <giuseppe.bilotta@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; ifnot, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "sound_data.h"
#include "sound_constants.h"
namespace sound {
/*
* ISoundDataOpen struct
*/
std::shared_ptr<ISoundDataOpen> ISoundDataOpen::fromOggFile(std::unique_ptr<RAIIOggFile> oggfile,
const std::string &filename_for_logging)
{
// Get some information about the OGG file
std::optional<OggFileDecodeInfo> decode_info = oggfile->getDecodeInfo(filename_for_logging);
if (!decode_info.has_value()) {
warningstream << "Audio: Error decoding "
<< filename_for_logging << std::endl;
return nullptr;
}
// use duration (in seconds) to decide whether to load all at once or to stream
if (decode_info->length_seconds <= SOUND_DURATION_MAX_SINGLE) {
return std::make_shared<SoundDataOpenBuffer>(std::move(oggfile), *decode_info);
} else {
return std::make_shared<SoundDataOpenStream>(std::move(oggfile), *decode_info);
}
}
/*
* SoundDataUnopenBuffer struct
*/
std::shared_ptr<ISoundDataOpen> SoundDataUnopenBuffer::open(const std::string &sound_name) &&
{
// load from m_buffer
auto oggfile = std::make_unique<RAIIOggFile>();
auto buffer_source = std::make_unique<OggVorbisBufferSource>();
buffer_source->buf = std::move(m_buffer);
oggfile->m_needs_clear = true;
if (ov_open_callbacks(buffer_source.release(), oggfile->get(), nullptr, 0,
OggVorbisBufferSource::s_ov_callbacks) != 0) {
warningstream << "Audio: Error opening " << sound_name << " for decoding"
<< std::endl;
return nullptr;
}
return ISoundDataOpen::fromOggFile(std::move(oggfile), sound_name);
}
/*
* SoundDataUnopenFile struct
*/
std::shared_ptr<ISoundDataOpen> SoundDataUnopenFile::open(const std::string &sound_name) &&
{
// load from file at m_path
auto oggfile = std::make_unique<RAIIOggFile>();
if (ov_fopen(m_path.c_str(), oggfile->get()) != 0) {
warningstream << "Audio: Error opening " << m_path << " for decoding"
<< std::endl;
return nullptr;
}
oggfile->m_needs_clear = true;
return ISoundDataOpen::fromOggFile(std::move(oggfile), sound_name);
}
/*
* SoundDataOpenBuffer struct
*/
SoundDataOpenBuffer::SoundDataOpenBuffer(std::unique_ptr<RAIIOggFile> oggfile,
const OggFileDecodeInfo &decode_info) : ISoundDataOpen(decode_info)
{
m_buffer = oggfile->loadBuffer(m_decode_info, 0, m_decode_info.length_samples);
if (m_buffer.get() == 0) {
warningstream << "SoundDataOpenBuffer: Failed to load sound \""
<< m_decode_info.name_for_logging << "\"" << std::endl;
return;
}
}
/*
* SoundDataOpenStream struct
*/
SoundDataOpenStream::SoundDataOpenStream(std::unique_ptr<RAIIOggFile> oggfile,
const OggFileDecodeInfo &decode_info) :
ISoundDataOpen(decode_info), m_oggfile(std::move(oggfile))
{
// do nothing here. buffers are loaded at getOrLoadBufferAt
}
std::tuple<ALuint, ALuint, ALuint> SoundDataOpenStream::getOrLoadBufferAt(ALuint offset)
{
if (offset >= m_decode_info.length_samples)
return {0, m_decode_info.length_samples, 0};
// find the right-most ContiguousBuffers, such that `m_start <= offset`
// equivalent: the first element from the right such that `!(m_start > offset)`
// (from the right, `offset` is a lower bound to the `m_start`s)
auto lower_rit = std::lower_bound(m_bufferss.rbegin(), m_bufferss.rend(), offset,
[](const ContiguousBuffers &bufs, ALuint offset) {
return bufs.m_start > offset;
});
if (lower_rit != m_bufferss.rend()) {
std::vector<SoundBufferUntil> &bufs = lower_rit->m_buffers;
// find the left-most SoundBufferUntil, such that `m_end > offset`
// equivalent: the first element from the left such that `m_end > offset`
// (returns first element where comp gives true)
auto upper_it = std::upper_bound(bufs.begin(), bufs.end(), offset,
[](ALuint offset, const SoundBufferUntil &buf) {
return offset < buf.m_end;
});
if (upper_it != bufs.end()) {
ALuint start = upper_it == bufs.begin() ? lower_rit->m_start
: (upper_it - 1)->m_end;
return {upper_it->m_buffer.get(), upper_it->m_end, offset - start};
}
}
// no loaded buffer starts before or at `offset`
// or no loaded buffer (that starts before or at `offset`) ends after `offset`
// lower_rit, but not reverse and 1 farther
auto after_it = m_bufferss.begin() + (m_bufferss.rend() - lower_rit);
return loadBufferAt(offset, after_it);
}
std::tuple<ALuint, ALuint, ALuint> SoundDataOpenStream::loadBufferAt(ALuint offset,
std::vector<ContiguousBuffers>::iterator after_it)
{
bool has_before = after_it != m_bufferss.begin();
bool has_after = after_it != m_bufferss.end();
ALuint end_before = has_before ? (after_it - 1)->m_buffers.back().m_end : 0;
ALuint start_after = has_after ? after_it->m_start : m_decode_info.length_samples;
const ALuint min_buf_len_samples = m_decode_info.freq * MIN_STREAM_BUFFER_LENGTH;
//
// 1) Find the actual start and end of the new buffer
//
ALuint new_buf_start = offset;
ALuint new_buf_end = offset + min_buf_len_samples;
// Don't load into next buffer, or past the end
if (new_buf_end > start_after) {
new_buf_end = start_after;
// Also move start (for min buf size) (but not *into* previous buffer)
if (new_buf_end - new_buf_start < min_buf_len_samples) {
new_buf_start = std::max(
end_before,
new_buf_end < min_buf_len_samples ? 0
: new_buf_end - min_buf_len_samples
);
}
}
// Widen if space to right or left is smaller than min buf size
if (new_buf_start - end_before < min_buf_len_samples)
new_buf_start = end_before;
if (start_after - new_buf_end < min_buf_len_samples)
new_buf_end = start_after;
//
// 2) Load [new_buf_start, new_buf_end)
//
// If it fails, we get a 0-buffer. we store it and won't try loading again
RAIIALSoundBuffer new_buf = m_oggfile->loadBuffer(m_decode_info, new_buf_start,
new_buf_end);
//
// 3) Insert before after_it
//
// Choose ContiguousBuffers to add the new SoundBufferUntil into:
// * `after_it - 1` (=before) if existent and if there's no space between its
// last buffer and the new buffer
// * A new ContiguousBuffers otherwise
auto it = has_before && new_buf_start == end_before ? after_it - 1
: m_bufferss.insert(after_it, ContiguousBuffers{new_buf_start, {}});
// Add the new SoundBufferUntil
size_t new_buf_i = it->m_buffers.size();
it->m_buffers.push_back(SoundBufferUntil{new_buf_end, std::move(new_buf)});
if (has_after && new_buf_end == start_after) {
// Merge after into my ContiguousBuffers
auto &bufs = it->m_buffers;
auto &bufs_after = (it + 1)->m_buffers;
bufs.insert(bufs.end(), std::make_move_iterator(bufs_after.begin()),
std::make_move_iterator(bufs_after.end()));
it = m_bufferss.erase(it + 1) - 1;
}
return {it->m_buffers[new_buf_i].m_buffer.get(), new_buf_end, offset - new_buf_start};
}
} // namespace sound