1 /*
2 * SPDX-License-Identifier: GPL-2.0-or-later
3 *
4 * Copyright (C) 2020-2021 The DOSBox Staging Team
5 * Copyright (C) 2018-2021 kcgen <kcgen@users.noreply.github.com>
6 * Copyright (C) 2001-2017 Ryan C. Gordon <icculus@icculus.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 */
22
23 /*
24 * DOSBox Vorbis decoder API implementation
25 * -------------------------------------
26 * It makes use of the stand-alone STB Vorbis library:
27 * - STB: https://github.com/nothings/stb (source)
28 * - STB: https://twitter.com/nothings (website/author info)
29 */
30
31 #if HAVE_CONFIG_H
32 # include <config.h>
33 #endif
34
35 #ifdef memcpy
36 # undef memcpy
37 #endif
38
39 #include <string.h> /* memcpy */
40 #include <math.h> /* lroundf */
41
42 #include "SDL_sound.h"
43 #define __SDL_SOUND_INTERNAL__
44 #include "SDL_sound_internal.h"
45
46 #ifdef asset
47 # undef assert
48 # define assert SDL_assert
49 #endif
50
51 #ifdef memset
52 # undef memset
53 # define memset SDL_memset
54 #endif
55
56 #define free SDL_free
57 #define qsort SDL_qsort
58 #define memcmp SDL_memcmp
59 #define malloc SDL_malloc
60 #define realloc SDL_realloc
61 #define dealloca(x) SDL_stack_free((x))
62
63 /* Configure and include stb_vorbis for compiling... */
64 #define STB_VORBIS_NO_STDIO 1
65 #define STB_VORBIS_NO_CRT 1
66 #define STB_VORBIS_NO_PUSHDATA_API 1
67 #define STB_VORBIS_MAX_CHANNELS 2
68 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
69 #define STB_VORBIS_BIG_ENDIAN 1
70 #endif
71
72 #include "stb_vorbis.h"
73
74 #ifdef DEBUG_CHATTER
vorbis_error_string(const int err)75 static const char *vorbis_error_string(const int err)
76 {
77 switch (err)
78 {
79 case VORBIS__no_error: return NULL;
80 case VORBIS_need_more_data: return "VORBIS: need more data";
81 case VORBIS_invalid_api_mixing: return "VORBIS: can't mix API modes";
82 case VORBIS_outofmem: return "VORBIS: out of memory";
83 case VORBIS_feature_not_supported: return "VORBIS: feature not supported";
84 case VORBIS_too_many_channels: return "VORBIS: too many channels";
85 case VORBIS_file_open_failure: return "VORBIS: failed opening the file";
86 case VORBIS_seek_without_length: return "VORBIS: can't seek in unknown length stream";
87 case VORBIS_unexpected_eof: return "VORBIS: unexpected eof";
88 case VORBIS_seek_invalid: return "VORBIS: invalid seek";
89 case VORBIS_invalid_setup: return "VORBIS: invalid setup";
90 case VORBIS_invalid_stream: return "VORBIS: invalid stream";
91 case VORBIS_missing_capture_pattern: return "VORBIS: missing capture pattern";
92 case VORBIS_invalid_stream_structure_version: return "VORBIS: invalid stream structure version";
93 case VORBIS_continued_packet_flag_invalid: return "VORBIS: continued packet flag invalid";
94 case VORBIS_incorrect_stream_serial_number: return "VORBIS: incorrect stream serial number";
95 case VORBIS_invalid_first_page: return "VORBIS: invalid first page";
96 case VORBIS_bad_packet_type: return "VORBIS: bad packet type";
97 case VORBIS_cant_find_last_page: return "VORBIS: can't find last page";
98 case VORBIS_seek_failed: return "VORBIS: seek failed";
99 case VORBIS_ogg_skeleton_not_supported: return "VORBIS: multi-track streams are not supported; "
100 "consider re-encoding without the Ogg Skeleton bitstream";
101 default: break;
102 } /* switch */
103
104 return "VORBIS: unknown error";
105 } /* vorbis_error_string */
106 #endif
107
VORBIS_init(void)108 static int VORBIS_init(void)
109 {
110 return 1; /* always succeeds. */
111 } /* VORBIS_init */
112
VORBIS_quit(void)113 static void VORBIS_quit(void)
114 {
115 /* it's a no-op. */
116 } /* VORBIS_quit */
117
VORBIS_open(Sound_Sample * sample,const char * ext)118 static int VORBIS_open(Sound_Sample *sample, const char *ext)
119 {
120 (void) ext; // deliberately unused, but present for API compliance
121 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
122 SDL_RWops *rw = internal->rw;
123 int err = 0;
124 stb_vorbis *stb = stb_vorbis_open_rwops(rw, 0, &err, NULL);
125
126 if (stb == NULL) {
127 SNDDBG(("%s (error code: %d)\n", vorbis_error_string(err), err));
128 return 0;
129 }
130 internal->decoder_private = stb;
131 sample->flags = SOUND_SAMPLEFLAG_CANSEEK;
132 sample->actual.format = AUDIO_S16SYS; // returns byte-order native to the running architecture
133 sample->actual.channels = stb->channels;
134 sample->actual.rate = stb->sample_rate;
135 const unsigned int num_frames = stb_vorbis_stream_length_in_samples(stb);
136 if (!num_frames) {
137 internal->total_time = -1;
138 }
139 else {
140 const unsigned int rate = stb->sample_rate;
141 internal->total_time = (num_frames / rate) * 1000;
142 internal->total_time += (num_frames % rate) * 1000 / rate;
143 } /* else */
144
145 return 1; /* we'll handle this data. */
146 } /* VORBIS_open */
147
148
VORBIS_close(Sound_Sample * sample)149 static void VORBIS_close(Sound_Sample *sample)
150 {
151 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
152 stb_vorbis *stb = (stb_vorbis *) internal->decoder_private;
153 stb_vorbis_close(stb);
154 } /* VORBIS_close */
155
156
VORBIS_read(Sound_Sample * sample,void * buffer,Uint32 desired_frames)157 static Uint32 VORBIS_read(Sound_Sample *sample, void* buffer, Uint32 desired_frames)
158 {
159 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
160 stb_vorbis *stb = (stb_vorbis *) internal->decoder_private;
161 const int channels = (int) sample->actual.channels;
162 const int desired_samples = desired_frames * channels;
163
164 // Note that for interleaved data, you pass in the number of shorts (the
165 // size of your array), but the return value is the number of samples per
166 // channel, not the total number of samples.
167
168 stb_vorbis_get_error(stb); /* clear any error state */
169 const int decoded_frames = stb_vorbis_get_samples_short_interleaved(stb, channels, (int16_t *) buffer, desired_samples);
170 const int err = stb_vorbis_get_error(stb);
171
172 if (decoded_frames == 0) {
173 sample->flags |= (err ? SOUND_SAMPLEFLAG_ERROR : SOUND_SAMPLEFLAG_EOF);
174 }
175 else if (decoded_frames < (int) desired_frames) {
176 sample->flags |= SOUND_SAMPLEFLAG_EAGAIN;
177 }
178 return decoded_frames;
179 } /* VORBIS_read */
180
181
VORBIS_rewind(Sound_Sample * sample)182 static int VORBIS_rewind(Sound_Sample *sample)
183 {
184 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
185 stb_vorbis *stb = (stb_vorbis *) internal->decoder_private;
186
187 if (!stb_vorbis_seek_start(stb)) {
188 SNDDBG(("%s\n", vorbis_error_string(stb_vorbis_get_error(stb))));
189 return 0;
190 }
191
192 return 1;
193 } /* VORBIS_rewind */
194
195
VORBIS_seek(Sound_Sample * sample,Uint32 ms)196 static int VORBIS_seek(Sound_Sample *sample, Uint32 ms)
197 {
198 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
199 stb_vorbis *stb = (stb_vorbis *) internal->decoder_private;
200 const float frames_per_ms = ((float) sample->actual.rate) / 1000.0f;
201 const Uint32 frame_offset = lroundf(frames_per_ms * ms);
202 const unsigned int sampnum = (unsigned int) frame_offset;
203
204 if (!stb_vorbis_seek(stb, sampnum)) {
205 SNDDBG(("%s\n", vorbis_error_string(stb_vorbis_get_error(stb))));
206 return 0;
207 }
208 return 1;
209 } /* VORBIS_seek */
210
211
212 static const char *extensions_vorbis[] = { "OGG", "OGA", "VORBIS", NULL };
213 const Sound_DecoderFunctions __Sound_DecoderFunctions_VORBIS =
214 {
215 {
216 extensions_vorbis,
217 "Ogg Vorbis audio",
218 "The DOSBox Staging Team"
219 },
220
221 VORBIS_init, /* init() method */
222 VORBIS_quit, /* quit() method */
223 VORBIS_open, /* open() method */
224 VORBIS_close, /* close() method */
225 VORBIS_read, /* read() method */
226 VORBIS_rewind, /* rewind() method */
227 VORBIS_seek /* seek() method */
228 };
229
230 /* end of SDL_sound_vorbis.c ... */
231