diff options
| author | victorfisac <[email protected]> | 2017-03-06 09:40:04 +0100 |
|---|---|---|
| committer | victorfisac <[email protected]> | 2017-03-06 09:40:04 +0100 |
| commit | 9261c3b8dc03d093bff5246a18ad9310ae8eaeb3 (patch) | |
| tree | af87165723ac563ee1a7e1c605c7a4df821d74ea /src/audio.c | |
| parent | e8630c78d069a1cba50b1a78108663ebc19e5b9b (diff) | |
| parent | b734802743f2089c8d649b27aea48ab71fa653b3 (diff) | |
| download | raylib-9261c3b8dc03d093bff5246a18ad9310ae8eaeb3.tar.gz raylib-9261c3b8dc03d093bff5246a18ad9310ae8eaeb3.zip | |
Merge remote-tracking branch 'refs/remotes/raysan5/develop' into develop
Diffstat (limited to 'src/audio.c')
| -rw-r--r-- | src/audio.c | 710 |
1 files changed, 341 insertions, 369 deletions
diff --git a/src/audio.c b/src/audio.c index 49aca4b0..659ead0f 100644 --- a/src/audio.c +++ b/src/audio.c @@ -3,28 +3,50 @@ * raylib.audio * * This module provides basic functionality to work with audio: -* Manage audio device (init/close) -* Load and Unload audio files (WAV, OGG, FLAC, XM, MOD) -* Play/Stop/Pause/Resume loaded audio -* Manage mixing channels -* Manage raw audio context +* Manage audio device (init/close) +* Load and Unload audio files (WAV, OGG, FLAC, XM, MOD) +* Play/Stop/Pause/Resume loaded audio +* Manage mixing channels +* Manage raw audio context * -* External libs: +* NOTES: +* +* Only up to two channels supported: MONO and STEREO (for additional channels, use AL_EXT_MCFORMATS) +* Only the following sample sizes supported: 8bit PCM, 16bit PCM, 32-bit float PCM (using AL_EXT_FLOAT32) +* +* CONFIGURATION: +* +* #define AUDIO_STANDALONE +* If defined, the module can be used as standalone library (independently of raylib). +* Required types and functions are defined in the same module. +* +* #define SUPPORT_FILEFORMAT_WAV / SUPPORT_LOAD_WAV / ENABLE_LOAD_WAV +* #define SUPPORT_FILEFORMAT_OGG +* #define SUPPORT_FILEFORMAT_XM +* #define SUPPORT_FILEFORMAT_MOD +* #define SUPPORT_FILEFORMAT_FLAC +* Selected desired fileformats to be supported for loading. Some of those formats are +* supported by default, to remove support, just comment unrequired #define in this module +* +* #define SUPPORT_RAW_AUDIO_BUFFERS +* +* DEPENDENCIES: * OpenAL Soft - Audio device management (http://kcat.strangesoft.net/openal.html) * stb_vorbis - OGG audio files loading (http://www.nothings.org/stb_vorbis/) * jar_xm - XM module file loading * jar_mod - MOD audio file loading * dr_flac - FLAC audio file loading * -* Module Configuration Flags: -* AUDIO_STANDALONE - Use this module as standalone library (independently of raylib) +* CONTRIBUTORS: * * Many thanks to Joshua Reisenauer (github: @kd7tck) for the following additions: -* XM audio module support (jar_xm) -* MOD audio module support (jar_mod) -* Mixing channels support -* Raw audio context support +* XM audio module support (jar_xm) +* MOD audio module support (jar_mod) +* Mixing channels support +* Raw audio context support +* * +* LICENSE: zlib/libpng * * Copyright (c) 2014-2016 Ramon Santamaria (@raysan5) * @@ -52,25 +74,25 @@ #include <stdarg.h> // Required for: va_list, va_start(), vfprintf(), va_end() #else #include "raylib.h" - #include "utils.h" // Required for: DecompressData() - // NOTE: Includes Android fopen() function map + #include "utils.h" // Required for: fopen() Android mapping, TraceLog() #endif -#include "AL/al.h" // OpenAL basic header -#include "AL/alc.h" // OpenAL context header (like OpenGL, OpenAL requires a context to work) +#ifdef __APPLE__ + #include "OpenAL/al.h" // OpenAL basic header + #include "OpenAL/alc.h" // OpenAL context header (like OpenGL, OpenAL requires a context to work) +#else + #include "AL/al.h" // OpenAL basic header + #include "AL/alc.h" // OpenAL context header (like OpenGL, OpenAL requires a context to work) + //#include "AL/alext.h" // OpenAL extensions header, required for AL_EXT_FLOAT32 and AL_EXT_MCFORMATS +#endif + +// OpenAL extension: AL_EXT_FLOAT32 - Support for 32bit float samples +// OpenAL extension: AL_EXT_MCFORMATS - Support for multi-channel formats (Quad, 5.1, 6.1, 7.1) #include <stdlib.h> // Required for: malloc(), free() #include <string.h> // Required for: strcmp(), strncmp() #include <stdio.h> // Required for: FILE, fopen(), fclose(), fread() -// Tokens defined by OpenAL extension: AL_EXT_float32 -#ifndef AL_FORMAT_MONO_FLOAT32 - #define AL_FORMAT_MONO_FLOAT32 0x10010 -#endif -#ifndef AL_FORMAT_STEREO_FLOAT32 - #define AL_FORMAT_STEREO_FLOAT32 0x10011 -#endif - //#define STB_VORBIS_HEADER_ONLY #include "external/stb_vorbis.h" // OGG loading functions @@ -93,11 +115,20 @@ //---------------------------------------------------------------------------------- #define MAX_STREAM_BUFFERS 2 // Number of buffers for each audio stream -// NOTE: Music buffer size is defined by number of samples, independent of sample size +// NOTE: Music buffer size is defined by number of samples, independent of sample size and channels number // After some math, considering a sampleRate of 48000, a buffer refill rate of 1/60 seconds // and double-buffering system, I concluded that a 4096 samples buffer should be enough // In case of music-stalls, just increase this number -#define AUDIO_BUFFER_SIZE 4096 // PCM data samples (i.e. short: 32Kb) +#define AUDIO_BUFFER_SIZE 4096 // PCM data samples (i.e. 16bit, Mono: 8Kb) + +// Support uncompressed PCM data in 32-bit float IEEE format +// NOTE: This definition is included in "AL/alext.h", but some OpenAL implementations +// could not provide the extensions header (Android), so its defined here +#if !defined(AL_EXT_float32) + #define AL_EXT_float32 1 + #define AL_FORMAT_MONO_FLOAT32 0x10010 + #define AL_FORMAT_STEREO_FLOAT32 0x10011 +#endif //---------------------------------------------------------------------------------- // Types and Structures Definition @@ -115,7 +146,7 @@ typedef struct MusicData { AudioStream stream; // Audio stream (double buffering) - bool loop; // Repeat music after finish (loop) + int loopCount; // Loops count (times music repeats), -1 means infinite loop unsigned int totalSamples; // Total number of samples unsigned int samplesLeft; // Number of samples left to end } MusicData; @@ -169,9 +200,11 @@ void InitAudioDevice(void) TraceLog(INFO, "Audio device and context initialized successfully: %s", 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); + alListener3f(AL_POSITION, 0.0f, 0.0f, 0.0f); + alListener3f(AL_VELOCITY, 0.0f, 0.0f, 0.0f); + alListener3f(AL_ORIENTATION, 0.0f, 0.0f, -1.0f); + + alListenerf(AL_GAIN, 1.0f); } } } @@ -208,11 +241,20 @@ bool IsAudioDeviceReady(void) } } +// Set master volume (listener) +void SetMasterVolume(float volume) +{ + if (volume < 0.0f) volume = 0.0f; + else if (volume > 1.0f) volume = 1.0f; + + alListenerf(AL_GAIN, volume); +} + //---------------------------------------------------------------------------------- // Module Functions Definition - Sounds loading and playing (.WAV) //---------------------------------------------------------------------------------- -// Load wave data from file into RAM +// Load wave data from file Wave LoadWave(const char *fileName) { Wave wave = { 0 }; @@ -220,39 +262,49 @@ Wave LoadWave(const char *fileName) if (strcmp(GetExtension(fileName), "wav") == 0) wave = LoadWAV(fileName); else if (strcmp(GetExtension(fileName), "ogg") == 0) wave = LoadOGG(fileName); else if (strcmp(GetExtension(fileName), "flac") == 0) wave = LoadFLAC(fileName); + else if (strcmp(GetExtension(fileName),"rres") == 0) + { + RRES rres = LoadResource(fileName, 0); + + // NOTE: Parameters for RRES_TYPE_WAVE are: sampleCount, sampleRate, sampleSize, channels + + if (rres[0].type == RRES_TYPE_WAVE) wave = LoadWaveEx(rres[0].data, rres[0].param1, rres[0].param2, rres[0].param3, rres[0].param4); + else TraceLog(WARNING, "[%s] Resource file does not contain wave data", fileName); + + UnloadResource(rres); + } else TraceLog(WARNING, "[%s] File extension not recognized, it can't be loaded", fileName); return wave; } -// Load wave data from float array data (32bit) -Wave LoadWaveEx(float *data, int sampleCount, int sampleRate, int sampleSize, int channels) +// Load wave data from raw array data +Wave LoadWaveEx(void *data, int sampleCount, int sampleRate, int sampleSize, int channels) { Wave wave; - + wave.data = data; wave.sampleCount = sampleCount; wave.sampleRate = sampleRate; - wave.sampleSize = 32; + wave.sampleSize = sampleSize; wave.channels = channels; - - // NOTE: Copy wave data to work with, - // user is responsible of input data to free + + // NOTE: Copy wave data to work with, user is responsible of input data to free Wave cwave = WaveCopy(wave); - + WaveFormat(&cwave, sampleRate, sampleSize, channels); - + return cwave; } -// Load sound to memory +// Load sound from file // NOTE: The entire file is loaded to memory to be played (no-streaming) Sound LoadSound(const char *fileName) { Wave wave = LoadWave(fileName); - + Sound sound = LoadSoundFromWave(wave); - + UnloadWave(wave); // Sound is loaded, we can unload wave return sound; @@ -275,7 +327,7 @@ Sound LoadSoundFromWave(Wave wave) { case 8: format = AL_FORMAT_MONO8; break; case 16: format = AL_FORMAT_MONO16; break; - case 32: format = AL_FORMAT_MONO_FLOAT32; break; + case 32: format = AL_FORMAT_MONO_FLOAT32; break; // Requires OpenAL extension: AL_EXT_FLOAT32 default: TraceLog(WARNING, "Wave sample size not supported: %i", wave.sampleSize); break; } } @@ -285,7 +337,7 @@ Sound LoadSoundFromWave(Wave wave) { case 8: format = AL_FORMAT_STEREO8; break; case 16: format = AL_FORMAT_STEREO16; break; - case 32: format = AL_FORMAT_STEREO_FLOAT32; break; + case 32: format = AL_FORMAT_STEREO_FLOAT32; break; // Requires OpenAL extension: AL_EXT_FLOAT32 default: TraceLog(WARNING, "Wave sample size not supported: %i", wave.sampleSize); break; } } @@ -295,10 +347,10 @@ Sound LoadSoundFromWave(Wave wave) 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); + alSourcef(source, AL_PITCH, 1.0f); + alSourcef(source, AL_GAIN, 1.0f); + alSource3f(source, AL_POSITION, 0.0f, 0.0f, 0.0f); + alSource3f(source, AL_VELOCITY, 0.0f, 0.0f, 0.0f); alSourcei(source, AL_LOOPING, AL_FALSE); // Convert loaded data to OpenAL buffer @@ -306,7 +358,7 @@ Sound LoadSoundFromWave(Wave wave) ALuint buffer; alGenBuffers(1, &buffer); // Generate pointer to buffer - unsigned int dataSize = wave.sampleCount*wave.sampleSize/8; // Size in bytes + unsigned int dataSize = wave.sampleCount*wave.channels*wave.sampleSize/8; // Size in bytes // Upload sound data to buffer alBufferData(buffer, format, wave.data, dataSize, wave.sampleRate); @@ -314,7 +366,7 @@ Sound LoadSoundFromWave(Wave wave) // Attach sound buffer to source alSourcei(source, AL_BUFFER, buffer); - TraceLog(INFO, "[SND ID %i][BUFR ID %i] Sound data loaded successfully (SampleRate: %i, SampleSize: %i, Channels: %i)", source, buffer, wave.sampleRate, wave.sampleSize, wave.channels); + TraceLog(INFO, "[SND ID %i][BUFR ID %i] Sound data loaded successfully (%i Hz, %i bit, %s)", source, buffer, wave.sampleRate, wave.sampleSize, (wave.channels == 1) ? "Mono" : "Stereo"); sound.source = source; sound.buffer = buffer; @@ -324,121 +376,10 @@ Sound LoadSoundFromWave(Wave wave) return sound; } -// Load sound to memory from rRES file (raylib Resource) -// TODO: Maybe rresName could be directly a char array with all the data? -Sound LoadSoundFromRES(const char *rresName, int resId) -{ - Sound sound = { 0 }; - -#if defined(AUDIO_STANDALONE) - TraceLog(WARNING, "Sound loading from rRES resource file not supported on standalone mode"); -#else - - bool found = false; - - char id[4]; // rRES file identifier - unsigned char version; // rRES file version and subversion - char useless; // rRES header reserved data - short numRes; - - ResInfoHeader infoHeader; - - FILE *rresFile = fopen(rresName, "rb"); - - if (rresFile == NULL) TraceLog(WARNING, "[%s] rRES raylib resource file could not be opened", rresName); - else - { - // 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')) - { - TraceLog(WARNING, "[%s] This is not a valid raylib resource file", rresName); - } - else - { - // Read number of resources embedded - fread(&numRes, sizeof(short), 1, rresFile); - - for (int i = 0; i < numRes; i++) - { - fread(&infoHeader, sizeof(ResInfoHeader), 1, rresFile); - - if (infoHeader.id == resId) - { - 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.sampleSize = 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); - - sound = LoadSoundFromWave(wave); - - // Sound is loaded, we can unload wave data - UnloadWave(wave); - } - else TraceLog(WARNING, "[%s] Required resource do not seem to be a valid SOUND resource", rresName); - } - 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); - } - - if (!found) TraceLog(WARNING, "[%s] Required resource id [%i] could not be found in the raylib resource file", rresName, resId); -#endif - return sound; -} - -// Unload Wave data +// Unload wave data void UnloadWave(Wave wave) { - free(wave.data); + if (wave.data != NULL) free(wave.data); TraceLog(INFO, "Unloaded wave data from RAM"); } @@ -446,6 +387,8 @@ void UnloadWave(Wave wave) // Unload sound void UnloadSound(Sound sound) { + alSourceStop(sound.source); + alDeleteSources(1, &sound.source); alDeleteBuffers(1, &sound.buffer); @@ -454,19 +397,19 @@ void UnloadSound(Sound sound) // Update sound buffer with new data // NOTE: data must match sound.format -void UpdateSound(Sound sound, void *data, int numSamples) +void UpdateSound(Sound sound, const void *data, int samplesCount) { ALint sampleRate, sampleSize, channels; alGetBufferi(sound.buffer, AL_FREQUENCY, &sampleRate); - alGetBufferi(sound.buffer, AL_BITS, &sampleSize); // It could also be retrieved from sound.format - alGetBufferi(sound.buffer, AL_CHANNELS, &channels); // It could also be retrieved from sound.format - + alGetBufferi(sound.buffer, AL_BITS, &sampleSize); // It could also be retrieved from sound.format + alGetBufferi(sound.buffer, AL_CHANNELS, &channels); // It could also be retrieved from sound.format + TraceLog(DEBUG, "UpdateSound() : AL_FREQUENCY: %i", sampleRate); TraceLog(DEBUG, "UpdateSound() : AL_BITS: %i", sampleSize); TraceLog(DEBUG, "UpdateSound() : AL_CHANNELS: %i", channels); - unsigned int dataSize = numSamples*sampleSize/8; // Size of data in bytes - + unsigned int dataSize = samplesCount*channels*sampleSize/8; // Size of data in bytes + alSourceStop(sound.source); // Stop sound alSourcei(sound.source, AL_BUFFER, 0); // Unbind buffer from sound to update //alDeleteBuffers(1, &sound.buffer); // Delete current buffer data @@ -547,69 +490,94 @@ void SetSoundPitch(Sound sound, float pitch) } // Convert wave data to desired format -// TODO: Consider channels (mono - stereo) void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels) { - if (wave->sampleSize != sampleSize) + // Format sample rate + // NOTE: Only supported 22050 <--> 44100 + if (wave->sampleRate != sampleRate) { - float *samples = GetWaveData(*wave); //Color *pixels = GetImageData(*image); + // TODO: Resample wave data (upsampling or downsampling) + // NOTE 1: To downsample, you have to drop samples or average them. + // NOTE 2: To upsample, you have to interpolate new samples. - free(wave->data); - - wave->sampleSize = sampleSize; + wave->sampleRate = sampleRate; + } - //sample *= 4.0f; // Arbitrary gain to get reasonable output volume... - //if (sample > 1.0f) sample = 1.0f; - //if (sample < -1.0f) sample = -1.0f; + // Format sample size + // NOTE: Only supported 8 bit <--> 16 bit <--> 32 bit + if (wave->sampleSize != sampleSize) + { + void *data = malloc(wave->sampleCount*wave->channels*sampleSize/8); - if (sampleSize == 8) + for (int i = 0; i < wave->sampleCount; i++) { - wave->data = (unsigned char *)malloc(wave->sampleCount*sizeof(unsigned char)); - - for (int i = 0; i < wave->sampleCount; i++) + for (int j = 0; j < wave->channels; j++) { - ((unsigned char *)wave->data)[i] = (unsigned char)((float)samples[i]*127 + 128); + if (sampleSize == 8) + { + if (wave->sampleSize == 16) ((unsigned char *)data)[wave->channels*i + j] = (unsigned char)(((float)(((short *)wave->data)[wave->channels*i + j])/32767.0f)*256); + else if (wave->sampleSize == 32) ((unsigned char *)data)[wave->channels*i + j] = (unsigned char)(((float *)wave->data)[wave->channels*i + j]*127.0f + 127); + } + else if (sampleSize == 16) + { + if (wave->sampleSize == 8) ((short *)data)[wave->channels*i + j] = (short)(((float)(((unsigned char *)wave->data)[wave->channels*i + j] - 127)/256.0f)*32767); + else if (wave->sampleSize == 32) ((short *)data)[wave->channels*i + j] = (short)((((float *)wave->data)[wave->channels*i + j])*32767); + } + else if (sampleSize == 32) + { + if (wave->sampleSize == 8) ((float *)data)[wave->channels*i + j] = (float)(((unsigned char *)wave->data)[wave->channels*i + j] - 127)/256.0f; + else if (wave->sampleSize == 16) ((float *)data)[wave->channels*i + j] = (float)(((short *)wave->data)[wave->channels*i + j])/32767.0f; + } } } - else if (sampleSize == 16) + + wave->sampleSize = sampleSize; + free(wave->data); + wave->data = data; + } + + // Format channels (interlaced mode) + // NOTE: Only supported mono <--> stereo + if (wave->channels != channels) + { + void *data = malloc(wave->sampleCount*channels*wave->sampleSize/8); + + if ((wave->channels == 1) && (channels == 2)) // mono ---> stereo (duplicate mono information) { - wave->data = (short *)malloc(wave->sampleCount*sizeof(short)); - for (int i = 0; i < wave->sampleCount; i++) { - ((short *)wave->data)[i] = (short)((float)samples[i]*32000); // SHRT_MAX = 32767 + for (int j = 0; j < channels; j++) + { + if (wave->sampleSize == 8) ((unsigned char *)data)[channels*i + j] = ((unsigned char *)wave->data)[i]; + else if (wave->sampleSize == 16) ((short *)data)[channels*i + j] = ((short *)wave->data)[i]; + else if (wave->sampleSize == 32) ((float *)data)[channels*i + j] = ((float *)wave->data)[i]; + } } } - else if (sampleSize == 32) + else if ((wave->channels == 2) && (channels == 1)) // stereo ---> mono (mix stereo channels) { - wave->data = (float *)malloc(wave->sampleCount*sizeof(float)); - - for (int i = 0; i < wave->sampleCount; i++) + for (int i = 0, j = 0; i < wave->sampleCount; i++, j += 2) { - ((float *)wave->data)[i] = (float)samples[i]; + if (wave->sampleSize == 8) ((unsigned char *)data)[i] = (((unsigned char *)wave->data)[j] + ((unsigned char *)wave->data)[j + 1])/2; + else if (wave->sampleSize == 16) ((short *)data)[i] = (((short *)wave->data)[j] + ((short *)wave->data)[j + 1])/2; + else if (wave->sampleSize == 32) ((float *)data)[i] = (((float *)wave->data)[j] + ((float *)wave->data)[j + 1])/2.0f; } } - else TraceLog(WARNING, "Wave formatting: Sample size not supported"); - - free(samples); - } - - // NOTE: Only supported 1 or 2 channels (mono or stereo) - if ((channels > 0) && (channels < 3) && (wave->channels != channels)) - { - // TODO: Add/remove channels interlaced data if required... + + // TODO: Add/remove additional interlaced channels + + wave->channels = channels; + free(wave->data); + wave->data = data; } } // Copy a wave to a new wave Wave WaveCopy(Wave wave) { - Wave newWave; + Wave newWave = { 0 }; - if (wave.sampleSize == 8) newWave.data = (unsigned char *)malloc(wave.sampleCount*wave.channels*sizeof(unsigned char)); - else if (wave.sampleSize == 16) newWave.data = (short *)malloc(wave.sampleCount*wave.channels*sizeof(short)); - else if (wave.sampleSize == 32) newWave.data = (float *)malloc(wave.sampleCount*wave.channels*sizeof(float)); - else TraceLog(WARNING, "Wave sample size not supported for copy"); + newWave.data = malloc(wave.sampleCount*wave.channels*wave.sampleSize/8); if (newWave.data != NULL) { @@ -629,40 +597,37 @@ Wave WaveCopy(Wave wave) // NOTE: Security check in case of out-of-range void WaveCrop(Wave *wave, int initSample, int finalSample) { - if ((initSample >= 0) && (initSample < finalSample) && + if ((initSample >= 0) && (initSample < finalSample) && (finalSample > 0) && (finalSample < wave->sampleCount)) { - // TODO: Review cropping (it could be simplified...) - - float *samples = GetWaveData(*wave); - float *cropSamples = (float *)malloc((finalSample - initSample)*sizeof(float)); - - for (int i = initSample; i < finalSample; i++) cropSamples[i] = samples[i]; + int sampleCount = finalSample - initSample; - free(wave->data); - wave->data = cropSamples; - int sampleSize = wave->sampleSize; - wave->sampleSize = 32; + void *data = malloc(sampleCount*wave->channels*wave->sampleSize/8); - WaveFormat(wave, wave->sampleRate, sampleSize, wave->channels); + memcpy(data, (unsigned char*)wave->data + (initSample*wave->channels*wave->sampleSize/8), sampleCount*wave->channels*wave->sampleSize/8); + + free(wave->data); + wave->data = data; } else TraceLog(WARNING, "Wave crop range out of bounds"); } // Get samples data from wave as a floats array // NOTE: Returned sample values are normalized to range [-1..1] -// TODO: Consider multiple channels (mono - stereo) float *GetWaveData(Wave wave) { - float *samples = (float *)malloc(wave.sampleCount*sizeof(float)); - + float *samples = (float *)malloc(wave.sampleCount*wave.channels*sizeof(float)); + for (int i = 0; i < wave.sampleCount; i++) { - if (wave.sampleSize == 8) samples[i] = (float)(((unsigned char *)wave.data)[i] - 127)/256.0f; - else if (wave.sampleSize == 16) samples[i] = (float)((short *)wave.data)[i]/32767.0f; - else if (wave.sampleSize == 32) samples[i] = ((float *)wave.data)[i]; + for (int j = 0; j < wave.channels; j++) + { + if (wave.sampleSize == 8) samples[wave.channels*i + j] = (float)(((unsigned char *)wave.data)[wave.channels*i + j] - 127)/256.0f; + else if (wave.sampleSize == 16) samples[wave.channels*i + j] = (float)((short *)wave.data)[wave.channels*i + j]/32767.0f; + else if (wave.sampleSize == 32) samples[wave.channels*i + j] = ((float *)wave.data)[wave.channels*i + j]; + } } - + return samples; } @@ -684,34 +649,34 @@ Music LoadMusicStream(const char *fileName) else { stb_vorbis_info info = stb_vorbis_get_info(music->ctxOgg); // Get Ogg file info - //float totalLengthSeconds = stb_vorbis_stream_length_in_seconds(music->ctxOgg); - // TODO: Support 32-bit sampleSize OGGs + // OGG bit rate defaults to 16 bit, it's enough for compressed format music->stream = InitAudioStream(info.sample_rate, 16, info.channels); - music->totalSamples = (unsigned int)stb_vorbis_stream_length_in_samples(music->ctxOgg)*info.channels; + music->totalSamples = (unsigned int)stb_vorbis_stream_length_in_samples(music->ctxOgg); // Independent by channel music->samplesLeft = music->totalSamples; music->ctxType = MUSIC_AUDIO_OGG; - music->loop = true; // We loop by default + music->loopCount = -1; // Infinite loop by default + TraceLog(DEBUG, "[%s] FLAC total samples: %i", fileName, music->totalSamples); TraceLog(DEBUG, "[%s] OGG sample rate: %i", fileName, info.sample_rate); TraceLog(DEBUG, "[%s] OGG channels: %i", fileName, info.channels); TraceLog(DEBUG, "[%s] OGG memory required: %i", fileName, info.temp_memory_required); - } } else if (strcmp(GetExtension(fileName), "flac") == 0) { music->ctxFlac = drflac_open_file(fileName); - + if (music->ctxFlac == NULL) TraceLog(WARNING, "[%s] FLAC audio file could not be opened", fileName); else { music->stream = InitAudioStream(music->ctxFlac->sampleRate, music->ctxFlac->bitsPerSample, music->ctxFlac->channels); - music->totalSamples = (unsigned int)music->ctxFlac->totalSampleCount; + music->totalSamples = (unsigned int)music->ctxFlac->totalSampleCount/music->ctxFlac->channels; music->samplesLeft = music->totalSamples; music->ctxType = MUSIC_AUDIO_FLAC; - music->loop = true; // We loop by default - + music->loopCount = -1; // Infinite loop by default + + TraceLog(DEBUG, "[%s] FLAC total samples: %i", fileName, music->totalSamples); TraceLog(DEBUG, "[%s] FLAC sample rate: %i", fileName, music->ctxFlac->sampleRate); TraceLog(DEBUG, "[%s] FLAC bits per sample: %i", fileName, music->ctxFlac->bitsPerSample); TraceLog(DEBUG, "[%s] FLAC channels: %i", fileName, music->ctxFlac->channels); @@ -723,14 +688,14 @@ Music LoadMusicStream(const char *fileName) if (!result) // XM context created successfully { - jar_xm_set_max_loop_count(music->ctxXm, 0); // Set infinite number of loops + jar_xm_set_max_loop_count(music->ctxXm, 0); // Set infinite number of loops // NOTE: Only stereo is supported for XM - music->stream = InitAudioStream(48000, 32, 2); + music->stream = InitAudioStream(48000, 16, 2); music->totalSamples = (unsigned int)jar_xm_get_remaining_samples(music->ctxXm); music->samplesLeft = music->totalSamples; music->ctxType = MUSIC_MODULE_XM; - music->loop = true; + music->loopCount = -1; // Infinite loop by default TraceLog(DEBUG, "[%s] XM number of samples: %i", fileName, music->totalSamples); TraceLog(DEBUG, "[%s] XM track length: %11.6f sec", fileName, (float)music->totalSamples/48000.0f); @@ -747,10 +712,10 @@ Music LoadMusicStream(const char *fileName) music->totalSamples = (unsigned int)jar_mod_max_samples(&music->ctxMod); music->samplesLeft = music->totalSamples; music->ctxType = MUSIC_MODULE_MOD; - music->loop = true; + music->loopCount = -1; // Infinite loop by default - TraceLog(INFO, "[%s] MOD number of samples: %i", fileName, music->samplesLeft); - TraceLog(INFO, "[%s] MOD track length: %11.6f sec", fileName, (float)music->totalSamples/48000.0f); + TraceLog(DEBUG, "[%s] MOD number of samples: %i", fileName, music->samplesLeft); + TraceLog(DEBUG, "[%s] MOD track length: %11.6f sec", fileName, (float)music->totalSamples/48000.0f); } else TraceLog(WARNING, "[%s] MOD file could not be opened", fileName); } @@ -794,115 +759,102 @@ void ResumeMusicStream(Music music) } // Stop music playing (close stream) -// TODO: Restart XM context void StopMusicStream(Music music) { alSourceStop(music->stream.source); + // Clear stream buffers + void *pcm = calloc(AUDIO_BUFFER_SIZE*music->stream.sampleSize/8*music->stream.channels, 1); + + for (int i = 0; i < MAX_STREAM_BUFFERS; i++) + { + alBufferData(music->stream.buffers[i], music->stream.format, pcm, AUDIO_BUFFER_SIZE*music->stream.sampleSize/8*music->stream.channels, music->stream.sampleRate); + } + + free(pcm); + + // Restart music context switch (music->ctxType) { case MUSIC_AUDIO_OGG: stb_vorbis_seek_start(music->ctxOgg); break; - case MUSIC_MODULE_XM: break; + case MUSIC_MODULE_XM: /* TODO: Restart XM context */ break; case MUSIC_MODULE_MOD: jar_mod_seek_start(&music->ctxMod); break; default: break; } - + music->samplesLeft = music->totalSamples; } // Update (re-fill) music buffers if data already processed +// TODO: Make sure buffers are ready for update... check music state void UpdateMusicStream(Music music) { ALenum state; ALint processed = 0; - + alGetSourcei(music->stream.source, AL_SOURCE_STATE, &state); // Get music stream state alGetSourcei(music->stream.source, AL_BUFFERS_PROCESSED, &processed); // Get processed buffers if (processed > 0) { bool active = true; - short pcm[AUDIO_BUFFER_SIZE]; - float pcmf[AUDIO_BUFFER_SIZE]; - int numBuffersToProcess = processed; - int numSamples = 0; // Total size of data steamed in L+R samples for xm floats, - // individual L or R for ogg shorts + // NOTE: Using dynamic allocation because it could require more than 16KB + void *pcm = calloc(AUDIO_BUFFER_SIZE*music->stream.channels*music->stream.sampleSize/8, 1); + + int numBuffersToProcess = processed; + int samplesCount = 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++) { + if (music->samplesLeft >= AUDIO_BUFFER_SIZE) samplesCount = AUDIO_BUFFER_SIZE; + else samplesCount = music->samplesLeft; + + // TODO: Really don't like ctxType thingy... 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, numSamplesOgg*music->stream.channels); - music->samplesLeft -= (numSamplesOgg*music->stream.channels); + // NOTE: Returns the number of samples to process (be careful! we ask for number of shorts!) + int numSamplesOgg = stb_vorbis_get_samples_short_interleaved(music->ctxOgg, music->stream.channels, (short *)pcm, samplesCount*music->stream.channels); } break; case MUSIC_AUDIO_FLAC: { - if (music->samplesLeft >= AUDIO_BUFFER_SIZE) numSamples = AUDIO_BUFFER_SIZE; - else numSamples = music->samplesLeft; - - int pcmi[AUDIO_BUFFER_SIZE]; - - // NOTE: Returns the number of samples to process (should be the same as numSamples) - unsigned int numSamplesFlac = (unsigned int)drflac_read_s32(music->ctxFlac, numSamples, pcmi); - - UpdateAudioStream(music->stream, pcmi, numSamplesFlac*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; + // NOTE: Returns the number of samples to process + unsigned int numSamplesFlac = (unsigned int)drflac_read_s16(music->ctxFlac, samplesCount*music->stream.channels, (short *)pcm); } break; + case MUSIC_MODULE_XM: jar_xm_generate_samples_16bit(music->ctxXm, pcm, samplesCount); break; + case MUSIC_MODULE_MOD: jar_mod_fillbuffer(&music->ctxMod, pcm, samplesCount, 0); break; default: break; } + UpdateAudioStream(music->stream, pcm, samplesCount); + music->samplesLeft -= samplesCount; + if (music->samplesLeft <= 0) { active = false; break; } } - + // This error is registered when UpdateAudioStream() fails if (alGetError() == AL_INVALID_VALUE) TraceLog(WARNING, "OpenAL: Error buffering data..."); // Reset audio stream for looping - if (!active) + if (!active) { StopMusicStream(music); // Stop music (and reset) - if (music->loop) PlayMusicStream(music); // Play again + // Decrease loopCount to stop when required + if (music->loopCount > 0) + { + music->loopCount--; // Decrease loop count + PlayMusicStream(music); // Play again + } } else { @@ -910,6 +862,8 @@ void UpdateMusicStream(Music music) // just make sure to play again on window restore if (state != AL_PLAYING) PlayMusicStream(music); } + + free(pcm); } } @@ -938,6 +892,13 @@ void SetMusicPitch(Music music, float pitch) alSourcef(music->stream.source, AL_PITCH, pitch); } +// Set music loop count (loop repeats) +// NOTE: If set to -1, means infinite loop +void SetMusicLoopCount(Music music, float count) +{ + music->loopCount = count; +} + // Get music time length (in seconds) float GetMusicTimeLength(Music music) { @@ -952,7 +913,7 @@ float GetMusicTimePlayed(Music music) float secondsPlayed = 0.0f; unsigned int samplesPlayed = music->totalSamples - music->samplesLeft; - secondsPlayed = (float)samplesPlayed/(music->stream.sampleRate*music->stream.channels); + secondsPlayed = (float)samplesPlayed/music->stream.sampleRate; return secondsPlayed; } @@ -964,64 +925,61 @@ AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, un stream.sampleRate = sampleRate; stream.sampleSize = sampleSize; - stream.channels = channels; + + // Only mono and stereo channels are supported, more channels require AL_EXT_MCFORMATS extension + if ((channels > 0) && (channels < 3)) stream.channels = channels; + else + { + TraceLog(WARNING, "Init audio stream: Number of channels not supported: %i", channels); + stream.channels = 1; // Fallback to mono channel + } // Setup OpenAL format - if (channels == 1) + if (stream.channels == 1) { switch (sampleSize) { case 8: stream.format = AL_FORMAT_MONO8; break; case 16: stream.format = AL_FORMAT_MONO16; break; - case 32: stream.format = AL_FORMAT_MONO_FLOAT32; break; + case 32: stream.format = AL_FORMAT_MONO_FLOAT32; break; // Requires OpenAL extension: AL_EXT_FLOAT32 default: TraceLog(WARNING, "Init audio stream: Sample size not supported: %i", sampleSize); break; } } - else if (channels == 2) + else if (stream.channels == 2) { switch (sampleSize) { case 8: stream.format = AL_FORMAT_STEREO8; break; case 16: stream.format = AL_FORMAT_STEREO16; break; - case 32: stream.format = AL_FORMAT_STEREO_FLOAT32; break; + case 32: stream.format = AL_FORMAT_STEREO_FLOAT32; break; // Requires OpenAL extension: AL_EXT_FLOAT32 default: TraceLog(WARNING, "Init audio stream: Sample size not supported: %i", sampleSize); break; } } - else TraceLog(WARNING, "Init audio stream: Number of channels not supported: %i", channels); // Create an audio source alGenSources(1, &stream.source); - alSourcef(stream.source, AL_PITCH, 1); - alSourcef(stream.source, AL_GAIN, 1); - alSource3f(stream.source, AL_POSITION, 0, 0, 0); - alSource3f(stream.source, AL_VELOCITY, 0, 0, 0); + alSourcef(stream.source, AL_PITCH, 1.0f); + alSourcef(stream.source, AL_GAIN, 1.0f); + alSource3f(stream.source, AL_POSITION, 0.0f, 0.0f, 0.0f); + alSource3f(stream.source, AL_VELOCITY, 0.0f, 0.0f, 0.0f); // Create Buffers (double buffering) alGenBuffers(MAX_STREAM_BUFFERS, stream.buffers); // Initialize buffer with zeros by default + // NOTE: Using dynamic allocation because it requires more than 16KB + void *pcm = calloc(AUDIO_BUFFER_SIZE*stream.sampleSize/8*stream.channels, 1); + for (int i = 0; i < MAX_STREAM_BUFFERS; i++) { - if (stream.sampleSize == 8) - { - unsigned char pcm[AUDIO_BUFFER_SIZE] = { 0 }; - alBufferData(stream.buffers[i], stream.format, pcm, AUDIO_BUFFER_SIZE*sizeof(unsigned char), stream.sampleRate); - } - else if (stream.sampleSize == 16) - { - short pcm[AUDIO_BUFFER_SIZE] = { 0 }; - alBufferData(stream.buffers[i], stream.format, pcm, AUDIO_BUFFER_SIZE*sizeof(short), stream.sampleRate); - } - else if (stream.sampleSize == 32) - { - float pcm[AUDIO_BUFFER_SIZE] = { 0.0f }; - alBufferData(stream.buffers[i], stream.format, pcm, AUDIO_BUFFER_SIZE*sizeof(float), stream.sampleRate); - } + alBufferData(stream.buffers[i], stream.format, pcm, AUDIO_BUFFER_SIZE*stream.sampleSize/8*stream.channels, stream.sampleRate); } + free(pcm); + alSourceQueueBuffers(stream.source, MAX_STREAM_BUFFERS, stream.buffers); - TraceLog(INFO, "[AUD ID %i] Audio stream loaded successfully", stream.source); + TraceLog(INFO, "[AUD ID %i] Audio stream loaded successfully (%i Hz, %i bit, %s)", stream.source, stream.sampleRate, stream.sampleSize, (stream.channels == 1) ? "Mono" : "Stereo"); return stream; } @@ -1052,8 +1010,8 @@ void CloseAudioStream(AudioStream stream) } // Update audio stream buffers with data -// NOTE: Only one buffer per call -void UpdateAudioStream(AudioStream stream, void *data, int numSamples) +// NOTE: Only updates one buffer per call +void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount) { ALuint buffer = 0; alSourceUnqueueBuffers(stream.source, 1, &buffer); @@ -1061,10 +1019,7 @@ void UpdateAudioStream(AudioStream stream, void *data, int numSamples) // 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); - + alBufferData(buffer, stream.format, data, samplesCount*stream.channels*stream.sampleSize/8, stream.sampleRate); alSourceQueueBuffers(stream.source, 1, &buffer); } } @@ -1119,7 +1074,7 @@ static Wave LoadWAV(const char *fileName) char chunkID[4]; int chunkSize; char format[4]; - } RiffHeader; + } WAVRiffHeader; typedef struct { char subChunkID[4]; @@ -1130,16 +1085,16 @@ static Wave LoadWAV(const char *fileName) int byteRate; short blockAlign; short bitsPerSample; - } WaveFormat; + } WAVFormat; typedef struct { char subChunkID[4]; int subChunkSize; - } WaveData; + } WAVData; - RiffHeader riffHeader; - WaveFormat waveFormat; - WaveData waveData; + WAVRiffHeader wavRiffHeader; + WAVFormat wavFormat; + WAVData wavData; Wave wave = { 0 }; FILE *wavFile; @@ -1154,54 +1109,70 @@ static Wave LoadWAV(const char *fileName) else { // Read in the first chunk into the struct - fread(&riffHeader, sizeof(RiffHeader), 1, wavFile); + fread(&wavRiffHeader, sizeof(WAVRiffHeader), 1, wavFile); // Check for RIFF and WAVE tags - if (strncmp(riffHeader.chunkID, "RIFF", 4) || - strncmp(riffHeader.format, "WAVE", 4)) + if (strncmp(wavRiffHeader.chunkID, "RIFF", 4) || + strncmp(wavRiffHeader.format, "WAVE", 4)) { 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); + fread(&wavFormat, sizeof(WAVFormat), 1, wavFile); // Check for fmt tag - if ((waveFormat.subChunkID[0] != 'f') || (waveFormat.subChunkID[1] != 'm') || - (waveFormat.subChunkID[2] != 't') || (waveFormat.subChunkID[3] != ' ')) + if ((wavFormat.subChunkID[0] != 'f') || (wavFormat.subChunkID[1] != 'm') || + (wavFormat.subChunkID[2] != 't') || (wavFormat.subChunkID[3] != ' ')) { TraceLog(WARNING, "[%s] Invalid Wave format", fileName); } else { // Check for extra parameters; - if (waveFormat.subChunkSize > 16) fseek(wavFile, sizeof(short), SEEK_CUR); + if (wavFormat.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); + fread(&wavData, sizeof(WAVData), 1, wavFile); // Check for data tag - if ((waveData.subChunkID[0] != 'd') || (waveData.subChunkID[1] != 'a') || - (waveData.subChunkID[2] != 't') || (waveData.subChunkID[3] != 'a')) + if ((wavData.subChunkID[0] != 'd') || (wavData.subChunkID[1] != 'a') || + (wavData.subChunkID[2] != 't') || (wavData.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); + wave.data = malloc(wavData.subChunkSize); // Read in the sound data into the soundData variable - fread(wave.data, waveData.subChunkSize, 1, wavFile); + 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(WARNING, "[%s] WAV 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(WARNING, "[%s] WAV channels number (%i) not supported, converted to 2 channels", fileName, wave.channels); + } - // Now we set the variables that we need later - wave.sampleCount = waveData.subChunkSize; - wave.sampleRate = waveFormat.sampleRate; - wave.sampleSize = waveFormat.bitsPerSample; - wave.channels = waveFormat.numChannels; + // NOTE: subChunkSize comes in bytes, we need to translate it to number of samples + wave.sampleCount = (wavData.subChunkSize/(wave.sampleSize/8))/wave.channels; - TraceLog(INFO, "[%s] WAV file loaded successfully (SampleRate: %i, SampleSize: %i, Channels: %i)", fileName, wave.sampleRate, wave.sampleSize, wave.channels); + TraceLog(INFO, "[%s] WAV file loaded successfully (%i Hz, %i bit, %s)", fileName, wave.sampleRate, wave.sampleSize, (wave.channels == 1) ? "Mono" : "Stereo"); } } } @@ -1232,22 +1203,19 @@ static Wave LoadOGG(const char *fileName) wave.sampleRate = info.sample_rate; wave.sampleSize = 16; // 16 bit per sample (short) wave.channels = info.channels; + wave.sampleCount = (int)stb_vorbis_stream_length_in_samples(oggFile); - int totalSamplesLength = (stb_vorbis_stream_length_in_samples(oggFile)*info.channels); float totalSeconds = stb_vorbis_stream_length_in_seconds(oggFile); - if (totalSeconds > 10) TraceLog(WARNING, "[%s] Ogg audio lenght is larger than 10 seconds (%f), that's a big file in memory, consider music streaming", fileName, totalSeconds); - int totalSamples = (int)(totalSeconds*info.sample_rate*info.channels); - wave.sampleCount = totalSamples; + wave.data = (short *)malloc(wave.sampleCount*wave.channels*sizeof(short)); - wave.data = (short *)malloc(totalSamplesLength*sizeof(short)); + // NOTE: Returns the number of samples to process (be careful! we ask for number of shorts!) + int numSamplesOgg = stb_vorbis_get_samples_short_interleaved(oggFile, info.channels, (short *)wave.data, wave.sampleCount*wave.channels); - int samplesObtained = stb_vorbis_get_samples_short_interleaved(oggFile, info.channels, (short *)wave.data, totalSamplesLength); + TraceLog(DEBUG, "[%s] Samples obtained: %i", fileName, numSamplesOgg); - TraceLog(DEBUG, "[%s] Samples obtained: %i", fileName, samplesObtained); - - TraceLog(INFO, "[%s] OGG file loaded successfully (SampleRate: %i, SampleSize: %i, Channels: %i)", fileName, wave.sampleRate, wave.sampleSize, wave.channels); + TraceLog(INFO, "[%s] OGG file loaded successfully (%i Hz, %i bit, %s)", fileName, wave.sampleRate, wave.sampleSize, (wave.channels == 1) ? "Mono" : "Stereo"); stb_vorbis_close(oggFile); } @@ -1263,13 +1231,17 @@ static Wave LoadFLAC(const char *fileName) // Decode an entire FLAC file in one go uint64_t totalSampleCount; - wave.data = drflac_open_and_decode_file_s32(fileName, &wave.channels, &wave.sampleRate, &totalSampleCount); - - wave.sampleCount = (int)totalSampleCount; - wave.sampleSize = 32; - + wave.data = drflac_open_and_decode_file_s16(fileName, &wave.channels, &wave.sampleRate, &totalSampleCount); + + wave.sampleCount = (int)totalSampleCount/wave.channels; + wave.sampleSize = 16; + + // NOTE: Only support up to 2 channels (mono, stereo) + if (wave.channels > 2) TraceLog(WARNING, "[%s] FLAC channels number (%i) not supported", fileName, wave.channels); + if (wave.data == NULL) TraceLog(WARNING, "[%s] FLAC data could not be loaded", fileName); - + else TraceLog(INFO, "[%s] FLAC file loaded successfully (%i Hz, %i bit, %s)", fileName, wave.sampleRate, wave.sampleSize, (wave.channels == 1) ? "Mono" : "Stereo"); + return wave; } @@ -1314,4 +1286,4 @@ void TraceLog(int msgType, const char *text, ...) if (msgType == ERROR) exit(1); // If ERROR message, exit program } -#endif
\ No newline at end of file +#endif |
