music_wav.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 17 Oct 2017 02:54:52 -0700
changeset 779 a2b494c054d5
parent 777 92882ef2ab81
child 797 b4b6adff699a
permissions -rw-r--r--
Updated Xcode project and fixed warnings
     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_AudioSpec *spec = &wave->spec;
   299     WaveFMT *format;
   300     Uint8 *data;
   301     SDL_bool loaded = SDL_FALSE;
   302 
   303     if (chunk_length < sizeof(*format)) {
   304         Mix_SetError("Wave format chunk too small");
   305         return SDL_FALSE;
   306     }
   307 
   308     data = (Uint8 *)SDL_malloc(chunk_length);
   309     if (!data) {
   310         Mix_SetError("Out of memory");
   311         return SDL_FALSE;
   312     }
   313     if (!SDL_RWread(wave->src, data, chunk_length, 1)) {
   314         Mix_SetError("Couldn't read %d bytes from WAV file", chunk_length);
   315         return SDL_FALSE;
   316     }
   317     format = (WaveFMT *)data;
   318 
   319     /* Decode the audio data format */
   320     switch (SDL_SwapLE16(format->encoding)) {
   321         case PCM_CODE:
   322             /* We can understand this */
   323             break;
   324         default:
   325             Mix_SetError("Unknown WAVE data format");
   326             goto done;
   327     }
   328     spec->freq = SDL_SwapLE32(format->frequency);
   329     switch (SDL_SwapLE16(format->bitspersample)) {
   330         case 8:
   331             spec->format = AUDIO_U8;
   332             break;
   333         case 16:
   334             spec->format = AUDIO_S16;
   335             break;
   336         default:
   337             Mix_SetError("Unknown PCM data format");
   338             goto done;
   339     }
   340     spec->channels = (Uint8) SDL_SwapLE16(format->channels);
   341     spec->samples = 4096;       /* Good default buffer size */
   342 
   343     loaded = SDL_TRUE;
   344 
   345 done:
   346     SDL_free(data);
   347     return loaded;
   348 }
   349 
   350 static SDL_bool ParseDATA(WAVStream *wave, Uint32 chunk_length)
   351 {
   352     wave->start = SDL_RWtell(wave->src);
   353     wave->stop = wave->start + chunk_length;
   354     SDL_RWseek(wave->src, chunk_length, RW_SEEK_CUR);
   355     return SDL_TRUE;
   356 }
   357 
   358 static SDL_bool AddLoopPoint(WAVStream *wave, Uint32 play_count, Uint32 start, Uint32 stop)
   359 {
   360     WAVLoopPoint *loop;
   361     WAVLoopPoint *loops = SDL_realloc(wave->loops, (wave->numloops + 1)*sizeof(*wave->loops));
   362     if (!loops) {
   363         Mix_SetError("Out of memory");
   364         return SDL_FALSE;
   365     }
   366 
   367     loop = &loops[ wave->numloops ];
   368     loop->start = start;
   369     loop->stop = stop;
   370     loop->initial_play_count = play_count;
   371     loop->current_play_count = play_count;
   372 
   373     wave->loops = loops;
   374     ++wave->numloops;
   375     return SDL_TRUE;
   376 }
   377 
   378 static SDL_bool ParseSMPL(WAVStream *wave, Uint32 chunk_length)
   379 {
   380     SamplerChunk *chunk;
   381     Uint8 *data;
   382     Uint32 i;
   383     SDL_bool loaded = SDL_FALSE;
   384 
   385     data = (Uint8 *)SDL_malloc(chunk_length);
   386     if (!data) {
   387         Mix_SetError("Out of memory");
   388         return SDL_FALSE;
   389     }
   390     if (!SDL_RWread(wave->src, data, chunk_length, 1)) {
   391         Mix_SetError("Couldn't read %d bytes from WAV file", chunk_length);
   392         return SDL_FALSE;
   393     }
   394     chunk = (SamplerChunk *)data;
   395 
   396     for (i = 0; i < SDL_SwapLE32(chunk->sample_loops); ++i) {
   397         const Uint32 LOOP_TYPE_FORWARD = 0;
   398         Uint32 loop_type = SDL_SwapLE32(chunk->loops[i].type);
   399         if (loop_type == LOOP_TYPE_FORWARD) {
   400             AddLoopPoint(wave, SDL_SwapLE32(chunk->loops[i].play_count), SDL_SwapLE32(chunk->loops[i].start), SDL_SwapLE32(chunk->loops[i].end));
   401         }
   402     }
   403 
   404     loaded = SDL_TRUE;
   405     SDL_free(data);
   406     return loaded;
   407 }
   408 
   409 static SDL_bool LoadWAVStream(WAVStream *wave)
   410 {
   411     SDL_RWops *src = wave->src;
   412     Uint32 chunk_type;
   413     Uint32 chunk_length;
   414     SDL_bool found_FMT = SDL_FALSE;
   415     SDL_bool found_DATA = SDL_FALSE;
   416 
   417     /* WAV magic header */
   418     Uint32 wavelen;
   419     Uint32 WAVEmagic;
   420 
   421     /* Check the magic header */
   422     wavelen = SDL_ReadLE32(src);
   423     WAVEmagic = SDL_ReadLE32(src);
   424 
   425     /* Read the chunks */
   426     for (; ;) {
   427         chunk_type = SDL_ReadLE32(src);
   428         chunk_length = SDL_ReadLE32(src);
   429 
   430         if (chunk_length == 0)
   431             break;
   432 
   433         switch (chunk_type)
   434         {
   435         case FMT:
   436             found_FMT = SDL_TRUE;
   437             if (!ParseFMT(wave, chunk_length))
   438                 return SDL_FALSE;
   439             break;
   440         case DATA:
   441             found_DATA = SDL_TRUE;
   442             if (!ParseDATA(wave, chunk_length))
   443                 return SDL_FALSE;
   444             break;
   445         case SMPL:
   446             if (!ParseSMPL(wave, chunk_length))
   447                 return SDL_FALSE;
   448             break;
   449         default:
   450             SDL_RWseek(src, chunk_length, RW_SEEK_CUR);
   451             break;
   452         }
   453     }
   454 
   455     if (!found_FMT) {
   456         Mix_SetError("Bad WAV file (no FMT chunk)");
   457         return SDL_FALSE;
   458     }
   459 
   460     if (!found_DATA) {
   461         Mix_SetError("Bad WAV file (no DATA chunk)");
   462         return SDL_FALSE;
   463     }
   464 
   465     return SDL_TRUE;
   466 }
   467 
   468 /* I couldn't get SANE_to_double() to work, so I stole this from libsndfile.
   469  * I don't pretend to fully understand it.
   470  */
   471 
   472 static Uint32 SANE_to_Uint32 (Uint8 *sanebuf)
   473 {
   474     /* Negative number? */
   475     if (sanebuf[0] & 0x80)
   476         return 0;
   477 
   478     /* Less than 1? */
   479     if (sanebuf[0] <= 0x3F)
   480         return 1;
   481 
   482     /* Way too big? */
   483     if (sanebuf[0] > 0x40)
   484         return 0x4000000;
   485 
   486     /* Still too big? */
   487     if (sanebuf[0] == 0x40 && sanebuf[1] > 0x1C)
   488         return 800000000;
   489 
   490     return ((sanebuf[2] << 23) | (sanebuf[3] << 15) | (sanebuf[4] << 7) |
   491             (sanebuf[5] >> 1)) >> (29 - sanebuf[1]);
   492 }
   493 
   494 static SDL_bool LoadAIFFStream(WAVStream *wave)
   495 {
   496     SDL_RWops *src = wave->src;
   497     SDL_AudioSpec *spec = &wave->spec;
   498     SDL_bool found_SSND = SDL_FALSE;
   499     SDL_bool found_COMM = SDL_FALSE;
   500 
   501     Uint32 chunk_type;
   502     Uint32 chunk_length;
   503     Sint64 next_chunk;
   504 
   505     /* AIFF magic header */
   506     Uint32 AIFFmagic;
   507     /* SSND chunk        */
   508     Uint32 offset;
   509     Uint32 blocksize;
   510     /* COMM format chunk */
   511     Uint16 channels = 0;
   512     Uint32 numsamples = 0;
   513     Uint16 samplesize = 0;
   514     Uint8 sane_freq[10];
   515     Uint32 frequency = 0;
   516 
   517     /* Check the magic header */
   518     chunk_length = SDL_ReadBE32(src);
   519     AIFFmagic = SDL_ReadLE32(src);
   520     if (AIFFmagic != AIFF) {
   521         Mix_SetError("Unrecognized file type (not AIFF)");
   522         return SDL_FALSE;
   523     }
   524 
   525     /* From what I understand of the specification, chunks may appear in
   526      * any order, and we should just ignore unknown ones.
   527      *
   528      * TODO: Better sanity-checking. E.g. what happens if the AIFF file
   529      *       contains compressed sound data?
   530      */
   531     do {
   532         chunk_type      = SDL_ReadLE32(src);
   533         chunk_length    = SDL_ReadBE32(src);
   534         next_chunk      = SDL_RWtell(src) + chunk_length;
   535 
   536         /* Paranoia to avoid infinite loops */
   537         if (chunk_length == 0)
   538             break;
   539 
   540         switch (chunk_type) {
   541         case SSND:
   542             found_SSND = SDL_TRUE;
   543             offset = SDL_ReadBE32(src);
   544             blocksize = SDL_ReadBE32(src);
   545             wave->start = SDL_RWtell(src) + offset;
   546             break;
   547 
   548         case COMM:
   549             found_COMM = SDL_TRUE;
   550 
   551             /* Read the audio data format chunk */
   552             channels = SDL_ReadBE16(src);
   553             numsamples = SDL_ReadBE32(src);
   554             samplesize = SDL_ReadBE16(src);
   555             SDL_RWread(src, sane_freq, sizeof(sane_freq), 1);
   556             frequency = SANE_to_Uint32(sane_freq);
   557             break;
   558 
   559         default:
   560             break;
   561         }
   562     } while ((!found_SSND || !found_COMM)
   563          && SDL_RWseek(src, next_chunk, RW_SEEK_SET) != -1);
   564 
   565     if (!found_SSND) {
   566         Mix_SetError("Bad AIFF file (no SSND chunk)");
   567         return SDL_FALSE;
   568     }
   569 
   570     if (!found_COMM) {
   571         Mix_SetError("Bad AIFF file (no COMM chunk)");
   572         return SDL_FALSE;
   573     }
   574 
   575     wave->stop = wave->start + channels * numsamples * (samplesize / 8);
   576 
   577     /* Decode the audio data format */
   578     SDL_memset(spec, 0, (sizeof *spec));
   579     spec->freq = frequency;
   580     switch (samplesize) {
   581         case 8:
   582             spec->format = AUDIO_S8;
   583             break;
   584         case 16:
   585             spec->format = AUDIO_S16MSB;
   586             break;
   587         default:
   588             Mix_SetError("Unknown samplesize in data format");
   589             return SDL_FALSE;
   590     }
   591     spec->channels = (Uint8) channels;
   592     spec->samples = 4096;       /* Good default buffer size */
   593 
   594     return SDL_TRUE;
   595 }
   596 
   597 Mix_MusicInterface Mix_MusicInterface_WAV =
   598 {
   599     "WAVE",
   600     MIX_MUSIC_WAVE,
   601     MUS_WAV,
   602     SDL_FALSE,
   603     SDL_FALSE,
   604 
   605     NULL,   /* Load */
   606     NULL,   /* Open */
   607     WAVStream_CreateFromRW,
   608     NULL,   /* CreateFromFile */
   609     WAVStream_SetVolume,
   610     WAVStream_Play,
   611     NULL,   /* IsPlaying */
   612     WAVStream_GetAudio,
   613     NULL,   /* Seek */
   614     NULL,   /* Pause */
   615     NULL,   /* Resume */
   616     NULL,   /* Stop */
   617     WAVStream_Delete,
   618     NULL,   /* Close */
   619     NULL,   /* Unload */
   620 };
   621 
   622 #endif /* MUSIC_WAV */
   623 
   624 /* vi: set ts=4 sw=4 expandtab: */