DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/libs/decoders/vorbis.c
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 ... */