Prechádzať zdrojové kódy

Support WAV music streaming #1198

Switched custom WAV laoding/saving funtionality to drwav library, it also provides the required mechanisms to stream wav data.
raysan5 5 rokov pred
rodič
commit
a6fcd32339
1 zmenil súbory, kde vykonal 80 pridanie a 200 odobranie
  1. 80 200
      src/raudio.c

+ 80 - 200
src/raudio.c

@@ -197,14 +197,13 @@ typedef struct tagBITMAPINFOHEADER {
     #include "external/jar_mod.h"       // MOD loading functions
 #endif
 
-#if defined(SUPPORT_FILEFORMAT_FLAC)
-    #define DRFLAC_MALLOC RL_MALLOC
-    #define DRFLAC_REALLOC RL_REALLOC
-    #define DRFLAC_FREE RL_FREE
+#if defined(SUPPORT_FILEFORMAT_WAV)
+    #define DRWAV_MALLOC RL_MALLOC
+    #define DRWAV_REALLOC RL_REALLOC
+    #define DRWAV_FREE RL_FREE
 
-    #define DR_FLAC_IMPLEMENTATION
-    #define DR_FLAC_NO_WIN32_IO
-    #include "external/dr_flac.h"       // FLAC loading functions
+    #define DR_WAV_IMPLEMENTATION
+    #include "external/dr_wav.h"        // WAV loading functions
 #endif
 
 #if defined(SUPPORT_FILEFORMAT_MP3)
@@ -216,6 +215,16 @@ typedef struct tagBITMAPINFOHEADER {
     #include "external/dr_mp3.h"        // MP3 loading functions
 #endif
 
+#if defined(SUPPORT_FILEFORMAT_FLAC)
+    #define DRFLAC_MALLOC RL_MALLOC
+    #define DRFLAC_REALLOC RL_REALLOC
+    #define DRFLAC_FREE RL_FREE
+
+    #define DR_FLAC_IMPLEMENTATION
+    #define DR_FLAC_NO_WIN32_IO
+    #include "external/dr_flac.h"       // FLAC loading functions
+#endif
+
 #if defined(_MSC_VER)
     #undef bool
 #endif
@@ -437,11 +446,11 @@ void InitAudioDevice(void)
     }
 
     TRACELOG(LOG_INFO, "AUDIO: Device initialized successfully");
-    TRACELOG(LOG_INFO, "    > Backend:      miniaudio / %s", ma_get_backend_name(AUDIO.System.context.backend));
-    TRACELOG(LOG_INFO, "    > Format:       %s -> %s", ma_get_format_name(AUDIO.System.device.playback.format), ma_get_format_name(AUDIO.System.device.playback.internalFormat));
-    TRACELOG(LOG_INFO, "    > Channels:     %d -> %d", AUDIO.System.device.playback.channels, AUDIO.System.device.playback.internalChannels);
-    TRACELOG(LOG_INFO, "    > Sample rate:  %d -> %d", AUDIO.System.device.sampleRate, AUDIO.System.device.playback.internalSampleRate);
-    TRACELOG(LOG_INFO, "    > Periods size: %d", AUDIO.System.device.playback.internalPeriodSizeInFrames*AUDIO.System.device.playback.internalPeriods);
+    TRACELOG(LOG_INFO, "    > Backend:       miniaudio / %s", ma_get_backend_name(AUDIO.System.context.backend));
+    TRACELOG(LOG_INFO, "    > Format:        %s -> %s", ma_get_format_name(AUDIO.System.device.playback.format), ma_get_format_name(AUDIO.System.device.playback.internalFormat));
+    TRACELOG(LOG_INFO, "    > Channels:      %d -> %d", AUDIO.System.device.playback.channels, AUDIO.System.device.playback.internalChannels);
+    TRACELOG(LOG_INFO, "    > Sample rate:   %d -> %d", AUDIO.System.device.sampleRate, AUDIO.System.device.playback.internalSampleRate);
+    TRACELOG(LOG_INFO, "    > Periods size:  %d", AUDIO.System.device.playback.internalPeriodSizeInFrames*AUDIO.System.device.playback.internalPeriods);
 
     InitAudioBufferPool();
 
@@ -1046,6 +1055,24 @@ Music LoadMusicStream(const char *fileName)
     bool musicLoaded = false;
 
     if (false) { }
+#if defined(SUPPORT_FILEFORMAT_WAV)
+    else if (IsFileExtension(fileName, ".wav"))
+    {
+        drwav *ctxWav = RL_MALLOC(sizeof(drwav));
+        bool success = drwav_init_file(ctxWav, fileName, NULL);
+
+        if (success)
+        {
+            music.ctxType = MUSIC_AUDIO_WAV;
+            music.ctxData = ctxWav;
+
+            music.stream = InitAudioStream(ctxWav->sampleRate, ctxWav->bitsPerSample, ctxWav->channels);
+            music.sampleCount = (unsigned int)ctxWav->totalPCMFrameCount*ctxWav->channels;
+            music.looping = true;   // Looping enabled by default
+            musicLoaded = true;
+        }
+    }
+#endif
 #if defined(SUPPORT_FILEFORMAT_OGG)
     else if (IsFileExtension(fileName, ".ogg"))
     {
@@ -1151,6 +1178,9 @@ Music LoadMusicStream(const char *fileName)
     if (!musicLoaded)
     {
         if (false) { }
+    #if defined(SUPPORT_FILEFORMAT_WAV)
+        else if (music.ctxType == MUSIC_AUDIO_WAV) drwav_uninit((drwav *)music.ctxData);
+    #endif
     #if defined(SUPPORT_FILEFORMAT_OGG)
         else if (music.ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close((stb_vorbis *)music.ctxData);
     #endif
@@ -1188,6 +1218,9 @@ void UnloadMusicStream(Music music)
     CloseAudioStream(music.stream);
 
     if (false) { }
+#if defined(SUPPORT_FILEFORMAT_WAV)
+    else if (music.ctxType == MUSIC_AUDIO_WAV) drwav_uninit((drwav *)music.ctxData);
+#endif
 #if defined(SUPPORT_FILEFORMAT_OGG)
     else if (music.ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close((stb_vorbis *)music.ctxData);
 #endif
@@ -1239,6 +1272,9 @@ void StopMusicStream(Music music)
 
     switch (music.ctxType)
     {
+#if defined(SUPPORT_FILEFORMAT_WAV)
+        case MUSIC_AUDIO_WAV: drwav_seek_to_pcm_frame((drwav *)music.ctxData, 0); break;
+#endif
 #if defined(SUPPORT_FILEFORMAT_OGG)
         case MUSIC_AUDIO_OGG: stb_vorbis_seek_start((stb_vorbis *)music.ctxData); break;
 #endif
@@ -1283,6 +1319,14 @@ void UpdateMusicStream(Music music)
 
         switch (music.ctxType)
         {
+        #if defined(SUPPORT_FILEFORMAT_WAV)
+            case MUSIC_AUDIO_WAV:
+            {
+                // NOTE: Returns the number of samples to process (not required)
+                drwav_read_pcm_frames_s16((drwav *)music.ctxData, samplesCount/music.stream.channels, (short *)pcm);
+
+            } break;
+        #endif
         #if defined(SUPPORT_FILEFORMAT_OGG)
             case MUSIC_AUDIO_OGG:
             {
@@ -1816,204 +1860,43 @@ static void CloseAudioBufferPool(void)
 // Load WAV file into Wave structure
 static Wave LoadWAV(const char *fileName)
 {
-    // Basic WAV headers structs
-    typedef struct {
-        char chunkID[4];
-        int chunkSize;
-        char format[4];
-    } WAVRiffHeader;
-
-    typedef struct {
-        char subChunkID[4];
-        int subChunkSize;
-        short audioFormat;
-        short numChannels;
-        int sampleRate;
-        int byteRate;
-        short blockAlign;
-        short bitsPerSample;
-    } WAVFormat;
-
-    typedef struct {
-        char subChunkID[4];
-        int subChunkSize;
-    } WAVData;
-
-    WAVRiffHeader wavRiffHeader = { 0 };
-    WAVFormat wavFormat = { 0 };
-    WAVData wavData = { 0 };
-
     Wave wave = { 0 };
-    FILE *wavFile = NULL;
 
-    wavFile = fopen(fileName, "rb");
+    // Decode an entire FLAC file in one go
+    unsigned long long int totalPCMFrameCount = 0;
+    wave.data = drwav_open_file_and_read_pcm_frames_s16(fileName, &wave.channels, &wave.sampleRate, &totalPCMFrameCount, NULL);
 
-    if (wavFile == NULL)
-    {
-        TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open WAV file", fileName);
-        wave.data = NULL;
-    }
+    if (wave.data == NULL) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to load WAV data", fileName);
     else
     {
-        // Read in the first chunk into the struct
-        fread(&wavRiffHeader, sizeof(WAVRiffHeader), 1, wavFile);
-
-        // Check for RIFF and WAVE tags
-        if ((wavRiffHeader.chunkID[0] != 'R') ||
-            (wavRiffHeader.chunkID[1] != 'I') ||
-            (wavRiffHeader.chunkID[2] != 'F') ||
-            (wavRiffHeader.chunkID[3] != 'F') ||
-            (wavRiffHeader.format[0] != 'W') ||
-            (wavRiffHeader.format[1] != 'A') ||
-            (wavRiffHeader.format[2] != 'V') ||
-            (wavRiffHeader.format[3] != 'E'))
-        {
-            TRACELOG(LOG_WARNING, "WAVE: [%s] RIFF or WAVE header are not valid", fileName);
-        }
-        else
-        {
-            // Read in the 2nd chunk for the wave info
-            fread(&wavFormat, sizeof(WAVFormat), 1, wavFile);
-
-            // Check for fmt tag
-            if ((wavFormat.subChunkID[0] != 'f') || (wavFormat.subChunkID[1] != 'm') ||
-                (wavFormat.subChunkID[2] != 't') || (wavFormat.subChunkID[3] != ' '))
-            {
-                TRACELOG(LOG_WARNING, "WAVE: [%s] Wave format header is not valid", fileName);
-            }
-            else
-            {
-                // Check for extra parameters;
-                if (wavFormat.subChunkSize > 16) fseek(wavFile, sizeof(short), SEEK_CUR);
-
-                // Read in the the last byte of data before the sound file
-                fread(&wavData, sizeof(WAVData), 1, wavFile);
-
-                // Check for data tag
-                if ((wavData.subChunkID[0] != 'd') || (wavData.subChunkID[1] != 'a') ||
-                    (wavData.subChunkID[2] != 't') || (wavData.subChunkID[3] != 'a'))
-                {
-                    TRACELOG(LOG_WARNING, "WAVE: [%s] Data header is not valid", fileName);
-                }
-                else
-                {
-                    // Allocate memory for data
-                    wave.data = RL_MALLOC(wavData.subChunkSize);
-
-                    // Read in the sound data into the soundData variable
-                    fread(wave.data, wavData.subChunkSize, 1, wavFile);
-
-                    // Store wave parameters
-                    wave.sampleRate = wavFormat.sampleRate;
-                    wave.sampleSize = wavFormat.bitsPerSample;
-                    wave.channels = wavFormat.numChannels;
-
-                    // NOTE: Only support 8 bit, 16 bit and 32 bit sample sizes
-                    if ((wave.sampleSize != 8) && (wave.sampleSize != 16) && (wave.sampleSize != 32))
-                    {
-                        TRACELOG(LOG_WARNING, "WAVE: [%s] Sample size (%ibit) not supported, converted to 16bit", fileName, wave.sampleSize);
-                        WaveFormat(&wave, wave.sampleRate, 16, wave.channels);
-                    }
-
-                    // NOTE: Only support up to 2 channels (mono, stereo)
-                    if (wave.channels > 2)
-                    {
-                        WaveFormat(&wave, wave.sampleRate, wave.sampleSize, 2);
-                        TRACELOG(LOG_WARNING, "WAVE: [%s] Channels number (%i) not supported, converted to 2 channels", fileName, wave.channels);
-                    }
-
-                    // NOTE: subChunkSize comes in bytes, we need to translate it to number of samples
-                    wave.sampleCount = (wavData.subChunkSize/(wave.sampleSize/8))/wave.channels;
-
-                    TRACELOG(LOG_INFO, "WAVE: [%s] File loaded successfully (%i Hz, %i bit, %s)", fileName, wave.sampleRate, wave.sampleSize, (wave.channels == 1)? "Mono" : "Stereo");
-                }
-            }
-        }
+        wave.sampleCount = (unsigned int)totalPCMFrameCount*wave.channels;
+        wave.sampleSize = 16;
 
-        fclose(wavFile);
+        TRACELOG(LOG_INFO, "WAVE: [%s] WAV file loaded successfully (%i Hz, %i bit, %s)", fileName, wave.sampleRate, wave.sampleSize, (wave.channels == 1)? "Mono" : "Stereo");
     }
-
+ 
     return wave;
 }
 
 // Save wave data as WAV file
 static int SaveWAV(Wave wave, const char *fileName)
 {
-    int success = 0;
-    int dataSize = wave.sampleCount*wave.channels*wave.sampleSize/8;
-
-    // Basic WAV headers structs
-    typedef struct {
-        char chunkID[4];
-        int chunkSize;
-        char format[4];
-    } RiffHeader;
-
-    typedef struct {
-        char subChunkID[4];
-        int subChunkSize;
-        short audioFormat;
-        short numChannels;
-        int sampleRate;
-        int byteRate;
-        short blockAlign;
-        short bitsPerSample;
-    } WaveFormat;
-
-    typedef struct {
-        char subChunkID[4];
-        int subChunkSize;
-    } WaveData;
-
-    FILE *wavFile = fopen(fileName, "wb");
-
-    if (wavFile == NULL) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open audio file", fileName);
-    else
-    {
-        RiffHeader riffHeader;
-        WaveFormat waveFormat;
-        WaveData waveData;
-
-        // Fill structs with data
-        riffHeader.chunkID[0] = 'R';
-        riffHeader.chunkID[1] = 'I';
-        riffHeader.chunkID[2] = 'F';
-        riffHeader.chunkID[3] = 'F';
-        riffHeader.chunkSize = 44 - 4 + wave.sampleCount*wave.sampleSize/8;
-        riffHeader.format[0] = 'W';
-        riffHeader.format[1] = 'A';
-        riffHeader.format[2] = 'V';
-        riffHeader.format[3] = 'E';
-
-        waveFormat.subChunkID[0] = 'f';
-        waveFormat.subChunkID[1] = 'm';
-        waveFormat.subChunkID[2] = 't';
-        waveFormat.subChunkID[3] = ' ';
-        waveFormat.subChunkSize = 16;
-        waveFormat.audioFormat = 1;
-        waveFormat.numChannels = wave.channels;
-        waveFormat.sampleRate = wave.sampleRate;
-        waveFormat.byteRate = wave.sampleRate*wave.sampleSize/8;
-        waveFormat.blockAlign = wave.sampleSize/8;
-        waveFormat.bitsPerSample = wave.sampleSize;
-
-        waveData.subChunkID[0] = 'd';
-        waveData.subChunkID[1] = 'a';
-        waveData.subChunkID[2] = 't';
-        waveData.subChunkID[3] = 'a';
-        waveData.subChunkSize = dataSize;
-
-        fwrite(&riffHeader, sizeof(RiffHeader), 1, wavFile);
-        fwrite(&waveFormat, sizeof(WaveFormat), 1, wavFile);
-        fwrite(&waveData, sizeof(WaveData), 1, wavFile);
-
-        success = (int)fwrite(wave.data, dataSize, 1, wavFile);
-
-        fclose(wavFile);
-    }
-
-    // If all data has been written correctly to file, success = 1
-    return success;
+    drwav wav = { 0 };
+    drwav_data_format format = { 0 };
+    format.container = drwav_container_riff;     // <-- drwav_container_riff = normal WAV files, drwav_container_w64 = Sony Wave64.
+    format.format = DR_WAVE_FORMAT_PCM;          // <-- Any of the DR_WAVE_FORMAT_* codes.
+    format.channels = wave.channels;
+    format.sampleRate = wave.sampleRate;
+    format.bitsPerSample = wave.sampleSize;
+    
+    drwav_init_file_write(&wav, fileName, &format, NULL);
+    drwav_write_pcm_frames(&wav, wave.sampleCount/wave.channels, wave.data);
+    
+    printf("save!\n");
+    
+    drwav_uninit(&wav);
+    
+    return true;
 }
 #endif
 
@@ -2069,9 +1952,6 @@ static Wave LoadFLAC(const char *fileName)
         wave.sampleCount = (unsigned int)totalSampleCount;
         wave.sampleSize = 16;
 
-        // NOTE: Only support up to 2 channels (mono, stereo)
-        if (wave.channels > 2) TRACELOG(LOG_WARNING, "WAVE: [%s] FLAC channels number (%i) not supported", fileName, wave.channels);
-
         TRACELOG(LOG_INFO, "WAVE: [%s] FLAC file loaded successfully (%i Hz, %i bit, %s)", fileName, wave.sampleRate, wave.sampleSize, (wave.channels == 1)? "Mono" : "Stereo");
     }