DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/libs/decoders/SDL_sound.c
00001 /*
00002  *  Modified SDL Sound API implementation
00003  *  -------------------------------------
00004  *  This file implements the API, the documentation for which can
00005  *  be found in SDL_sound.h.  This API has been changed from its
00006  *  original implementation as follows:
00007  *    - Cut down in size; most notably exclusion of the conversion routines
00008  *    - Small bug fixes and warnings cleaned up
00009  *    - Elimination of intermediate buffers, allowing direct decoding
00010  *    - Moved from sample-based logic to frame-based (channel-agnostic)
00011  *
00012  *  Copyright (C) 2020       The DOSBox Team
00013  *  Copyright (C) 2018-2019  Kevin R. Croft <krcroft@gmail.com>
00014  *  Copyright (C) 2001-2017  Ryan C. Gordon <icculus@icculus.org>
00015  *
00016  *  This program is free software; you can redistribute it and/or modify
00017  *  it under the terms of the GNU General Public License as published by
00018  *  the Free Software Foundation; either version 2 of the License, or
00019  *  (at your option) any later version.
00020  *
00021  *  This program is distributed in the hope that it will be useful,
00022  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00023  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00024  *  GNU General Public License for more details.
00025  *
00026  *  You should have received a copy of the GNU General Public License along
00027  *  with this program; if not, write to the Free Software Foundation, Inc.,
00028  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00029  */
00030 
00031 #include <stdio.h>
00032 #include <ctype.h>
00033 
00034 #if HAVE_CONFIG_H
00035 #  include <config.h>
00036 #endif
00037 
00038 #include <SDL.h>
00039 #include <SDL_thread.h>
00040 #include "SDL_sound.h"
00041 
00042 #define __SDL_SOUND_INTERNAL__
00043 #include "SDL_sound_internal.h"
00044 
00045 typedef struct
00046 {
00047     int available;
00048     const Sound_DecoderFunctions *funcs;
00049 } decoder_element;
00050 
00051 /* Supported decoder drivers... */
00052 extern const Sound_DecoderFunctions __Sound_DecoderFunctions_FLAC;
00053 extern const Sound_DecoderFunctions __Sound_DecoderFunctions_MP3;
00054 #ifdef USE_OPUS
00055 extern const Sound_DecoderFunctions __Sound_DecoderFunctions_OPUS;
00056 #endif
00057 extern const Sound_DecoderFunctions __Sound_DecoderFunctions_VORBIS;
00058 extern const Sound_DecoderFunctions __Sound_DecoderFunctions_WAV;
00059 
00060 static decoder_element decoders[] =
00061 {
00062     { 0, &__Sound_DecoderFunctions_FLAC },
00063     { 0, &__Sound_DecoderFunctions_MP3 },
00064 #ifdef USE_OPUS
00065     { 0, &__Sound_DecoderFunctions_OPUS },
00066 #endif
00067     { 0, &__Sound_DecoderFunctions_VORBIS },
00068     { 0, &__Sound_DecoderFunctions_WAV },
00069     { 0, NULL }
00070 };
00071 
00072 
00073 
00074 /* General SDL_sound state ... */
00075 
00076 typedef struct __SOUND_ERRMSGTYPE__
00077 {
00078     Uint32 tid;
00079     int error_available;
00080     char error_string[128];
00081     struct __SOUND_ERRMSGTYPE__ *next;
00082 } ErrMsg;
00083 
00084 static ErrMsg *error_msgs = NULL;
00085 static SDL_mutex *errorlist_mutex = NULL;
00086 
00087 static Sound_Sample *sample_list = NULL;  /* this is a linked list. */
00088 static SDL_mutex *samplelist_mutex = NULL;
00089 
00090 static const Sound_DecoderInfo **available_decoders = NULL;
00091 static int initialized = 0;
00092 
00093 
00094 /* functions ... */
00095 
00096 void Sound_GetLinkedVersion(Sound_Version *ver)
00097 {
00098     if (ver != NULL)
00099     {
00100         ver->major = SOUND_VER_MAJOR;
00101         ver->minor = SOUND_VER_MINOR;
00102         ver->patch = SOUND_VER_PATCH;
00103     } /* if */
00104 } /* Sound_GetLinkedVersion */
00105 
00106 
00107 int Sound_Init(void)
00108 {
00109     size_t i;
00110     size_t pos = 0;
00111     size_t total = sizeof (decoders) / sizeof (decoders[0]);
00112     BAIL_IF_MACRO(initialized, ERR_IS_INITIALIZED, 0);
00113 
00114     sample_list = NULL;
00115     error_msgs = NULL;
00116 
00117     available_decoders = (const Sound_DecoderInfo **)
00118                             malloc((total) * sizeof (Sound_DecoderInfo *));
00119     BAIL_IF_MACRO(available_decoders == NULL, ERR_OUT_OF_MEMORY, 0);
00120 
00121     SDL_InitSubSystem(SDL_INIT_AUDIO);
00122 
00123     errorlist_mutex = SDL_CreateMutex();
00124     samplelist_mutex = SDL_CreateMutex();
00125 
00126     for (i = 0; decoders[i].funcs != NULL; i++)
00127     {
00128         decoders[i].available = decoders[i].funcs->init();
00129         if (decoders[i].available)
00130         {
00131             available_decoders[pos] = &(decoders[i].funcs->info);
00132             pos++;
00133         } /* if */
00134     } /* for */
00135 
00136     available_decoders[pos] = NULL;
00137 
00138     initialized = 1;
00139     return(1);
00140 } /* Sound_Init */
00141 
00142 
00143 int Sound_Quit(void)
00144 {
00145     ErrMsg *err;
00146     ErrMsg *nexterr = NULL;
00147     size_t i;
00148 
00149     BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, 0);
00150 
00151     while (((volatile Sound_Sample *) sample_list) != NULL)
00152     {
00153         Sound_Sample *sample = sample_list;
00154         Sound_FreeSample(sample); /* Updates sample_list. */
00155     }
00156 
00157     initialized = 0;
00158 
00159     SDL_DestroyMutex(samplelist_mutex);
00160     samplelist_mutex = NULL;
00161     sample_list = NULL;
00162 
00163     for (i = 0; decoders[i].funcs != NULL; i++)
00164     {
00165         if (decoders[i].available)
00166         {
00167             decoders[i].funcs->quit();
00168             decoders[i].available = 0;
00169         } /* if */
00170     } /* for */
00171 
00172     if (available_decoders != NULL)
00173         free((void *) available_decoders);
00174     available_decoders = NULL;
00175 
00176     /* clean up error state for each thread... */
00177     SDL_LockMutex(errorlist_mutex);
00178     for (err = error_msgs; err != NULL; err = nexterr)
00179     {
00180         nexterr = err->next;
00181         free(err);
00182     } /* for */
00183     error_msgs = NULL;
00184     SDL_UnlockMutex(errorlist_mutex);
00185     SDL_DestroyMutex(errorlist_mutex);
00186     errorlist_mutex = NULL;
00187 
00188     return(1);
00189 } /* Sound_Quit */
00190 
00191 
00192 const Sound_DecoderInfo **Sound_AvailableDecoders(void)
00193 {
00194     return(available_decoders);  /* READ. ONLY. */
00195 } /* Sound_AvailableDecoders */
00196 
00197 
00198 static ErrMsg *findErrorForCurrentThread(void)
00199 {
00200     ErrMsg *i;
00201     Uint32 tid;
00202 
00203     if (error_msgs != NULL)
00204     {
00205         tid = SDL_ThreadID();
00206 
00207         SDL_LockMutex(errorlist_mutex);
00208         for (i = error_msgs; i != NULL; i = i->next)
00209         {
00210             if (i->tid == tid)
00211             {
00212                 SDL_UnlockMutex(errorlist_mutex);
00213                 return(i);
00214             } /* if */
00215         } /* for */
00216         SDL_UnlockMutex(errorlist_mutex);
00217     } /* if */
00218 
00219     return(NULL);   /* no error available. */
00220 } /* findErrorForCurrentThread */
00221 
00222 
00223 const char *Sound_GetError(void)
00224 {
00225     const char *retval = NULL;
00226     ErrMsg *err;
00227 
00228     if (!initialized)
00229         return(ERR_NOT_INITIALIZED);
00230 
00231     err = findErrorForCurrentThread();
00232     if ((err != NULL) && (err->error_available))
00233     {
00234         retval = err->error_string;
00235         err->error_available = 0;
00236     } /* if */
00237 
00238     return(retval);
00239 } /* Sound_GetError */
00240 
00241 
00242 void Sound_ClearError(void)
00243 {
00244     ErrMsg *err;
00245 
00246     if (!initialized)
00247         return;
00248 
00249     err = findErrorForCurrentThread();
00250     if (err != NULL)
00251         err->error_available = 0;
00252 } /* Sound_ClearError */
00253 
00254 
00255 /*
00256  * This is declared in the internal header.
00257  */
00258 void __Sound_SetError(const char *str)
00259 {
00260     ErrMsg *err;
00261 
00262     if (str == NULL)
00263         return;
00264 
00265     SNDDBG(("__Sound_SetError(\"%s\");%s\n", str,
00266               (initialized) ? "" : " [NOT INITIALIZED!]"));
00267 
00268     if (!initialized)
00269         return;
00270 
00271     err = findErrorForCurrentThread();
00272     if (err == NULL)
00273     {
00274         err = (ErrMsg *) malloc(sizeof (ErrMsg));
00275         if (err == NULL)
00276             return;   /* uhh...? */
00277 
00278         memset((void *) err, '\0', sizeof (ErrMsg));
00279         err->tid = SDL_ThreadID();
00280 
00281         SDL_LockMutex(errorlist_mutex);
00282         err->next = error_msgs;
00283         error_msgs = err;
00284         SDL_UnlockMutex(errorlist_mutex);
00285     } /* if */
00286 
00287     err->error_available = 1;
00288     snprintf(err->error_string, sizeof (err->error_string), "%s", str);
00289 } /* __Sound_SetError */
00290 
00291 
00292 Uint32 __Sound_convertMsToBytePos(Sound_AudioInfo *info, Uint32 ms)
00293 {
00294     /* "frames" == "sample frames" */
00295     float frames_per_ms = ((float) info->rate) / 1000.0f;
00296     Uint32 frame_offset = (Uint32) (frames_per_ms * ((float) ms));
00297     Uint32 frame_size = (Uint32) ((info->format & 0xFF) / 8) * info->channels;
00298     return(frame_offset * frame_size);
00299 } /* __Sound_convertMsToBytePos */
00300 
00301 
00302 /*
00303  * -ansi and -pedantic flags prevent use of strcasecmp() on Linux, and
00304  *  I honestly don't want to mess around with figuring out if a given
00305  *  platform has "strcasecmp", "stricmp", or
00306  *  "compare_two_damned_strings_case_insensitive", which I hear is in the
00307  *  next release of Carbon.  :)  This is exported so decoders may use it if
00308  *  they like.
00309  */
00310 int __Sound_strcasecmp(const char *x, const char *y)
00311 {
00312     int ux, uy;
00313 
00314     if (x == y)  /* same pointer? Both NULL? */
00315         return(0);
00316 
00317     if (x == NULL)
00318         return(-1);
00319 
00320     if (y == NULL)
00321         return(1);
00322        
00323     do
00324     {
00325         ux = toupper((int) *x);
00326         uy = toupper((int) *y);
00327         if (ux > uy)
00328             return(1);
00329         else if (ux < uy)
00330             return(-1);
00331         x++;
00332         y++;
00333     } while ((ux) && (uy));
00334 
00335     return(0);
00336 } /* __Sound_strcasecmp */
00337 
00338 
00339 /*
00340  * Allocate a Sound_Sample, and fill in most of its fields. Those that need
00341  *  to be filled in later, by a decoder, will be initialized to zero.
00342  */
00343 static Sound_Sample *alloc_sample(SDL_RWops *rw, Sound_AudioInfo *desired)
00344 {
00345     /*
00346      * !!! FIXME: We're going to need to pool samples, since the mixer
00347      * !!! FIXME:  might be allocating tons of these on a regular basis.
00348      */
00349     Sound_Sample *retval = NULL;
00350     Sound_Sample *sample = (Sound_Sample *)malloc(sizeof (Sound_Sample));
00351     Sound_SampleInternal *internal = (Sound_SampleInternal *)malloc(sizeof (Sound_SampleInternal));
00352     if (sample && internal) {
00353         memset(sample, '\0', sizeof (Sound_Sample));
00354         memset(internal, '\0', sizeof (Sound_SampleInternal));
00355         if (desired != NULL) {
00356             memcpy(&sample->desired, desired, sizeof (Sound_AudioInfo));
00357         }
00358         internal->rw = rw;
00359         sample->opaque = internal;
00360         retval = sample;
00361     } else {
00362         __Sound_SetError(ERR_OUT_OF_MEMORY);
00363         free(sample);
00364         free(internal);
00365     }
00366     return retval;
00367 } /* alloc_sample */
00368 
00369 
00370 #if (defined DEBUG_CHATTER)
00371 static __inline__ const char *fmt_to_str(Uint16 fmt)
00372 {
00373     switch(fmt)
00374     {
00375         case AUDIO_U8:
00376             return("U8");
00377         case AUDIO_S8:
00378             return("S8");
00379         case AUDIO_U16LSB:
00380             return("U16LSB");
00381         case AUDIO_S16LSB:
00382             return("S16LSB");
00383         case AUDIO_U16MSB:
00384             return("U16MSB");
00385         case AUDIO_S16MSB:
00386             return("S16MSB");
00387     } /* switch */
00388 
00389     return("Unknown");
00390 } /* fmt_to_str */
00391 #endif
00392 
00393 
00394 /*
00395  * The bulk of the Sound_NewSample() work is done here...
00396  *  Ask the specified decoder to handle the data in (rw), and if
00397  *  so, construct the Sound_Sample. Otherwise, try to wind (rw)'s stream
00398  *  back to where it was, and return false.
00399  */
00400 static int init_sample(const Sound_DecoderFunctions *funcs,
00401                         Sound_Sample *sample, const char *ext,
00402                         Sound_AudioInfo *_desired)
00403 {
00404     Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
00405     Sound_AudioInfo desired;
00406     const Sint64 pos = SDL_RWtell(internal->rw);
00407 
00408         /* fill in the funcs for this decoder... */
00409     sample->decoder = &funcs->info;
00410     internal->funcs = funcs;
00411     if (!funcs->open(sample, ext))
00412     {
00413         SDL_RWseek(internal->rw, pos, SEEK_SET);  /* set for next try... */
00414         return(0);
00415     } /* if */
00416 
00417     /* success; we've got a decoder! */
00418 
00419     memcpy(&desired, (_desired != NULL) ? _desired : &sample->actual,
00420             sizeof (Sound_AudioInfo));
00421 
00422     if (desired.format == 0)
00423         desired.format = sample->actual.format;
00424     if (desired.channels == 0)
00425         desired.channels = sample->actual.channels;
00426     if (desired.rate == 0)
00427         desired.rate = sample->actual.rate;
00428 
00429 
00430         /* these pointers are all one and the same. */
00431     memcpy(&sample->desired, &desired, sizeof (Sound_AudioInfo));
00432 
00433     /* Prepend our new Sound_Sample to the sample_list... */
00434     SDL_LockMutex(samplelist_mutex);
00435     internal->next = sample_list;
00436     if (sample_list != NULL)
00437         ((Sound_SampleInternal *) sample_list->opaque)->prev = sample;
00438     sample_list = sample;
00439     SDL_UnlockMutex(samplelist_mutex);
00440 
00441     SNDDBG(("New sample DESIRED format: %s format, %d rate, %d channels.\n",
00442             fmt_to_str(sample->desired.format),
00443             sample->desired.rate,
00444             sample->desired.channels));
00445 
00446     SNDDBG(("New sample ACTUAL format: %s format, %d rate, %d channels.\n",
00447             fmt_to_str(sample->actual.format),
00448             sample->actual.rate,
00449             sample->actual.channels));
00450     return(1);
00451 } /* init_sample */
00452 
00453 
00454 Sound_Sample *Sound_NewSample(SDL_RWops *rw, const char *ext,
00455                               Sound_AudioInfo *desired)
00456 {
00457     Sound_Sample *retval;
00458     decoder_element *decoder;
00459 
00460     /* sanity checks. */
00461     BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, NULL);
00462     BAIL_IF_MACRO(rw == NULL, ERR_INVALID_ARGUMENT, NULL);
00463 
00464     retval = alloc_sample(rw, desired);
00465     if (!retval)
00466         return(NULL);  /* alloc_sample() sets error message... */
00467 
00468     if (ext != NULL)
00469     {
00470         for (decoder = &decoders[0]; decoder->funcs != NULL; decoder++)
00471         {
00472             if (decoder->available)
00473             {
00474                 const char **decoderExt = decoder->funcs->info.extensions;
00475                 while (*decoderExt)
00476                 {
00477                     if (__Sound_strcasecmp(*decoderExt, ext) == 0)
00478                     {
00479                         if (init_sample(decoder->funcs, retval, ext, desired))
00480                             return(retval);
00481                         break;  /* done with this decoder either way. */
00482                     } /* if */
00483                     decoderExt++;
00484                 } /* while */
00485             } /* if */
00486         } /* for */
00487     } /* if */
00488 
00489     /* no direct extension match? Try everything we've got... */
00490     for (decoder = &decoders[0]; decoder->funcs != NULL; decoder++)
00491     {
00492         if (decoder->available)
00493         {
00494             int should_try = 1;
00495             const char **decoderExt = decoder->funcs->info.extensions;
00496 
00497                 /* skip if we would have tried decoder above... */
00498             while (*decoderExt)
00499             {
00500                 if (__Sound_strcasecmp(*decoderExt, ext) == 0)
00501                 {
00502                     should_try = 0;
00503                     break;
00504                 } /* if */
00505                 decoderExt++;
00506             } /* while */
00507 
00508             if (should_try)
00509             {
00510                 if (init_sample(decoder->funcs, retval, ext, desired))
00511                     return(retval);
00512             } /* if */
00513         } /* if */
00514     } /* for */
00515 
00516     /* nothing could handle the sound data... */
00517     free(retval->opaque);
00518     free(retval);
00519     SDL_RWclose(rw);
00520     __Sound_SetError(ERR_UNSUPPORTED_FORMAT);
00521     return(NULL);
00522 } /* Sound_NewSample */
00523 
00524 
00525 Sound_Sample *Sound_NewSampleFromFile(const char *filename,
00526                                       Sound_AudioInfo *desired)
00527 {
00528     const char *ext;
00529     SDL_RWops *rw;
00530 
00531     BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, NULL);
00532     BAIL_IF_MACRO(filename == NULL, ERR_INVALID_ARGUMENT, NULL);
00533 
00534     ext = strrchr(filename, '.');
00535 
00536 
00537     SNDDBG(("Sound_NewSampleFromFile ext = `%s`", ext));
00538 
00539     rw = SDL_RWFromFile(filename, "rb");
00540     /* !!! FIXME: rw = RWops_FromFile(filename, "rb");*/
00541     BAIL_IF_MACRO(rw == NULL, SDL_GetError(), NULL);
00542 
00543     if (ext != NULL)
00544         ext++;
00545 
00546     return(Sound_NewSample(rw, ext, desired));
00547 } /* Sound_NewSampleFromFile */
00548 
00549 void Sound_FreeSample(Sound_Sample *sample)
00550 {
00551     Sound_SampleInternal *internal;
00552 
00553     if (!initialized)
00554     {
00555         __Sound_SetError(ERR_NOT_INITIALIZED);
00556         return;
00557     } /* if */
00558 
00559     if (sample == NULL)
00560     {
00561         __Sound_SetError(ERR_INVALID_ARGUMENT);
00562         return;
00563     } /* if */
00564 
00565     internal = (Sound_SampleInternal *) sample->opaque;
00566 
00567     SDL_LockMutex(samplelist_mutex);
00568 
00569     /* update the sample_list... */
00570     if (internal->prev != NULL)
00571     {
00572         Sound_SampleInternal *prevInternal;
00573         prevInternal = (Sound_SampleInternal *) internal->prev->opaque;
00574         prevInternal->next = internal->next;
00575     } /* if */
00576     else
00577     {
00578         assert(sample_list == sample);
00579         sample_list = internal->next;
00580     } /* else */
00581 
00582     if (internal->next != NULL)
00583     {
00584         Sound_SampleInternal *nextInternal;
00585         nextInternal = (Sound_SampleInternal *) internal->next->opaque;
00586         nextInternal->prev = internal->prev;
00587     } /* if */
00588 
00589     SDL_UnlockMutex(samplelist_mutex);
00590 
00591     /* nuke it... */
00592     internal->funcs->close(sample);
00593 
00594     if (internal->rw != NULL)  /* this condition is a "just in case" thing. */
00595         SDL_RWclose(internal->rw);
00596 
00597     free(internal);
00598     free(sample);
00599 } /* Sound_FreeSample */
00600 
00601 Uint32 Sound_Decode_Direct(Sound_Sample *sample, void* buffer, Uint32 desired_frames)
00602 {
00603     Sound_SampleInternal *internal = NULL;
00604 
00605         /* a boatload of sanity checks... */
00606     BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, 0);
00607     BAIL_IF_MACRO(sample == NULL, ERR_INVALID_ARGUMENT, 0);
00608     BAIL_IF_MACRO(sample->flags & SOUND_SAMPLEFLAG_ERROR, ERR_PREV_ERROR, 0);
00609     BAIL_IF_MACRO(sample->flags & SOUND_SAMPLEFLAG_EOF, ERR_PREV_EOF, 0);
00610 
00611     internal = (Sound_SampleInternal *) sample->opaque;
00612 
00613         /* reset EAGAIN. Decoder can flip it back on if it needs to. */
00614     sample->flags &= ~SOUND_SAMPLEFLAG_EAGAIN;
00615     return internal->funcs->read(sample, buffer, desired_frames);
00616 } /* Sound_Decode */
00617 
00618 
00619 int Sound_Rewind(Sound_Sample *sample)
00620 {
00621     Sound_SampleInternal *internal;
00622     BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, 0);
00623 
00624     internal = (Sound_SampleInternal *) sample->opaque;
00625     if (!internal->funcs->rewind(sample))
00626     {
00627         sample->flags |= SOUND_SAMPLEFLAG_ERROR;
00628         return(0);
00629     } /* if */
00630 
00631     sample->flags &= ~SOUND_SAMPLEFLAG_EAGAIN;
00632     sample->flags &= ~SOUND_SAMPLEFLAG_ERROR;
00633     sample->flags &= ~SOUND_SAMPLEFLAG_EOF;
00634 
00635     return(1);
00636 } /* Sound_Rewind */
00637 
00638 
00639 int Sound_Seek(Sound_Sample *sample, Uint32 ms)
00640 {
00641     Sound_SampleInternal *internal;
00642 
00643     BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, 0);
00644     if (!(sample->flags & SOUND_SAMPLEFLAG_CANSEEK))
00645         BAIL_MACRO(ERR_CANNOT_SEEK, 0);
00646 
00647     internal = (Sound_SampleInternal *) sample->opaque;
00648     BAIL_IF_MACRO(!internal->funcs->seek(sample, ms), NULL, 0);
00649 
00650     sample->flags &= ~SOUND_SAMPLEFLAG_EAGAIN;
00651     sample->flags &= ~SOUND_SAMPLEFLAG_ERROR;
00652     sample->flags &= ~SOUND_SAMPLEFLAG_EOF;
00653 
00654     return(1);
00655 } /* Sound_Rewind */
00656 
00657 
00658 Sint32 Sound_GetDuration(Sound_Sample *sample)
00659 {
00660     Sound_SampleInternal *internal;
00661     BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, -1);
00662     internal = (Sound_SampleInternal *) sample->opaque;
00663     return(internal->total_time);
00664 } /* Sound_GetDuration */
00665 
00666 /* end of SDL_sound.c ... */