浏览代码

Complete review and update

Simplified module for Music and AudioStream
Added support for raw audio streaming (with example)
raysan5 9 年之前
父节点
当前提交
68d647c1af

+ 1 - 1
examples/audio_module_playing.c

@@ -57,7 +57,7 @@ int main()
     // Create a RenderTexture2D to be used for render to texture
     RenderTexture2D target = LoadRenderTexture(screenWidth, screenHeight);
 
-    Music xm = LoadMusicStream("resources/audio/2t2m_spa.xm");
+    Music xm = LoadMusicStream("resources/audio/mini1111.xm");
     
     PlayMusicStream(xm);
 

+ 0 - 3
examples/audio_music_stream.c

@@ -59,9 +59,6 @@ int main()
             SetMusicVolume(volume);
         }
 */
-        if (IsWindowMinimized()) PauseMusicStream(music);
-        else ResumeMusicStream(music);
-
         timePlayed = GetMusicTimePlayed(music)/GetMusicTimeLength(music)*100*4; // We scale by 4 to fit 400 pixels
 
         UpdateMusicStream(music);        // Update music buffer with new stream data

+ 106 - 0
examples/audio_raw_stream.c

@@ -0,0 +1,106 @@
+/*******************************************************************************************
+*
+*   raylib [audio] example - Raw audio streaming
+*
+*   NOTE: This example requires OpenAL Soft library installed
+*
+*   This example has been created using raylib 1.6 (www.raylib.com)
+*   raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
+*
+*   Copyright (c) 2015 Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include <stdlib.h>         // Required for: malloc(), free()
+#include <math.h>           // Required for: sinf()
+
+int main()
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    int screenWidth = 800;
+    int screenHeight = 450;
+
+    SetConfigFlags(FLAG_MSAA_4X_HINT);
+    InitWindow(screenWidth, screenHeight, "raylib [audio] example - raw audio streaming");
+
+    InitAudioDevice();              // Initialize audio device
+
+    AudioStream stream = InitAudioStream(44100, 32, 1);  // Init raw audio stream
+    
+    // Fill audio stream with some samples (sine wave)
+    float *data = (float *)malloc(sizeof(float)*44100);
+    
+    for (int i = 0; i < 44100; i++)
+    {
+        data[i] = sinf(2*PI*(float)i*DEG2RAD);
+    }
+    
+    PlayAudioStream(stream);
+    
+    int totalSamples = 44100;
+    int samplesLeft = totalSamples;
+    
+    Vector2 position = { 0, 0 };
+
+    SetTargetFPS(30);               // Set our game to run at 30 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        
+        // Refill audio stream if required
+        if (IsAudioBufferProcessed(stream)) 
+        {
+            int numSamples = 0;
+            if (samplesLeft >= 4096) numSamples = 4096;
+            else numSamples = samplesLeft;
+
+            UpdateAudioStream(stream, data + (totalSamples - samplesLeft), numSamples);
+            
+            samplesLeft -= numSamples;
+            
+            // Reset samples feeding (loop audio)
+            if (samplesLeft <= 0) samplesLeft = totalSamples;
+        }
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            DrawText("SINE WAVE SHOULD BE PLAYING!", 240, 140, 20, LIGHTGRAY);
+            
+            // NOTE: Draw a part of the sine wave (only screen width)
+            for (int i = 0; i < GetScreenWidth(); i++)
+            {
+                position.x = i;
+                position.y = 250 + 50*data[i];
+                
+                DrawPixelV(position, RED);
+            }
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    free(data);                 // Unload sine wave data
+    
+    CloseAudioStream(stream);   // Close raw audio stream and delete buffers from RAM
+
+    CloseAudioDevice();         // Close audio device (music streaming is automatically stopped)
+
+    CloseWindow();              // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}

+ 0 - 1
examples/audio_sound_loading.c

@@ -36,7 +36,6 @@ int main()
         // Update
         //----------------------------------------------------------------------------------
         if (IsKeyPressed(KEY_SPACE)) PlaySound(fxWav);      // Play WAV sound
-
         if (IsKeyPressed(KEY_ENTER)) PlaySound(fxOgg);      // Play OGG sound
         //----------------------------------------------------------------------------------
 

二进制
examples/resources/audio/2t2m_spa.xm


二进制
examples/resources/audio/chiptun1.mod


二进制
examples/resources/audio/mini1111.xm


+ 119 - 111
src/audio.c

@@ -100,17 +100,6 @@
 
 typedef enum { MUSIC_AUDIO_OGG = 0, MUSIC_MODULE_XM, MUSIC_MODULE_MOD } MusicContextType;
 
-// Used to create custom audio streams that are not bound to a specific file.
-typedef struct AudioStream {
-    unsigned int sampleRate;             // Frequency (samples per second): default is 48000
-    unsigned int sampleSize;             // BitDepth (bits per sample): 8, 16, 32 (24 not supported)
-    unsigned int channels;               // Number of channels
-
-    ALenum format;                       // OpenAL format specifier
-    ALuint source;                       // OpenAL source
-    ALuint buffers[MAX_STREAM_BUFFERS];  // OpenAL buffers (double buffering)
-} AudioStream;
-
 // Music type (file streaming from memory)
 typedef struct Music {
     MusicContextType ctxType;           // Type of music context (OGG, XM, MOD)
@@ -118,7 +107,7 @@ typedef struct Music {
     jar_xm_context_t *ctxXm;            // XM chiptune context
     jar_mod_context_t ctxMod;           // MOD chiptune context
 
-    AudioStream stream;                 // Audio stream
+    AudioStream stream;                 // Audio stream (double buffering)
 
     bool loop;                          // Repeat music after finish (loop)
     unsigned int totalSamples;          // Total number of samples
@@ -141,12 +130,6 @@ static Wave LoadWAV(const char *fileName);         // Load WAV file
 static Wave LoadOGG(char *fileName);               // Load OGG file
 static void UnloadWave(Wave wave);                 // Unload wave data
 
-static bool BufferMusicStream(Music music, int numBuffersToProcess);  // Fill music buffers with data
-
-static AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, unsigned int channels);
-static void BufferAudioStream(AudioStream stream, void *data, int numSamples);
-static void CloseAudioStream(AudioStream stream);
-
 #if defined(AUDIO_STANDALONE)
 const char *GetExtension(const char *fileName);     // Get the extension for a filename
 void TraceLog(int msgType, const char *text, ...);  // Outputs a trace log message (INFO, ERROR, WARNING)
@@ -595,33 +578,89 @@ void StopMusicStream(Music music)
 // Update (re-fill) music buffers if data already processed
 void UpdateMusicStream(Music music)
 {
-    ALenum state;
-    bool active = true;
     ALint processed = 0;
 
     // Determine if music stream is ready to be written
     alGetSourcei(music->stream.source, AL_BUFFERS_PROCESSED, &processed);
-
+    
+    int numBuffersToProcess = processed;
+    
     if (processed > 0)
     {
-        active = BufferMusicStream(music, processed);
+        bool active = true;
+        short pcm[AUDIO_BUFFER_SIZE];
+        float pcmf[AUDIO_BUFFER_SIZE];
+        
+        int numSamples = 0;     // Total size of data steamed in L+R samples for xm floats, 
+                                // individual L or R for ogg shorts
+
+        for (int i = 0; i < numBuffersToProcess; i++)
+        {
+            switch (music->ctxType)
+            {
+                case MUSIC_AUDIO_OGG: 
+                {
+                    if (music->samplesLeft >= AUDIO_BUFFER_SIZE) numSamples = AUDIO_BUFFER_SIZE;
+                    else numSamples = music->samplesLeft;
+            
+                    // NOTE: Returns the number of samples to process (should be the same as numSamples -> it is)
+                    int numSamplesOgg = stb_vorbis_get_samples_short_interleaved(music->ctxOgg, music->stream.channels, pcm, numSamples);
+
+                    // TODO: Review stereo channels Ogg, not enough samples served!
+                    UpdateAudioStream(music->stream, pcm, numSamples*music->stream.channels);
+                    music->samplesLeft -= (numSamples*music->stream.channels);
+                    
+                } break;
+                case MUSIC_MODULE_XM: 
+                {
+                    if (music->samplesLeft >= AUDIO_BUFFER_SIZE/2) numSamples = AUDIO_BUFFER_SIZE/2;
+                    else numSamples = music->samplesLeft;
+            
+                    // NOTE: Output buffer is 2*numsamples elements (left and right value for each sample)
+                    jar_xm_generate_samples(music->ctxXm, pcmf, numSamples);
+                    UpdateAudioStream(music->stream, pcmf, numSamples*2);           // Using 32bit PCM data
+                    music->samplesLeft -= numSamples;
+                    
+                    //TraceLog(INFO, "Samples left: %i", music->samplesLeft);
+                    
+                } break;
+                case MUSIC_MODULE_MOD: 
+                {
+                    if (music->samplesLeft >= AUDIO_BUFFER_SIZE/2) numSamples = AUDIO_BUFFER_SIZE/2;
+                    else numSamples = music->samplesLeft;
+                    
+                    // NOTE: Output buffer size is nbsample*channels (default: 48000Hz, 16bit, Stereo)
+                    jar_mod_fillbuffer(&music->ctxMod, pcm, numSamples, 0); 
+                    UpdateAudioStream(music->stream, pcm, numSamples*2);
+                    music->samplesLeft -= numSamples;
+                    
+                } break;
+                default: break;
+            }
 
+            if (music->samplesLeft <= 0)
+            {
+                active = false;
+                break;
+            }
+        }
+        
+        // Reset audio stream for looping
         if (!active && music->loop)
         {
             // Restart music context (if required)
+            //if (music->ctxType == MUSIC_MODULE_XM) 
             if (music->ctxType == MUSIC_MODULE_MOD) jar_mod_seek_start(&music->ctxMod);
             else if (music->ctxType == MUSIC_AUDIO_OGG) stb_vorbis_seek_start(music->ctxOgg);
             
+            // Reset samples left to total samples
             music->samplesLeft = music->totalSamples;
-
-            // Determine if music stream is ready to be written
-            alGetSourcei(music->stream.source, AL_BUFFERS_PROCESSED, &processed);
-
-            active = BufferMusicStream(music, processed);
         }
 
-        if (alGetError() != AL_NO_ERROR) TraceLog(WARNING, "Error buffering data...");
+        // This error is registered when UpdateAudioStream() fails
+        if (alGetError() == AL_INVALID_VALUE) TraceLog(WARNING, "OpenAL: Error buffering data...");
 
+        ALenum state;
         alGetSourcei(music->stream.source, AL_SOURCE_STATE, &state);
 
         if (state != AL_PLAYING && active) alSourcePlay(music->stream.source);
@@ -668,36 +707,14 @@ float GetMusicTimePlayed(Music music)
 {
     float secondsPlayed = 0.0f;
 
-    if (music->ctxType == MUSIC_MODULE_XM)
-    {
-        uint64_t samplesPlayed;
-        jar_xm_get_position(music->ctxXm, NULL, NULL, NULL, &samplesPlayed);
-        
-        // TODO: Not sure if this is the correct value
-        secondsPlayed = (float)samplesPlayed/(music->stream.sampleRate*music->stream.channels);
-    }
-    else if (music->ctxType == MUSIC_MODULE_MOD)
-    {
-        long samplesPlayed = jar_mod_current_samples(&music->ctxMod);
-        
-        secondsPlayed = (float)samplesPlayed/music->stream.sampleRate;
-    }
-    else if (music->ctxType == MUSIC_AUDIO_OGG)
-    {
-        unsigned int samplesPlayed = music->totalSamples - music->samplesLeft;
-        
-        secondsPlayed = (float)samplesPlayed/(music->stream.sampleRate*music->stream.channels);
-    }
+    unsigned int samplesPlayed = music->totalSamples - music->samplesLeft;
+    secondsPlayed = (float)samplesPlayed/(music->stream.sampleRate*music->stream.channels);
 
     return secondsPlayed;
 }
 
-//----------------------------------------------------------------------------------
-// Module specific Functions Definition
-//----------------------------------------------------------------------------------
-
 // Init audio stream (to stream audio pcm data)
-static AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, unsigned int channels)
+AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, unsigned int channels)
 {
     AudioStream stream = { 0 };
     
@@ -735,7 +752,7 @@ static AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleS
     alSource3f(stream.source, AL_POSITION, 0, 0, 0);
     alSource3f(stream.source, AL_VELOCITY, 0, 0, 0);
 
-    // Create Buffers
+    // Create Buffers (double buffering)
     alGenBuffers(MAX_STREAM_BUFFERS, stream.buffers);
 
     // Initialize buffer with zeros by default
@@ -766,7 +783,7 @@ static AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleS
 }
 
 // Close audio stream and free memory
-static void CloseAudioStream(AudioStream stream)
+void CloseAudioStream(AudioStream stream)
 {
     // Stop playing channel
     alSourceStop(stream.source);
@@ -790,75 +807,66 @@ static void CloseAudioStream(AudioStream stream)
     TraceLog(INFO, "[AUD ID %i] Unloaded audio stream data", stream.source);
 }
 
-// Push more audio data into audio stream, only one buffer per call
-static void BufferAudioStream(AudioStream stream, void *data, int numSamples)
-{   
+// Update audio stream buffers with data
+// NOTE: Only one buffer per call
+void UpdateAudioStream(AudioStream stream, void *data, int numSamples)
+{
     ALuint buffer = 0;
     alSourceUnqueueBuffers(stream.source, 1, &buffer);
     
-    //TraceLog(DEBUG, "Buffer to refill: %i", buffer);
+    // Check if any buffer was available for unqueue
+    if (alGetError() != AL_INVALID_VALUE)
+    {
+        if (stream.sampleSize == 8) alBufferData(buffer, stream.format, (unsigned char *)data, numSamples*sizeof(unsigned char), stream.sampleRate);
+        else if (stream.sampleSize == 16) alBufferData(buffer, stream.format, (short *)data, numSamples*sizeof(short), stream.sampleRate);
+        else if (stream.sampleSize == 32) alBufferData(buffer, stream.format, (float *)data, numSamples*sizeof(float), stream.sampleRate);
+        
+        alSourceQueueBuffers(stream.source, 1, &buffer);
+    }
+}
+
+// Check if any audio stream buffers requires refill
+bool IsAudioBufferProcessed(AudioStream stream)
+{
+    ALint processed = 0;
 
-    if (stream.sampleSize == 8) alBufferData(buffer, stream.format, (unsigned char *)data, numSamples*sizeof(unsigned char), stream.sampleRate);
-    else if (stream.sampleSize == 16) alBufferData(buffer, stream.format, (short *)data, numSamples*sizeof(short), stream.sampleRate);
-    else if (stream.sampleSize == 32) alBufferData(buffer, stream.format, (float *)data, numSamples*sizeof(float), stream.sampleRate);
+    // Determine if music stream is ready to be written
+    alGetSourcei(stream.source, AL_BUFFERS_PROCESSED, &processed);
 
-    alSourceQueueBuffers(stream.source, 1, &buffer);
+    return (processed > 0);
 }
 
-// Fill music buffers with new data from music stream
-static bool BufferMusicStream(Music music, int numBuffersToProcess)
+// Play audio stream
+void PlayAudioStream(AudioStream stream)
 {
-    short pcm[AUDIO_BUFFER_SIZE];
-    float pcmf[AUDIO_BUFFER_SIZE];
+    alSourcePlay(stream.source);
+}
 
-    int size = 0;              // Total size of data steamed in L+R samples for xm floats, individual L or R for ogg shorts
-    bool active = true;        // We can get more data from stream (not finished)
-    
-    for (int i = 0; i < numBuffersToProcess; i++)
-    {
-        if (music->samplesLeft >= AUDIO_BUFFER_SIZE) size = AUDIO_BUFFER_SIZE;
-        else size = music->samplesLeft;
+// Play audio stream
+void PauseAudioStream(AudioStream stream)
+{
+    alSourcePause(stream.source);
+}
 
-        switch (music->ctxType)
-        {
-            case MUSIC_AUDIO_OGG: 
-            {
-                // NOTE: Returns the number of samples to process (should be the same as size)
-                int numSamples = stb_vorbis_get_samples_short_interleaved(music->ctxOgg, music->stream.channels, pcm, size);
-
-                BufferAudioStream(music->stream, pcm, numSamples*music->stream.channels);
-                music->samplesLeft -= (numSamples*music->stream.channels);
-                
-            } break;
-            case MUSIC_MODULE_XM: 
-            {
-                // NOTE: Output buffer is 2*numsamples elements (left and right value for each sample)
-                jar_xm_generate_samples(music->ctxXm, pcmf, size/2);
-                BufferAudioStream(music->stream, pcmf, size);           // Using 32bit PCM data
-                music->samplesLeft -= (size/2);
-                
-            } break;
-            case MUSIC_MODULE_MOD: 
-            {
-                // NOTE: Output buffer size is nbsample*channels (default: 48000Hz, 16bit, Stereo)
-                jar_mod_fillbuffer(&music->ctxMod, pcm, size/2, 0); 
-                BufferAudioStream(music->stream, pcm, size);
-                music->samplesLeft -= (size/2);
-                
-            } break;
-            default: break;
-        }
+// Resume audio stream playing
+void ResumeAudioStream(AudioStream stream)
+{
+    ALenum state;
+    alGetSourcei(stream.source, AL_SOURCE_STATE, &state);
 
-        if (music->samplesLeft <= 0)
-        {
-            active = false;
-            break;
-        }
-    }
-    
-    return active;
+    if (state == AL_PAUSED) alSourcePlay(stream.source);
 }
 
+// Stop audio stream
+void StopAudioStream(AudioStream stream)
+{
+    alSourceStop(stream.source);
+}
+
+//----------------------------------------------------------------------------------
+// Module specific Functions Definition
+//----------------------------------------------------------------------------------
+
 // Load WAV file into Wave structure
 static Wave LoadWAV(const char *fileName)
 {

+ 25 - 2
src/audio.h

@@ -76,9 +76,21 @@ typedef struct Wave {
 } Wave;
 
 // Music type (file streaming from memory)
-// NOTE: Anything longer than ~10 seconds should be streamed into a mix channel...
+// NOTE: Anything longer than ~10 seconds should be streamed
 typedef struct Music *Music;
 
+// Audio stream type
+// NOTE: Useful to create custom audio streams not bound to a specific file
+typedef struct AudioStream {
+    unsigned int sampleRate;    // Frequency (samples per second)
+    unsigned int sampleSize;    // Bit depth (bits per sample): 8, 16, 32 (24 not supported)
+    unsigned int channels;      // Number of channels (1-mono, 2-stereo)
+
+    int format;                 // OpenAL audio format specifier
+    unsigned int source;        // OpenAL audio source id
+    unsigned int buffers[2];    // OpenAL audio buffers (double buffering)
+} AudioStream;
+
 #ifdef __cplusplus
 extern "C" {            // Prevents name mangling of functions
 #endif
@@ -93,7 +105,7 @@ extern "C" {            // Prevents name mangling of functions
 //----------------------------------------------------------------------------------
 void InitAudioDevice(void);                                     // Initialize audio device and context
 void CloseAudioDevice(void);                                    // Close the audio device and context (and music stream)
-bool IsAudioDeviceReady(void);                                  // Check if device has been initialized successfully
+bool IsAudioDeviceReady(void);                                  // Check if audio device has been initialized successfully
 
 Sound LoadSound(char *fileName);                                // Load sound to memory
 Sound LoadSoundFromWave(Wave wave);                             // Load sound to memory from wave data
@@ -120,6 +132,17 @@ void SetMusicPitch(Music music, float pitch);                   // Set pitch for
 float GetMusicTimeLength(Music music);                          // Get music time length (in seconds)
 float GetMusicTimePlayed(Music music);                          // Get current music time played (in seconds)
 
+AudioStream InitAudioStream(unsigned int sampleRate, 
+                            unsigned int sampleSize, 
+                            unsigned int channels);             // Init audio stream (to stream audio pcm data)
+void UpdateAudioStream(AudioStream stream, void *data, int numSamples); // Update audio stream buffers with data
+void CloseAudioStream(AudioStream stream);                      // Close audio stream and free memory
+bool IsAudioBufferProcessed(AudioStream stream);                // Check if any audio stream buffers requires refill
+void PlayAudioStream(AudioStream stream);                       // Play audio stream
+void PauseAudioStream(AudioStream stream);                      // Pause audio stream
+void ResumeAudioStream(AudioStream stream);                     // Resume audio stream
+void StopAudioStream(AudioStream stream);                       // Stop audio stream
+
 #ifdef __cplusplus
 }
 #endif

+ 26 - 3
src/raylib.h

@@ -499,8 +499,8 @@ typedef struct Ray {
 
 // Sound source type
 typedef struct Sound {
-    unsigned int source;    // Sound audio source id
-    unsigned int buffer;    // Sound audio buffer id
+    unsigned int source;    // OpenAL audio source id
+    unsigned int buffer;    // OpenAL audio buffer id
 } Sound;
 
 // Wave type, defines audio wave data
@@ -516,6 +516,18 @@ typedef struct Wave {
 // NOTE: Anything longer than ~10 seconds should be streamed
 typedef struct Music *Music;
 
+// Audio stream type
+// NOTE: Useful to create custom audio streams not bound to a specific file
+typedef struct AudioStream {
+    unsigned int sampleRate;    // Frequency (samples per second)
+    unsigned int sampleSize;    // Bit depth (bits per sample): 8, 16, 32 (24 not supported)
+    unsigned int channels;      // Number of channels (1-mono, 2-stereo)
+
+    int format;                 // OpenAL audio format specifier
+    unsigned int source;        // OpenAL audio source id
+    unsigned int buffers[2];    // OpenAL audio buffers (double buffering)
+} AudioStream;
+
 // Texture formats
 // NOTE: Support depends on OpenGL version and platform
 typedef enum {
@@ -923,7 +935,7 @@ void ToggleVrMode(void);                    // Enable/Disable VR experience (dev
 //------------------------------------------------------------------------------------
 void InitAudioDevice(void);                                     // Initialize audio device and context
 void CloseAudioDevice(void);                                    // Close the audio device and context (and music stream)
-bool IsAudioDeviceReady(void);                                  // True if call to InitAudioDevice() was successful and CloseAudioDevice() has not been called yet
+bool IsAudioDeviceReady(void);                                  // Check if audio device has been initialized successfully
 
 Sound LoadSound(char *fileName);                                // Load sound to memory
 Sound LoadSoundFromWave(Wave wave);                             // Load sound to memory from wave data
@@ -950,6 +962,17 @@ void SetMusicPitch(Music music, float pitch);                   // Set pitch for
 float GetMusicTimeLength(Music music);                          // Get music time length (in seconds)
 float GetMusicTimePlayed(Music music);                          // Get current music time played (in seconds)
 
+AudioStream InitAudioStream(unsigned int sampleRate, 
+                            unsigned int sampleSize, 
+                            unsigned int channels);             // Init audio stream (to stream audio pcm data)
+void UpdateAudioStream(AudioStream stream, void *data, int numSamples); // Update audio stream buffers with data
+void CloseAudioStream(AudioStream stream);                      // Close audio stream and free memory
+bool IsAudioBufferProcessed(AudioStream stream);                // Check if any audio stream buffers requires refill
+void PlayAudioStream(AudioStream stream);                       // Play audio stream
+void PauseAudioStream(AudioStream stream);                      // Pause audio stream
+void ResumeAudioStream(AudioStream stream);                     // Resume audio stream
+void StopAudioStream(AudioStream stream);                       // Stop audio stream
+
 #ifdef __cplusplus
 }
 #endif