raudio.c (109654B)
1 /********************************************************************************************** 2 * 3 * raudio v1.1 - A simple and easy-to-use audio library based on miniaudio 4 * 5 * FEATURES: 6 * - Manage audio device (init/close) 7 * - Manage raw audio context 8 * - Manage mixing channels 9 * - Load and unload audio files 10 * - Format wave data (sample rate, size, channels) 11 * - Play/Stop/Pause/Resume loaded audio 12 * 13 * CONFIGURATION: 14 * #define SUPPORT_MODULE_RAUDIO 15 * raudio module is included in the build 16 * 17 * #define RAUDIO_STANDALONE 18 * Define to use the module as standalone library (independently of raylib). 19 * Required types and functions are defined in the same module. 20 * 21 * #define SUPPORT_FILEFORMAT_WAV 22 * #define SUPPORT_FILEFORMAT_OGG 23 * #define SUPPORT_FILEFORMAT_MP3 24 * #define SUPPORT_FILEFORMAT_QOA 25 * #define SUPPORT_FILEFORMAT_FLAC 26 * #define SUPPORT_FILEFORMAT_XM 27 * #define SUPPORT_FILEFORMAT_MOD 28 * Selected desired fileformats to be supported for loading. Some of those formats are 29 * supported by default, to remove support, just comment unrequired #define in this module 30 * 31 * DEPENDENCIES: 32 * miniaudio.h - Audio device management lib (https://github.com/mackron/miniaudio) 33 * stb_vorbis.h - Ogg audio files loading (http://www.nothings.org/stb_vorbis/) 34 * dr_wav.h - WAV audio files loading (http://github.com/mackron/dr_libs) 35 * dr_mp3.h - MP3 audio file loading (https://github.com/mackron/dr_libs) 36 * dr_flac.h - FLAC audio file loading (https://github.com/mackron/dr_libs) 37 * jar_xm.h - XM module file loading 38 * jar_mod.h - MOD audio file loading 39 * 40 * CONTRIBUTORS: 41 * David Reid (github: @mackron) (Nov. 2017): 42 * - Complete port to miniaudio library 43 * 44 * Joshua Reisenauer (github: @kd7tck) (2015): 45 * - XM audio module support (jar_xm) 46 * - MOD audio module support (jar_mod) 47 * - Mixing channels support 48 * - Raw audio context support 49 * 50 * 51 * LICENSE: zlib/libpng 52 * 53 * Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) 54 * 55 * This software is provided "as-is", without any express or implied warranty. In no event 56 * will the authors be held liable for any damages arising from the use of this software. 57 * 58 * Permission is granted to anyone to use this software for any purpose, including commercial 59 * applications, and to alter it and redistribute it freely, subject to the following restrictions: 60 * 61 * 1. The origin of this software must not be misrepresented; you must not claim that you 62 * wrote the original software. If you use this software in a product, an acknowledgment 63 * in the product documentation would be appreciated but is not required. 64 * 65 * 2. Altered source versions must be plainly marked as such, and must not be misrepresented 66 * as being the original software. 67 * 68 * 3. This notice may not be removed or altered from any source distribution. 69 * 70 **********************************************************************************************/ 71 72 #if defined(RAUDIO_STANDALONE) 73 #include "raudio.h" 74 #else 75 #include "raylib.h" // Declares module functions 76 77 // Check if config flags have been externally provided on compilation line 78 #if !defined(EXTERNAL_CONFIG_FLAGS) 79 #include "config.h" // Defines module configuration flags 80 #endif 81 #include "utils.h" // Required for: fopen() Android mapping 82 #endif 83 84 #if defined(SUPPORT_MODULE_RAUDIO) 85 86 #if defined(_WIN32) 87 // To avoid conflicting windows.h symbols with raylib, some flags are defined 88 // WARNING: Those flags avoid inclusion of some Win32 headers that could be required 89 // by user at some point and won't be included... 90 //------------------------------------------------------------------------------------- 91 92 // If defined, the following flags inhibit definition of the indicated items. 93 #define NOGDICAPMASKS // CC_*, LC_*, PC_*, CP_*, TC_*, RC_ 94 #define NOVIRTUALKEYCODES // VK_* 95 #define NOWINMESSAGES // WM_*, EM_*, LB_*, CB_* 96 #define NOWINSTYLES // WS_*, CS_*, ES_*, LBS_*, SBS_*, CBS_* 97 #define NOSYSMETRICS // SM_* 98 #define NOMENUS // MF_* 99 #define NOICONS // IDI_* 100 #define NOKEYSTATES // MK_* 101 #define NOSYSCOMMANDS // SC_* 102 #define NORASTEROPS // Binary and Tertiary raster ops 103 #define NOSHOWWINDOW // SW_* 104 #define OEMRESOURCE // OEM Resource values 105 #define NOATOM // Atom Manager routines 106 #define NOCLIPBOARD // Clipboard routines 107 #define NOCOLOR // Screen colors 108 #define NOCTLMGR // Control and Dialog routines 109 #define NODRAWTEXT // DrawText() and DT_* 110 #define NOGDI // All GDI defines and routines 111 #define NOKERNEL // All KERNEL defines and routines 112 #define NOUSER // All USER defines and routines 113 //#define NONLS // All NLS defines and routines 114 #define NOMB // MB_* and MessageBox() 115 #define NOMEMMGR // GMEM_*, LMEM_*, GHND, LHND, associated routines 116 #define NOMETAFILE // typedef METAFILEPICT 117 #define NOMINMAX // Macros min(a,b) and max(a,b) 118 #define NOMSG // typedef MSG and associated routines 119 #define NOOPENFILE // OpenFile(), OemToAnsi, AnsiToOem, and OF_* 120 #define NOSCROLL // SB_* and scrolling routines 121 #define NOSERVICE // All Service Controller routines, SERVICE_ equates, etc. 122 #define NOSOUND // Sound driver routines 123 #define NOTEXTMETRIC // typedef TEXTMETRIC and associated routines 124 #define NOWH // SetWindowsHook and WH_* 125 #define NOWINOFFSETS // GWL_*, GCL_*, associated routines 126 #define NOCOMM // COMM driver routines 127 #define NOKANJI // Kanji support stuff. 128 #define NOHELP // Help engine interface. 129 #define NOPROFILER // Profiler interface. 130 #define NODEFERWINDOWPOS // DeferWindowPos routines 131 #define NOMCX // Modem Configuration Extensions 132 133 // Type required before windows.h inclusion 134 typedef struct tagMSG *LPMSG; 135 136 #include <windows.h> // Windows functionality (miniaudio) 137 138 // Type required by some unused function... 139 typedef struct tagBITMAPINFOHEADER { 140 DWORD biSize; 141 LONG biWidth; 142 LONG biHeight; 143 WORD biPlanes; 144 WORD biBitCount; 145 DWORD biCompression; 146 DWORD biSizeImage; 147 LONG biXPelsPerMeter; 148 LONG biYPelsPerMeter; 149 DWORD biClrUsed; 150 DWORD biClrImportant; 151 } BITMAPINFOHEADER, *PBITMAPINFOHEADER; 152 153 #include <objbase.h> // Component Object Model (COM) header 154 #include <mmreg.h> // Windows Multimedia, defines some WAVE structs 155 #include <mmsystem.h> // Windows Multimedia, used by Windows GDI, defines DIBINDEX macro 156 157 // Some required types defined for MSVC/TinyC compiler 158 #if defined(_MSC_VER) || defined(__TINYC__) 159 #include "propidl.h" 160 #endif 161 #endif 162 163 #define MA_MALLOC RL_MALLOC 164 #define MA_FREE RL_FREE 165 166 #define MA_NO_JACK 167 #define MA_NO_WAV 168 #define MA_NO_FLAC 169 #define MA_NO_MP3 170 #define MA_NO_RESOURCE_MANAGER 171 #define MA_NO_NODE_GRAPH 172 #define MA_NO_ENGINE 173 #define MA_NO_GENERATION 174 175 // Threading model: Default: [0] COINIT_MULTITHREADED: COM calls objects on any thread (free threading) 176 #define MA_COINIT_VALUE 2 // [2] COINIT_APARTMENTTHREADED: Each object has its own thread (apartment model) 177 178 #define MINIAUDIO_IMPLEMENTATION 179 //#define MA_DEBUG_OUTPUT 180 #include "external/miniaudio.h" // Audio device initialization and management 181 #undef PlaySound // Win32 API: windows.h > mmsystem.h defines PlaySound macro 182 183 #include <stdlib.h> // Required for: malloc(), free() 184 #include <stdio.h> // Required for: FILE, fopen(), fclose(), fread() 185 #include <string.h> // Required for: strcmp() [Used in IsFileExtension(), LoadWaveFromMemory(), LoadMusicStreamFromMemory()] 186 187 #if defined(RAUDIO_STANDALONE) 188 #ifndef TRACELOG 189 #define TRACELOG(level, ...) printf(__VA_ARGS__) 190 #endif 191 192 // Allow custom memory allocators 193 #ifndef RL_MALLOC 194 #define RL_MALLOC(sz) malloc(sz) 195 #endif 196 #ifndef RL_CALLOC 197 #define RL_CALLOC(n,sz) calloc(n,sz) 198 #endif 199 #ifndef RL_REALLOC 200 #define RL_REALLOC(ptr,sz) realloc(ptr,sz) 201 #endif 202 #ifndef RL_FREE 203 #define RL_FREE(ptr) free(ptr) 204 #endif 205 #endif 206 207 #if defined(SUPPORT_FILEFORMAT_WAV) 208 #define DRWAV_MALLOC RL_MALLOC 209 #define DRWAV_REALLOC RL_REALLOC 210 #define DRWAV_FREE RL_FREE 211 212 #define DR_WAV_IMPLEMENTATION 213 #include "external/dr_wav.h" // WAV loading functions 214 #endif 215 216 #if defined(SUPPORT_FILEFORMAT_OGG) 217 // TODO: Remap stb_vorbis malloc()/free() calls to RL_MALLOC/RL_FREE 218 #include "external/stb_vorbis.c" // OGG loading functions 219 #endif 220 221 #if defined(SUPPORT_FILEFORMAT_MP3) 222 #define DRMP3_MALLOC RL_MALLOC 223 #define DRMP3_REALLOC RL_REALLOC 224 #define DRMP3_FREE RL_FREE 225 226 #define DR_MP3_IMPLEMENTATION 227 #include "external/dr_mp3.h" // MP3 loading functions 228 #endif 229 230 #if defined(SUPPORT_FILEFORMAT_QOA) 231 #define QOA_MALLOC RL_MALLOC 232 #define QOA_FREE RL_FREE 233 234 #if defined(_MSC_VER) // Disable some MSVC warning 235 #pragma warning(push) 236 #pragma warning(disable : 4018) 237 #pragma warning(disable : 4267) 238 #pragma warning(disable : 4244) 239 #endif 240 241 #define QOA_IMPLEMENTATION 242 #include "external/qoa.h" // QOA loading and saving functions 243 #include "external/qoaplay.c" // QOA stream playing helper functions 244 245 #if defined(_MSC_VER) 246 #pragma warning(pop) // Disable MSVC warning suppression 247 #endif 248 #endif 249 250 #if defined(SUPPORT_FILEFORMAT_FLAC) 251 #define DRFLAC_MALLOC RL_MALLOC 252 #define DRFLAC_REALLOC RL_REALLOC 253 #define DRFLAC_FREE RL_FREE 254 255 #define DR_FLAC_IMPLEMENTATION 256 #define DR_FLAC_NO_WIN32_IO 257 #include "external/dr_flac.h" // FLAC loading functions 258 #endif 259 260 #if defined(SUPPORT_FILEFORMAT_XM) 261 #define JARXM_MALLOC RL_MALLOC 262 #define JARXM_FREE RL_FREE 263 264 #if defined(_MSC_VER) // Disable some MSVC warning 265 #pragma warning(push) 266 #pragma warning(disable : 4244) 267 #endif 268 269 #define JAR_XM_IMPLEMENTATION 270 #include "external/jar_xm.h" // XM loading functions 271 272 #if defined(_MSC_VER) 273 #pragma warning(pop) // Disable MSVC warning suppression 274 #endif 275 #endif 276 277 #if defined(SUPPORT_FILEFORMAT_MOD) 278 #define JARMOD_MALLOC RL_MALLOC 279 #define JARMOD_FREE RL_FREE 280 281 #define JAR_MOD_IMPLEMENTATION 282 #include "external/jar_mod.h" // MOD loading functions 283 #endif 284 285 //---------------------------------------------------------------------------------- 286 // Defines and Macros 287 //---------------------------------------------------------------------------------- 288 #ifndef AUDIO_DEVICE_FORMAT 289 #define AUDIO_DEVICE_FORMAT ma_format_f32 // Device output format (float-32bit) 290 #endif 291 #ifndef AUDIO_DEVICE_CHANNELS 292 #define AUDIO_DEVICE_CHANNELS 2 // Device output channels: stereo 293 #endif 294 #ifndef AUDIO_DEVICE_SAMPLE_RATE 295 #define AUDIO_DEVICE_SAMPLE_RATE 0 // Device output sample rate 296 #endif 297 298 #ifndef MAX_AUDIO_BUFFER_POOL_CHANNELS 299 #define MAX_AUDIO_BUFFER_POOL_CHANNELS 16 // Audio pool channels 300 #endif 301 302 //---------------------------------------------------------------------------------- 303 // Types and Structures Definition 304 //---------------------------------------------------------------------------------- 305 #if defined(RAUDIO_STANDALONE) 306 // Trace log level 307 // NOTE: Organized by priority level 308 typedef enum { 309 LOG_ALL = 0, // Display all logs 310 LOG_TRACE, // Trace logging, intended for internal use only 311 LOG_DEBUG, // Debug logging, used for internal debugging, it should be disabled on release builds 312 LOG_INFO, // Info logging, used for program execution info 313 LOG_WARNING, // Warning logging, used on recoverable failures 314 LOG_ERROR, // Error logging, used on unrecoverable failures 315 LOG_FATAL, // Fatal logging, used to abort program: exit(EXIT_FAILURE) 316 LOG_NONE // Disable logging 317 } TraceLogLevel; 318 #endif 319 320 // Music context type 321 // NOTE: Depends on data structure provided by the library 322 // in charge of reading the different file types 323 typedef enum { 324 MUSIC_AUDIO_NONE = 0, // No audio context loaded 325 MUSIC_AUDIO_WAV, // WAV audio context 326 MUSIC_AUDIO_OGG, // OGG audio context 327 MUSIC_AUDIO_FLAC, // FLAC audio context 328 MUSIC_AUDIO_MP3, // MP3 audio context 329 MUSIC_AUDIO_QOA, // QOA audio context 330 MUSIC_MODULE_XM, // XM module audio context 331 MUSIC_MODULE_MOD // MOD module audio context 332 } MusicContextType; 333 334 // NOTE: Different logic is used when feeding data to the playback device 335 // depending on whether data is streamed (Music vs Sound) 336 typedef enum { 337 AUDIO_BUFFER_USAGE_STATIC = 0, 338 AUDIO_BUFFER_USAGE_STREAM 339 } AudioBufferUsage; 340 341 // Audio buffer struct 342 struct rAudioBuffer { 343 ma_data_converter converter; // Audio data converter 344 345 AudioCallback callback; // Audio buffer callback for buffer filling on audio threads 346 rAudioProcessor *processor; // Audio processor 347 348 float volume; // Audio buffer volume 349 float pitch; // Audio buffer pitch 350 float pan; // Audio buffer pan (0.0f to 1.0f) 351 352 bool playing; // Audio buffer state: AUDIO_PLAYING 353 bool paused; // Audio buffer state: AUDIO_PAUSED 354 bool looping; // Audio buffer looping, default to true for AudioStreams 355 int usage; // Audio buffer usage mode: STATIC or STREAM 356 357 bool isSubBufferProcessed[2]; // SubBuffer processed (virtual double buffer) 358 unsigned int sizeInFrames; // Total buffer size in frames 359 unsigned int frameCursorPos; // Frame cursor position 360 unsigned int framesProcessed; // Total frames processed in this buffer (required for play timing) 361 362 unsigned char *data; // Data buffer, on music stream keeps filling 363 364 rAudioBuffer *next; // Next audio buffer on the list 365 rAudioBuffer *prev; // Previous audio buffer on the list 366 }; 367 368 // Audio processor struct 369 // NOTE: Useful to apply effects to an AudioBuffer 370 struct rAudioProcessor { 371 AudioCallback process; // Processor callback function 372 rAudioProcessor *next; // Next audio processor on the list 373 rAudioProcessor *prev; // Previous audio processor on the list 374 }; 375 376 #define AudioBuffer rAudioBuffer // HACK: To avoid CoreAudio (macOS) symbol collision 377 378 // Audio data context 379 typedef struct AudioData { 380 struct { 381 ma_context context; // miniaudio context data 382 ma_device device; // miniaudio device 383 ma_mutex lock; // miniaudio mutex lock 384 bool isReady; // Check if audio device is ready 385 size_t pcmBufferSize; // Pre-allocated buffer size 386 void *pcmBuffer; // Pre-allocated buffer to read audio data from file/memory 387 } System; 388 struct { 389 AudioBuffer *first; // Pointer to first AudioBuffer in the list 390 AudioBuffer *last; // Pointer to last AudioBuffer in the list 391 int defaultSize; // Default audio buffer size for audio streams 392 } Buffer; 393 rAudioProcessor *mixedProcessor; 394 } AudioData; 395 396 //---------------------------------------------------------------------------------- 397 // Global Variables Definition 398 //---------------------------------------------------------------------------------- 399 static AudioData AUDIO = { // Global AUDIO context 400 401 // NOTE: Music buffer size is defined by number of samples, independent of sample size and channels number 402 // After some math, considering a sampleRate of 48000, a buffer refill rate of 1/60 seconds and a 403 // standard double-buffering system, a 4096 samples buffer has been chosen, it should be enough 404 // In case of music-stalls, just increase this number 405 .Buffer.defaultSize = 0, 406 .mixedProcessor = NULL 407 }; 408 409 //---------------------------------------------------------------------------------- 410 // Module specific Functions Declaration 411 //---------------------------------------------------------------------------------- 412 static void OnLog(void *pUserData, ma_uint32 level, const char *pMessage); 413 414 // Reads audio data from an AudioBuffer object in internal/device formats 415 static ma_uint32 ReadAudioBufferFramesInInternalFormat(AudioBuffer *audioBuffer, void *framesOut, ma_uint32 frameCount); 416 static ma_uint32 ReadAudioBufferFramesInMixingFormat(AudioBuffer *audioBuffer, float *framesOut, ma_uint32 frameCount); 417 418 static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const void *pFramesInput, ma_uint32 frameCount); 419 static void MixAudioFrames(float *framesOut, const float *framesIn, ma_uint32 frameCount, AudioBuffer *buffer); 420 421 static bool IsAudioBufferPlayingInLockedState(AudioBuffer *buffer); 422 static void StopAudioBufferInLockedState(AudioBuffer *buffer); 423 static void UpdateAudioStreamInLockedState(AudioStream stream, const void *data, int frameCount); 424 425 #if defined(RAUDIO_STANDALONE) 426 static bool IsFileExtension(const char *fileName, const char *ext); // Check file extension 427 static const char *GetFileExtension(const char *fileName); // Get pointer to extension for a filename string (includes the dot: .png) 428 static const char *GetFileName(const char *filePath); // Get pointer to filename for a path string 429 static const char *GetFileNameWithoutExt(const char *filePath); // Get filename string without extension (uses static string) 430 431 static unsigned char *LoadFileData(const char *fileName, int *dataSize); // Load file data as byte array (read) 432 static bool SaveFileData(const char *fileName, void *data, int dataSize); // Save data to file from byte array (write) 433 static bool SaveFileText(const char *fileName, char *text); // Save text data to file (write), string must be '\0' terminated 434 #endif 435 436 //---------------------------------------------------------------------------------- 437 // AudioBuffer management functions declaration 438 // NOTE: Those functions are not exposed by raylib... for the moment 439 //---------------------------------------------------------------------------------- 440 AudioBuffer *LoadAudioBuffer(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 sizeInFrames, int usage); 441 void UnloadAudioBuffer(AudioBuffer *buffer); 442 443 bool IsAudioBufferPlaying(AudioBuffer *buffer); 444 void PlayAudioBuffer(AudioBuffer *buffer); 445 void StopAudioBuffer(AudioBuffer *buffer); 446 void PauseAudioBuffer(AudioBuffer *buffer); 447 void ResumeAudioBuffer(AudioBuffer *buffer); 448 void SetAudioBufferVolume(AudioBuffer *buffer, float volume); 449 void SetAudioBufferPitch(AudioBuffer *buffer, float pitch); 450 void SetAudioBufferPan(AudioBuffer *buffer, float pan); 451 void TrackAudioBuffer(AudioBuffer *buffer); 452 void UntrackAudioBuffer(AudioBuffer *buffer); 453 454 455 //---------------------------------------------------------------------------------- 456 // Module Functions Definition - Audio Device initialization and Closing 457 //---------------------------------------------------------------------------------- 458 // Initialize audio device 459 void InitAudioDevice(void) 460 { 461 // Init audio context 462 ma_context_config ctxConfig = ma_context_config_init(); 463 ma_log_callback_init(OnLog, NULL); 464 465 ma_result result = ma_context_init(NULL, 0, &ctxConfig, &AUDIO.System.context); 466 if (result != MA_SUCCESS) 467 { 468 TRACELOG(LOG_WARNING, "AUDIO: Failed to initialize context"); 469 return; 470 } 471 472 // Init audio device 473 // NOTE: Using the default device. Format is floating point because it simplifies mixing 474 ma_device_config config = ma_device_config_init(ma_device_type_playback); 475 config.playback.pDeviceID = NULL; // NULL for the default playback AUDIO.System.device 476 config.playback.format = AUDIO_DEVICE_FORMAT; 477 config.playback.channels = AUDIO_DEVICE_CHANNELS; 478 config.capture.pDeviceID = NULL; // NULL for the default capture AUDIO.System.device 479 config.capture.format = ma_format_s16; 480 config.capture.channels = 1; 481 config.sampleRate = AUDIO_DEVICE_SAMPLE_RATE; 482 config.dataCallback = OnSendAudioDataToDevice; 483 config.pUserData = NULL; 484 485 result = ma_device_init(&AUDIO.System.context, &config, &AUDIO.System.device); 486 if (result != MA_SUCCESS) 487 { 488 TRACELOG(LOG_WARNING, "AUDIO: Failed to initialize playback device"); 489 ma_context_uninit(&AUDIO.System.context); 490 return; 491 } 492 493 // Mixing happens on a separate thread which means we need to synchronize. I'm using a mutex here to make things simple, but may 494 // want to look at something a bit smarter later on to keep everything real-time, if that's necessary 495 if (ma_mutex_init(&AUDIO.System.lock) != MA_SUCCESS) 496 { 497 TRACELOG(LOG_WARNING, "AUDIO: Failed to create mutex for mixing"); 498 ma_device_uninit(&AUDIO.System.device); 499 ma_context_uninit(&AUDIO.System.context); 500 return; 501 } 502 503 // Keep the device running the whole time. May want to consider doing something a bit smarter and only have the device running 504 // while there's at least one sound being played 505 result = ma_device_start(&AUDIO.System.device); 506 if (result != MA_SUCCESS) 507 { 508 TRACELOG(LOG_WARNING, "AUDIO: Failed to start playback device"); 509 ma_device_uninit(&AUDIO.System.device); 510 ma_context_uninit(&AUDIO.System.context); 511 return; 512 } 513 514 TRACELOG(LOG_INFO, "AUDIO: Device initialized successfully"); 515 TRACELOG(LOG_INFO, " > Backend: miniaudio | %s", ma_get_backend_name(AUDIO.System.context.backend)); 516 TRACELOG(LOG_INFO, " > Format: %s -> %s", ma_get_format_name(AUDIO.System.device.playback.format), ma_get_format_name(AUDIO.System.device.playback.internalFormat)); 517 TRACELOG(LOG_INFO, " > Channels: %d -> %d", AUDIO.System.device.playback.channels, AUDIO.System.device.playback.internalChannels); 518 TRACELOG(LOG_INFO, " > Sample rate: %d -> %d", AUDIO.System.device.sampleRate, AUDIO.System.device.playback.internalSampleRate); 519 TRACELOG(LOG_INFO, " > Periods size: %d", AUDIO.System.device.playback.internalPeriodSizeInFrames*AUDIO.System.device.playback.internalPeriods); 520 521 AUDIO.System.isReady = true; 522 } 523 524 // Close the audio device for all contexts 525 void CloseAudioDevice(void) 526 { 527 if (AUDIO.System.isReady) 528 { 529 ma_mutex_uninit(&AUDIO.System.lock); 530 ma_device_uninit(&AUDIO.System.device); 531 ma_context_uninit(&AUDIO.System.context); 532 533 AUDIO.System.isReady = false; 534 RL_FREE(AUDIO.System.pcmBuffer); 535 AUDIO.System.pcmBuffer = NULL; 536 AUDIO.System.pcmBufferSize = 0; 537 538 TRACELOG(LOG_INFO, "AUDIO: Device closed successfully"); 539 } 540 else TRACELOG(LOG_WARNING, "AUDIO: Device could not be closed, not currently initialized"); 541 } 542 543 // Check if device has been initialized successfully 544 bool IsAudioDeviceReady(void) 545 { 546 return AUDIO.System.isReady; 547 } 548 549 // Set master volume (listener) 550 void SetMasterVolume(float volume) 551 { 552 ma_device_set_master_volume(&AUDIO.System.device, volume); 553 } 554 555 // Get master volume (listener) 556 float GetMasterVolume(void) 557 { 558 float volume = 0.0f; 559 ma_device_get_master_volume(&AUDIO.System.device, &volume); 560 return volume; 561 } 562 563 //---------------------------------------------------------------------------------- 564 // Module Functions Definition - Audio Buffer management 565 //---------------------------------------------------------------------------------- 566 567 // Initialize a new audio buffer (filled with silence) 568 AudioBuffer *LoadAudioBuffer(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 sizeInFrames, int usage) 569 { 570 AudioBuffer *audioBuffer = (AudioBuffer *)RL_CALLOC(1, sizeof(AudioBuffer)); 571 572 if (audioBuffer == NULL) 573 { 574 TRACELOG(LOG_WARNING, "AUDIO: Failed to allocate memory for buffer"); 575 return NULL; 576 } 577 578 if (sizeInFrames > 0) audioBuffer->data = RL_CALLOC(sizeInFrames*channels*ma_get_bytes_per_sample(format), 1); 579 580 // Audio data runs through a format converter 581 ma_data_converter_config converterConfig = ma_data_converter_config_init(format, AUDIO_DEVICE_FORMAT, channels, AUDIO_DEVICE_CHANNELS, sampleRate, AUDIO.System.device.sampleRate); 582 converterConfig.allowDynamicSampleRate = true; 583 584 ma_result result = ma_data_converter_init(&converterConfig, NULL, &audioBuffer->converter); 585 586 if (result != MA_SUCCESS) 587 { 588 TRACELOG(LOG_WARNING, "AUDIO: Failed to create data conversion pipeline"); 589 RL_FREE(audioBuffer); 590 return NULL; 591 } 592 593 // Init audio buffer values 594 audioBuffer->volume = 1.0f; 595 audioBuffer->pitch = 1.0f; 596 audioBuffer->pan = 0.5f; 597 598 audioBuffer->callback = NULL; 599 audioBuffer->processor = NULL; 600 601 audioBuffer->playing = false; 602 audioBuffer->paused = false; 603 audioBuffer->looping = false; 604 605 audioBuffer->usage = usage; 606 audioBuffer->frameCursorPos = 0; 607 audioBuffer->sizeInFrames = sizeInFrames; 608 609 // Buffers should be marked as processed by default so that a call to 610 // UpdateAudioStream() immediately after initialization works correctly 611 audioBuffer->isSubBufferProcessed[0] = true; 612 audioBuffer->isSubBufferProcessed[1] = true; 613 614 // Track audio buffer to linked list next position 615 TrackAudioBuffer(audioBuffer); 616 617 return audioBuffer; 618 } 619 620 // Delete an audio buffer 621 void UnloadAudioBuffer(AudioBuffer *buffer) 622 { 623 if (buffer != NULL) 624 { 625 UntrackAudioBuffer(buffer); 626 ma_data_converter_uninit(&buffer->converter, NULL); 627 RL_FREE(buffer->data); 628 RL_FREE(buffer); 629 } 630 } 631 632 // Check if an audio buffer is playing from a program state without lock 633 bool IsAudioBufferPlaying(AudioBuffer *buffer) 634 { 635 bool result = false; 636 ma_mutex_lock(&AUDIO.System.lock); 637 result = IsAudioBufferPlayingInLockedState(buffer); 638 ma_mutex_unlock(&AUDIO.System.lock); 639 return result; 640 } 641 642 // Play an audio buffer 643 // NOTE: Buffer is restarted to the start 644 // Use PauseAudioBuffer() and ResumeAudioBuffer() if the playback position should be maintained 645 void PlayAudioBuffer(AudioBuffer *buffer) 646 { 647 if (buffer != NULL) 648 { 649 ma_mutex_lock(&AUDIO.System.lock); 650 buffer->playing = true; 651 buffer->paused = false; 652 buffer->frameCursorPos = 0; 653 ma_mutex_unlock(&AUDIO.System.lock); 654 } 655 } 656 657 // Stop an audio buffer from a program state without lock 658 void StopAudioBuffer(AudioBuffer *buffer) 659 { 660 ma_mutex_lock(&AUDIO.System.lock); 661 StopAudioBufferInLockedState(buffer); 662 ma_mutex_unlock(&AUDIO.System.lock); 663 } 664 665 // Pause an audio buffer 666 void PauseAudioBuffer(AudioBuffer *buffer) 667 { 668 if (buffer != NULL) 669 { 670 ma_mutex_lock(&AUDIO.System.lock); 671 buffer->paused = true; 672 ma_mutex_unlock(&AUDIO.System.lock); 673 } 674 } 675 676 // Resume an audio buffer 677 void ResumeAudioBuffer(AudioBuffer *buffer) 678 { 679 if (buffer != NULL) 680 { 681 ma_mutex_lock(&AUDIO.System.lock); 682 buffer->paused = false; 683 ma_mutex_unlock(&AUDIO.System.lock); 684 } 685 } 686 687 // Set volume for an audio buffer 688 void SetAudioBufferVolume(AudioBuffer *buffer, float volume) 689 { 690 if (buffer != NULL) 691 { 692 ma_mutex_lock(&AUDIO.System.lock); 693 buffer->volume = volume; 694 ma_mutex_unlock(&AUDIO.System.lock); 695 } 696 } 697 698 // Set pitch for an audio buffer 699 void SetAudioBufferPitch(AudioBuffer *buffer, float pitch) 700 { 701 if ((buffer != NULL) && (pitch > 0.0f)) 702 { 703 ma_mutex_lock(&AUDIO.System.lock); 704 // Pitching is just an adjustment of the sample rate 705 // Note that this changes the duration of the sound: 706 // - higher pitches will make the sound faster 707 // - lower pitches make it slower 708 ma_uint32 outputSampleRate = (ma_uint32)((float)buffer->converter.sampleRateOut/pitch); 709 ma_data_converter_set_rate(&buffer->converter, buffer->converter.sampleRateIn, outputSampleRate); 710 711 buffer->pitch = pitch; 712 ma_mutex_unlock(&AUDIO.System.lock); 713 } 714 } 715 716 // Set pan for an audio buffer 717 void SetAudioBufferPan(AudioBuffer *buffer, float pan) 718 { 719 if (pan < 0.0f) pan = 0.0f; 720 else if (pan > 1.0f) pan = 1.0f; 721 722 if (buffer != NULL) 723 { 724 ma_mutex_lock(&AUDIO.System.lock); 725 buffer->pan = pan; 726 ma_mutex_unlock(&AUDIO.System.lock); 727 } 728 } 729 730 // Track audio buffer to linked list next position 731 void TrackAudioBuffer(AudioBuffer *buffer) 732 { 733 ma_mutex_lock(&AUDIO.System.lock); 734 { 735 if (AUDIO.Buffer.first == NULL) AUDIO.Buffer.first = buffer; 736 else 737 { 738 AUDIO.Buffer.last->next = buffer; 739 buffer->prev = AUDIO.Buffer.last; 740 } 741 742 AUDIO.Buffer.last = buffer; 743 } 744 ma_mutex_unlock(&AUDIO.System.lock); 745 } 746 747 // Untrack audio buffer from linked list 748 void UntrackAudioBuffer(AudioBuffer *buffer) 749 { 750 ma_mutex_lock(&AUDIO.System.lock); 751 { 752 if (buffer->prev == NULL) AUDIO.Buffer.first = buffer->next; 753 else buffer->prev->next = buffer->next; 754 755 if (buffer->next == NULL) AUDIO.Buffer.last = buffer->prev; 756 else buffer->next->prev = buffer->prev; 757 758 buffer->prev = NULL; 759 buffer->next = NULL; 760 } 761 ma_mutex_unlock(&AUDIO.System.lock); 762 } 763 764 //---------------------------------------------------------------------------------- 765 // Module Functions Definition - Sounds loading and playing (.WAV) 766 //---------------------------------------------------------------------------------- 767 768 // Load wave data from file 769 Wave LoadWave(const char *fileName) 770 { 771 Wave wave = { 0 }; 772 773 // Loading file to memory 774 int dataSize = 0; 775 unsigned char *fileData = LoadFileData(fileName, &dataSize); 776 777 // Loading wave from memory data 778 if (fileData != NULL) wave = LoadWaveFromMemory(GetFileExtension(fileName), fileData, dataSize); 779 780 UnloadFileData(fileData); 781 782 return wave; 783 } 784 785 // Load wave from memory buffer, fileType refers to extension: i.e. ".wav" 786 // WARNING: File extension must be provided in lower-case 787 Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int dataSize) 788 { 789 Wave wave = { 0 }; 790 791 if (false) { } 792 #if defined(SUPPORT_FILEFORMAT_WAV) 793 else if ((strcmp(fileType, ".wav") == 0) || (strcmp(fileType, ".WAV") == 0)) 794 { 795 drwav wav = { 0 }; 796 bool success = drwav_init_memory(&wav, fileData, dataSize, NULL); 797 798 if (success) 799 { 800 wave.frameCount = (unsigned int)wav.totalPCMFrameCount; 801 wave.sampleRate = wav.sampleRate; 802 wave.sampleSize = 16; 803 wave.channels = wav.channels; 804 wave.data = (short *)RL_MALLOC(wave.frameCount*wave.channels*sizeof(short)); 805 806 // NOTE: We are forcing conversion to 16bit sample size on reading 807 drwav_read_pcm_frames_s16(&wav, wav.totalPCMFrameCount, wave.data); 808 } 809 else TRACELOG(LOG_WARNING, "WAVE: Failed to load WAV data"); 810 811 drwav_uninit(&wav); 812 } 813 #endif 814 #if defined(SUPPORT_FILEFORMAT_OGG) 815 else if ((strcmp(fileType, ".ogg") == 0) || (strcmp(fileType, ".OGG") == 0)) 816 { 817 stb_vorbis *oggData = stb_vorbis_open_memory((unsigned char *)fileData, dataSize, NULL, NULL); 818 819 if (oggData != NULL) 820 { 821 stb_vorbis_info info = stb_vorbis_get_info(oggData); 822 823 wave.sampleRate = info.sample_rate; 824 wave.sampleSize = 16; // By default, ogg data is 16 bit per sample (short) 825 wave.channels = info.channels; 826 wave.frameCount = (unsigned int)stb_vorbis_stream_length_in_samples(oggData); // NOTE: It returns frames! 827 wave.data = (short *)RL_MALLOC(wave.frameCount*wave.channels*sizeof(short)); 828 829 // NOTE: Get the number of samples to process (be careful! we ask for number of shorts, not bytes!) 830 stb_vorbis_get_samples_short_interleaved(oggData, info.channels, (short *)wave.data, wave.frameCount*wave.channels); 831 stb_vorbis_close(oggData); 832 } 833 else TRACELOG(LOG_WARNING, "WAVE: Failed to load OGG data"); 834 } 835 #endif 836 #if defined(SUPPORT_FILEFORMAT_MP3) 837 else if ((strcmp(fileType, ".mp3") == 0) || (strcmp(fileType, ".MP3") == 0)) 838 { 839 drmp3_config config = { 0 }; 840 unsigned long long int totalFrameCount = 0; 841 842 // NOTE: We are forcing conversion to 32bit float sample size on reading 843 wave.data = drmp3_open_memory_and_read_pcm_frames_f32(fileData, dataSize, &config, &totalFrameCount, NULL); 844 wave.sampleSize = 32; 845 846 if (wave.data != NULL) 847 { 848 wave.channels = config.channels; 849 wave.sampleRate = config.sampleRate; 850 wave.frameCount = (int)totalFrameCount; 851 } 852 else TRACELOG(LOG_WARNING, "WAVE: Failed to load MP3 data"); 853 854 } 855 #endif 856 #if defined(SUPPORT_FILEFORMAT_QOA) 857 else if ((strcmp(fileType, ".qoa") == 0) || (strcmp(fileType, ".QOA") == 0)) 858 { 859 qoa_desc qoa = { 0 }; 860 861 // NOTE: Returned sample data is always 16 bit? 862 wave.data = qoa_decode(fileData, dataSize, &qoa); 863 wave.sampleSize = 16; 864 865 if (wave.data != NULL) 866 { 867 wave.channels = qoa.channels; 868 wave.sampleRate = qoa.samplerate; 869 wave.frameCount = qoa.samples; 870 } 871 else TRACELOG(LOG_WARNING, "WAVE: Failed to load QOA data"); 872 873 } 874 #endif 875 #if defined(SUPPORT_FILEFORMAT_FLAC) 876 else if ((strcmp(fileType, ".flac") == 0) || (strcmp(fileType, ".FLAC") == 0)) 877 { 878 unsigned long long int totalFrameCount = 0; 879 880 // NOTE: We are forcing conversion to 16bit sample size on reading 881 wave.data = drflac_open_memory_and_read_pcm_frames_s16(fileData, dataSize, &wave.channels, &wave.sampleRate, &totalFrameCount, NULL); 882 wave.sampleSize = 16; 883 884 if (wave.data != NULL) wave.frameCount = (unsigned int)totalFrameCount; 885 else TRACELOG(LOG_WARNING, "WAVE: Failed to load FLAC data"); 886 } 887 #endif 888 else TRACELOG(LOG_WARNING, "WAVE: Data format not supported"); 889 890 TRACELOG(LOG_INFO, "WAVE: Data loaded successfully (%i Hz, %i bit, %i channels)", wave.sampleRate, wave.sampleSize, wave.channels); 891 892 return wave; 893 } 894 895 // Checks if wave data is valid (data loaded and parameters) 896 bool IsWaveValid(Wave wave) 897 { 898 bool result = false; 899 900 if ((wave.data != NULL) && // Validate wave data available 901 (wave.frameCount > 0) && // Validate frame count 902 (wave.sampleRate > 0) && // Validate sample rate is supported 903 (wave.sampleSize > 0) && // Validate sample size is supported 904 (wave.channels > 0)) result = true; // Validate number of channels supported 905 906 return result; 907 } 908 909 // Load sound from file 910 // NOTE: The entire file is loaded to memory to be played (no-streaming) 911 Sound LoadSound(const char *fileName) 912 { 913 Wave wave = LoadWave(fileName); 914 915 Sound sound = LoadSoundFromWave(wave); 916 917 UnloadWave(wave); // Sound is loaded, we can unload wave 918 919 return sound; 920 } 921 922 // Load sound from wave data 923 // NOTE: Wave data must be unallocated manually 924 Sound LoadSoundFromWave(Wave wave) 925 { 926 Sound sound = { 0 }; 927 928 if (wave.data != NULL) 929 { 930 // When using miniaudio we need to do our own mixing 931 // To simplify this we need convert the format of each sound to be consistent with 932 // the format used to open the playback AUDIO.System.device. We can do this two ways: 933 // 934 // 1) Convert the whole sound in one go at load time (here) 935 // 2) Convert the audio data in chunks at mixing time 936 // 937 // First option has been selected, format conversion is done on the loading stage 938 // The downside is that it uses more memory if the original sound is u8 or s16 939 ma_format formatIn = ((wave.sampleSize == 8)? ma_format_u8 : ((wave.sampleSize == 16)? ma_format_s16 : ma_format_f32)); 940 ma_uint32 frameCountIn = wave.frameCount; 941 942 ma_uint32 frameCount = (ma_uint32)ma_convert_frames(NULL, 0, AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO.System.device.sampleRate, NULL, frameCountIn, formatIn, wave.channels, wave.sampleRate); 943 if (frameCount == 0) TRACELOG(LOG_WARNING, "SOUND: Failed to get frame count for format conversion"); 944 945 AudioBuffer *audioBuffer = LoadAudioBuffer(AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO.System.device.sampleRate, frameCount, AUDIO_BUFFER_USAGE_STATIC); 946 if (audioBuffer == NULL) 947 { 948 TRACELOG(LOG_WARNING, "SOUND: Failed to create buffer"); 949 return sound; // early return to avoid dereferencing the audioBuffer null pointer 950 } 951 952 frameCount = (ma_uint32)ma_convert_frames(audioBuffer->data, frameCount, AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO.System.device.sampleRate, wave.data, frameCountIn, formatIn, wave.channels, wave.sampleRate); 953 if (frameCount == 0) TRACELOG(LOG_WARNING, "SOUND: Failed format conversion"); 954 955 sound.frameCount = frameCount; 956 sound.stream.sampleRate = AUDIO.System.device.sampleRate; 957 sound.stream.sampleSize = 32; 958 sound.stream.channels = AUDIO_DEVICE_CHANNELS; 959 sound.stream.buffer = audioBuffer; 960 } 961 962 return sound; 963 } 964 965 // Clone sound from existing sound data, clone does not own wave data 966 // NOTE: Wave data must be unallocated manually and will be shared across all clones 967 Sound LoadSoundAlias(Sound source) 968 { 969 Sound sound = { 0 }; 970 971 if (source.stream.buffer->data != NULL) 972 { 973 AudioBuffer *audioBuffer = LoadAudioBuffer(AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO.System.device.sampleRate, 0, AUDIO_BUFFER_USAGE_STATIC); 974 975 if (audioBuffer == NULL) 976 { 977 TRACELOG(LOG_WARNING, "SOUND: Failed to create buffer"); 978 return sound; // Early return to avoid dereferencing the audioBuffer null pointer 979 } 980 981 audioBuffer->sizeInFrames = source.stream.buffer->sizeInFrames; 982 audioBuffer->volume = source.stream.buffer->volume; 983 audioBuffer->data = source.stream.buffer->data; 984 985 sound.frameCount = source.frameCount; 986 sound.stream.sampleRate = AUDIO.System.device.sampleRate; 987 sound.stream.sampleSize = 32; 988 sound.stream.channels = AUDIO_DEVICE_CHANNELS; 989 sound.stream.buffer = audioBuffer; 990 } 991 992 return sound; 993 } 994 995 996 // Checks if a sound is valid (data loaded and buffers initialized) 997 bool IsSoundValid(Sound sound) 998 { 999 bool result = false; 1000 1001 if ((sound.frameCount > 0) && // Validate frame count 1002 (sound.stream.buffer != NULL) && // Validate stream buffer 1003 (sound.stream.sampleRate > 0) && // Validate sample rate is supported 1004 (sound.stream.sampleSize > 0) && // Validate sample size is supported 1005 (sound.stream.channels > 0)) result = true; // Validate number of channels supported 1006 1007 return result; 1008 } 1009 1010 // Unload wave data 1011 void UnloadWave(Wave wave) 1012 { 1013 RL_FREE(wave.data); 1014 //TRACELOG(LOG_INFO, "WAVE: Unloaded wave data from RAM"); 1015 } 1016 1017 // Unload sound 1018 void UnloadSound(Sound sound) 1019 { 1020 UnloadAudioBuffer(sound.stream.buffer); 1021 //TRACELOG(LOG_INFO, "SOUND: Unloaded sound data from RAM"); 1022 } 1023 1024 void UnloadSoundAlias(Sound alias) 1025 { 1026 // Untrack and unload just the sound buffer, not the sample data, it is shared with the source for the alias 1027 if (alias.stream.buffer != NULL) 1028 { 1029 UntrackAudioBuffer(alias.stream.buffer); 1030 ma_data_converter_uninit(&alias.stream.buffer->converter, NULL); 1031 RL_FREE(alias.stream.buffer); 1032 } 1033 } 1034 1035 // Update sound buffer with new data 1036 void UpdateSound(Sound sound, const void *data, int frameCount) 1037 { 1038 if (sound.stream.buffer != NULL) 1039 { 1040 StopAudioBuffer(sound.stream.buffer); 1041 1042 memcpy(sound.stream.buffer->data, data, frameCount*ma_get_bytes_per_frame(sound.stream.buffer->converter.formatIn, sound.stream.buffer->converter.channelsIn)); 1043 } 1044 } 1045 1046 // Export wave data to file 1047 bool ExportWave(Wave wave, const char *fileName) 1048 { 1049 bool success = false; 1050 1051 if (false) { } 1052 #if defined(SUPPORT_FILEFORMAT_WAV) 1053 else if (IsFileExtension(fileName, ".wav")) 1054 { 1055 drwav wav = { 0 }; 1056 drwav_data_format format = { 0 }; 1057 format.container = drwav_container_riff; 1058 if (wave.sampleSize == 32) format.format = DR_WAVE_FORMAT_IEEE_FLOAT; 1059 else format.format = DR_WAVE_FORMAT_PCM; 1060 format.channels = wave.channels; 1061 format.sampleRate = wave.sampleRate; 1062 format.bitsPerSample = wave.sampleSize; 1063 1064 void *fileData = NULL; 1065 size_t fileDataSize = 0; 1066 success = drwav_init_memory_write(&wav, &fileData, &fileDataSize, &format, NULL); 1067 if (success) success = (int)drwav_write_pcm_frames(&wav, wave.frameCount, wave.data); 1068 drwav_result result = drwav_uninit(&wav); 1069 1070 if (result == DRWAV_SUCCESS) success = SaveFileData(fileName, (unsigned char *)fileData, (unsigned int)fileDataSize); 1071 1072 drwav_free(fileData, NULL); 1073 } 1074 #endif 1075 #if defined(SUPPORT_FILEFORMAT_QOA) 1076 else if (IsFileExtension(fileName, ".qoa")) 1077 { 1078 if (wave.sampleSize == 16) 1079 { 1080 qoa_desc qoa = { 0 }; 1081 qoa.channels = wave.channels; 1082 qoa.samplerate = wave.sampleRate; 1083 qoa.samples = wave.frameCount; 1084 1085 int bytesWritten = qoa_write(fileName, wave.data, &qoa); 1086 if (bytesWritten > 0) success = true; 1087 } 1088 else TRACELOG(LOG_WARNING, "AUDIO: Wave data must be 16 bit per sample for QOA format export"); 1089 } 1090 #endif 1091 else if (IsFileExtension(fileName, ".raw")) 1092 { 1093 // Export raw sample data (without header) 1094 // NOTE: It's up to the user to track wave parameters 1095 success = SaveFileData(fileName, wave.data, wave.frameCount*wave.channels*wave.sampleSize/8); 1096 } 1097 1098 if (success) TRACELOG(LOG_INFO, "FILEIO: [%s] Wave data exported successfully", fileName); 1099 else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export wave data", fileName); 1100 1101 return success; 1102 } 1103 1104 // Export wave sample data to code (.h) 1105 bool ExportWaveAsCode(Wave wave, const char *fileName) 1106 { 1107 bool success = false; 1108 1109 #ifndef TEXT_BYTES_PER_LINE 1110 #define TEXT_BYTES_PER_LINE 20 1111 #endif 1112 1113 int waveDataSize = wave.frameCount*wave.channels*wave.sampleSize/8; 1114 1115 // NOTE: Text data buffer size is estimated considering wave data size in bytes 1116 // and requiring 12 char bytes for every byte; the actual size varies, but 1117 // the longest possible char being appended is "%.4ff,\n ", which is 12 bytes. 1118 char *txtData = (char *)RL_CALLOC(waveDataSize*12 + 2000, sizeof(char)); 1119 1120 int byteCount = 0; 1121 byteCount += sprintf(txtData + byteCount, "\n//////////////////////////////////////////////////////////////////////////////////\n"); 1122 byteCount += sprintf(txtData + byteCount, "// //\n"); 1123 byteCount += sprintf(txtData + byteCount, "// WaveAsCode exporter v1.1 - Wave data exported as an array of bytes //\n"); 1124 byteCount += sprintf(txtData + byteCount, "// //\n"); 1125 byteCount += sprintf(txtData + byteCount, "// more info and bugs-report: github.com/raysan5/raylib //\n"); 1126 byteCount += sprintf(txtData + byteCount, "// feedback and support: ray[at]raylib.com //\n"); 1127 byteCount += sprintf(txtData + byteCount, "// //\n"); 1128 byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2018-2024 Ramon Santamaria (@raysan5) //\n"); 1129 byteCount += sprintf(txtData + byteCount, "// //\n"); 1130 byteCount += sprintf(txtData + byteCount, "//////////////////////////////////////////////////////////////////////////////////\n\n"); 1131 1132 // Get file name from path and convert variable name to uppercase 1133 char varFileName[256] = { 0 }; 1134 strcpy(varFileName, GetFileNameWithoutExt(fileName)); 1135 for (int i = 0; varFileName[i] != '\0'; i++) if (varFileName[i] >= 'a' && varFileName[i] <= 'z') { varFileName[i] = varFileName[i] - 32; } 1136 1137 // Add wave information 1138 byteCount += sprintf(txtData + byteCount, "// Wave data information\n"); 1139 byteCount += sprintf(txtData + byteCount, "#define %s_FRAME_COUNT %u\n", varFileName, wave.frameCount); 1140 byteCount += sprintf(txtData + byteCount, "#define %s_SAMPLE_RATE %u\n", varFileName, wave.sampleRate); 1141 byteCount += sprintf(txtData + byteCount, "#define %s_SAMPLE_SIZE %u\n", varFileName, wave.sampleSize); 1142 byteCount += sprintf(txtData + byteCount, "#define %s_CHANNELS %u\n\n", varFileName, wave.channels); 1143 1144 // Write wave data as an array of values 1145 // Wave data is exported as byte array for 8/16bit and float array for 32bit float data 1146 // NOTE: Frame data exported is channel-interlaced: frame01[sampleChannel1, sampleChannel2, ...], frame02[], frame03[] 1147 if (wave.sampleSize == 32) 1148 { 1149 byteCount += sprintf(txtData + byteCount, "static float %s_DATA[%i] = {\n", varFileName, waveDataSize/4); 1150 for (int i = 1; i < waveDataSize/4; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "%.4ff,\n " : "%.4ff, "), ((float *)wave.data)[i - 1]); 1151 byteCount += sprintf(txtData + byteCount, "%.4ff };\n", ((float *)wave.data)[waveDataSize/4 - 1]); 1152 } 1153 else 1154 { 1155 byteCount += sprintf(txtData + byteCount, "static unsigned char %s_DATA[%i] = { ", varFileName, waveDataSize); 1156 for (int i = 1; i < waveDataSize; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "0x%x,\n " : "0x%x, "), ((unsigned char *)wave.data)[i - 1]); 1157 byteCount += sprintf(txtData + byteCount, "0x%x };\n", ((unsigned char *)wave.data)[waveDataSize - 1]); 1158 } 1159 1160 // NOTE: Text data length exported is determined by '\0' (NULL) character 1161 success = SaveFileText(fileName, txtData); 1162 1163 RL_FREE(txtData); 1164 1165 if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Wave as code exported successfully", fileName); 1166 else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export wave as code", fileName); 1167 1168 return success; 1169 } 1170 1171 // Play a sound 1172 void PlaySound(Sound sound) 1173 { 1174 PlayAudioBuffer(sound.stream.buffer); 1175 } 1176 1177 // Pause a sound 1178 void PauseSound(Sound sound) 1179 { 1180 PauseAudioBuffer(sound.stream.buffer); 1181 } 1182 1183 // Resume a paused sound 1184 void ResumeSound(Sound sound) 1185 { 1186 ResumeAudioBuffer(sound.stream.buffer); 1187 } 1188 1189 // Stop reproducing a sound 1190 void StopSound(Sound sound) 1191 { 1192 StopAudioBuffer(sound.stream.buffer); 1193 } 1194 1195 // Check if a sound is playing 1196 bool IsSoundPlaying(Sound sound) 1197 { 1198 bool result = false; 1199 1200 if (IsAudioBufferPlaying(sound.stream.buffer)) result = true; 1201 1202 return result; 1203 } 1204 1205 // Set volume for a sound 1206 void SetSoundVolume(Sound sound, float volume) 1207 { 1208 SetAudioBufferVolume(sound.stream.buffer, volume); 1209 } 1210 1211 // Set pitch for a sound 1212 void SetSoundPitch(Sound sound, float pitch) 1213 { 1214 SetAudioBufferPitch(sound.stream.buffer, pitch); 1215 } 1216 1217 // Set pan for a sound 1218 void SetSoundPan(Sound sound, float pan) 1219 { 1220 SetAudioBufferPan(sound.stream.buffer, pan); 1221 } 1222 1223 // Convert wave data to desired format 1224 void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels) 1225 { 1226 ma_format formatIn = ((wave->sampleSize == 8)? ma_format_u8 : ((wave->sampleSize == 16)? ma_format_s16 : ma_format_f32)); 1227 ma_format formatOut = ((sampleSize == 8)? ma_format_u8 : ((sampleSize == 16)? ma_format_s16 : ma_format_f32)); 1228 1229 ma_uint32 frameCountIn = wave->frameCount; 1230 ma_uint32 frameCount = (ma_uint32)ma_convert_frames(NULL, 0, formatOut, channels, sampleRate, NULL, frameCountIn, formatIn, wave->channels, wave->sampleRate); 1231 1232 if (frameCount == 0) 1233 { 1234 TRACELOG(LOG_WARNING, "WAVE: Failed to get frame count for format conversion"); 1235 return; 1236 } 1237 1238 void *data = RL_MALLOC(frameCount*channels*(sampleSize/8)); 1239 1240 frameCount = (ma_uint32)ma_convert_frames(data, frameCount, formatOut, channels, sampleRate, wave->data, frameCountIn, formatIn, wave->channels, wave->sampleRate); 1241 if (frameCount == 0) 1242 { 1243 TRACELOG(LOG_WARNING, "WAVE: Failed format conversion"); 1244 return; 1245 } 1246 1247 wave->frameCount = frameCount; 1248 wave->sampleSize = sampleSize; 1249 wave->sampleRate = sampleRate; 1250 wave->channels = channels; 1251 1252 RL_FREE(wave->data); 1253 wave->data = data; 1254 } 1255 1256 // Copy a wave to a new wave 1257 Wave WaveCopy(Wave wave) 1258 { 1259 Wave newWave = { 0 }; 1260 1261 newWave.data = RL_MALLOC(wave.frameCount*wave.channels*wave.sampleSize/8); 1262 1263 if (newWave.data != NULL) 1264 { 1265 // NOTE: Size must be provided in bytes 1266 memcpy(newWave.data, wave.data, wave.frameCount*wave.channels*wave.sampleSize/8); 1267 1268 newWave.frameCount = wave.frameCount; 1269 newWave.sampleRate = wave.sampleRate; 1270 newWave.sampleSize = wave.sampleSize; 1271 newWave.channels = wave.channels; 1272 } 1273 1274 return newWave; 1275 } 1276 1277 // Crop a wave to defined frames range 1278 // NOTE: Security check in case of out-of-range 1279 void WaveCrop(Wave *wave, int initFrame, int finalFrame) 1280 { 1281 if ((initFrame >= 0) && (initFrame < finalFrame) && ((unsigned int)finalFrame <= wave->frameCount)) 1282 { 1283 int frameCount = finalFrame - initFrame; 1284 1285 void *data = RL_MALLOC(frameCount*wave->channels*wave->sampleSize/8); 1286 1287 memcpy(data, (unsigned char *)wave->data + (initFrame*wave->channels*wave->sampleSize/8), frameCount*wave->channels*wave->sampleSize/8); 1288 1289 RL_FREE(wave->data); 1290 wave->data = data; 1291 wave->frameCount = (unsigned int)frameCount; 1292 } 1293 else TRACELOG(LOG_WARNING, "WAVE: Crop range out of bounds"); 1294 } 1295 1296 // Load samples data from wave as a floats array 1297 // NOTE 1: Returned sample values are normalized to range [-1..1] 1298 // NOTE 2: Sample data allocated should be freed with UnloadWaveSamples() 1299 float *LoadWaveSamples(Wave wave) 1300 { 1301 float *samples = (float *)RL_MALLOC(wave.frameCount*wave.channels*sizeof(float)); 1302 1303 // NOTE: sampleCount is the total number of interlaced samples (including channels) 1304 1305 for (unsigned int i = 0; i < wave.frameCount*wave.channels; i++) 1306 { 1307 if (wave.sampleSize == 8) samples[i] = (float)(((unsigned char *)wave.data)[i] - 128)/128.0f; 1308 else if (wave.sampleSize == 16) samples[i] = (float)(((short *)wave.data)[i])/32768.0f; 1309 else if (wave.sampleSize == 32) samples[i] = ((float *)wave.data)[i]; 1310 } 1311 1312 return samples; 1313 } 1314 1315 // Unload samples data loaded with LoadWaveSamples() 1316 void UnloadWaveSamples(float *samples) 1317 { 1318 RL_FREE(samples); 1319 } 1320 1321 //---------------------------------------------------------------------------------- 1322 // Module Functions Definition - Music loading and stream playing 1323 //---------------------------------------------------------------------------------- 1324 1325 // Load music stream from file 1326 Music LoadMusicStream(const char *fileName) 1327 { 1328 Music music = { 0 }; 1329 bool musicLoaded = false; 1330 1331 if (false) { } 1332 #if defined(SUPPORT_FILEFORMAT_WAV) 1333 else if (IsFileExtension(fileName, ".wav")) 1334 { 1335 drwav *ctxWav = RL_CALLOC(1, sizeof(drwav)); 1336 bool success = drwav_init_file(ctxWav, fileName, NULL); 1337 1338 if (success) 1339 { 1340 music.ctxType = MUSIC_AUDIO_WAV; 1341 music.ctxData = ctxWav; 1342 int sampleSize = ctxWav->bitsPerSample; 1343 if (ctxWav->bitsPerSample == 24) sampleSize = 16; // Forcing conversion to s16 on UpdateMusicStream() 1344 1345 music.stream = LoadAudioStream(ctxWav->sampleRate, sampleSize, ctxWav->channels); 1346 music.frameCount = (unsigned int)ctxWav->totalPCMFrameCount; 1347 music.looping = true; // Looping enabled by default 1348 musicLoaded = true; 1349 } 1350 else 1351 { 1352 RL_FREE(ctxWav); 1353 } 1354 } 1355 #endif 1356 #if defined(SUPPORT_FILEFORMAT_OGG) 1357 else if (IsFileExtension(fileName, ".ogg")) 1358 { 1359 // Open ogg audio stream 1360 stb_vorbis *ctxOgg = stb_vorbis_open_filename(fileName, NULL, NULL); 1361 1362 if (ctxOgg != NULL) 1363 { 1364 music.ctxType = MUSIC_AUDIO_OGG; 1365 music.ctxData = ctxOgg; 1366 stb_vorbis_info info = stb_vorbis_get_info((stb_vorbis *)music.ctxData); // Get Ogg file info 1367 1368 // OGG bit rate defaults to 16 bit, it's enough for compressed format 1369 music.stream = LoadAudioStream(info.sample_rate, 16, info.channels); 1370 1371 // WARNING: It seems this function returns length in frames, not samples, so we multiply by channels 1372 music.frameCount = (unsigned int)stb_vorbis_stream_length_in_samples((stb_vorbis *)music.ctxData); 1373 music.looping = true; // Looping enabled by default 1374 musicLoaded = true; 1375 } 1376 else 1377 { 1378 stb_vorbis_close(ctxOgg); 1379 } 1380 } 1381 #endif 1382 #if defined(SUPPORT_FILEFORMAT_MP3) 1383 else if (IsFileExtension(fileName, ".mp3")) 1384 { 1385 drmp3 *ctxMp3 = RL_CALLOC(1, sizeof(drmp3)); 1386 int result = drmp3_init_file(ctxMp3, fileName, NULL); 1387 1388 if (result > 0) 1389 { 1390 music.ctxType = MUSIC_AUDIO_MP3; 1391 music.ctxData = ctxMp3; 1392 music.stream = LoadAudioStream(ctxMp3->sampleRate, 32, ctxMp3->channels); 1393 music.frameCount = (unsigned int)drmp3_get_pcm_frame_count(ctxMp3); 1394 music.looping = true; // Looping enabled by default 1395 musicLoaded = true; 1396 } 1397 else 1398 { 1399 RL_FREE(ctxMp3); 1400 } 1401 } 1402 #endif 1403 #if defined(SUPPORT_FILEFORMAT_QOA) 1404 else if (IsFileExtension(fileName, ".qoa")) 1405 { 1406 qoaplay_desc *ctxQoa = qoaplay_open(fileName); 1407 1408 if (ctxQoa != NULL) 1409 { 1410 music.ctxType = MUSIC_AUDIO_QOA; 1411 music.ctxData = ctxQoa; 1412 // NOTE: We are loading samples are 32bit float normalized data, so, 1413 // we configure the output audio stream to also use float 32bit 1414 music.stream = LoadAudioStream(ctxQoa->info.samplerate, 32, ctxQoa->info.channels); 1415 music.frameCount = ctxQoa->info.samples; 1416 music.looping = true; // Looping enabled by default 1417 musicLoaded = true; 1418 } 1419 else{} //No uninit required 1420 } 1421 #endif 1422 #if defined(SUPPORT_FILEFORMAT_FLAC) 1423 else if (IsFileExtension(fileName, ".flac")) 1424 { 1425 drflac *ctxFlac = drflac_open_file(fileName, NULL); 1426 1427 if (ctxFlac != NULL) 1428 { 1429 music.ctxType = MUSIC_AUDIO_FLAC; 1430 music.ctxData = ctxFlac; 1431 int sampleSize = ctxFlac->bitsPerSample; 1432 if (ctxFlac->bitsPerSample == 24) sampleSize = 16; // Forcing conversion to s16 on UpdateMusicStream() 1433 music.stream = LoadAudioStream(ctxFlac->sampleRate, sampleSize, ctxFlac->channels); 1434 music.frameCount = (unsigned int)ctxFlac->totalPCMFrameCount; 1435 music.looping = true; // Looping enabled by default 1436 musicLoaded = true; 1437 } 1438 else 1439 { 1440 drflac_free(ctxFlac, NULL); 1441 } 1442 } 1443 #endif 1444 #if defined(SUPPORT_FILEFORMAT_XM) 1445 else if (IsFileExtension(fileName, ".xm")) 1446 { 1447 jar_xm_context_t *ctxXm = NULL; 1448 int result = jar_xm_create_context_from_file(&ctxXm, AUDIO.System.device.sampleRate, fileName); 1449 1450 if (result == 0) // XM AUDIO.System.context created successfully 1451 { 1452 music.ctxType = MUSIC_MODULE_XM; 1453 music.ctxData = ctxXm; 1454 jar_xm_set_max_loop_count(ctxXm, 0); // Set infinite number of loops 1455 1456 unsigned int bits = 32; 1457 if (AUDIO_DEVICE_FORMAT == ma_format_s16) bits = 16; 1458 else if (AUDIO_DEVICE_FORMAT == ma_format_u8) bits = 8; 1459 1460 // NOTE: Only stereo is supported for XM 1461 music.stream = LoadAudioStream(AUDIO.System.device.sampleRate, bits, AUDIO_DEVICE_CHANNELS); 1462 music.frameCount = (unsigned int)jar_xm_get_remaining_samples(ctxXm); // NOTE: Always 2 channels (stereo) 1463 music.looping = true; // Looping enabled by default 1464 jar_xm_reset(ctxXm); // Make sure we start at the beginning of the song 1465 musicLoaded = true; 1466 } 1467 else 1468 { 1469 jar_xm_free_context(ctxXm); 1470 } 1471 } 1472 #endif 1473 #if defined(SUPPORT_FILEFORMAT_MOD) 1474 else if (IsFileExtension(fileName, ".mod")) 1475 { 1476 jar_mod_context_t *ctxMod = RL_CALLOC(1, sizeof(jar_mod_context_t)); 1477 jar_mod_init(ctxMod); 1478 int result = jar_mod_load_file(ctxMod, fileName); 1479 1480 if (result > 0) 1481 { 1482 music.ctxType = MUSIC_MODULE_MOD; 1483 music.ctxData = ctxMod; 1484 // NOTE: Only stereo is supported for MOD 1485 music.stream = LoadAudioStream(AUDIO.System.device.sampleRate, 16, AUDIO_DEVICE_CHANNELS); 1486 music.frameCount = (unsigned int)jar_mod_max_samples(ctxMod); // NOTE: Always 2 channels (stereo) 1487 music.looping = true; // Looping enabled by default 1488 musicLoaded = true; 1489 } 1490 else 1491 { 1492 jar_mod_unload(ctxMod); 1493 RL_FREE(ctxMod); 1494 } 1495 } 1496 #endif 1497 else TRACELOG(LOG_WARNING, "STREAM: [%s] File format not supported", fileName); 1498 1499 if (!musicLoaded) 1500 { 1501 TRACELOG(LOG_WARNING, "FILEIO: [%s] Music file could not be opened", fileName); 1502 } 1503 else 1504 { 1505 // Show some music stream info 1506 TRACELOG(LOG_INFO, "FILEIO: [%s] Music file loaded successfully", fileName); 1507 TRACELOG(LOG_INFO, " > Sample rate: %i Hz", music.stream.sampleRate); 1508 TRACELOG(LOG_INFO, " > Sample size: %i bits", music.stream.sampleSize); 1509 TRACELOG(LOG_INFO, " > Channels: %i (%s)", music.stream.channels, (music.stream.channels == 1)? "Mono" : (music.stream.channels == 2)? "Stereo" : "Multi"); 1510 TRACELOG(LOG_INFO, " > Total frames: %i", music.frameCount); 1511 } 1512 1513 return music; 1514 } 1515 1516 // Load music stream from memory buffer, fileType refers to extension: i.e. ".wav" 1517 // WARNING: File extension must be provided in lower-case 1518 Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, int dataSize) 1519 { 1520 Music music = { 0 }; 1521 bool musicLoaded = false; 1522 1523 if (false) { } 1524 #if defined(SUPPORT_FILEFORMAT_WAV) 1525 else if ((strcmp(fileType, ".wav") == 0) || (strcmp(fileType, ".WAV") == 0)) 1526 { 1527 drwav *ctxWav = RL_CALLOC(1, sizeof(drwav)); 1528 1529 bool success = drwav_init_memory(ctxWav, (const void *)data, dataSize, NULL); 1530 1531 if (success) 1532 { 1533 music.ctxType = MUSIC_AUDIO_WAV; 1534 music.ctxData = ctxWav; 1535 int sampleSize = ctxWav->bitsPerSample; 1536 if (ctxWav->bitsPerSample == 24) sampleSize = 16; // Forcing conversion to s16 on UpdateMusicStream() 1537 1538 music.stream = LoadAudioStream(ctxWav->sampleRate, sampleSize, ctxWav->channels); 1539 music.frameCount = (unsigned int)ctxWav->totalPCMFrameCount; 1540 music.looping = true; // Looping enabled by default 1541 musicLoaded = true; 1542 } 1543 else { 1544 drwav_uninit(ctxWav); 1545 RL_FREE(ctxWav); 1546 } 1547 } 1548 #endif 1549 #if defined(SUPPORT_FILEFORMAT_OGG) 1550 else if ((strcmp(fileType, ".ogg") == 0) || (strcmp(fileType, ".OGG") == 0)) 1551 { 1552 // Open ogg audio stream 1553 stb_vorbis* ctxOgg = stb_vorbis_open_memory((const unsigned char *)data, dataSize, NULL, NULL); 1554 1555 if (ctxOgg != NULL) 1556 { 1557 music.ctxType = MUSIC_AUDIO_OGG; 1558 music.ctxData = ctxOgg; 1559 stb_vorbis_info info = stb_vorbis_get_info((stb_vorbis *)music.ctxData); // Get Ogg file info 1560 1561 // OGG bit rate defaults to 16 bit, it's enough for compressed format 1562 music.stream = LoadAudioStream(info.sample_rate, 16, info.channels); 1563 1564 // WARNING: It seems this function returns length in frames, not samples, so we multiply by channels 1565 music.frameCount = (unsigned int)stb_vorbis_stream_length_in_samples((stb_vorbis *)music.ctxData); 1566 music.looping = true; // Looping enabled by default 1567 musicLoaded = true; 1568 } 1569 else 1570 { 1571 stb_vorbis_close(ctxOgg); 1572 } 1573 } 1574 #endif 1575 #if defined(SUPPORT_FILEFORMAT_MP3) 1576 else if ((strcmp(fileType, ".mp3") == 0) || (strcmp(fileType, ".MP3") == 0)) 1577 { 1578 drmp3 *ctxMp3 = RL_CALLOC(1, sizeof(drmp3)); 1579 int success = drmp3_init_memory(ctxMp3, (const void*)data, dataSize, NULL); 1580 1581 if (success) 1582 { 1583 music.ctxType = MUSIC_AUDIO_MP3; 1584 music.ctxData = ctxMp3; 1585 music.stream = LoadAudioStream(ctxMp3->sampleRate, 32, ctxMp3->channels); 1586 music.frameCount = (unsigned int)drmp3_get_pcm_frame_count(ctxMp3); 1587 music.looping = true; // Looping enabled by default 1588 musicLoaded = true; 1589 } 1590 else 1591 { 1592 drmp3_uninit(ctxMp3); 1593 RL_FREE(ctxMp3); 1594 } 1595 } 1596 #endif 1597 #if defined(SUPPORT_FILEFORMAT_QOA) 1598 else if ((strcmp(fileType, ".qoa") == 0) || (strcmp(fileType, ".QOA") == 0)) 1599 { 1600 qoaplay_desc *ctxQoa = NULL; 1601 if ((data != NULL) && (dataSize > 0)) 1602 { 1603 ctxQoa = qoaplay_open_memory(data, dataSize); 1604 } 1605 1606 if (ctxQoa != NULL) 1607 { 1608 music.ctxType = MUSIC_AUDIO_QOA; 1609 music.ctxData = ctxQoa; 1610 // NOTE: We are loading samples are 32bit float normalized data, so, 1611 // we configure the output audio stream to also use float 32bit 1612 music.stream = LoadAudioStream(ctxQoa->info.samplerate, 32, ctxQoa->info.channels); 1613 music.frameCount = ctxQoa->info.samples; 1614 music.looping = true; // Looping enabled by default 1615 musicLoaded = true; 1616 } 1617 else{} //No uninit required 1618 } 1619 #endif 1620 #if defined(SUPPORT_FILEFORMAT_FLAC) 1621 else if ((strcmp(fileType, ".flac") == 0) || (strcmp(fileType, ".FLAC") == 0)) 1622 { 1623 drflac *ctxFlac = drflac_open_memory((const void*)data, dataSize, NULL); 1624 1625 if (ctxFlac != NULL) 1626 { 1627 music.ctxType = MUSIC_AUDIO_FLAC; 1628 music.ctxData = ctxFlac; 1629 int sampleSize = ctxFlac->bitsPerSample; 1630 if (ctxFlac->bitsPerSample == 24) sampleSize = 16; // Forcing conversion to s16 on UpdateMusicStream() 1631 music.stream = LoadAudioStream(ctxFlac->sampleRate, sampleSize, ctxFlac->channels); 1632 music.frameCount = (unsigned int)ctxFlac->totalPCMFrameCount; 1633 music.looping = true; // Looping enabled by default 1634 musicLoaded = true; 1635 } 1636 else 1637 { 1638 drflac_free(ctxFlac, NULL); 1639 } 1640 } 1641 #endif 1642 #if defined(SUPPORT_FILEFORMAT_XM) 1643 else if ((strcmp(fileType, ".xm") == 0) || (strcmp(fileType, ".XM") == 0)) 1644 { 1645 jar_xm_context_t *ctxXm = NULL; 1646 int result = jar_xm_create_context_safe(&ctxXm, (const char *)data, dataSize, AUDIO.System.device.sampleRate); 1647 if (result == 0) // XM AUDIO.System.context created successfully 1648 { 1649 music.ctxType = MUSIC_MODULE_XM; 1650 music.ctxData = ctxXm; 1651 jar_xm_set_max_loop_count(ctxXm, 0); // Set infinite number of loops 1652 1653 unsigned int bits = 32; 1654 if (AUDIO_DEVICE_FORMAT == ma_format_s16) bits = 16; 1655 else if (AUDIO_DEVICE_FORMAT == ma_format_u8) bits = 8; 1656 1657 // NOTE: Only stereo is supported for XM 1658 music.stream = LoadAudioStream(AUDIO.System.device.sampleRate, bits, 2); 1659 music.frameCount = (unsigned int)jar_xm_get_remaining_samples(ctxXm); // NOTE: Always 2 channels (stereo) 1660 music.looping = true; // Looping enabled by default 1661 jar_xm_reset(ctxXm); // Make sure we start at the beginning of the song 1662 1663 musicLoaded = true; 1664 } 1665 else 1666 { 1667 jar_xm_free_context(ctxXm); 1668 } 1669 } 1670 #endif 1671 #if defined(SUPPORT_FILEFORMAT_MOD) 1672 else if ((strcmp(fileType, ".mod") == 0) || (strcmp(fileType, ".MOD") == 0)) 1673 { 1674 jar_mod_context_t *ctxMod = (jar_mod_context_t *)RL_MALLOC(sizeof(jar_mod_context_t)); 1675 int result = 0; 1676 1677 jar_mod_init(ctxMod); 1678 1679 // Copy data to allocated memory for default UnloadMusicStream 1680 unsigned char *newData = (unsigned char *)RL_MALLOC(dataSize); 1681 int it = dataSize/sizeof(unsigned char); 1682 for (int i = 0; i < it; i++) newData[i] = data[i]; 1683 1684 // Memory loaded version for jar_mod_load_file() 1685 if (dataSize && (dataSize < 32*1024*1024)) 1686 { 1687 ctxMod->modfilesize = dataSize; 1688 ctxMod->modfile = newData; 1689 if (jar_mod_load(ctxMod, (void *)ctxMod->modfile, dataSize)) result = dataSize; 1690 } 1691 1692 if (result > 0) 1693 { 1694 music.ctxType = MUSIC_MODULE_MOD; 1695 music.ctxData = ctxMod; 1696 1697 // NOTE: Only stereo is supported for MOD 1698 music.stream = LoadAudioStream(AUDIO.System.device.sampleRate, 16, 2); 1699 music.frameCount = (unsigned int)jar_mod_max_samples(ctxMod); // NOTE: Always 2 channels (stereo) 1700 music.looping = true; // Looping enabled by default 1701 musicLoaded = true; 1702 } 1703 else 1704 { 1705 jar_mod_unload(ctxMod); 1706 RL_FREE(ctxMod); 1707 } 1708 } 1709 #endif 1710 else TRACELOG(LOG_WARNING, "STREAM: Data format not supported"); 1711 1712 if (!musicLoaded) 1713 { 1714 TRACELOG(LOG_WARNING, "FILEIO: Music data could not be loaded"); 1715 } 1716 else 1717 { 1718 // Show some music stream info 1719 TRACELOG(LOG_INFO, "FILEIO: Music data loaded successfully"); 1720 TRACELOG(LOG_INFO, " > Sample rate: %i Hz", music.stream.sampleRate); 1721 TRACELOG(LOG_INFO, " > Sample size: %i bits", music.stream.sampleSize); 1722 TRACELOG(LOG_INFO, " > Channels: %i (%s)", music.stream.channels, (music.stream.channels == 1)? "Mono" : (music.stream.channels == 2)? "Stereo" : "Multi"); 1723 TRACELOG(LOG_INFO, " > Total frames: %i", music.frameCount); 1724 } 1725 1726 return music; 1727 } 1728 1729 // Checks if a music stream is valid (context and buffers initialized) 1730 bool IsMusicValid(Music music) 1731 { 1732 return ((music.ctxData != NULL) && // Validate context loaded 1733 (music.frameCount > 0) && // Validate audio frame count 1734 (music.stream.sampleRate > 0) && // Validate sample rate is supported 1735 (music.stream.sampleSize > 0) && // Validate sample size is supported 1736 (music.stream.channels > 0)); // Validate number of channels supported 1737 } 1738 1739 // Unload music stream 1740 void UnloadMusicStream(Music music) 1741 { 1742 UnloadAudioStream(music.stream); 1743 1744 if (music.ctxData != NULL) 1745 { 1746 if (false) { } 1747 #if defined(SUPPORT_FILEFORMAT_WAV) 1748 else if (music.ctxType == MUSIC_AUDIO_WAV) drwav_uninit((drwav *)music.ctxData); 1749 #endif 1750 #if defined(SUPPORT_FILEFORMAT_OGG) 1751 else if (music.ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close((stb_vorbis *)music.ctxData); 1752 #endif 1753 #if defined(SUPPORT_FILEFORMAT_MP3) 1754 else if (music.ctxType == MUSIC_AUDIO_MP3) { drmp3_uninit((drmp3 *)music.ctxData); RL_FREE(music.ctxData); } 1755 #endif 1756 #if defined(SUPPORT_FILEFORMAT_QOA) 1757 else if (music.ctxType == MUSIC_AUDIO_QOA) qoaplay_close((qoaplay_desc *)music.ctxData); 1758 #endif 1759 #if defined(SUPPORT_FILEFORMAT_FLAC) 1760 else if (music.ctxType == MUSIC_AUDIO_FLAC) drflac_free((drflac *)music.ctxData, NULL); 1761 #endif 1762 #if defined(SUPPORT_FILEFORMAT_XM) 1763 else if (music.ctxType == MUSIC_MODULE_XM) jar_xm_free_context((jar_xm_context_t *)music.ctxData); 1764 #endif 1765 #if defined(SUPPORT_FILEFORMAT_MOD) 1766 else if (music.ctxType == MUSIC_MODULE_MOD) { jar_mod_unload((jar_mod_context_t *)music.ctxData); RL_FREE(music.ctxData); } 1767 #endif 1768 } 1769 } 1770 1771 // Start music playing (open stream) from beginning 1772 void PlayMusicStream(Music music) 1773 { 1774 PlayAudioStream(music.stream); 1775 } 1776 1777 // Pause music playing 1778 void PauseMusicStream(Music music) 1779 { 1780 PauseAudioStream(music.stream); 1781 } 1782 1783 // Resume music playing 1784 void ResumeMusicStream(Music music) 1785 { 1786 ResumeAudioStream(music.stream); 1787 } 1788 1789 // Stop music playing (close stream) 1790 void StopMusicStream(Music music) 1791 { 1792 StopAudioStream(music.stream); 1793 1794 switch (music.ctxType) 1795 { 1796 #if defined(SUPPORT_FILEFORMAT_WAV) 1797 case MUSIC_AUDIO_WAV: drwav_seek_to_first_pcm_frame((drwav *)music.ctxData); break; 1798 #endif 1799 #if defined(SUPPORT_FILEFORMAT_OGG) 1800 case MUSIC_AUDIO_OGG: stb_vorbis_seek_start((stb_vorbis *)music.ctxData); break; 1801 #endif 1802 #if defined(SUPPORT_FILEFORMAT_MP3) 1803 case MUSIC_AUDIO_MP3: drmp3_seek_to_start_of_stream((drmp3 *)music.ctxData); break; 1804 #endif 1805 #if defined(SUPPORT_FILEFORMAT_QOA) 1806 case MUSIC_AUDIO_QOA: qoaplay_rewind((qoaplay_desc *)music.ctxData); break; 1807 #endif 1808 #if defined(SUPPORT_FILEFORMAT_FLAC) 1809 case MUSIC_AUDIO_FLAC: drflac__seek_to_first_frame((drflac *)music.ctxData); break; 1810 #endif 1811 #if defined(SUPPORT_FILEFORMAT_XM) 1812 case MUSIC_MODULE_XM: jar_xm_reset((jar_xm_context_t *)music.ctxData); break; 1813 #endif 1814 #if defined(SUPPORT_FILEFORMAT_MOD) 1815 case MUSIC_MODULE_MOD: jar_mod_seek_start((jar_mod_context_t *)music.ctxData); break; 1816 #endif 1817 default: break; 1818 } 1819 } 1820 1821 // Seek music to a certain position (in seconds) 1822 void SeekMusicStream(Music music, float position) 1823 { 1824 // Seeking is not supported in module formats 1825 if ((music.ctxType == MUSIC_MODULE_XM) || (music.ctxType == MUSIC_MODULE_MOD)) return; 1826 1827 unsigned int positionInFrames = (unsigned int)(position*music.stream.sampleRate); 1828 1829 switch (music.ctxType) 1830 { 1831 #if defined(SUPPORT_FILEFORMAT_WAV) 1832 case MUSIC_AUDIO_WAV: drwav_seek_to_pcm_frame((drwav *)music.ctxData, positionInFrames); break; 1833 #endif 1834 #if defined(SUPPORT_FILEFORMAT_OGG) 1835 case MUSIC_AUDIO_OGG: stb_vorbis_seek_frame((stb_vorbis *)music.ctxData, positionInFrames); break; 1836 #endif 1837 #if defined(SUPPORT_FILEFORMAT_MP3) 1838 case MUSIC_AUDIO_MP3: drmp3_seek_to_pcm_frame((drmp3 *)music.ctxData, positionInFrames); break; 1839 #endif 1840 #if defined(SUPPORT_FILEFORMAT_QOA) 1841 case MUSIC_AUDIO_QOA: 1842 { 1843 int qoaFrame = positionInFrames/QOA_FRAME_LEN; 1844 qoaplay_seek_frame((qoaplay_desc *)music.ctxData, qoaFrame); // Seeks to QOA frame, not PCM frame 1845 1846 // We need to compute QOA frame number and update positionInFrames 1847 positionInFrames = ((qoaplay_desc *)music.ctxData)->sample_position; 1848 } break; 1849 #endif 1850 #if defined(SUPPORT_FILEFORMAT_FLAC) 1851 case MUSIC_AUDIO_FLAC: drflac_seek_to_pcm_frame((drflac *)music.ctxData, positionInFrames); break; 1852 #endif 1853 default: break; 1854 } 1855 1856 ma_mutex_lock(&AUDIO.System.lock); 1857 music.stream.buffer->framesProcessed = positionInFrames; 1858 ma_mutex_unlock(&AUDIO.System.lock); 1859 } 1860 1861 // Update (re-fill) music buffers if data already processed 1862 void UpdateMusicStream(Music music) 1863 { 1864 if (music.stream.buffer == NULL) return; 1865 1866 ma_mutex_lock(&AUDIO.System.lock); 1867 1868 unsigned int subBufferSizeInFrames = music.stream.buffer->sizeInFrames/2; 1869 1870 // On first call of this function we lazily pre-allocated a temp buffer to read audio files/memory data in 1871 int frameSize = music.stream.channels*music.stream.sampleSize/8; 1872 unsigned int pcmSize = subBufferSizeInFrames*frameSize; 1873 1874 if (AUDIO.System.pcmBufferSize < pcmSize) 1875 { 1876 RL_FREE(AUDIO.System.pcmBuffer); 1877 AUDIO.System.pcmBuffer = RL_CALLOC(1, pcmSize); 1878 AUDIO.System.pcmBufferSize = pcmSize; 1879 } 1880 1881 // Check both sub-buffers to check if they require refilling 1882 for (int i = 0; i < 2; i++) 1883 { 1884 if (!music.stream.buffer->isSubBufferProcessed[i]) continue; // No refilling required, move to next sub-buffer 1885 1886 unsigned int framesLeft = music.frameCount - music.stream.buffer->framesProcessed; // Frames left to be processed 1887 unsigned int framesToStream = 0; // Total frames to be streamed 1888 1889 if ((framesLeft >= subBufferSizeInFrames) || music.looping) framesToStream = subBufferSizeInFrames; 1890 else framesToStream = framesLeft; 1891 1892 int frameCountStillNeeded = framesToStream; 1893 int frameCountReadTotal = 0; 1894 1895 switch (music.ctxType) 1896 { 1897 #if defined(SUPPORT_FILEFORMAT_WAV) 1898 case MUSIC_AUDIO_WAV: 1899 { 1900 if (music.stream.sampleSize == 16) 1901 { 1902 while (true) 1903 { 1904 int frameCountRead = (int)drwav_read_pcm_frames_s16((drwav *)music.ctxData, frameCountStillNeeded, (short *)((char *)AUDIO.System.pcmBuffer + frameCountReadTotal*frameSize)); 1905 frameCountReadTotal += frameCountRead; 1906 frameCountStillNeeded -= frameCountRead; 1907 if (frameCountStillNeeded == 0) break; 1908 else drwav_seek_to_first_pcm_frame((drwav *)music.ctxData); 1909 } 1910 } 1911 else if (music.stream.sampleSize == 32) 1912 { 1913 while (true) 1914 { 1915 int frameCountRead = (int)drwav_read_pcm_frames_f32((drwav *)music.ctxData, frameCountStillNeeded, (float *)((char *)AUDIO.System.pcmBuffer + frameCountReadTotal*frameSize)); 1916 frameCountReadTotal += frameCountRead; 1917 frameCountStillNeeded -= frameCountRead; 1918 if (frameCountStillNeeded == 0) break; 1919 else drwav_seek_to_first_pcm_frame((drwav *)music.ctxData); 1920 } 1921 } 1922 } break; 1923 #endif 1924 #if defined(SUPPORT_FILEFORMAT_OGG) 1925 case MUSIC_AUDIO_OGG: 1926 { 1927 while (true) 1928 { 1929 int frameCountRead = stb_vorbis_get_samples_short_interleaved((stb_vorbis *)music.ctxData, music.stream.channels, (short *)((char *)AUDIO.System.pcmBuffer + frameCountReadTotal*frameSize), frameCountStillNeeded*music.stream.channels); 1930 frameCountReadTotal += frameCountRead; 1931 frameCountStillNeeded -= frameCountRead; 1932 if (frameCountStillNeeded == 0) break; 1933 else stb_vorbis_seek_start((stb_vorbis *)music.ctxData); 1934 } 1935 } break; 1936 #endif 1937 #if defined(SUPPORT_FILEFORMAT_MP3) 1938 case MUSIC_AUDIO_MP3: 1939 { 1940 while (true) 1941 { 1942 int frameCountRead = (int)drmp3_read_pcm_frames_f32((drmp3 *)music.ctxData, frameCountStillNeeded, (float *)((char *)AUDIO.System.pcmBuffer + frameCountReadTotal*frameSize)); 1943 frameCountReadTotal += frameCountRead; 1944 frameCountStillNeeded -= frameCountRead; 1945 if (frameCountStillNeeded == 0) break; 1946 else drmp3_seek_to_start_of_stream((drmp3 *)music.ctxData); 1947 } 1948 } break; 1949 #endif 1950 #if defined(SUPPORT_FILEFORMAT_QOA) 1951 case MUSIC_AUDIO_QOA: 1952 { 1953 unsigned int frameCountRead = qoaplay_decode((qoaplay_desc *)music.ctxData, (float *)AUDIO.System.pcmBuffer, framesToStream); 1954 frameCountReadTotal += frameCountRead; 1955 /* 1956 while (true) 1957 { 1958 int frameCountRead = (int)qoaplay_decode((qoaplay_desc *)music.ctxData, (float *)((char *)AUDIO.System.pcmBuffer + frameCountReadTotal*frameSize), frameCountStillNeeded); 1959 frameCountReadTotal += frameCountRead; 1960 frameCountStillNeeded -= frameCountRead; 1961 if (frameCountStillNeeded == 0) break; 1962 else qoaplay_rewind((qoaplay_desc *)music.ctxData); 1963 } 1964 */ 1965 } break; 1966 #endif 1967 #if defined(SUPPORT_FILEFORMAT_FLAC) 1968 case MUSIC_AUDIO_FLAC: 1969 { 1970 while (true) 1971 { 1972 int frameCountRead = (int)drflac_read_pcm_frames_s16((drflac *)music.ctxData, frameCountStillNeeded, (short *)((char *)AUDIO.System.pcmBuffer + frameCountReadTotal*frameSize)); 1973 frameCountReadTotal += frameCountRead; 1974 frameCountStillNeeded -= frameCountRead; 1975 if (frameCountStillNeeded == 0) break; 1976 else drflac__seek_to_first_frame((drflac *)music.ctxData); 1977 } 1978 } break; 1979 #endif 1980 #if defined(SUPPORT_FILEFORMAT_XM) 1981 case MUSIC_MODULE_XM: 1982 { 1983 // NOTE: Internally we consider 2 channels generation, so sampleCount/2 1984 if (AUDIO_DEVICE_FORMAT == ma_format_f32) jar_xm_generate_samples((jar_xm_context_t *)music.ctxData, (float *)AUDIO.System.pcmBuffer, framesToStream); 1985 else if (AUDIO_DEVICE_FORMAT == ma_format_s16) jar_xm_generate_samples_16bit((jar_xm_context_t *)music.ctxData, (short *)AUDIO.System.pcmBuffer, framesToStream); 1986 else if (AUDIO_DEVICE_FORMAT == ma_format_u8) jar_xm_generate_samples_8bit((jar_xm_context_t *)music.ctxData, (char *)AUDIO.System.pcmBuffer, framesToStream); 1987 //jar_xm_reset((jar_xm_context_t *)music.ctxData); 1988 1989 } break; 1990 #endif 1991 #if defined(SUPPORT_FILEFORMAT_MOD) 1992 case MUSIC_MODULE_MOD: 1993 { 1994 // NOTE: 3rd parameter (nbsample) specify the number of stereo 16bits samples you want, so sampleCount/2 1995 jar_mod_fillbuffer((jar_mod_context_t *)music.ctxData, (short *)AUDIO.System.pcmBuffer, framesToStream, 0); 1996 //jar_mod_seek_start((jar_mod_context_t *)music.ctxData); 1997 1998 } break; 1999 #endif 2000 default: break; 2001 } 2002 2003 UpdateAudioStreamInLockedState(music.stream, AUDIO.System.pcmBuffer, framesToStream); 2004 2005 music.stream.buffer->framesProcessed = music.stream.buffer->framesProcessed%music.frameCount; 2006 2007 if (framesLeft <= subBufferSizeInFrames) 2008 { 2009 if (!music.looping) 2010 { 2011 ma_mutex_unlock(&AUDIO.System.lock); 2012 // Streaming is ending, we filled latest frames from input 2013 StopMusicStream(music); 2014 return; 2015 } 2016 } 2017 } 2018 2019 ma_mutex_unlock(&AUDIO.System.lock); 2020 } 2021 2022 // Check if any music is playing 2023 bool IsMusicStreamPlaying(Music music) 2024 { 2025 return IsAudioStreamPlaying(music.stream); 2026 } 2027 2028 // Set volume for music 2029 void SetMusicVolume(Music music, float volume) 2030 { 2031 SetAudioStreamVolume(music.stream, volume); 2032 } 2033 2034 // Set pitch for music 2035 void SetMusicPitch(Music music, float pitch) 2036 { 2037 SetAudioBufferPitch(music.stream.buffer, pitch); 2038 } 2039 2040 // Set pan for a music 2041 void SetMusicPan(Music music, float pan) 2042 { 2043 SetAudioBufferPan(music.stream.buffer, pan); 2044 } 2045 2046 // Get music time length (in seconds) 2047 float GetMusicTimeLength(Music music) 2048 { 2049 float totalSeconds = 0.0f; 2050 2051 totalSeconds = (float)music.frameCount/music.stream.sampleRate; 2052 2053 return totalSeconds; 2054 } 2055 2056 // Get current music time played (in seconds) 2057 float GetMusicTimePlayed(Music music) 2058 { 2059 float secondsPlayed = 0.0f; 2060 if (music.stream.buffer != NULL) 2061 { 2062 #if defined(SUPPORT_FILEFORMAT_XM) 2063 if (music.ctxType == MUSIC_MODULE_XM) 2064 { 2065 uint64_t framesPlayed = 0; 2066 2067 jar_xm_get_position(music.ctxData, NULL, NULL, NULL, &framesPlayed); 2068 secondsPlayed = (float)framesPlayed/music.stream.sampleRate; 2069 } 2070 else 2071 #endif 2072 { 2073 ma_mutex_lock(&AUDIO.System.lock); 2074 //ma_uint32 frameSizeInBytes = ma_get_bytes_per_sample(music.stream.buffer->dsp.formatConverterIn.config.formatIn)*music.stream.buffer->dsp.formatConverterIn.config.channels; 2075 int framesProcessed = (int)music.stream.buffer->framesProcessed; 2076 int subBufferSize = (int)music.stream.buffer->sizeInFrames/2; 2077 int framesInFirstBuffer = music.stream.buffer->isSubBufferProcessed[0]? 0 : subBufferSize; 2078 int framesInSecondBuffer = music.stream.buffer->isSubBufferProcessed[1]? 0 : subBufferSize; 2079 int framesSentToMix = music.stream.buffer->frameCursorPos%subBufferSize; 2080 int framesPlayed = (framesProcessed - framesInFirstBuffer - framesInSecondBuffer + framesSentToMix)%(int)music.frameCount; 2081 if (framesPlayed < 0) framesPlayed += music.frameCount; 2082 secondsPlayed = (float)framesPlayed/music.stream.sampleRate; 2083 ma_mutex_unlock(&AUDIO.System.lock); 2084 } 2085 } 2086 2087 return secondsPlayed; 2088 } 2089 2090 // Load audio stream (to stream audio pcm data) 2091 AudioStream LoadAudioStream(unsigned int sampleRate, unsigned int sampleSize, unsigned int channels) 2092 { 2093 AudioStream stream = { 0 }; 2094 2095 stream.sampleRate = sampleRate; 2096 stream.sampleSize = sampleSize; 2097 stream.channels = channels; 2098 2099 ma_format formatIn = ((stream.sampleSize == 8)? ma_format_u8 : ((stream.sampleSize == 16)? ma_format_s16 : ma_format_f32)); 2100 2101 // The size of a streaming buffer must be at least double the size of a period 2102 unsigned int periodSize = AUDIO.System.device.playback.internalPeriodSizeInFrames; 2103 2104 // If the buffer is not set, compute one that would give us a buffer good enough for a decent frame rate 2105 unsigned int subBufferSize = (AUDIO.Buffer.defaultSize == 0)? AUDIO.System.device.sampleRate/30 : AUDIO.Buffer.defaultSize; 2106 2107 if (subBufferSize < periodSize) subBufferSize = periodSize; 2108 2109 // Create a double audio buffer of defined size 2110 stream.buffer = LoadAudioBuffer(formatIn, stream.channels, stream.sampleRate, subBufferSize*2, AUDIO_BUFFER_USAGE_STREAM); 2111 2112 if (stream.buffer != NULL) 2113 { 2114 stream.buffer->looping = true; // Always loop for streaming buffers 2115 TRACELOG(LOG_INFO, "STREAM: Initialized successfully (%i Hz, %i bit, %s)", stream.sampleRate, stream.sampleSize, (stream.channels == 1)? "Mono" : "Stereo"); 2116 } 2117 else TRACELOG(LOG_WARNING, "STREAM: Failed to load audio buffer, stream could not be created"); 2118 2119 return stream; 2120 } 2121 2122 // Checks if an audio stream is valid (buffers initialized) 2123 bool IsAudioStreamValid(AudioStream stream) 2124 { 2125 return ((stream.buffer != NULL) && // Validate stream buffer 2126 (stream.sampleRate > 0) && // Validate sample rate is supported 2127 (stream.sampleSize > 0) && // Validate sample size is supported 2128 (stream.channels > 0)); // Validate number of channels supported 2129 } 2130 2131 // Unload audio stream and free memory 2132 void UnloadAudioStream(AudioStream stream) 2133 { 2134 UnloadAudioBuffer(stream.buffer); 2135 2136 TRACELOG(LOG_INFO, "STREAM: Unloaded audio stream data from RAM"); 2137 } 2138 2139 // Update audio stream buffers with data 2140 // NOTE 1: Only updates one buffer of the stream source: dequeue -> update -> queue 2141 // NOTE 2: To dequeue a buffer it needs to be processed: IsAudioStreamProcessed() 2142 void UpdateAudioStream(AudioStream stream, const void *data, int frameCount) 2143 { 2144 ma_mutex_lock(&AUDIO.System.lock); 2145 UpdateAudioStreamInLockedState(stream, data, frameCount); 2146 ma_mutex_unlock(&AUDIO.System.lock); 2147 } 2148 2149 // Check if any audio stream buffers requires refill 2150 bool IsAudioStreamProcessed(AudioStream stream) 2151 { 2152 if (stream.buffer == NULL) return false; 2153 2154 bool result = false; 2155 ma_mutex_lock(&AUDIO.System.lock); 2156 result = stream.buffer->isSubBufferProcessed[0] || stream.buffer->isSubBufferProcessed[1]; 2157 ma_mutex_unlock(&AUDIO.System.lock); 2158 return result; 2159 } 2160 2161 // Play audio stream 2162 void PlayAudioStream(AudioStream stream) 2163 { 2164 PlayAudioBuffer(stream.buffer); 2165 } 2166 2167 // Play audio stream 2168 void PauseAudioStream(AudioStream stream) 2169 { 2170 PauseAudioBuffer(stream.buffer); 2171 } 2172 2173 // Resume audio stream playing 2174 void ResumeAudioStream(AudioStream stream) 2175 { 2176 ResumeAudioBuffer(stream.buffer); 2177 } 2178 2179 // Check if audio stream is playing 2180 bool IsAudioStreamPlaying(AudioStream stream) 2181 { 2182 return IsAudioBufferPlaying(stream.buffer); 2183 } 2184 2185 // Stop audio stream 2186 void StopAudioStream(AudioStream stream) 2187 { 2188 StopAudioBuffer(stream.buffer); 2189 } 2190 2191 // Set volume for audio stream (1.0 is max level) 2192 void SetAudioStreamVolume(AudioStream stream, float volume) 2193 { 2194 SetAudioBufferVolume(stream.buffer, volume); 2195 } 2196 2197 // Set pitch for audio stream (1.0 is base level) 2198 void SetAudioStreamPitch(AudioStream stream, float pitch) 2199 { 2200 SetAudioBufferPitch(stream.buffer, pitch); 2201 } 2202 2203 // Set pan for audio stream 2204 void SetAudioStreamPan(AudioStream stream, float pan) 2205 { 2206 SetAudioBufferPan(stream.buffer, pan); 2207 } 2208 2209 // Default size for new audio streams 2210 void SetAudioStreamBufferSizeDefault(int size) 2211 { 2212 AUDIO.Buffer.defaultSize = size; 2213 } 2214 2215 // Audio thread callback to request new data 2216 void SetAudioStreamCallback(AudioStream stream, AudioCallback callback) 2217 { 2218 if (stream.buffer != NULL) 2219 { 2220 ma_mutex_lock(&AUDIO.System.lock); 2221 stream.buffer->callback = callback; 2222 ma_mutex_unlock(&AUDIO.System.lock); 2223 } 2224 } 2225 2226 // Add processor to audio stream. Contrary to buffers, the order of processors is important 2227 // The new processor must be added at the end. As there aren't supposed to be a lot of processors attached to 2228 // a given stream, we iterate through the list to find the end. That way we don't need a pointer to the last element 2229 void AttachAudioStreamProcessor(AudioStream stream, AudioCallback process) 2230 { 2231 ma_mutex_lock(&AUDIO.System.lock); 2232 2233 rAudioProcessor *processor = (rAudioProcessor *)RL_CALLOC(1, sizeof(rAudioProcessor)); 2234 processor->process = process; 2235 2236 rAudioProcessor *last = stream.buffer->processor; 2237 2238 while (last && last->next) 2239 { 2240 last = last->next; 2241 } 2242 if (last) 2243 { 2244 processor->prev = last; 2245 last->next = processor; 2246 } 2247 else stream.buffer->processor = processor; 2248 2249 ma_mutex_unlock(&AUDIO.System.lock); 2250 } 2251 2252 // Remove processor from audio stream 2253 void DetachAudioStreamProcessor(AudioStream stream, AudioCallback process) 2254 { 2255 ma_mutex_lock(&AUDIO.System.lock); 2256 2257 rAudioProcessor *processor = stream.buffer->processor; 2258 2259 while (processor) 2260 { 2261 rAudioProcessor *next = processor->next; 2262 rAudioProcessor *prev = processor->prev; 2263 2264 if (processor->process == process) 2265 { 2266 if (stream.buffer->processor == processor) stream.buffer->processor = next; 2267 if (prev) prev->next = next; 2268 if (next) next->prev = prev; 2269 2270 RL_FREE(processor); 2271 } 2272 2273 processor = next; 2274 } 2275 2276 ma_mutex_unlock(&AUDIO.System.lock); 2277 } 2278 2279 // Add processor to audio pipeline. Order of processors is important 2280 // Works the same way as {Attach,Detach}AudioStreamProcessor() functions, except 2281 // these two work on the already mixed output just before sending it to the sound hardware 2282 void AttachAudioMixedProcessor(AudioCallback process) 2283 { 2284 ma_mutex_lock(&AUDIO.System.lock); 2285 2286 rAudioProcessor *processor = (rAudioProcessor *)RL_CALLOC(1, sizeof(rAudioProcessor)); 2287 processor->process = process; 2288 2289 rAudioProcessor *last = AUDIO.mixedProcessor; 2290 2291 while (last && last->next) 2292 { 2293 last = last->next; 2294 } 2295 if (last) 2296 { 2297 processor->prev = last; 2298 last->next = processor; 2299 } 2300 else AUDIO.mixedProcessor = processor; 2301 2302 ma_mutex_unlock(&AUDIO.System.lock); 2303 } 2304 2305 // Remove processor from audio pipeline 2306 void DetachAudioMixedProcessor(AudioCallback process) 2307 { 2308 ma_mutex_lock(&AUDIO.System.lock); 2309 2310 rAudioProcessor *processor = AUDIO.mixedProcessor; 2311 2312 while (processor) 2313 { 2314 rAudioProcessor *next = processor->next; 2315 rAudioProcessor *prev = processor->prev; 2316 2317 if (processor->process == process) 2318 { 2319 if (AUDIO.mixedProcessor == processor) AUDIO.mixedProcessor = next; 2320 if (prev) prev->next = next; 2321 if (next) next->prev = prev; 2322 2323 RL_FREE(processor); 2324 } 2325 2326 processor = next; 2327 } 2328 2329 ma_mutex_unlock(&AUDIO.System.lock); 2330 } 2331 2332 2333 //---------------------------------------------------------------------------------- 2334 // Module specific Functions Definition 2335 //---------------------------------------------------------------------------------- 2336 2337 // Log callback function 2338 static void OnLog(void *pUserData, ma_uint32 level, const char *pMessage) 2339 { 2340 TRACELOG(LOG_WARNING, "miniaudio: %s", pMessage); // All log messages from miniaudio are errors 2341 } 2342 2343 // Reads audio data from an AudioBuffer object in internal format 2344 static ma_uint32 ReadAudioBufferFramesInInternalFormat(AudioBuffer *audioBuffer, void *framesOut, ma_uint32 frameCount) 2345 { 2346 // Using audio buffer callback 2347 if (audioBuffer->callback) 2348 { 2349 audioBuffer->callback(framesOut, frameCount); 2350 audioBuffer->framesProcessed += frameCount; 2351 2352 return frameCount; 2353 } 2354 2355 ma_uint32 subBufferSizeInFrames = (audioBuffer->sizeInFrames > 1)? audioBuffer->sizeInFrames/2 : audioBuffer->sizeInFrames; 2356 ma_uint32 currentSubBufferIndex = audioBuffer->frameCursorPos/subBufferSizeInFrames; 2357 2358 if (currentSubBufferIndex > 1) return 0; 2359 2360 // Another thread can update the processed state of buffers, so 2361 // we just take a copy here to try and avoid potential synchronization problems 2362 bool isSubBufferProcessed[2] = { 0 }; 2363 isSubBufferProcessed[0] = audioBuffer->isSubBufferProcessed[0]; 2364 isSubBufferProcessed[1] = audioBuffer->isSubBufferProcessed[1]; 2365 2366 ma_uint32 frameSizeInBytes = ma_get_bytes_per_frame(audioBuffer->converter.formatIn, audioBuffer->converter.channelsIn); 2367 2368 // Fill out every frame until we find a buffer that's marked as processed. Then fill the remainder with 0 2369 ma_uint32 framesRead = 0; 2370 while (1) 2371 { 2372 // We break from this loop differently depending on the buffer's usage 2373 // - For static buffers, we simply fill as much data as we can 2374 // - For streaming buffers we only fill half of the buffer that are processed 2375 // Unprocessed halves must keep their audio data in-tact 2376 if (audioBuffer->usage == AUDIO_BUFFER_USAGE_STATIC) 2377 { 2378 if (framesRead >= frameCount) break; 2379 } 2380 else 2381 { 2382 if (isSubBufferProcessed[currentSubBufferIndex]) break; 2383 } 2384 2385 ma_uint32 totalFramesRemaining = (frameCount - framesRead); 2386 if (totalFramesRemaining == 0) break; 2387 2388 ma_uint32 framesRemainingInOutputBuffer; 2389 if (audioBuffer->usage == AUDIO_BUFFER_USAGE_STATIC) 2390 { 2391 framesRemainingInOutputBuffer = audioBuffer->sizeInFrames - audioBuffer->frameCursorPos; 2392 } 2393 else 2394 { 2395 ma_uint32 firstFrameIndexOfThisSubBuffer = subBufferSizeInFrames*currentSubBufferIndex; 2396 framesRemainingInOutputBuffer = subBufferSizeInFrames - (audioBuffer->frameCursorPos - firstFrameIndexOfThisSubBuffer); 2397 } 2398 2399 ma_uint32 framesToRead = totalFramesRemaining; 2400 if (framesToRead > framesRemainingInOutputBuffer) framesToRead = framesRemainingInOutputBuffer; 2401 2402 memcpy((unsigned char *)framesOut + (framesRead*frameSizeInBytes), audioBuffer->data + (audioBuffer->frameCursorPos*frameSizeInBytes), framesToRead*frameSizeInBytes); 2403 audioBuffer->frameCursorPos = (audioBuffer->frameCursorPos + framesToRead)%audioBuffer->sizeInFrames; 2404 framesRead += framesToRead; 2405 2406 // If we've read to the end of the buffer, mark it as processed 2407 if (framesToRead == framesRemainingInOutputBuffer) 2408 { 2409 audioBuffer->isSubBufferProcessed[currentSubBufferIndex] = true; 2410 isSubBufferProcessed[currentSubBufferIndex] = true; 2411 2412 currentSubBufferIndex = (currentSubBufferIndex + 1)%2; 2413 2414 // We need to break from this loop if we're not looping 2415 if (!audioBuffer->looping) 2416 { 2417 StopAudioBufferInLockedState(audioBuffer); 2418 break; 2419 } 2420 } 2421 } 2422 2423 // Zero-fill excess 2424 ma_uint32 totalFramesRemaining = (frameCount - framesRead); 2425 if (totalFramesRemaining > 0) 2426 { 2427 memset((unsigned char *)framesOut + (framesRead*frameSizeInBytes), 0, totalFramesRemaining*frameSizeInBytes); 2428 2429 // For static buffers we can fill the remaining frames with silence for safety, but we don't want 2430 // to report those frames as "read". The reason for this is that the caller uses the return value 2431 // to know whether a non-looping sound has finished playback 2432 if (audioBuffer->usage != AUDIO_BUFFER_USAGE_STATIC) framesRead += totalFramesRemaining; 2433 } 2434 2435 return framesRead; 2436 } 2437 2438 // Reads audio data from an AudioBuffer object in device format, returned data will be in a format appropriate for mixing 2439 static ma_uint32 ReadAudioBufferFramesInMixingFormat(AudioBuffer *audioBuffer, float *framesOut, ma_uint32 frameCount) 2440 { 2441 // What's going on here is that we're continuously converting data from the AudioBuffer's internal format to the mixing format, which 2442 // should be defined by the output format of the data converter. We do this until frameCount frames have been output. The important 2443 // detail to remember here is that we never, ever attempt to read more input data than is required for the specified number of output 2444 // frames. This can be achieved with ma_data_converter_get_required_input_frame_count() 2445 ma_uint8 inputBuffer[4096] = { 0 }; 2446 ma_uint32 inputBufferFrameCap = sizeof(inputBuffer)/ma_get_bytes_per_frame(audioBuffer->converter.formatIn, audioBuffer->converter.channelsIn); 2447 2448 ma_uint32 totalOutputFramesProcessed = 0; 2449 while (totalOutputFramesProcessed < frameCount) 2450 { 2451 ma_uint64 outputFramesToProcessThisIteration = frameCount - totalOutputFramesProcessed; 2452 ma_uint64 inputFramesToProcessThisIteration = 0; 2453 2454 (void)ma_data_converter_get_required_input_frame_count(&audioBuffer->converter, outputFramesToProcessThisIteration, &inputFramesToProcessThisIteration); 2455 if (inputFramesToProcessThisIteration > inputBufferFrameCap) 2456 { 2457 inputFramesToProcessThisIteration = inputBufferFrameCap; 2458 } 2459 2460 float *runningFramesOut = framesOut + (totalOutputFramesProcessed*audioBuffer->converter.channelsOut); 2461 2462 /* At this point we can convert the data to our mixing format. */ 2463 ma_uint64 inputFramesProcessedThisIteration = ReadAudioBufferFramesInInternalFormat(audioBuffer, inputBuffer, (ma_uint32)inputFramesToProcessThisIteration); /* Safe cast. */ 2464 ma_uint64 outputFramesProcessedThisIteration = outputFramesToProcessThisIteration; 2465 ma_data_converter_process_pcm_frames(&audioBuffer->converter, inputBuffer, &inputFramesProcessedThisIteration, runningFramesOut, &outputFramesProcessedThisIteration); 2466 2467 totalOutputFramesProcessed += (ma_uint32)outputFramesProcessedThisIteration; /* Safe cast. */ 2468 2469 if (inputFramesProcessedThisIteration < inputFramesToProcessThisIteration) 2470 { 2471 break; /* Ran out of input data. */ 2472 } 2473 2474 /* This should never be hit, but will add it here for safety. Ensures we get out of the loop when no input nor output frames are processed. */ 2475 if (inputFramesProcessedThisIteration == 0 && outputFramesProcessedThisIteration == 0) 2476 { 2477 break; 2478 } 2479 } 2480 2481 return totalOutputFramesProcessed; 2482 } 2483 2484 // Sending audio data to device callback function 2485 // This function will be called when miniaudio needs more data 2486 // NOTE: All the mixing takes place here 2487 static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const void *pFramesInput, ma_uint32 frameCount) 2488 { 2489 (void)pDevice; 2490 2491 // Mixing is basically just an accumulation, we need to initialize the output buffer to 0 2492 memset(pFramesOut, 0, frameCount*pDevice->playback.channels*ma_get_bytes_per_sample(pDevice->playback.format)); 2493 2494 // Using a mutex here for thread-safety which makes things not real-time 2495 // This is unlikely to be necessary for this project, but may want to consider how you might want to avoid this 2496 ma_mutex_lock(&AUDIO.System.lock); 2497 { 2498 for (AudioBuffer *audioBuffer = AUDIO.Buffer.first; audioBuffer != NULL; audioBuffer = audioBuffer->next) 2499 { 2500 // Ignore stopped or paused sounds 2501 if (!audioBuffer->playing || audioBuffer->paused) continue; 2502 2503 ma_uint32 framesRead = 0; 2504 2505 while (1) 2506 { 2507 if (framesRead >= frameCount) break; 2508 2509 // Just read as much data as we can from the stream 2510 ma_uint32 framesToRead = (frameCount - framesRead); 2511 2512 while (framesToRead > 0) 2513 { 2514 float tempBuffer[1024] = { 0 }; // Frames for stereo 2515 2516 ma_uint32 framesToReadRightNow = framesToRead; 2517 if (framesToReadRightNow > sizeof(tempBuffer)/sizeof(tempBuffer[0])/AUDIO_DEVICE_CHANNELS) 2518 { 2519 framesToReadRightNow = sizeof(tempBuffer)/sizeof(tempBuffer[0])/AUDIO_DEVICE_CHANNELS; 2520 } 2521 2522 ma_uint32 framesJustRead = ReadAudioBufferFramesInMixingFormat(audioBuffer, tempBuffer, framesToReadRightNow); 2523 if (framesJustRead > 0) 2524 { 2525 float *framesOut = (float *)pFramesOut + (framesRead*AUDIO.System.device.playback.channels); 2526 float *framesIn = tempBuffer; 2527 2528 // Apply processors chain if defined 2529 rAudioProcessor *processor = audioBuffer->processor; 2530 while (processor) 2531 { 2532 processor->process(framesIn, framesJustRead); 2533 processor = processor->next; 2534 } 2535 2536 MixAudioFrames(framesOut, framesIn, framesJustRead, audioBuffer); 2537 2538 framesToRead -= framesJustRead; 2539 framesRead += framesJustRead; 2540 } 2541 2542 if (!audioBuffer->playing) 2543 { 2544 framesRead = frameCount; 2545 break; 2546 } 2547 2548 // If we weren't able to read all the frames we requested, break 2549 if (framesJustRead < framesToReadRightNow) 2550 { 2551 if (!audioBuffer->looping) 2552 { 2553 StopAudioBufferInLockedState(audioBuffer); 2554 break; 2555 } 2556 else 2557 { 2558 // Should never get here, but just for safety, 2559 // move the cursor position back to the start and continue the loop 2560 audioBuffer->frameCursorPos = 0; 2561 continue; 2562 } 2563 } 2564 } 2565 2566 // If for some reason we weren't able to read every frame we'll need to break from the loop 2567 // Not doing this could theoretically put us into an infinite loop 2568 if (framesToRead > 0) break; 2569 } 2570 } 2571 } 2572 2573 rAudioProcessor *processor = AUDIO.mixedProcessor; 2574 while (processor) 2575 { 2576 processor->process(pFramesOut, frameCount); 2577 processor = processor->next; 2578 } 2579 2580 ma_mutex_unlock(&AUDIO.System.lock); 2581 } 2582 2583 // Main mixing function, pretty simple in this project, just an accumulation 2584 // NOTE: framesOut is both an input and an output, it is initially filled with zeros outside of this function 2585 static void MixAudioFrames(float *framesOut, const float *framesIn, ma_uint32 frameCount, AudioBuffer *buffer) 2586 { 2587 const float localVolume = buffer->volume; 2588 const ma_uint32 channels = AUDIO.System.device.playback.channels; 2589 2590 if (channels == 2) // We consider panning 2591 { 2592 const float left = buffer->pan; 2593 const float right = 1.0f - left; 2594 2595 // Fast sine approximation in [0..1] for pan law: y = 0.5f*x*(3 - x*x); 2596 const float levels[2] = { localVolume*0.5f*left*(3.0f - left*left), localVolume*0.5f*right*(3.0f - right*right) }; 2597 2598 float *frameOut = framesOut; 2599 const float *frameIn = framesIn; 2600 2601 for (ma_uint32 frame = 0; frame < frameCount; frame++) 2602 { 2603 frameOut[0] += (frameIn[0]*levels[0]); 2604 frameOut[1] += (frameIn[1]*levels[1]); 2605 2606 frameOut += 2; 2607 frameIn += 2; 2608 } 2609 } 2610 else // We do not consider panning 2611 { 2612 for (ma_uint32 frame = 0; frame < frameCount; frame++) 2613 { 2614 for (ma_uint32 c = 0; c < channels; c++) 2615 { 2616 float *frameOut = framesOut + (frame*channels); 2617 const float *frameIn = framesIn + (frame*channels); 2618 2619 // Output accumulates input multiplied by volume to provided output (usually 0) 2620 frameOut[c] += (frameIn[c]*localVolume); 2621 } 2622 } 2623 } 2624 } 2625 2626 // Check if an audio buffer is playing, assuming the audio system mutex has been locked 2627 static bool IsAudioBufferPlayingInLockedState(AudioBuffer *buffer) 2628 { 2629 bool result = false; 2630 2631 if (buffer != NULL) result = (buffer->playing && !buffer->paused); 2632 2633 return result; 2634 } 2635 2636 // Stop an audio buffer, assuming the audio system mutex has been locked 2637 static void StopAudioBufferInLockedState(AudioBuffer *buffer) 2638 { 2639 if (buffer != NULL) 2640 { 2641 if (IsAudioBufferPlayingInLockedState(buffer)) 2642 { 2643 buffer->playing = false; 2644 buffer->paused = false; 2645 buffer->frameCursorPos = 0; 2646 buffer->framesProcessed = 0; 2647 buffer->isSubBufferProcessed[0] = true; 2648 buffer->isSubBufferProcessed[1] = true; 2649 } 2650 } 2651 } 2652 2653 // Update audio stream, assuming the audio system mutex has been locked 2654 static void UpdateAudioStreamInLockedState(AudioStream stream, const void *data, int frameCount) 2655 { 2656 if (stream.buffer != NULL) 2657 { 2658 if (stream.buffer->isSubBufferProcessed[0] || stream.buffer->isSubBufferProcessed[1]) 2659 { 2660 ma_uint32 subBufferToUpdate = 0; 2661 2662 if (stream.buffer->isSubBufferProcessed[0] && stream.buffer->isSubBufferProcessed[1]) 2663 { 2664 // Both buffers are available for updating 2665 // Update the first one and make sure the cursor is moved back to the front 2666 subBufferToUpdate = 0; 2667 stream.buffer->frameCursorPos = 0; 2668 } 2669 else 2670 { 2671 // Just update whichever sub-buffer is processed 2672 subBufferToUpdate = (stream.buffer->isSubBufferProcessed[0])? 0 : 1; 2673 } 2674 2675 ma_uint32 subBufferSizeInFrames = stream.buffer->sizeInFrames/2; 2676 unsigned char *subBuffer = stream.buffer->data + ((subBufferSizeInFrames*stream.channels*(stream.sampleSize/8))*subBufferToUpdate); 2677 2678 // Total frames processed in buffer is always the complete size, filled with 0 if required 2679 stream.buffer->framesProcessed += subBufferSizeInFrames; 2680 2681 // Does this API expect a whole buffer to be updated in one go? 2682 // Assuming so, but if not will need to change this logic 2683 if (subBufferSizeInFrames >= (ma_uint32)frameCount) 2684 { 2685 ma_uint32 framesToWrite = (ma_uint32)frameCount; 2686 2687 ma_uint32 bytesToWrite = framesToWrite*stream.channels*(stream.sampleSize/8); 2688 memcpy(subBuffer, data, bytesToWrite); 2689 2690 // Any leftover frames should be filled with zeros 2691 ma_uint32 leftoverFrameCount = subBufferSizeInFrames - framesToWrite; 2692 2693 if (leftoverFrameCount > 0) memset(subBuffer + bytesToWrite, 0, leftoverFrameCount*stream.channels*(stream.sampleSize/8)); 2694 2695 stream.buffer->isSubBufferProcessed[subBufferToUpdate] = false; 2696 } 2697 else TRACELOG(LOG_WARNING, "STREAM: Attempting to write too many frames to buffer"); 2698 } 2699 else TRACELOG(LOG_WARNING, "STREAM: Buffer not available for updating"); 2700 } 2701 } 2702 2703 // Some required functions for audio standalone module version 2704 #if defined(RAUDIO_STANDALONE) 2705 // Check file extension 2706 static bool IsFileExtension(const char *fileName, const char *ext) 2707 { 2708 bool result = false; 2709 const char *fileExt; 2710 2711 if ((fileExt = strrchr(fileName, '.')) != NULL) 2712 { 2713 if (strcmp(fileExt, ext) == 0) result = true; 2714 } 2715 2716 return result; 2717 } 2718 2719 // Get pointer to extension for a filename string (includes the dot: .png) 2720 static const char *GetFileExtension(const char *fileName) 2721 { 2722 const char *dot = strrchr(fileName, '.'); 2723 2724 if (!dot || dot == fileName) return NULL; 2725 2726 return dot; 2727 } 2728 2729 // String pointer reverse break: returns right-most occurrence of charset in s 2730 static const char *strprbrk(const char *s, const char *charset) 2731 { 2732 const char *latestMatch = NULL; 2733 for (; s = strpbrk(s, charset), s != NULL; latestMatch = s++) { } 2734 return latestMatch; 2735 } 2736 2737 // Get pointer to filename for a path string 2738 static const char *GetFileName(const char *filePath) 2739 { 2740 const char *fileName = NULL; 2741 if (filePath != NULL) fileName = strprbrk(filePath, "\\/"); 2742 2743 if (!fileName) return filePath; 2744 2745 return fileName + 1; 2746 } 2747 2748 // Get filename string without extension (uses static string) 2749 static const char *GetFileNameWithoutExt(const char *filePath) 2750 { 2751 #define MAX_FILENAMEWITHOUTEXT_LENGTH 256 2752 2753 static char fileName[MAX_FILENAMEWITHOUTEXT_LENGTH] = { 0 }; 2754 memset(fileName, 0, MAX_FILENAMEWITHOUTEXT_LENGTH); 2755 2756 if (filePath != NULL) strcpy(fileName, GetFileName(filePath)); // Get filename with extension 2757 2758 int size = (int)strlen(fileName); // Get size in bytes 2759 2760 for (int i = 0; (i < size) && (i < MAX_FILENAMEWITHOUTEXT_LENGTH); i++) 2761 { 2762 if (fileName[i] == '.') 2763 { 2764 // NOTE: We break on first '.' found 2765 fileName[i] = '\0'; 2766 break; 2767 } 2768 } 2769 2770 return fileName; 2771 } 2772 2773 // Load data from file into a buffer 2774 static unsigned char *LoadFileData(const char *fileName, int *dataSize) 2775 { 2776 unsigned char *data = NULL; 2777 *dataSize = 0; 2778 2779 if (fileName != NULL) 2780 { 2781 FILE *file = fopen(fileName, "rb"); 2782 2783 if (file != NULL) 2784 { 2785 // WARNING: On binary streams SEEK_END could not be found, 2786 // using fseek() and ftell() could not work in some (rare) cases 2787 fseek(file, 0, SEEK_END); 2788 int size = ftell(file); 2789 fseek(file, 0, SEEK_SET); 2790 2791 if (size > 0) 2792 { 2793 data = (unsigned char *)RL_MALLOC(size*sizeof(unsigned char)); 2794 2795 // NOTE: fread() returns number of read elements instead of bytes, so we read [1 byte, size elements] 2796 unsigned int count = (unsigned int)fread(data, sizeof(unsigned char), size, file); 2797 *dataSize = count; 2798 2799 if (count != size) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially loaded", fileName); 2800 else TRACELOG(LOG_INFO, "FILEIO: [%s] File loaded successfully", fileName); 2801 } 2802 else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to read file", fileName); 2803 2804 fclose(file); 2805 } 2806 else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open file", fileName); 2807 } 2808 else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid"); 2809 2810 return data; 2811 } 2812 2813 // Save data to file from buffer 2814 static bool SaveFileData(const char *fileName, void *data, int dataSize) 2815 { 2816 if (fileName != NULL) 2817 { 2818 FILE *file = fopen(fileName, "wb"); 2819 2820 if (file != NULL) 2821 { 2822 unsigned int count = (unsigned int)fwrite(data, sizeof(unsigned char), dataSize, file); 2823 2824 if (count == 0) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to write file", fileName); 2825 else if (count != dataSize) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially written", fileName); 2826 else TRACELOG(LOG_INFO, "FILEIO: [%s] File saved successfully", fileName); 2827 2828 fclose(file); 2829 } 2830 else 2831 { 2832 TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open file", fileName); 2833 return false; 2834 } 2835 } 2836 else 2837 { 2838 TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid"); 2839 return false; 2840 } 2841 2842 return true; 2843 } 2844 2845 // Save text data to file (write), string must be '\0' terminated 2846 static bool SaveFileText(const char *fileName, char *text) 2847 { 2848 if (fileName != NULL) 2849 { 2850 FILE *file = fopen(fileName, "wt"); 2851 2852 if (file != NULL) 2853 { 2854 int count = fprintf(file, "%s", text); 2855 2856 if (count == 0) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to write text file", fileName); 2857 else TRACELOG(LOG_INFO, "FILEIO: [%s] Text file saved successfully", fileName); 2858 2859 fclose(file); 2860 } 2861 else 2862 { 2863 TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open text file", fileName); 2864 return false; 2865 } 2866 } 2867 else 2868 { 2869 TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid"); 2870 return false; 2871 } 2872 2873 return true; 2874 } 2875 #endif 2876 2877 #undef AudioBuffer 2878 2879 #endif // SUPPORT_MODULE_RAUDIO