1 /*
2 * SampleFormatVorbis.cpp
3 * ----------------------
4 * Purpose: Vorbis sample import
5 * Notes :
6 * Authors: OpenMPT Devs
7 * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
8 */
9
10
11 #include "stdafx.h"
12 #include "Sndfile.h"
13 #ifndef MODPLUG_NO_FILESAVE
14 #include "../common/mptFileIO.h"
15 #endif
16 #include "../common/misc_util.h"
17 #include "Tagging.h"
18 #include "Loaders.h"
19 #include "../common/FileReader.h"
20 #include "modsmp_ctrl.h"
21 #include "openmpt/soundbase/Copy.hpp"
22 #include "mpt/audio/span.hpp"
23 #include "../soundlib/ModSampleCopy.h"
24 //#include "mpt/crc/crc.hpp"
25 #include "OggStream.h"
26 #ifdef MPT_WITH_OGG
27 #if MPT_COMPILER_CLANG
28 #pragma clang diagnostic push
29 #pragma clang diagnostic ignored "-Wreserved-id-macro"
30 #endif // MPT_COMPILER_CLANG
31 #include <ogg/ogg.h>
32 #if MPT_COMPILER_CLANG
33 #pragma clang diagnostic pop
34 #endif // MPT_COMPILER_CLANG
35 #endif // MPT_WITH_OGG
36 #if defined(MPT_WITH_VORBIS)
37 #if MPT_COMPILER_CLANG
38 #pragma clang diagnostic push
39 #pragma clang diagnostic ignored "-Wreserved-id-macro"
40 #endif // MPT_COMPILER_CLANG
41 #include <vorbis/codec.h>
42 #if MPT_COMPILER_CLANG
43 #pragma clang diagnostic pop
44 #endif // MPT_COMPILER_CLANG
45 #endif // MPT_WITH_VORBIS
46 #if defined(MPT_WITH_VORBISFILE)
47 #if MPT_COMPILER_CLANG
48 #pragma clang diagnostic push
49 #pragma clang diagnostic ignored "-Wreserved-id-macro"
50 #endif // MPT_COMPILER_CLANG
51 #include <vorbis/vorbisfile.h>
52 #if MPT_COMPILER_CLANG
53 #pragma clang diagnostic pop
54 #endif // MPT_COMPILER_CLANG
55 #endif // MPT_WITH_VORBISFILE
56 #ifdef MPT_WITH_STBVORBIS
57 #include <stb_vorbis/stb_vorbis.c>
58 #endif // MPT_WITH_STBVORBIS
59
60
61 OPENMPT_NAMESPACE_BEGIN
62
63
64 ////////////////////////////////////////////////////////////////////////////////
65 // Vorbis
66
67 #if defined(MPT_WITH_VORBISFILE)
68
VorbisfileFilereaderRead(void * ptr,size_t size,size_t nmemb,void * datasource)69 static size_t VorbisfileFilereaderRead(void *ptr, size_t size, size_t nmemb, void *datasource)
70 {
71 FileReader &file = *static_cast<FileReader*>(datasource);
72 return file.ReadRaw(mpt::span(mpt::void_cast<std::byte*>(ptr), size * nmemb)).size() / size;
73 }
74
VorbisfileFilereaderSeek(void * datasource,ogg_int64_t offset,int whence)75 static int VorbisfileFilereaderSeek(void *datasource, ogg_int64_t offset, int whence)
76 {
77 FileReader &file = *static_cast<FileReader*>(datasource);
78 switch(whence)
79 {
80 case SEEK_SET:
81 {
82 if(!mpt::in_range<FileReader::off_t>(offset))
83 {
84 return -1;
85 }
86 return file.Seek(mpt::saturate_cast<FileReader::off_t>(offset)) ? 0 : -1;
87 }
88 break;
89 case SEEK_CUR:
90 {
91 if(offset < 0)
92 {
93 if(offset == std::numeric_limits<ogg_int64_t>::min())
94 {
95 return -1;
96 }
97 if(!mpt::in_range<FileReader::off_t>(0-offset))
98 {
99 return -1;
100 }
101 return file.SkipBack(mpt::saturate_cast<FileReader::off_t>(0 - offset)) ? 0 : -1;
102 } else
103 {
104 if(!mpt::in_range<FileReader::off_t>(offset))
105 {
106 return -1;
107 }
108 return file.Skip(mpt::saturate_cast<FileReader::off_t>(offset)) ? 0 : -1;
109 }
110 }
111 break;
112 case SEEK_END:
113 {
114 if(!mpt::in_range<FileReader::off_t>(offset))
115 {
116 return -1;
117 }
118 if(!mpt::in_range<FileReader::off_t>(file.GetLength() + offset))
119 {
120 return -1;
121 }
122 return file.Seek(mpt::saturate_cast<FileReader::off_t>(file.GetLength() + offset)) ? 0 : -1;
123 }
124 break;
125 default:
126 return -1;
127 }
128 }
129
VorbisfileFilereaderTell(void * datasource)130 static long VorbisfileFilereaderTell(void *datasource)
131 {
132 FileReader &file = *static_cast<FileReader*>(datasource);
133 MPT_MAYBE_CONSTANT_IF(!mpt::in_range<long>(file.GetPosition()))
134 {
135 return -1;
136 }
137 return static_cast<long>(file.GetPosition());
138 }
139
140 #if defined(MPT_WITH_VORBIS)
UStringFromVorbis(const char * str)141 static mpt::ustring UStringFromVorbis(const char *str)
142 {
143 return str ? mpt::ToUnicode(mpt::Charset::UTF8, str) : mpt::ustring();
144 }
145 #endif // MPT_WITH_VORBIS
146
GetVorbisFileTags(OggVorbis_File & vf)147 static FileTags GetVorbisFileTags(OggVorbis_File &vf)
148 {
149 FileTags tags;
150 #if defined(MPT_WITH_VORBIS)
151 vorbis_comment *vc = ov_comment(&vf, -1);
152 if(!vc)
153 {
154 return tags;
155 }
156 tags.encoder = UStringFromVorbis(vorbis_comment_query(vc, "ENCODER", 0));
157 tags.title = UStringFromVorbis(vorbis_comment_query(vc, "TITLE", 0));
158 tags.comments = UStringFromVorbis(vorbis_comment_query(vc, "DESCRIPTION", 0));
159 tags.bpm = UStringFromVorbis(vorbis_comment_query(vc, "BPM", 0)); // non-standard
160 tags.artist = UStringFromVorbis(vorbis_comment_query(vc, "ARTIST", 0));
161 tags.album = UStringFromVorbis(vorbis_comment_query(vc, "ALBUM", 0));
162 tags.trackno = UStringFromVorbis(vorbis_comment_query(vc, "TRACKNUMBER", 0));
163 tags.year = UStringFromVorbis(vorbis_comment_query(vc, "DATE", 0));
164 tags.url = UStringFromVorbis(vorbis_comment_query(vc, "CONTACT", 0));
165 tags.genre = UStringFromVorbis(vorbis_comment_query(vc, "GENRE", 0));
166 #else // !MPT_WITH_VORBIS
167 MPT_UNREFERENCED_PARAMETER(vf);
168 #endif // MPT_WITH_VORBIS
169 return tags;
170 }
171
172 #endif // MPT_WITH_VORBISFILE
173
ReadVorbisSample(SAMPLEINDEX sample,FileReader & file)174 bool CSoundFile::ReadVorbisSample(SAMPLEINDEX sample, FileReader &file)
175 {
176
177 #if defined(MPT_WITH_VORBISFILE) || defined(MPT_WITH_STBVORBIS)
178
179 file.Rewind();
180
181 int rate = 0;
182 int channels = 0;
183 std::vector<int16> raw_sample_data;
184
185 std::string sampleName;
186
187 #endif // VORBIS
188
189 #if defined(MPT_WITH_VORBISFILE)
190
191 bool unsupportedSample = false;
192
193 ov_callbacks callbacks = {
194 &VorbisfileFilereaderRead,
195 &VorbisfileFilereaderSeek,
196 NULL,
197 &VorbisfileFilereaderTell
198 };
199 OggVorbis_File vf;
200 MemsetZero(vf);
201 if(ov_open_callbacks(&file, &vf, NULL, 0, callbacks) == 0)
202 {
203 if(ov_streams(&vf) == 1)
204 { // we do not support chained vorbis samples
205 vorbis_info *vi = ov_info(&vf, -1);
206 if(vi && vi->rate > 0 && vi->channels > 0)
207 {
208 sampleName = mpt::ToCharset(GetCharsetInternal(), GetSampleNameFromTags(GetVorbisFileTags(vf)));
209 rate = vi->rate;
210 channels = vi->channels;
211 std::size_t offset = 0;
212 int current_section = 0;
213 long decodedSamples = 0;
214 bool eof = false;
215
216 if(auto length = ov_pcm_total(&vf, 0); length != OV_EINVAL)
217 raw_sample_data.reserve(std::min(MAX_SAMPLE_LENGTH, mpt::saturate_cast<SmpLength>(length)) * std::clamp(channels, 1, 2));
218
219 while(!eof)
220 {
221 float **output = nullptr;
222 long ret = ov_read_float(&vf, &output, 1024, ¤t_section);
223 if(ret == 0)
224 {
225 eof = true;
226 } else if(ret < 0)
227 {
228 // stream error, just try to continue
229 } else
230 {
231 decodedSamples = ret;
232 if(decodedSamples > 0 && (channels == 1 || channels == 2))
233 {
234 raw_sample_data.resize(raw_sample_data.size() + (channels * decodedSamples));
235 CopyAudio(mpt::audio_span_interleaved(raw_sample_data.data() + (offset * channels), channels, decodedSamples), mpt::audio_span_planar(output, channels, decodedSamples));
236 offset += decodedSamples;
237 if((raw_sample_data.size() / channels) > MAX_SAMPLE_LENGTH)
238 {
239 break;
240 }
241 }
242 }
243 }
244 } else
245 {
246 unsupportedSample = true;
247 }
248 } else
249 {
250 unsupportedSample = true;
251 }
252 ov_clear(&vf);
253 } else
254 {
255 unsupportedSample = true;
256 }
257
258 if(unsupportedSample)
259 {
260 return false;
261 }
262
263 #elif defined(MPT_WITH_STBVORBIS)
264
265 // NOTE/TODO: stb_vorbis does not handle inferred negative PCM sample position
266 // at stream start. (See
267 // <https://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-132000A.2>). This
268 // means that, for remuxed and re-aligned/cutted (at stream start) Vorbis
269 // files, stb_vorbis will include superfluous samples at the beginning.
270
271 FileReader::PinnedView fileView = file.GetPinnedView();
272 const std::byte* data = fileView.data();
273 std::size_t dataLeft = fileView.size();
274
275 std::size_t offset = 0;
276 int consumed = 0;
277 int error = 0;
278 stb_vorbis *vorb = stb_vorbis_open_pushdata(mpt::byte_cast<const unsigned char*>(data), mpt::saturate_cast<int>(dataLeft), &consumed, &error, nullptr);
279 file.Skip(consumed);
280 data += consumed;
281 dataLeft -= consumed;
282 if(!vorb)
283 {
284 return false;
285 }
286 rate = stb_vorbis_get_info(vorb).sample_rate;
287 channels = stb_vorbis_get_info(vorb).channels;
288 if(rate <= 0 || channels <= 0)
289 {
290 return false;
291 }
292 while((error == VORBIS__no_error || (error == VORBIS_need_more_data && dataLeft > 0)))
293 {
294 int frame_channels = 0;
295 int decodedSamples = 0;
296 float **output = nullptr;
297 consumed = stb_vorbis_decode_frame_pushdata(vorb, mpt::byte_cast<const unsigned char*>(data), mpt::saturate_cast<int>(dataLeft), &frame_channels, &output, &decodedSamples);
298 file.Skip(consumed);
299 data += consumed;
300 dataLeft -= consumed;
301 LimitMax(frame_channels, channels);
302 if(decodedSamples > 0 && (frame_channels == 1 || frame_channels == 2))
303 {
304 raw_sample_data.resize(raw_sample_data.size() + (channels * decodedSamples));
305 CopyAudio(mpt::audio_span_interleaved(raw_sample_data.data() + (offset * channels), channels, decodedSamples), mpt::audio_span_planar(output, channels, decodedSamples));
306 offset += decodedSamples;
307 if((raw_sample_data.size() / channels) > MAX_SAMPLE_LENGTH)
308 {
309 break;
310 }
311 }
312 error = stb_vorbis_get_error(vorb);
313 }
314 stb_vorbis_close(vorb);
315
316 #endif // VORBIS
317
318 #if defined(MPT_WITH_VORBISFILE) || defined(MPT_WITH_STBVORBIS)
319
320 if(rate <= 0 || channels <= 0 || raw_sample_data.empty())
321 {
322 return false;
323 }
324
325 DestroySampleThreadsafe(sample);
326 ModSample &mptSample = Samples[sample];
327 mptSample.Initialize();
328 mptSample.nC5Speed = rate;
329 mptSample.nLength = std::min(MAX_SAMPLE_LENGTH, mpt::saturate_cast<SmpLength>(raw_sample_data.size() / channels));
330
331 mptSample.uFlags.set(CHN_16BIT);
332 mptSample.uFlags.set(CHN_STEREO, channels == 2);
333
334 if(!mptSample.AllocateSample())
335 {
336 return false;
337 }
338
339 if(raw_sample_data.size() / channels > MAX_SAMPLE_LENGTH)
340 {
341 AddToLog(LogWarning, U_("Sample has been truncated!"));
342 }
343
344 std::copy(raw_sample_data.begin(), raw_sample_data.begin() + mptSample.nLength * channels, mptSample.sample16());
345
346 mptSample.Convert(MOD_TYPE_IT, GetType());
347 mptSample.PrecomputeLoops(*this, false);
348 m_szNames[sample] = sampleName;
349
350 return true;
351
352 #else // !VORBIS
353
354 MPT_UNREFERENCED_PARAMETER(sample);
355 MPT_UNREFERENCED_PARAMETER(file);
356
357 return false;
358
359 #endif // VORBIS
360
361 }
362
363
364 OPENMPT_NAMESPACE_END
365