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, &current_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