diff --git a/examples/audio_module_playing.c b/examples/audio_module_playing.c index 07165c762..fe9ea15ca 100644 --- a/examples/audio_module_playing.c +++ b/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); diff --git a/examples/audio_music_stream.c b/examples/audio_music_stream.c index 1d86bd1ad..c552d0304 100644 --- a/examples/audio_music_stream.c +++ b/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 diff --git a/examples/audio_raw_stream.c b/examples/audio_raw_stream.c new file mode 100644 index 000000000..37a5b4ff0 --- /dev/null +++ b/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 // Required for: malloc(), free() +#include // 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; +} \ No newline at end of file diff --git a/examples/audio_sound_loading.c b/examples/audio_sound_loading.c index 8819aad11..f081e8ed3 100644 --- a/examples/audio_sound_loading.c +++ b/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 //---------------------------------------------------------------------------------- diff --git a/examples/resources/audio/2t2m_spa.xm b/examples/resources/audio/2t2m_spa.xm deleted file mode 100644 index fa416ef29..000000000 Binary files a/examples/resources/audio/2t2m_spa.xm and /dev/null differ diff --git a/examples/resources/audio/chiptun1.mod b/examples/resources/audio/chiptun1.mod new file mode 100644 index 000000000..00d16885d Binary files /dev/null and b/examples/resources/audio/chiptun1.mod differ diff --git a/examples/resources/audio/mini1111.xm b/examples/resources/audio/mini1111.xm new file mode 100644 index 000000000..a185c1a2a Binary files /dev/null and b/examples/resources/audio/mini1111.xm differ diff --git a/src/audio.c b/src/audio.c index d1c425d5f..befed61c7 100644 --- a/src/audio.c +++ b/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); - - 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); -} - -// Fill music buffers with new data from music stream -static bool BufferMusicStream(Music music, int numBuffersToProcess) -{ - short pcm[AUDIO_BUFFER_SIZE]; - float pcmf[AUDIO_BUFFER_SIZE]; - - 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++) + // Check if any buffer was available for unqueue + if (alGetError() != AL_INVALID_VALUE) { - if (music->samplesLeft >= AUDIO_BUFFER_SIZE) size = AUDIO_BUFFER_SIZE; - else size = music->samplesLeft; - - 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; - } - - if (music->samplesLeft <= 0) - { - active = false; - break; - } + 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); } - - return active; } +// Check if any audio stream buffers requires refill +bool IsAudioBufferProcessed(AudioStream stream) +{ + ALint processed = 0; + + // Determine if music stream is ready to be written + alGetSourcei(stream.source, AL_BUFFERS_PROCESSED, &processed); + + return (processed > 0); +} + +// Play audio stream +void PlayAudioStream(AudioStream stream) +{ + alSourcePlay(stream.source); +} + +// Play audio stream +void PauseAudioStream(AudioStream stream) +{ + alSourcePause(stream.source); +} + +// Resume audio stream playing +void ResumeAudioStream(AudioStream stream) +{ + ALenum state; + alGetSourcei(stream.source, AL_SOURCE_STATE, &state); + + 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) { diff --git a/src/audio.h b/src/audio.h index c91713397..dbd889393 100644 --- a/src/audio.h +++ b/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 diff --git a/src/raylib.h b/src/raylib.h index 4b9f6ca0b..3ee7a7939 100644 --- a/src/raylib.h +++ b/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