DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/libs/decoders/wav.c
00001 /*
00002  *  DOSBox-X MP3 decoder API implementation
00003  *  ---------------------------------------
00004  *  It makes use of the dr_wav library by David Reid (mackron@gmail.com)
00005  *  Source links:
00006  *   - dr_libs: https://github.com/mackron/dr_libs (source)
00007  *   - dr_wav: http://mackron.github.io/dr_wav.html (website)
00008  *
00009  *  Copyright (C) 2020       The DOSBox Team
00010  *  Copyright (C) 2018-2019  Kevin R. Croft <krcroft@gmail.com>
00011  *  Copyright (C) 2001-2017  Ryan C. Gordon <icculus@icculus.org>
00012  *
00013  *  This program is free software; you can redistribute it and/or modify
00014  *  it under the terms of the GNU General Public License as published by
00015  *  the Free Software Foundation; either version 2 of the License, or
00016  *  (at your option) any later version.
00017  *
00018  *  This program is distributed in the hope that it will be useful,
00019  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00020  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00021  *  GNU General Public License for more details.
00022  *
00023  *  You should have received a copy of the GNU General Public License along
00024  *  with this program; if not, write to the Free Software Foundation, Inc.,
00025  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00026  */
00027 
00028 #if HAVE_CONFIG_H
00029 #  include <config.h>
00030 #endif
00031 
00032 #include <math.h> /* llroundf */
00033 
00034 #include "SDL_sound.h"
00035 #define __SDL_SOUND_INTERNAL__
00036 #include "SDL_sound_internal.h"
00037 
00038 /* Map dr_wav's memory routines to SDL's */
00039 #define DRWAV_FREE(p)                   SDL_free((p))
00040 #define DRWAV_MALLOC(sz)                SDL_malloc((sz))
00041 #define DRWAV_REALLOC(p, sz)            SDL_realloc((p), (sz))
00042 #define DRWAV_ZERO_MEMORY(p, sz)        SDL_memset((p), 0, (sz))
00043 #define DRWAV_COPY_MEMORY(dst, src, sz) SDL_memcpy((dst), (src), (sz))
00044 
00045 #define DR_WAV_NO_STDIO
00046 #define DR_WAV_IMPLEMENTATION
00047 #include "dr_wav.h"
00048 
00049 static size_t wav_read(void* pUserData, void* pBufferOut, size_t bytesToRead)
00050 {
00051     Uint8 *ptr = (Uint8 *) pBufferOut;
00052     Sound_Sample *sample = (Sound_Sample *) pUserData;
00053     Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
00054     SDL_RWops *rwops = internal->rw;
00055     size_t bytes_read = 0;
00056 
00057     while (bytes_read < bytesToRead) {
00058         const size_t rc = SDL_RWread(rwops, ptr, 1, bytesToRead - bytes_read);
00059         if (rc == 0) {
00060             sample->flags |= SOUND_SAMPLEFLAG_EOF;
00061             break;
00062         } /* if */
00063         bytes_read += rc;
00064         ptr += rc;
00065     } /* while */
00066 
00067     return bytes_read;
00068 } /* wav_read */
00069 
00070 static drwav_bool32 wav_seek(void* pUserData, int offset, drwav_seek_origin origin)
00071 {
00072     const int whence = (origin == drwav_seek_origin_start) ? RW_SEEK_SET : RW_SEEK_CUR;
00073     Sound_Sample *sample = (Sound_Sample *) pUserData;
00074     Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
00075     return (SDL_RWseek(internal->rw, offset, whence) != -1) ? DRWAV_TRUE : DRWAV_FALSE;
00076 } /* wav_seek */
00077 
00078 
00079 static int WAV_init(void)
00080 {
00081     return 1;  /* always succeeds. */
00082 } /* WAV_init */
00083 
00084 
00085 static void WAV_quit(void)
00086 {
00087     /* it's a no-op. */
00088 } /* WAV_quit */
00089 
00090 static void WAV_close(Sound_Sample *sample)
00091 {
00092     Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
00093     drwav *dr = (drwav *) internal->decoder_private;
00094     if (dr != NULL) {
00095         (void) drwav_uninit(dr);
00096         SDL_free(dr);
00097         internal->decoder_private = NULL;
00098     }
00099     return;
00100 } /* WAV_close */
00101 
00102 static int WAV_open(Sound_Sample *sample, const char *ext)
00103 {
00104     (void) ext; // deliberately unused, but present for API compliance
00105     Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
00106     drwav* dr = (drwav*)SDL_malloc(sizeof(drwav));
00107     drwav_result result = drwav_init_ex(dr, wav_read, wav_seek, NULL, sample, NULL, 0, NULL);
00108     internal->decoder_private = dr;
00109 
00110     if (result == DRWAV_TRUE) {
00111         SNDDBG(("WAV: Codec accepted the data stream.\n"));
00112         sample->flags = SOUND_SAMPLEFLAG_CANSEEK;
00113         sample->actual.rate = dr->sampleRate;
00114         sample->actual.format = AUDIO_S16SYS;
00115         sample->actual.channels = (Uint8)(dr->channels);
00116 
00117         const Uint64 frames = (Uint64) dr->totalPCMFrameCount;
00118         if (frames == 0) {
00119             internal->total_time = -1;
00120         }
00121         else {
00122             const Uint32 rate = (Uint32) dr->sampleRate;
00123             internal->total_time = ( (Sint32)frames / rate) * 1000;
00124             internal->total_time += ((frames % rate) * 1000) / rate;
00125         }  /* else */
00126 
00127     } /* if result != DRWAV_TRUE */
00128     else {
00129         SNDDBG(("WAV: Codec could not parse the data stream.\n"));
00130         WAV_close(sample);
00131     }
00132     return result;
00133 } /* WAV_open */
00134 
00135 
00136 static Uint32 WAV_read(Sound_Sample *sample, void* buffer, Uint32 desired_frames)
00137 {
00138     Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
00139     drwav *dr = (drwav *) internal->decoder_private;
00140     const drwav_uint64 frames_read = drwav_read_pcm_frames_s16(dr,
00141                                                                desired_frames,
00142                                                                (drwav_int16 *) buffer);
00143     return (Uint32)frames_read;
00144 } /* WAV_read */
00145 
00146 
00147 static int WAV_rewind(Sound_Sample *sample)
00148 {
00149     Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
00150     drwav *dr = (drwav *) internal->decoder_private;
00151     return (drwav_seek_to_pcm_frame(dr, 0) == DRWAV_TRUE);
00152 } /* WAV_rewind */
00153 
00154 static int WAV_seek(Sound_Sample *sample, Uint32 ms)
00155 {
00156     Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
00157     drwav *dr = (drwav *) internal->decoder_private;
00158     const float frames_per_ms = ((float) sample->actual.rate) / 1000.0f;
00159     const drwav_uint64 frame_offset = llroundf(frames_per_ms * ms);
00160     return (drwav_seek_to_pcm_frame(dr, frame_offset) == DRWAV_TRUE);
00161 } /* WAV_seek */
00162 
00163 static const char *extensions_wav[] = { "WAV", "W64", NULL };
00164 
00165 const Sound_DecoderFunctions __Sound_DecoderFunctions_WAV =
00166 {
00167     {
00168         extensions_wav,
00169         "WAV Audio Codec",
00170         "The DOSBox-X project"
00171     },
00172 
00173     WAV_init,       /*   init() method */
00174     WAV_quit,       /*   quit() method */
00175     WAV_open,       /*   open() method */
00176     WAV_close,      /*  close() method */
00177     WAV_read,       /*   read() method */
00178     WAV_rewind,     /* rewind() method */
00179     WAV_seek        /*   seek() method */
00180 };
00181 /* end of wav.c ... */