diff options
| author | Ray <raysan5@gmail.com> | 2014-04-19 14:21:22 +0200 |
|---|---|---|
| committer | Ray <raysan5@gmail.com> | 2014-04-19 14:21:22 +0200 |
| commit | 650a8f7f159d3ce2addb1b0fdb31c3f460005391 (patch) | |
| tree | 01f1a6968b784d07d63f6bf741c1527145cea25c /src/audio.c | |
| parent | 1c8dce429ee5a58d535fcca0e29fe3711aa3fcb5 (diff) | |
| parent | e6b82cb111c3485c5e6131fe29791f938305bce3 (diff) | |
| download | raylib-650a8f7f159d3ce2addb1b0fdb31c3f460005391.tar.gz raylib-650a8f7f159d3ce2addb1b0fdb31c3f460005391.zip | |
Merge pull request #2 from raysan5/testing
Integrate raylib 1.1 changes into master
Diffstat (limited to 'src/audio.c')
| -rw-r--r-- | src/audio.c | 611 |
1 files changed, 416 insertions, 195 deletions
diff --git a/src/audio.c b/src/audio.c index 4b75942f..35874b59 100644 --- a/src/audio.c +++ b/src/audio.c @@ -37,7 +37,7 @@ #include "utils.h" // rRES data decompression utility function -//#include "stb_vorbis.h" // TODO: OGG loading functions +//#include "stb_vorbis.h" // OGG loading functions //---------------------------------------------------------------------------------- // Defines and Macros @@ -47,6 +47,29 @@ //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- +// Sound source type (all file loaded in memory) +/* +struct Sound { + unsigned int source; + unsigned int buffer; +}; + +// Music type (file streamming from memory) +// NOTE: Anything longer than ~10 seconds should be Music... +struct Music { + stb_vorbis* stream; + stb_vorbis_info info; + + ALuint id; + ALuint buffers[2]; + ALuint source; + ALenum format; + + int bufferSize; + int totalSamplesLeft; + bool loop; +}; +*/ // Wave file data typedef struct Wave { @@ -60,7 +83,8 @@ typedef struct Wave { //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -// Nop... +static bool musicIsPlaying; +static Music *currentMusic; //---------------------------------------------------------------------------------- // Module specific Functions Declaration @@ -68,6 +92,10 @@ typedef struct Wave { static Wave LoadWAV(char *fileName); static void UnloadWAV(Wave wave); //static Ogg LoadOGG(char *fileName); +static bool MusicStream(Music music, ALuint buffer); + +extern bool MusicStreamUpdate(); +extern void PlayCurrentMusic(); //---------------------------------------------------------------------------------- // Module Functions Definition - Window and OpenGL Context Functions @@ -79,30 +107,27 @@ void InitAudioDevice() // Open and initialize a device with default settings ALCdevice *device = alcOpenDevice(NULL); - if(!device) - { - fprintf(stderr, "Could not open a device!\n"); - exit(1); - } + if(!device) TraceLog(ERROR, "Could not open audio device"); ALCcontext *context = alcCreateContext(device, NULL); if(context == NULL || alcMakeContextCurrent(context) == ALC_FALSE) { - if(context != NULL) alcDestroyContext(context); + if(context != NULL) alcDestroyContext(context); alcCloseDevice(device); - fprintf(stderr, "Could not set a context!\n"); - exit(1); + TraceLog(ERROR, "Could not setup audio context"); } - printf("Opened \"%s\"\n", alcGetString(device, ALC_DEVICE_SPECIFIER)); + TraceLog(INFO, "Audio device and context initialized: %s\n", alcGetString(device, ALC_DEVICE_SPECIFIER)); // Listener definition (just for 2D) alListener3f(AL_POSITION, 0, 0, 0); alListener3f(AL_VELOCITY, 0, 0, 0); alListener3f(AL_ORIENTATION, 0, 0, -1); + + musicIsPlaying = false; } // Close the audio device for the current context, and destroys the context @@ -111,7 +136,7 @@ void CloseAudioDevice() ALCdevice *device; ALCcontext *context = alcGetCurrentContext(); - if (context == NULL) return; + if (context == NULL) TraceLog(WARNING, "Could not get current audio context for closing"); device = alcGetContextsDevice(context); @@ -131,7 +156,7 @@ Sound LoadSound(char *fileName) // NOTE: Buffer space is allocated inside LoadWAV, Wave must be freed Wave wave = LoadWAV(fileName); - ALenum format; + ALenum format = 0; // The OpenAL format is worked out by looking at the number of channels and the bits per sample if (wave.channels == 1) { @@ -169,10 +194,8 @@ Sound LoadSound(char *fileName) // Unallocate WAV data UnloadWAV(wave); - printf("Sample rate: %i\n", wave.sampleRate); - printf("Channels: %i\n", wave.channels); - - printf("Audio file loaded...!\n"); + TraceLog(INFO, "[%s] Sound file loaded successfully", fileName); + TraceLog(INFO, "[%s] Sample rate: %i - Channels: %i", fileName, wave.sampleRate, wave.channels); sound.source = source; sound.buffer = buffer; @@ -196,139 +219,136 @@ Sound LoadSoundFromRES(const char *rresName, int resId) FILE *rresFile = fopen(rresName, "rb"); - if (!rresFile) printf("Error opening raylib Resource file\n"); - - // Read rres file (basic file check - id) - fread(&id[0], sizeof(char), 1, rresFile); - fread(&id[1], sizeof(char), 1, rresFile); - fread(&id[2], sizeof(char), 1, rresFile); - fread(&id[3], sizeof(char), 1, rresFile); - fread(&version, sizeof(char), 1, rresFile); - fread(&useless, sizeof(char), 1, rresFile); - - if ((id[0] != 'r') && (id[1] != 'R') && (id[2] != 'E') &&(id[3] != 'S')) + if (!rresFile) TraceLog(WARNING, "[%s] Could not open raylib resource file", rresName); + else { - printf("This is not a valid raylib Resource file!\n"); - exit(1); - } - - // Read number of resources embedded - fread(&numRes, sizeof(short), 1, rresFile); - - for (int i = 0; i < numRes; i++) - { - fread(&infoHeader, sizeof(ResInfoHeader), 1, rresFile); + // Read rres file (basic file check - id) + fread(&id[0], sizeof(char), 1, rresFile); + fread(&id[1], sizeof(char), 1, rresFile); + fread(&id[2], sizeof(char), 1, rresFile); + fread(&id[3], sizeof(char), 1, rresFile); + fread(&version, sizeof(char), 1, rresFile); + fread(&useless, sizeof(char), 1, rresFile); - if (infoHeader.id == resId) + if ((id[0] != 'r') && (id[1] != 'R') && (id[2] != 'E') &&(id[3] != 'S')) { - found = true; - - // Check data is of valid SOUND type - if (infoHeader.type == 1) // SOUND data type - { - // TODO: Check data compression type - // NOTE: We suppose compression type 2 (DEFLATE - default) - - // Reading SOUND parameters - Wave wave; - short sampleRate, bps; - char channels, reserved; + TraceLog(WARNING, "[%s] This is not a valid raylib resource file", rresName); + } + else + { + // Read number of resources embedded + fread(&numRes, sizeof(short), 1, rresFile); - fread(&sampleRate, sizeof(short), 1, rresFile); // Sample rate (frequency) - fread(&bps, sizeof(short), 1, rresFile); // Bits per sample - fread(&channels, 1, 1, rresFile); // Channels (1 - mono, 2 - stereo) - fread(&reserved, 1, 1, rresFile); // <reserved> - - printf("Sample rate: %i\n", (int)sampleRate); - printf("Bits per sample: %i\n", (int)bps); - printf("Channels: %i\n", (int)channels); - - wave.sampleRate = sampleRate; - wave.dataSize = infoHeader.srcSize; - wave.bitsPerSample = bps; - wave.channels = (short)channels; - - unsigned char *data = malloc(infoHeader.size); - - fread(data, infoHeader.size, 1, rresFile); - - wave.data = DecompressData(data, infoHeader.size, infoHeader.srcSize); - - free(data); - - // Convert wave to Sound (OpenAL) - ALenum format; + for (int i = 0; i < numRes; i++) + { + fread(&infoHeader, sizeof(ResInfoHeader), 1, rresFile); - // The OpenAL format is worked out by looking at the number of channels and the bits per sample - if (wave.channels == 1) + if (infoHeader.id == resId) { - if (wave.bitsPerSample == 8 ) format = AL_FORMAT_MONO8; - else if (wave.bitsPerSample == 16) format = AL_FORMAT_MONO16; - } - else if (wave.channels == 2) - { - if (wave.bitsPerSample == 8 ) format = AL_FORMAT_STEREO8; - else if (wave.bitsPerSample == 16) format = AL_FORMAT_STEREO16; + found = true; + + // Check data is of valid SOUND type + if (infoHeader.type == 1) // SOUND data type + { + // TODO: Check data compression type + // NOTE: We suppose compression type 2 (DEFLATE - default) + + // Reading SOUND parameters + Wave wave; + short sampleRate, bps; + char channels, reserved; + + fread(&sampleRate, sizeof(short), 1, rresFile); // Sample rate (frequency) + fread(&bps, sizeof(short), 1, rresFile); // Bits per sample + fread(&channels, 1, 1, rresFile); // Channels (1 - mono, 2 - stereo) + fread(&reserved, 1, 1, rresFile); // <reserved> + + wave.sampleRate = sampleRate; + wave.dataSize = infoHeader.srcSize; + wave.bitsPerSample = bps; + wave.channels = (short)channels; + + unsigned char *data = malloc(infoHeader.size); + + fread(data, infoHeader.size, 1, rresFile); + + wave.data = DecompressData(data, infoHeader.size, infoHeader.srcSize); + + free(data); + + // Convert wave to Sound (OpenAL) + ALenum format = 0; + + // The OpenAL format is worked out by looking at the number of channels and the bits per sample + if (wave.channels == 1) + { + if (wave.bitsPerSample == 8 ) format = AL_FORMAT_MONO8; + else if (wave.bitsPerSample == 16) format = AL_FORMAT_MONO16; + } + else if (wave.channels == 2) + { + if (wave.bitsPerSample == 8 ) format = AL_FORMAT_STEREO8; + else if (wave.bitsPerSample == 16) format = AL_FORMAT_STEREO16; + } + + + // Create an audio source + ALuint source; + alGenSources(1, &source); // Generate pointer to audio source + + alSourcef(source, AL_PITCH, 1); + alSourcef(source, AL_GAIN, 1); + alSource3f(source, AL_POSITION, 0, 0, 0); + alSource3f(source, AL_VELOCITY, 0, 0, 0); + alSourcei(source, AL_LOOPING, AL_FALSE); + + // Convert loaded data to OpenAL buffer + //---------------------------------------- + ALuint buffer; + alGenBuffers(1, &buffer); // Generate pointer to buffer + + // Upload sound data to buffer + alBufferData(buffer, format, (void*)wave.data, wave.dataSize, wave.sampleRate); + + // Attach sound buffer to source + alSourcei(source, AL_BUFFER, buffer); + + // Unallocate WAV data + UnloadWAV(wave); + + TraceLog(INFO, "[%s] Sound loaded successfully from resource, sample rate: %i", rresName, (int)sampleRate); + + sound.source = source; + sound.buffer = buffer; + } + else + { + TraceLog(WARNING, "[%s] Required resource do not seem to be a valid SOUND resource", rresName); + } } - - - // Create an audio source - ALuint source; - alGenSources(1, &source); // Generate pointer to audio source - - alSourcef(source, AL_PITCH, 1); - alSourcef(source, AL_GAIN, 1); - alSource3f(source, AL_POSITION, 0, 0, 0); - alSource3f(source, AL_VELOCITY, 0, 0, 0); - alSourcei(source, AL_LOOPING, AL_FALSE); - - // Convert loaded data to OpenAL buffer - //---------------------------------------- - ALuint buffer; - alGenBuffers(1, &buffer); // Generate pointer to buffer - - // Upload sound data to buffer - alBufferData(buffer, format, (void*)wave.data, wave.dataSize, wave.sampleRate); - - // Attach sound buffer to source - alSourcei(source, AL_BUFFER, buffer); - - // Unallocate WAV data - UnloadWAV(wave); - - printf("Audio file loaded...!\n"); - - sound.source = source; - sound.buffer = buffer; - } - else - { - - printf("Required resource do not seem to be a valid IMAGE resource\n"); - exit(2); + else + { + // Depending on type, skip the right amount of parameters + switch (infoHeader.type) + { + case 0: fseek(rresFile, 6, SEEK_CUR); break; // IMAGE: Jump 6 bytes of parameters + case 1: fseek(rresFile, 6, SEEK_CUR); break; // SOUND: Jump 6 bytes of parameters + case 2: fseek(rresFile, 5, SEEK_CUR); break; // MODEL: Jump 5 bytes of parameters (TODO: Review) + case 3: break; // TEXT: No parameters + case 4: break; // RAW: No parameters + default: break; + } + + // Jump DATA to read next infoHeader + fseek(rresFile, infoHeader.size, SEEK_CUR); + } } } - else - { - // Depending on type, skip the right amount of parameters - switch (infoHeader.type) - { - case 0: fseek(rresFile, 6, SEEK_CUR); break; // IMAGE: Jump 6 bytes of parameters - case 1: fseek(rresFile, 6, SEEK_CUR); break; // SOUND: Jump 6 bytes of parameters - case 2: fseek(rresFile, 5, SEEK_CUR); break; // MODEL: Jump 5 bytes of parameters (TODO: Review) - case 3: break; // TEXT: No parameters - case 4: break; // RAW: No parameters - default: break; - } - - // Jump DATA to read next infoHeader - fseek(rresFile, infoHeader.size, SEEK_CUR); - } + + fclose(rresFile); } - fclose(rresFile); - - if (!found) printf("Required resource id could not be found in the raylib Resource file!\n"); + if (!found) TraceLog(WARNING, "[%s] Required resource id [%i] could not be found in the raylib resource file", rresName, resId); return sound; } @@ -345,7 +365,7 @@ void PlaySound(Sound sound) { alSourcePlay(sound.source); // Play the sound - printf("Playing sound!\n"); + TraceLog(INFO, "Playing sound"); // Find the current position of the sound being played // NOTE: Only work when the entire file is in a single buffer @@ -390,7 +410,7 @@ void StopSound(Sound sound) } // Check if a sound is playing -bool IsPlaying(Sound sound) +bool SoundIsPlaying(Sound sound) { bool playing = false; ALint state; @@ -401,6 +421,16 @@ bool IsPlaying(Sound sound) return playing; } +// Check if music is playing +bool MusicIsPlaying(Music music) +{ + ALenum state; + + alGetSourcei(music.source, AL_SOURCE_STATE, &state); + + return (state == AL_PLAYING); +} + // Set volume for a sound void SetVolume(Sound sound, float volume) { @@ -450,61 +480,65 @@ static Wave LoadWAV(char *fileName) if (!wavFile) { - printf("Could not open WAV file.\n"); - exit(1); + TraceLog(WARNING, "[%s] Could not open WAV file", fileName); } - - // Read in the first chunk into the struct - fread(&riffHeader, sizeof(RiffHeader), 1, wavFile); - - // Check for RIFF and WAVE tags - if ((riffHeader.chunkID[0] != 'R' || - riffHeader.chunkID[1] != 'I' || - riffHeader.chunkID[2] != 'F' || - riffHeader.chunkID[3] != 'F') || - (riffHeader.format[0] != 'W' || - riffHeader.format[1] != 'A' || - riffHeader.format[2] != 'V' || - riffHeader.format[3] != 'E')) - printf("Invalid RIFF or WAVE Header"); - - // Read in the 2nd chunk for the wave info - fread(&waveFormat, sizeof(WaveFormat), 1, wavFile); - - // Check for fmt tag - if (waveFormat.subChunkID[0] != 'f' || - waveFormat.subChunkID[1] != 'm' || - waveFormat.subChunkID[2] != 't' || - waveFormat.subChunkID[3] != ' ') - printf("Invalid Wave Format"); - - // Check for extra parameters; - if (waveFormat.subChunkSize > 16) - fseek(wavFile, sizeof(short), SEEK_CUR); - - // Read in the the last byte of data before the sound file - fread(&waveData, sizeof(WaveData), 1, wavFile); - - // Check for data tag - if (waveData.subChunkID[0] != 'd' || - waveData.subChunkID[1] != 'a' || - waveData.subChunkID[2] != 't' || - waveData.subChunkID[3] != 'a') - printf("Invalid data header"); - - // Allocate memory for data - wave.data = (unsigned char *)malloc(sizeof(unsigned char) * waveData.subChunkSize); - - // Read in the sound data into the soundData variable - fread(wave.data, waveData.subChunkSize, 1, wavFile); - - // Now we set the variables that we need later - wave.dataSize = waveData.subChunkSize; - wave.sampleRate = waveFormat.sampleRate; - wave.channels = waveFormat.numChannels; - wave.bitsPerSample = waveFormat.bitsPerSample; + else + { + // Read in the first chunk into the struct + fread(&riffHeader, sizeof(RiffHeader), 1, wavFile); + + // Check for RIFF and WAVE tags + if (((riffHeader.chunkID[0] != 'R') || (riffHeader.chunkID[1] != 'I') || (riffHeader.chunkID[2] != 'F') || (riffHeader.chunkID[3] != 'F')) || + ((riffHeader.format[0] != 'W') || (riffHeader.format[1] != 'A') || (riffHeader.format[2] != 'V') || (riffHeader.format[3] != 'E'))) + { + TraceLog(WARNING, "[%s] Invalid RIFF or WAVE Header", fileName); + } + else + { + // Read in the 2nd chunk for the wave info + fread(&waveFormat, sizeof(WaveFormat), 1, wavFile); + + // Check for fmt tag + if ((waveFormat.subChunkID[0] != 'f') || (waveFormat.subChunkID[1] != 'm') || + (waveFormat.subChunkID[2] != 't') || (waveFormat.subChunkID[3] != ' ')) + { + TraceLog(WARNING, "[%s] Invalid Wave format", fileName); + } + else + { + // Check for extra parameters; + if (waveFormat.subChunkSize > 16) fseek(wavFile, sizeof(short), SEEK_CUR); + + // Read in the the last byte of data before the sound file + fread(&waveData, sizeof(WaveData), 1, wavFile); + + // Check for data tag + if ((waveData.subChunkID[0] != 'd') || (waveData.subChunkID[1] != 'a') || + (waveData.subChunkID[2] != 't') || (waveData.subChunkID[3] != 'a')) + { + TraceLog(WARNING, "[%s] Invalid data header", fileName); + } + else + { + // Allocate memory for data + wave.data = (unsigned char *)malloc(sizeof(unsigned char) * waveData.subChunkSize); + + // Read in the sound data into the soundData variable + fread(wave.data, waveData.subChunkSize, 1, wavFile); + + // Now we set the variables that we need later + wave.dataSize = waveData.subChunkSize; + wave.sampleRate = waveFormat.sampleRate; + wave.channels = waveFormat.numChannels; + wave.bitsPerSample = waveFormat.bitsPerSample; + + TraceLog(INFO, "[%s] Wave file loaded successfully", fileName); + } + } + } - fclose(wavFile); + fclose(wavFile); + } return wave; } @@ -516,5 +550,192 @@ static void UnloadWAV(Wave wave) } // TODO: Ogg data loading -//static Ogg LoadOGG(char *fileName) { } +Music LoadMusic(char *fileName) +{ + Music music; + + // Open audio stream + music.stream = stb_vorbis_open_filename(fileName, NULL, NULL); + + if (music.stream == NULL) TraceLog(WARNING, "Could not open ogg audio file"); + else + { + // Get file info + music.info = stb_vorbis_get_info(music.stream); + + printf("Ogg sample rate: %i\n", music.info.sample_rate); + printf("Ogg channels: %i\n", music.info.channels); + printf("Temp memory required: %i\n", music.info.temp_memory_required); + + if (music.info.channels == 2) music.format = AL_FORMAT_STEREO16; + else music.format = AL_FORMAT_MONO16; + + music.bufferSize = 4096*8; + music.loop = true; // We loop by default + + // Create an audio source + alGenSources(1, &music.source); // Generate pointer to audio source + + alSourcef(music.source, AL_PITCH, 1); + alSourcef(music.source, AL_GAIN, 1); + alSource3f(music.source, AL_POSITION, 0, 0, 0); + alSource3f(music.source, AL_VELOCITY, 0, 0, 0); + alSourcei(music.source, AL_LOOPING, AL_TRUE); // We loop by default + + // Convert loaded data to OpenAL buffers + alGenBuffers(2, music.buffers); + /* + if (!MusicStream(music, music.buffers[0])) exit(1); + if (!MusicStream(music, music.buffers[1])) exit(1); + + alSourceQueueBuffers(music.source, 2, music.buffers); + + PlayMusic(music); + */ + music.totalSamplesLeft = stb_vorbis_stream_length_in_samples(music.stream) * music.info.channels; + + currentMusic = &music; + } + + return music; +} + +void UnloadMusic(Music music) +{ + StopMusic(music); + + alDeleteSources(1, &music.source); + alDeleteBuffers(2, music.buffers); + + stb_vorbis_close(music.stream); +} + +void PlayMusic(Music music) +{ + //if (MusicIsPlaying(music)) return true; + + if (!MusicStream(music, music.buffers[0])) TraceLog(WARNING, "MusicStream returned 0"); + if (!MusicStream(music, music.buffers[1])) TraceLog(WARNING, "MusicStream returned 0"); + + alSourceQueueBuffers(music.source, 2, music.buffers); + alSourcePlay(music.source); + + TraceLog(INFO, "Playing music"); +} + +extern void PlayCurrentMusic() +{ + if (!MusicStream(*currentMusic, currentMusic->buffers[0])) TraceLog(WARNING, "MusicStream returned 0"); + if (!MusicStream(*currentMusic, currentMusic->buffers[1])) TraceLog(WARNING, "MusicStream returned 0"); + + alSourceQueueBuffers(currentMusic->source, 2, currentMusic->buffers); + alSourcePlay(currentMusic->source); +} + +// Stop reproducing music +void StopMusic(Music music) +{ + alSourceStop(music.source); + + musicIsPlaying = false; +} + +static bool MusicStream(Music music, ALuint buffer) +{ + //Uncomment this to avoid VLAs + //#define BUFFER_SIZE 4096*32 + #ifndef BUFFER_SIZE//VLAs ftw + #define BUFFER_SIZE (music.bufferSize) + #endif + ALshort pcm[BUFFER_SIZE]; + + int size = 0; + int result = 0; + + while (size < BUFFER_SIZE) + { + result = stb_vorbis_get_samples_short_interleaved(music.stream, music.info.channels, pcm+size, BUFFER_SIZE-size); + + if (result > 0) size += (result*music.info.channels); + else break; + } + + if (size == 0) return false; + + alBufferData(buffer, music.format, pcm, size*sizeof(ALshort), music.info.sample_rate); + + music.totalSamplesLeft -= size; + + #undef BUFFER_SIZE + + return true; +} +/* +extern bool MusicStreamUpdate() +{ + ALint processed = 0; + + alGetSourcei(currentMusic->source, AL_BUFFERS_PROCESSED, &processed); + + while (processed--) + { + ALuint buffer = 0; + + alSourceUnqueueBuffers(currentMusic->source, 1, &buffer); + + if (!MusicStream(*currentMusic, buffer)) + { + bool shouldExit = true; + + if (currentMusic->loop) + { + stb_vorbis_seek_start(currentMusic->stream); + currentMusic->totalSamplesLeft = stb_vorbis_stream_length_in_samples(currentMusic->stream) * currentMusic->info.channels; + + shouldExit = !MusicStream(*currentMusic, buffer); + } + + if (shouldExit) return false; + } + + alSourceQueueBuffers(currentMusic->source, 1, &buffer); + } + + return true; +} +*/ +extern bool MusicStreamUpdate() +{ + int processed; + bool active = true; + + alGetSourcei(currentMusic->source, AL_BUFFERS_PROCESSED, &processed); + + printf("Data processed: %i\n", processed); + + while (processed--) + { + ALuint buffer = 0; + + alSourceUnqueueBuffers(currentMusic->source, 1, &buffer); + + active = MusicStream(*currentMusic, buffer); + + alSourceQueueBuffers(currentMusic->source, 1, &buffer); + } + + return active; +} +void MusicStreamEmpty() +{ + int queued; + + alGetSourcei(currentMusic->source, AL_BUFFERS_QUEUED, &queued); + + while(queued--) + { + ALuint buffer; + alSourceUnqueueBuffers(currentMusic->source, 1, &buffer); + } +}
\ No newline at end of file |
