1 
2 /*
3  * REminiscence - Flashback interpreter
4  * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
5  */
6 
7 #ifdef USE_TREMOR
8 #include <tremor/ivorbisfile.h>
9 #endif
10 #ifdef USE_STB_VORBIS
11 #include "stb_vorbis.c"
12 #endif
13 #include "file.h"
14 #include "mixer.h"
15 #include "ogg_player.h"
16 #include "util.h"
17 
18 #ifdef USE_TREMOR
19 struct VorbisFile: File {
20 	uint32_t offset;
21 
readHelperVorbisFile22 	static size_t readHelper(void *ptr, size_t size, size_t nmemb, void *datasource) {
23 		VorbisFile *vf = (VorbisFile *)datasource;
24 		if (size != 0 && nmemb != 0) {
25 			const int n = vf->read(ptr, size * nmemb);
26 			if (n > 0) {
27 				vf->offset += n;
28 				return n / size;
29 			}
30 		}
31 		return 0;
32 	}
seekHelperVorbisFile33 	static int seekHelper(void *datasource, ogg_int64_t offset, int whence) {
34 		VorbisFile *vf = (VorbisFile *)datasource;
35 		switch (whence) {
36 		case SEEK_SET:
37 			vf->offset = offset;
38 			break;
39 		case SEEK_CUR:
40 			vf->offset += offset;
41 			break;
42 		case SEEK_END:
43 			vf->offset = vf->size() + offset;
44 			break;
45 		}
46 		vf->seek(vf->offset);
47 		return 0;
48 	}
closeHelperVorbisFile49 	static int closeHelper(void *datasource) {
50 		VorbisFile *vf = (VorbisFile *)datasource;
51 		vf->close();
52 		delete vf;
53 		return 0;
54 	}
tellHelperVorbisFile55 	static long tellHelper(void *datasource) {
56 		VorbisFile *vf = (VorbisFile *)datasource;
57 		return vf->offset;
58 	}
59 };
60 
61 struct OggDecoder_impl {
OggDecoder_implOggDecoder_impl62 	OggDecoder_impl()
63 		: _open(false), _readBuf(0), _readBufSize(0) {
64 	}
~OggDecoder_implOggDecoder_impl65 	~OggDecoder_impl() {
66 		free(_readBuf);
67 		_readBuf = 0;
68 		if (_open) {
69 			ov_clear(&_ovf);
70 		}
71 	}
72 
loadOggDecoder_impl73 	bool load(const char *name, FileSystem *fs, int mixerSampleRate) {
74 		if (!_f.open(name, "rb", fs)) {
75 			return false;
76 		}
77 		_f.offset = 0;
78 		ov_callbacks ovcb;
79 		ovcb.read_func  = VorbisFile::readHelper;
80 		ovcb.seek_func  = VorbisFile::seekHelper;
81 		ovcb.close_func = VorbisFile::closeHelper;
82 		ovcb.tell_func  = VorbisFile::tellHelper;
83 		if (ov_open_callbacks(&_f, &_ovf, 0, 0, ovcb) < 0) {
84 			warning("Invalid .ogg file");
85 			return false;
86 		}
87 		_open = true;
88 		vorbis_info *vi = ov_info(&_ovf, -1);
89 		if ((vi->channels != 1 && vi->channels != 2) || vi->rate != mixerSampleRate) {
90 			warning("Unhandled ogg/pcm format ch %d rate %d", vi->channels, vi->rate);
91 			return false;
92 		}
93 		_channels = vi->channels;
94 		return true;
95 	}
readOggDecoder_impl96 	int read(int16_t *dst, int samples) {
97 		int size = samples * _channels * sizeof(int16_t);
98 		if (size > _readBufSize) {
99 			_readBufSize = size;
100 			free(_readBuf);
101 			_readBuf = (int16_t *)malloc(_readBufSize);
102 			if (!_readBuf) {
103 				return 0;
104 			}
105 		}
106 		int count = 0;
107 		while (size > 0) {
108 			const int len = ov_read(&_ovf, (char *)_readBuf, size, 0);
109 			if (len < 0) {
110 				// error in decoder
111 				return count;
112 			} else if (len == 0) {
113 				// loop
114 				ov_raw_seek(&_ovf, 0);
115 				continue;
116 			}
117 			assert((len & 1) == 0);
118 			switch (_channels) {
119 			case 2:
120 				assert((len & 3) == 0);
121 				for (int i = 0; i < len / 2; i += 2) {
122 					const int16_t s16 = (_readBuf[i] + _readBuf[i + 1]) / 2;
123 					*dst = ADDC_S16(*dst, s16);
124 					++dst;
125 				}
126 				break;
127 			case 1:
128 				for (int i = 0; i < len / 2; ++i) {
129 					*dst = ADDC_S16(*dst, _readBuf[i]);
130 					++dst;
131 				}
132 				break;
133 			}
134 			size -= len;
135 			count += len;
136 		}
137 		assert(size == 0);
138 		return count;
139 	}
140 
141 	VorbisFile _f;
142 	OggVorbis_File _ovf;
143 	int _channels;
144 	bool _open;
145 	int16_t *_readBuf;
146 	int _readBufSize;
147 };
148 #endif
149 
150 #ifdef USE_STB_VORBIS
151 static const int kMusicVolume = 192;
152 
153 struct OggDecoder_impl {
OggDecoder_implOggDecoder_impl154 	OggDecoder_impl()
155 		: _v(0) {
156 	}
~OggDecoder_implOggDecoder_impl157 	~OggDecoder_impl() {
158 		if (_v) {
159 			stb_vorbis_close(_v);
160 			_v = 0;
161 		}
162 	}
loadOggDecoder_impl163 	bool load(const char *name, FileSystem *fs, int mixerSampleRate) {
164 		if (!_f.open(name, "rb", fs)) {
165 			return false;
166 		}
167 		_count = _f.read(_buffer, sizeof(_buffer));
168 		if (_count > 0) {
169 			int bytes = 0;
170 			int error = 0;
171 			_v = stb_vorbis_open_pushdata(_buffer, _count, &bytes, &error, 0);
172 			if (_v) {
173 				_offset = bytes;
174 				stb_vorbis_info info = stb_vorbis_get_info(_v);
175 				if (info.channels != 2 || (int)info.sample_rate != mixerSampleRate) {
176 					warning("Unhandled ogg/pcm format ch %d rate %d", info.channels, info.sample_rate);
177 					return false;
178 				}
179 				_decodedSamplesLen = 0;
180 				return true;
181 			}
182 		}
183 		return false;
184 	}
readOggDecoder_impl185 	int read(int16_t *dst, int samples) {
186 		int total = 0;
187 		if (_decodedSamplesLen != 0) {
188 			const int len = MIN(_decodedSamplesLen, samples);
189 			for (int i = 0; i < len; ++i) {
190 				const int sample = (_decodedSamples[0][i] + _decodedSamples[1][i]) / 2;
191 				*dst = ADDC_S16(*dst, ((sample * kMusicVolume) >> 8));
192 				++dst;
193 			}
194 			total += len;
195 			_decodedSamplesLen -= len;
196 		}
197 		while (total < samples) {
198 			int channels = 0;
199 			float **outputs;
200 			int count;
201 			int bytes = stb_vorbis_decode_frame_pushdata(_v, _buffer + _offset, _count - _offset, &channels, &outputs, &count);
202 			if (bytes == 0) {
203 				if (_offset != _count) {
204 					memmove(_buffer, _buffer + _offset, _count - _offset);
205 					_offset = _count - _offset;
206 				} else {
207 					_offset = 0;
208 				}
209 				_count = sizeof(_buffer) - _offset;
210 				bytes = _f.read(_buffer + _offset, _count);
211 				if (bytes < 0) {
212 					break;
213 				}
214 				if (bytes == 0) {
215 					// rewind
216 					_f.seek(0);
217 					_count = _f.read(_buffer, sizeof(_buffer));
218 					stb_vorbis_flush_pushdata(_v);
219 				} else {
220 					_count = _offset + bytes;
221 				}
222 				_offset = 0;
223 				continue;
224 			}
225 			_offset += bytes;
226 			if (channels == 2) {
227 				const int remain = samples - total;
228 				const int len = MIN(count, remain);
229 				for (int i = 0; i < len; ++i) {
230 					const int l = int(outputs[0][i] * 32768 + .5);
231 					const int r = int(outputs[1][i] * 32768 + .5);
232 					const int sample = (l + r) / 2;
233 					*dst = ADDC_S16(*dst, ((sample * kMusicVolume) >> 8));
234 					++dst;
235 				}
236 				if (count > remain) {
237 					_decodedSamplesLen = count - remain;
238 					assert(_decodedSamplesLen < 1024);
239 					for (int i = 0; i < _decodedSamplesLen; ++i) {
240 						_decodedSamples[0][i] = int(outputs[0][len + i] * 32768 + .5);
241 						_decodedSamples[1][i] = int(outputs[1][len + i] * 32768 + .5);
242 					}
243 					total = samples;
244 					break;
245 				}
246 			} else {
247 				warning("Invalid decoded data channels %d count %d", channels, count);
248 			}
249 			total += count;
250 		}
251 		return total;
252 	}
253 
254 	uint8_t _buffer[8192];
255 	int16_t _decodedSamples[2][1024];
256 	int _decodedSamplesLen;
257 	uint32_t _offset, _count;
258 	stb_vorbis *_v;
259 	File _f;
260 };
261 #endif
262 
OggPlayer(Mixer * mixer,FileSystem * fs)263 OggPlayer::OggPlayer(Mixer *mixer, FileSystem *fs)
264 	: _mix(mixer), _fs(fs) {
265 	_impl = new OggDecoder_impl;
266 }
267 
~OggPlayer()268 OggPlayer::~OggPlayer() {
269 	delete _impl;
270 	_impl = 0;
271 }
272 
playTrack(int num)273 bool OggPlayer::playTrack(int num) {
274 	stopTrack();
275 	char buf[16];
276 	snprintf(buf, sizeof(buf), "track%02d.ogg", num);
277 	if (_impl->load(buf, _fs, _mix->getSampleRate())) {
278 		debug(DBG_INFO, "Playing '%s'", buf);
279 		_mix->setPremixHook(mixCallback, this);
280 		return true;
281 	}
282 	return false;
283 }
284 
stopTrack()285 void OggPlayer::stopTrack() {
286 	if (_impl) {
287 		_mix->setPremixHook(0, 0);
288 	}
289 }
290 
pauseTrack()291 void OggPlayer::pauseTrack() {
292 	if (_impl) {
293 		_mix->setPremixHook(0, 0);
294 	}
295 }
296 
resumeTrack()297 void OggPlayer::resumeTrack() {
298 	if (_impl) {
299 		_mix->setPremixHook(mixCallback, this);
300 	}
301 }
302 
mix(int16_t * buf,int len)303 bool OggPlayer::mix(int16_t *buf, int len) {
304 	if (_impl) {
305 		return _impl->read(buf, len) != 0;
306 	}
307 	return false;
308 }
309 
mixCallback(void * param,int16_t * buf,int len)310 bool OggPlayer::mixCallback(void *param, int16_t *buf, int len) {
311 	return ((OggPlayer *)param)->mix(buf, len);
312 }
313 
314