DOSBox-X
|
00001 /* 00002 * DOSBox-X MP3 decoder API implementation 00003 * --------------------------------------- 00004 * It makes use of the stand-alone STB Vorbis library: 00005 * - STB: https://github.com/nothings/stb (source) 00006 * - STB: https://twitter.com/nothings (website/author info) 00007 * 00008 * Copyright (C) 2018-2020 The DOSBox Team 00009 * Copyright (C) 2001-2017 Ryan C. Gordon <icculus@icculus.org> 00010 * 00011 * This program is free software; you can redistribute it and/or modify 00012 * it under the terms of the GNU General Public License as published by 00013 * the Free Software Foundation; either version 2 of the License, or 00014 * (at your option) any later version. 00015 * 00016 * This program is distributed in the hope that it will be useful, 00017 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00019 * GNU General Public License for more details. 00020 * 00021 * You should have received a copy of the GNU General Public License along 00022 * with this program; if not, write to the Free Software Foundation, Inc., 00023 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00024 */ 00025 00026 #if HAVE_CONFIG_H 00027 # include <config.h> 00028 #endif 00029 00030 #ifdef memcpy 00031 # undef memcpy 00032 #endif 00033 00034 #include <string.h> /* memcpy */ 00035 #include <math.h> /* lroundf */ 00036 00037 #include "SDL_sound.h" 00038 #define __SDL_SOUND_INTERNAL__ 00039 #include "SDL_sound_internal.h" 00040 00041 #ifdef asset 00042 # undef assert 00043 # define assert SDL_assert 00044 #endif 00045 00046 #ifdef memset 00047 # undef memset 00048 # define memset SDL_memset 00049 #endif 00050 00051 #define qsort SDL_qsort 00052 #define memcmp SDL_memcmp 00053 #define dealloca(x) SDL_stack_free((x)) 00054 00055 /* Configure and include stb_vorbis for compiling... */ 00056 #define STB_VORBIS_NO_STDIO 1 00057 #define STB_VORBIS_NO_CRT 1 00058 #define STB_VORBIS_NO_PUSHDATA_API 1 00059 #define STB_VORBIS_MAX_CHANNELS 2 00060 #if SDL_BYTEORDER == SDL_BIG_ENDIAN 00061 #define STB_VORBIS_BIG_ENDIAN 1 00062 #endif 00063 00064 #include "stb_vorbis.h" 00065 00066 #ifdef DEBUG_CHATTER 00067 static const char *vorbis_error_string(const int err) 00068 { 00069 switch (err) 00070 { 00071 case VORBIS__no_error: return NULL; 00072 case VORBIS_need_more_data: return "VORBIS: need more data"; 00073 case VORBIS_invalid_api_mixing: return "VORBIS: can't mix API modes"; 00074 case VORBIS_outofmem: return "VORBIS: out of memory"; 00075 case VORBIS_feature_not_supported: return "VORBIS: feature not supported"; 00076 case VORBIS_too_many_channels: return "VORBIS: too many channels"; 00077 case VORBIS_file_open_failure: return "VORBIS: failed opening the file"; 00078 case VORBIS_seek_without_length: return "VORBIS: can't seek in unknown length stream"; 00079 case VORBIS_unexpected_eof: return "VORBIS: unexpected eof"; 00080 case VORBIS_seek_invalid: return "VORBIS: invalid seek"; 00081 case VORBIS_invalid_setup: return "VORBIS: invalid setup"; 00082 case VORBIS_invalid_stream: return "VORBIS: invalid stream"; 00083 case VORBIS_missing_capture_pattern: return "VORBIS: missing capture pattern"; 00084 case VORBIS_invalid_stream_structure_version: return "VORBIS: invalid stream structure version"; 00085 case VORBIS_continued_packet_flag_invalid: return "VORBIS: continued packet flag invalid"; 00086 case VORBIS_incorrect_stream_serial_number: return "VORBIS: incorrect stream serial number"; 00087 case VORBIS_invalid_first_page: return "VORBIS: invalid first page"; 00088 case VORBIS_bad_packet_type: return "VORBIS: bad packet type"; 00089 case VORBIS_cant_find_last_page: return "VORBIS: can't find last page"; 00090 case VORBIS_seek_failed: return "VORBIS: seek failed"; 00091 case VORBIS_ogg_skeleton_not_supported: return "VORBIS: multi-track streams are not supported; " 00092 "consider re-encoding without the Ogg Skeleton bitstream"; 00093 default: break; 00094 } /* switch */ 00095 00096 return "VORBIS: unknown error"; 00097 } /* vorbis_error_string */ 00098 #endif 00099 00100 static int VORBIS_init(void) 00101 { 00102 return 1; /* always succeeds. */ 00103 } /* VORBIS_init */ 00104 00105 static void VORBIS_quit(void) 00106 { 00107 /* it's a no-op. */ 00108 } /* VORBIS_quit */ 00109 00110 static int VORBIS_open(Sound_Sample *sample, const char *ext) 00111 { 00112 (void) ext; // deliberately unused, but present for API compliance 00113 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; 00114 SDL_RWops *rw = internal->rw; 00115 int err = 0; 00116 stb_vorbis *stb = stb_vorbis_open_rwops(rw, 0, &err, NULL); 00117 00118 if (stb == NULL) { 00119 SNDDBG(("%s (error code: %d)\n", vorbis_error_string(err), err)); 00120 return 0; 00121 } 00122 internal->decoder_private = stb; 00123 sample->flags = SOUND_SAMPLEFLAG_CANSEEK; 00124 sample->actual.format = AUDIO_S16SYS; // returns byte-order native to the running architecture 00125 sample->actual.channels = stb->channels; 00126 sample->actual.rate = stb->sample_rate; 00127 const unsigned int num_frames = stb_vorbis_stream_length_in_samples(stb); 00128 if (!num_frames) { 00129 internal->total_time = -1; 00130 } 00131 else { 00132 const unsigned int rate = stb->sample_rate; 00133 internal->total_time = (num_frames / rate) * 1000; 00134 internal->total_time += (num_frames % rate) * 1000 / rate; 00135 } /* else */ 00136 00137 return 1; /* we'll handle this data. */ 00138 } /* VORBIS_open */ 00139 00140 00141 static void VORBIS_close(Sound_Sample *sample) 00142 { 00143 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; 00144 stb_vorbis *stb = (stb_vorbis *) internal->decoder_private; 00145 stb_vorbis_close(stb); 00146 } /* VORBIS_close */ 00147 00148 00149 static Uint32 VORBIS_read(Sound_Sample *sample, void* buffer, Uint32 desired_frames) 00150 { 00151 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; 00152 stb_vorbis *stb = (stb_vorbis *) internal->decoder_private; 00153 const int channels = (int) sample->actual.channels; 00154 const int desired_samples = desired_frames * channels; 00155 00156 // Note that for interleaved data, you pass in the number of shorts (the 00157 // size of your array), but the return value is the number of samples per 00158 // channel, not the total number of samples. 00159 00160 stb_vorbis_get_error(stb); /* clear any error state */ 00161 const int decoded_frames = stb_vorbis_get_samples_short_interleaved(stb, channels, (int16_t *) buffer, desired_samples); 00162 const int err = stb_vorbis_get_error(stb); 00163 00164 if (decoded_frames == 0) { 00165 sample->flags |= (err ? SOUND_SAMPLEFLAG_ERROR : SOUND_SAMPLEFLAG_EOF); 00166 } 00167 else if (decoded_frames < (int) desired_frames) { 00168 sample->flags |= SOUND_SAMPLEFLAG_EAGAIN; 00169 } 00170 return decoded_frames; 00171 } /* VORBIS_read */ 00172 00173 00174 static int VORBIS_rewind(Sound_Sample *sample) 00175 { 00176 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; 00177 stb_vorbis *stb = (stb_vorbis *) internal->decoder_private; 00178 00179 if (!stb_vorbis_seek_start(stb)) { 00180 SNDDBG(("%s\n", vorbis_error_string(stb_vorbis_get_error(stb)))); 00181 return 0; 00182 } 00183 00184 return 1; 00185 } /* VORBIS_rewind */ 00186 00187 00188 static int VORBIS_seek(Sound_Sample *sample, Uint32 ms) 00189 { 00190 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; 00191 stb_vorbis *stb = (stb_vorbis *) internal->decoder_private; 00192 const float frames_per_ms = ((float) sample->actual.rate) / 1000.0f; 00193 const Uint32 frame_offset = lroundf(frames_per_ms * ms); 00194 const unsigned int sampnum = (unsigned int) frame_offset; 00195 00196 if (!stb_vorbis_seek(stb, sampnum)) { 00197 SNDDBG(("%s\n", vorbis_error_string(stb_vorbis_get_error(stb)))); 00198 return 0; 00199 } 00200 return 1; 00201 } /* VORBIS_seek */ 00202 00203 00204 static const char *extensions_vorbis[] = { "OGG", "OGA", "VORBIS", NULL }; 00205 const Sound_DecoderFunctions __Sound_DecoderFunctions_VORBIS = 00206 { 00207 { 00208 extensions_vorbis, 00209 "Ogg Vorbis audio", 00210 "The DOSBox Team" 00211 }, 00212 00213 VORBIS_init, /* init() method */ 00214 VORBIS_quit, /* quit() method */ 00215 VORBIS_open, /* open() method */ 00216 VORBIS_close, /* close() method */ 00217 VORBIS_read, /* read() method */ 00218 VORBIS_rewind, /* rewind() method */ 00219 VORBIS_seek /* seek() method */ 00220 }; 00221 00222 /* end of SDL_sound_vorbis.c ... */