music_wav.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 17 Oct 2017 02:33:47 -0700
changeset 777 92882ef2ab81
parent 760 wavestream.c@b94b48c76c69
child 779 a2b494c054d5
permissions -rw-r--r--
Rewrote music.c to support any number of decode libraries using a compiled-in plugin interface
Mix_LoadWAV_RW() can now load sound formats that were previously available only as music.

This is still work in progress. Testing and project updates need to happen on other platforms.
     1 /*
     2   SDL_mixer:  An audio mixer library based on the SDL library
     3   Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 
    22 #ifdef MUSIC_WAV
    23 
    24 /* This file supports streaming WAV files */
    25 
    26 #include "music_wav.h"
    27 
    28 
    29 typedef struct {
    30     SDL_bool active;
    31     Uint32 start;
    32     Uint32 stop;
    33     Uint32 initial_play_count;
    34     Uint32 current_play_count;
    35 } WAVLoopPoint;
    36 
    37 typedef struct {
    38     SDL_RWops *src;
    39     SDL_bool freesrc;
    40     SDL_AudioSpec spec;
    41     int volume;
    42     Sint64 start;
    43     Sint64 stop;
    44     SDL_AudioCVT cvt;
    45     int numloops;
    46     WAVLoopPoint *loops;
    47 } WAVStream;
    48 
    49 /*
    50     Taken with permission from SDL_wave.h, part of the SDL library,
    51     available at: http://www.libsdl.org/
    52     and placed under the same license as this mixer library.
    53 */
    54 
    55 /* WAVE files are little-endian */
    56 
    57 /*******************************************/
    58 /* Define values for Microsoft WAVE format */
    59 /*******************************************/
    60 #define RIFF        0x46464952      /* "RIFF" */
    61 #define WAVE        0x45564157      /* "WAVE" */
    62 #define FMT         0x20746D66      /* "fmt " */
    63 #define DATA        0x61746164      /* "data" */
    64 #define SMPL        0x6c706d73      /* "smpl" */
    65 #define PCM_CODE    1
    66 #define ADPCM_CODE  2
    67 #define WAVE_MONO   1
    68 #define WAVE_STEREO 2
    69 
    70 typedef struct {
    71 /* Not saved in the chunk we read:
    72     Uint32  chunkID;
    73     Uint32  chunkLen;
    74 */
    75     Uint16  encoding;
    76     Uint16  channels;       /* 1 = mono, 2 = stereo */
    77     Uint32  frequency;      /* One of 11025, 22050, or 44100 Hz */
    78     Uint32  byterate;       /* Average bytes per second */
    79     Uint16  blockalign;     /* Bytes per sample block */
    80     Uint16  bitspersample;      /* One of 8, 12, 16, or 4 for ADPCM */
    81 } WaveFMT;
    82 
    83 typedef struct {
    84     Uint32 identifier;
    85     Uint32 type;
    86     Uint32 start;
    87     Uint32 end;
    88     Uint32 fraction;
    89     Uint32 play_count;
    90 } SampleLoop;
    91 
    92 typedef struct {
    93 /* Not saved in the chunk we read:
    94     Uint32  chunkID;
    95     Uint32  chunkLen;
    96 */
    97     Uint32  manufacturer;
    98     Uint32  product;
    99     Uint32  sample_period;
   100     Uint32  MIDI_unity_note;
   101     Uint32  MIDI_pitch_fraction;
   102     Uint32  SMTPE_format;
   103     Uint32  SMTPE_offset;
   104     Uint32  sample_loops;
   105     Uint32  sampler_data;
   106     SampleLoop loops[];
   107 } SamplerChunk;
   108 
   109 /*********************************************/
   110 /* Define values for AIFF (IFF audio) format */
   111 /*********************************************/
   112 #define FORM        0x4d524f46      /* "FORM" */
   113 #define AIFF        0x46464941      /* "AIFF" */
   114 #define SSND        0x444e5353      /* "SSND" */
   115 #define COMM        0x4d4d4f43      /* "COMM" */
   116 
   117 
   118 /* Function to load the WAV/AIFF stream */
   119 static SDL_bool LoadWAVStream(WAVStream *wave);
   120 static SDL_bool LoadAIFFStream(WAVStream *wave);
   121 
   122 
   123 /* Load a WAV stream from the given RWops object */
   124 static void *WAVStream_CreateFromRW(SDL_RWops *src, int freesrc)
   125 {
   126     WAVStream *wave;
   127     SDL_bool loaded = SDL_FALSE;
   128 
   129     wave = (WAVStream *)SDL_calloc(1, sizeof(*wave));
   130     if (wave) {
   131         Uint32 magic;
   132 
   133         wave->src = src;
   134         wave->freesrc = freesrc;
   135 
   136         magic = SDL_ReadLE32(src);
   137         if (magic == RIFF || magic == WAVE) {
   138             loaded = LoadWAVStream(wave);
   139         } else if (magic == FORM) {
   140             loaded = LoadAIFFStream(wave);
   141         } else {
   142             Mix_SetError("Unknown WAVE format");
   143         }
   144         if (!loaded) {
   145             SDL_free(wave);
   146             return(NULL);
   147         }
   148         SDL_BuildAudioCVT(&wave->cvt,
   149             wave->spec.format, wave->spec.channels, wave->spec.freq,
   150             music_spec.format, music_spec.channels, music_spec.freq);
   151         wave->volume = MIX_MAX_VOLUME;
   152     } else {
   153         SDL_OutOfMemory();
   154     }
   155     return wave;
   156 }
   157 
   158 static void WAVStream_SetVolume(void *context, int volume)
   159 {
   160     WAVStream *wave = (WAVStream *)context;
   161     wave->volume = volume;
   162 }
   163 
   164 /* Start playback of a given WAV stream */
   165 static int WAVStream_Play(void *context)
   166 {
   167     WAVStream *wave = (WAVStream *)context;
   168     int i;
   169     for (i = 0; i < wave->numloops; ++i) {
   170         WAVLoopPoint *loop = &wave->loops[i];
   171         loop->active = SDL_TRUE;
   172         loop->current_play_count = loop->initial_play_count;
   173     }
   174     SDL_RWseek(wave->src, wave->start, RW_SEEK_SET);
   175     return 0;
   176 }
   177 
   178 /* Play some of a stream previously started with WAVStream_Play() */
   179 static int PlaySome(WAVStream *wave, Uint8 *stream, int len)
   180 {
   181     Sint64 pos, stop;
   182     WAVLoopPoint *loop;
   183     Sint64 loop_start;
   184     Sint64 loop_stop;
   185     int i;
   186     int consumed;
   187 
   188     pos = SDL_RWtell(wave->src);
   189     stop = wave->stop;
   190     loop = NULL;
   191     for (i = 0; i < wave->numloops; ++i) {
   192         loop = &wave->loops[i];
   193         if (loop->active) {
   194             const int bytes_per_sample = (SDL_AUDIO_BITSIZE(wave->spec.format) / 8) * wave->spec.channels;
   195             loop_start = wave->start + loop->start * bytes_per_sample;
   196             loop_stop = wave->start + (loop->stop + 1) * bytes_per_sample;
   197             if (pos >= loop_start && pos < loop_stop)
   198             {
   199                 stop = loop_stop;
   200                 break;
   201             }
   202         }
   203         loop = NULL;
   204     }
   205 
   206     if (wave->cvt.needed) {
   207         int original_len = (int)((double)len/wave->cvt.len_ratio); 
   208         /* Make sure the length is a multiple of the sample size */
   209         {
   210             const int bytes_per_sample = (SDL_AUDIO_BITSIZE(wave->spec.format) / 8) * wave->spec.channels;
   211             const int alignment_mask = (bytes_per_sample - 1);
   212             original_len &= ~alignment_mask;
   213         }
   214         if (wave->cvt.len != original_len) {
   215             int worksize;
   216             if (wave->cvt.buf != NULL) {
   217                 SDL_free(wave->cvt.buf);
   218             }
   219             worksize = original_len*wave->cvt.len_mult;
   220             wave->cvt.buf=(Uint8 *)SDL_malloc(worksize);
   221             if (wave->cvt.buf == NULL) {
   222                 return 0;
   223             }
   224             wave->cvt.len = original_len;
   225         }
   226         if ((stop - pos) < original_len) {
   227             original_len = (int)(stop - pos);
   228         }
   229         original_len = (int)SDL_RWread(wave->src, wave->cvt.buf, 1, original_len);
   230         wave->cvt.len = original_len;
   231         SDL_ConvertAudio(&wave->cvt);
   232         SDL_MixAudioFormat(stream, wave->cvt.buf, music_spec.format, wave->cvt.len_cvt, wave->volume);
   233         consumed = wave->cvt.len_cvt;
   234     } else {
   235         Uint8 *data;
   236         if ((stop - pos) < len) {
   237             len = (int)(stop - pos);
   238         }
   239         data = SDL_stack_alloc(Uint8, len);
   240         if (data) {
   241             len = (int)SDL_RWread(wave->src, data, 1, len);
   242             SDL_MixAudioFormat(stream, data, music_spec.format, len, wave->volume);
   243             SDL_stack_free(data);
   244         }
   245         consumed = len;
   246     }
   247 
   248     if (loop && SDL_RWtell(wave->src) >= stop) {
   249         if (loop->current_play_count == 1) {
   250             loop->active = SDL_FALSE;
   251         } else {
   252             if (loop->current_play_count > 0) {
   253                 --loop->current_play_count;
   254             }
   255             SDL_RWseek(wave->src, loop_start, RW_SEEK_SET);
   256         }
   257     }
   258     return consumed;
   259 }
   260 
   261 static int WAVStream_GetAudio(void *context, void *data, int bytes)
   262 {
   263     WAVStream *wave = (WAVStream *)context;
   264     Uint8 *stream = (Uint8 *)data;
   265     int len = bytes;
   266 
   267     while ((SDL_RWtell(wave->src) < wave->stop) && (len > 0)) {
   268         int consumed = PlaySome(wave, stream, len);
   269         if (!consumed)
   270             break;
   271 
   272         stream += consumed;
   273         len -= consumed;
   274     }
   275     return len;
   276 }
   277 
   278 /* Close the given WAV stream */
   279 static void WAVStream_Delete(void *context)
   280 {
   281     WAVStream *wave = (WAVStream *)context;
   282 
   283     /* Clean up associated data */
   284     if (wave->loops) {
   285         SDL_free(wave->loops);
   286     }
   287     if (wave->cvt.buf) {
   288         SDL_free(wave->cvt.buf);
   289     }
   290     if (wave->freesrc) {
   291         SDL_RWclose(wave->src);
   292     }
   293     SDL_free(wave);
   294 }
   295 
   296 static SDL_bool ParseFMT(WAVStream *wave, Uint32 chunk_length)
   297 {
   298     SDL_RWops *src = wave->src;
   299     SDL_AudioSpec *spec = &wave->spec;
   300     WaveFMT *format;
   301     Uint8 *data;
   302     SDL_bool loaded = SDL_FALSE;
   303 
   304     if (chunk_length < sizeof(*format)) {
   305         Mix_SetError("Wave format chunk too small");
   306         return SDL_FALSE;
   307     }
   308 
   309     data = (Uint8 *)SDL_malloc(chunk_length);
   310     if (!data) {
   311         Mix_SetError("Out of memory");
   312         return SDL_FALSE;
   313     }
   314     if (!SDL_RWread(wave->src, data, chunk_length, 1)) {
   315         Mix_SetError("Couldn't read %d bytes from WAV file", chunk_length);
   316         return SDL_FALSE;
   317     }
   318     format = (WaveFMT *)data;
   319 
   320     /* Decode the audio data format */
   321     switch (SDL_SwapLE16(format->encoding)) {
   322         case PCM_CODE:
   323             /* We can understand this */
   324             break;
   325         default:
   326             Mix_SetError("Unknown WAVE data format");
   327             goto done;
   328     }
   329     spec->freq = SDL_SwapLE32(format->frequency);
   330     switch (SDL_SwapLE16(format->bitspersample)) {
   331         case 8:
   332             spec->format = AUDIO_U8;
   333             break;
   334         case 16:
   335             spec->format = AUDIO_S16;
   336             break;
   337         default:
   338             Mix_SetError("Unknown PCM data format");
   339             goto done;
   340     }
   341     spec->channels = (Uint8) SDL_SwapLE16(format->channels);
   342     spec->samples = 4096;       /* Good default buffer size */
   343 
   344     loaded = SDL_TRUE;
   345 
   346 done:
   347     SDL_free(data);
   348     return loaded;
   349 }
   350 
   351 static SDL_bool ParseDATA(WAVStream *wave, Uint32 chunk_length)
   352 {
   353     wave->start = SDL_RWtell(wave->src);
   354     wave->stop = wave->start + chunk_length;
   355     SDL_RWseek(wave->src, chunk_length, RW_SEEK_CUR);
   356     return SDL_TRUE;
   357 }
   358 
   359 static SDL_bool AddLoopPoint(WAVStream *wave, Uint32 play_count, Uint32 start, Uint32 stop)
   360 {
   361     WAVLoopPoint *loop;
   362     WAVLoopPoint *loops = SDL_realloc(wave->loops, (wave->numloops + 1)*sizeof(*wave->loops));
   363     if (!loops) {
   364         Mix_SetError("Out of memory");
   365         return SDL_FALSE;
   366     }
   367 
   368     loop = &loops[ wave->numloops ];
   369     loop->start = start;
   370     loop->stop = stop;
   371     loop->initial_play_count = play_count;
   372     loop->current_play_count = play_count;
   373 
   374     wave->loops = loops;
   375     ++wave->numloops;
   376     return SDL_TRUE;
   377 }
   378 
   379 static SDL_bool ParseSMPL(WAVStream *wave, Uint32 chunk_length)
   380 {
   381     SamplerChunk *chunk;
   382     Uint8 *data;
   383     Uint32 i;
   384     SDL_bool loaded = SDL_FALSE;
   385 
   386     data = (Uint8 *)SDL_malloc(chunk_length);
   387     if (!data) {
   388         Mix_SetError("Out of memory");
   389         return SDL_FALSE;
   390     }
   391     if (!SDL_RWread(wave->src, data, chunk_length, 1)) {
   392         Mix_SetError("Couldn't read %d bytes from WAV file", chunk_length);
   393         return SDL_FALSE;
   394     }
   395     chunk = (SamplerChunk *)data;
   396 
   397     for (i = 0; i < SDL_SwapLE32(chunk->sample_loops); ++i) {
   398         const Uint32 LOOP_TYPE_FORWARD = 0;
   399         Uint32 loop_type = SDL_SwapLE32(chunk->loops[i].type);
   400         if (loop_type == LOOP_TYPE_FORWARD) {
   401             AddLoopPoint(wave, SDL_SwapLE32(chunk->loops[i].play_count), SDL_SwapLE32(chunk->loops[i].start), SDL_SwapLE32(chunk->loops[i].end));
   402         }
   403     }
   404 
   405     loaded = SDL_TRUE;
   406     SDL_free(data);
   407     return loaded;
   408 }
   409 
   410 static SDL_bool LoadWAVStream(WAVStream *wave)
   411 {
   412     SDL_RWops *src = wave->src;
   413     Uint32 chunk_type;
   414     Uint32 chunk_length;
   415     SDL_bool found_FMT = SDL_FALSE;
   416     SDL_bool found_DATA = SDL_FALSE;
   417 
   418     /* WAV magic header */
   419     Uint32 wavelen;
   420     Uint32 WAVEmagic;
   421 
   422     /* Check the magic header */
   423     wavelen = SDL_ReadLE32(src);
   424     WAVEmagic = SDL_ReadLE32(src);
   425 
   426     /* Read the chunks */
   427     for (; ;) {
   428         chunk_type = SDL_ReadLE32(src);
   429         chunk_length = SDL_ReadLE32(src);
   430 
   431         if (chunk_length == 0)
   432             break;
   433 
   434         switch (chunk_type)
   435         {
   436         case FMT:
   437             found_FMT = SDL_TRUE;
   438             if (!ParseFMT(wave, chunk_length))
   439                 return SDL_FALSE;
   440             break;
   441         case DATA:
   442             found_DATA = SDL_TRUE;
   443             if (!ParseDATA(wave, chunk_length))
   444                 return SDL_FALSE;
   445             break;
   446         case SMPL:
   447             if (!ParseSMPL(wave, chunk_length))
   448                 return SDL_FALSE;
   449             break;
   450         default:
   451             SDL_RWseek(src, chunk_length, RW_SEEK_CUR);
   452             break;
   453         }
   454     }
   455 
   456     if (!found_FMT) {
   457         Mix_SetError("Bad WAV file (no FMT chunk)");
   458         return SDL_FALSE;
   459     }
   460 
   461     if (!found_DATA) {
   462         Mix_SetError("Bad WAV file (no DATA chunk)");
   463         return SDL_FALSE;
   464     }
   465 
   466     return SDL_TRUE;
   467 }
   468 
   469 /* I couldn't get SANE_to_double() to work, so I stole this from libsndfile.
   470  * I don't pretend to fully understand it.
   471  */
   472 
   473 static Uint32 SANE_to_Uint32 (Uint8 *sanebuf)
   474 {
   475     /* Negative number? */
   476     if (sanebuf[0] & 0x80)
   477         return 0;
   478 
   479     /* Less than 1? */
   480     if (sanebuf[0] <= 0x3F)
   481         return 1;
   482 
   483     /* Way too big? */
   484     if (sanebuf[0] > 0x40)
   485         return 0x4000000;
   486 
   487     /* Still too big? */
   488     if (sanebuf[0] == 0x40 && sanebuf[1] > 0x1C)
   489         return 800000000;
   490 
   491     return ((sanebuf[2] << 23) | (sanebuf[3] << 15) | (sanebuf[4] << 7) |
   492             (sanebuf[5] >> 1)) >> (29 - sanebuf[1]);
   493 }
   494 
   495 static SDL_bool LoadAIFFStream(WAVStream *wave)
   496 {
   497     SDL_RWops *src = wave->src;
   498     SDL_AudioSpec *spec = &wave->spec;
   499     SDL_bool found_SSND = SDL_FALSE;
   500     SDL_bool found_COMM = SDL_FALSE;
   501 
   502     Uint32 chunk_type;
   503     Uint32 chunk_length;
   504     Sint64 next_chunk;
   505 
   506     /* AIFF magic header */
   507     Uint32 AIFFmagic;
   508     /* SSND chunk        */
   509     Uint32 offset;
   510     Uint32 blocksize;
   511     /* COMM format chunk */
   512     Uint16 channels = 0;
   513     Uint32 numsamples = 0;
   514     Uint16 samplesize = 0;
   515     Uint8 sane_freq[10];
   516     Uint32 frequency = 0;
   517 
   518     /* Check the magic header */
   519     chunk_length = SDL_ReadBE32(src);
   520     AIFFmagic = SDL_ReadLE32(src);
   521     if (AIFFmagic != AIFF) {
   522         Mix_SetError("Unrecognized file type (not AIFF)");
   523         return SDL_FALSE;
   524     }
   525 
   526     /* From what I understand of the specification, chunks may appear in
   527      * any order, and we should just ignore unknown ones.
   528      *
   529      * TODO: Better sanity-checking. E.g. what happens if the AIFF file
   530      *       contains compressed sound data?
   531      */
   532     do {
   533         chunk_type      = SDL_ReadLE32(src);
   534         chunk_length    = SDL_ReadBE32(src);
   535         next_chunk      = SDL_RWtell(src) + chunk_length;
   536 
   537         /* Paranoia to avoid infinite loops */
   538         if (chunk_length == 0)
   539             break;
   540 
   541         switch (chunk_type) {
   542         case SSND:
   543             found_SSND = SDL_TRUE;
   544             offset = SDL_ReadBE32(src);
   545             blocksize = SDL_ReadBE32(src);
   546             wave->start = SDL_RWtell(src) + offset;
   547             break;
   548 
   549         case COMM:
   550             found_COMM = SDL_TRUE;
   551 
   552             /* Read the audio data format chunk */
   553             channels = SDL_ReadBE16(src);
   554             numsamples = SDL_ReadBE32(src);
   555             samplesize = SDL_ReadBE16(src);
   556             SDL_RWread(src, sane_freq, sizeof(sane_freq), 1);
   557             frequency = SANE_to_Uint32(sane_freq);
   558             break;
   559 
   560         default:
   561             break;
   562         }
   563     } while ((!found_SSND || !found_COMM)
   564          && SDL_RWseek(src, next_chunk, RW_SEEK_SET) != -1);
   565 
   566     if (!found_SSND) {
   567         Mix_SetError("Bad AIFF file (no SSND chunk)");
   568         return SDL_FALSE;
   569     }
   570 
   571     if (!found_COMM) {
   572         Mix_SetError("Bad AIFF file (no COMM chunk)");
   573         return SDL_FALSE;
   574     }
   575 
   576     wave->stop = wave->start + channels * numsamples * (samplesize / 8);
   577 
   578     /* Decode the audio data format */
   579     SDL_memset(spec, 0, (sizeof *spec));
   580     spec->freq = frequency;
   581     switch (samplesize) {
   582         case 8:
   583             spec->format = AUDIO_S8;
   584             break;
   585         case 16:
   586             spec->format = AUDIO_S16MSB;
   587             break;
   588         default:
   589             Mix_SetError("Unknown samplesize in data format");
   590             return SDL_FALSE;
   591     }
   592     spec->channels = (Uint8) channels;
   593     spec->samples = 4096;       /* Good default buffer size */
   594 
   595     return SDL_TRUE;
   596 }
   597 
   598 Mix_MusicInterface Mix_MusicInterface_WAV =
   599 {
   600     "WAVE",
   601     MIX_MUSIC_WAVE,
   602     MUS_WAV,
   603     SDL_FALSE,
   604     SDL_FALSE,
   605 
   606     NULL,   /* Load */
   607     NULL,   /* Open */
   608     WAVStream_CreateFromRW,
   609     NULL,   /* CreateFromFile */
   610     WAVStream_SetVolume,
   611     WAVStream_Play,
   612     NULL,   /* IsPlaying */
   613     WAVStream_GetAudio,
   614     NULL,   /* Seek */
   615     NULL,   /* Pause */
   616     NULL,   /* Resume */
   617     NULL,   /* Stop */
   618     WAVStream_Delete,
   619     NULL,   /* Close */
   620     NULL,   /* Unload */
   621 };
   622 
   623 #endif /* MUSIC_WAV */
   624 
   625 /* vi: set ts=4 sw=4 expandtab: */