github.com/Konstantin8105/c4go@v0.0.0-20240505174241-768bb1c65a51/tests/raylib/raudio.c (about) 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 * 15 * #define SUPPORT_MODULE_RAUDIO 16 * raudio module is included in the build 17 * 18 * #define RAUDIO_STANDALONE 19 * Define to use the module as standalone library (independently of raylib). 20 * Required types and functions are defined in the same module. 21 * 22 * #define SUPPORT_FILEFORMAT_WAV 23 * #define SUPPORT_FILEFORMAT_OGG 24 * #define SUPPORT_FILEFORMAT_XM 25 * #define SUPPORT_FILEFORMAT_MOD 26 * #define SUPPORT_FILEFORMAT_FLAC 27 * #define SUPPORT_FILEFORMAT_MP3 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-2022 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 MINIAUDIO_IMPLEMENTATION 171 //#define MA_DEBUG_OUTPUT 172 #include "external/miniaudio.h" // Audio device initialization and management 173 #undef PlaySound // Win32 API: windows.h > mmsystem.h defines PlaySound macro 174 175 #include <stdlib.h> // Required for: malloc(), free() 176 #include <stdio.h> // Required for: FILE, fopen(), fclose(), fread() 177 #include <string.h> // Required for: strcmp() [Used in IsFileExtension(), LoadWaveFromMemory(), LoadMusicStreamFromMemory()] 178 179 #if defined(RAUDIO_STANDALONE) 180 #ifndef TRACELOG 181 #define TRACELOG(level, ...) printf(__VA_ARGS__) 182 #endif 183 184 // Allow custom memory allocators 185 #ifndef RL_MALLOC 186 #define RL_MALLOC(sz) malloc(sz) 187 #endif 188 #ifndef RL_CALLOC 189 #define RL_CALLOC(n,sz) calloc(n,sz) 190 #endif 191 #ifndef RL_REALLOC 192 #define RL_REALLOC(ptr,sz) realloc(ptr,sz) 193 #endif 194 #ifndef RL_FREE 195 #define RL_FREE(ptr) free(ptr) 196 #endif 197 #endif 198 199 #if defined(SUPPORT_FILEFORMAT_OGG) 200 // TODO: Remap stb_vorbis malloc()/free() calls to RL_MALLOC/RL_FREE 201 202 #define STB_VORBIS_IMPLEMENTATION 203 #include "external/stb_vorbis.h" // OGG loading functions 204 #endif 205 206 #if defined(SUPPORT_FILEFORMAT_XM) 207 #define JARXM_MALLOC RL_MALLOC 208 #define JARXM_FREE RL_FREE 209 210 #define JAR_XM_IMPLEMENTATION 211 #include "external/jar_xm.h" // XM loading functions 212 #endif 213 214 #if defined(SUPPORT_FILEFORMAT_MOD) 215 #define JARMOD_MALLOC RL_MALLOC 216 #define JARMOD_FREE RL_FREE 217 218 #define JAR_MOD_IMPLEMENTATION 219 #include "external/jar_mod.h" // MOD loading functions 220 #endif 221 222 #if defined(SUPPORT_FILEFORMAT_WAV) 223 #define DRWAV_MALLOC RL_MALLOC 224 #define DRWAV_REALLOC RL_REALLOC 225 #define DRWAV_FREE RL_FREE 226 227 #define DR_WAV_IMPLEMENTATION 228 #include "external/dr_wav.h" // WAV loading functions 229 #endif 230 231 #if defined(SUPPORT_FILEFORMAT_MP3) 232 #define DRMP3_MALLOC RL_MALLOC 233 #define DRMP3_REALLOC RL_REALLOC 234 #define DRMP3_FREE RL_FREE 235 236 #define DR_MP3_IMPLEMENTATION 237 #include "external/dr_mp3.h" // MP3 loading functions 238 #endif 239 240 #if defined(SUPPORT_FILEFORMAT_FLAC) 241 #define DRFLAC_MALLOC RL_MALLOC 242 #define DRFLAC_REALLOC RL_REALLOC 243 #define DRFLAC_FREE RL_FREE 244 245 #define DR_FLAC_IMPLEMENTATION 246 #define DR_FLAC_NO_WIN32_IO 247 #include "external/dr_flac.h" // FLAC loading functions 248 #endif 249 250 //---------------------------------------------------------------------------------- 251 // Defines and Macros 252 //---------------------------------------------------------------------------------- 253 #ifndef AUDIO_DEVICE_FORMAT 254 #define AUDIO_DEVICE_FORMAT ma_format_f32 // Device output format (float-32bit) 255 #endif 256 #ifndef AUDIO_DEVICE_CHANNELS 257 #define AUDIO_DEVICE_CHANNELS 2 // Device output channels: stereo 258 #endif 259 #ifndef AUDIO_DEVICE_SAMPLE_RATE 260 #define AUDIO_DEVICE_SAMPLE_RATE 0 // Device output sample rate 261 #endif 262 263 #ifndef MAX_AUDIO_BUFFER_POOL_CHANNELS 264 #define MAX_AUDIO_BUFFER_POOL_CHANNELS 16 // Audio pool channels 265 #endif 266 267 //---------------------------------------------------------------------------------- 268 // Types and Structures Definition 269 //---------------------------------------------------------------------------------- 270 271 // Music context type 272 // NOTE: Depends on data structure provided by the library 273 // in charge of reading the different file types 274 typedef enum { 275 MUSIC_AUDIO_NONE = 0, // No audio context loaded 276 MUSIC_AUDIO_WAV, // WAV audio context 277 MUSIC_AUDIO_OGG, // OGG audio context 278 MUSIC_AUDIO_FLAC, // FLAC audio context 279 MUSIC_AUDIO_MP3, // MP3 audio context 280 MUSIC_MODULE_XM, // XM module audio context 281 MUSIC_MODULE_MOD // MOD module audio context 282 } MusicContextType; 283 284 #if defined(RAUDIO_STANDALONE) 285 // Trace log level 286 // NOTE: Organized by priority level 287 typedef enum { 288 LOG_ALL = 0, // Display all logs 289 LOG_TRACE, // Trace logging, intended for internal use only 290 LOG_DEBUG, // Debug logging, used for internal debugging, it should be disabled on release builds 291 LOG_INFO, // Info logging, used for program execution info 292 LOG_WARNING, // Warning logging, used on recoverable failures 293 LOG_ERROR, // Error logging, used on unrecoverable failures 294 LOG_FATAL, // Fatal logging, used to abort program: exit(EXIT_FAILURE) 295 LOG_NONE // Disable logging 296 } TraceLogLevel; 297 #endif 298 299 // NOTE: Different logic is used when feeding data to the playback device 300 // depending on whether or not data is streamed (Music vs Sound) 301 typedef enum { 302 AUDIO_BUFFER_USAGE_STATIC = 0, 303 AUDIO_BUFFER_USAGE_STREAM 304 } AudioBufferUsage; 305 306 // Audio buffer struct 307 struct rAudioBuffer { 308 ma_data_converter converter; // Audio data converter 309 310 AudioCallback callback; // Audio buffer callback for buffer filling on audio threads 311 rAudioProcessor *processor; // Audio processor 312 313 float volume; // Audio buffer volume 314 float pitch; // Audio buffer pitch 315 float pan; // Audio buffer pan (0.0f to 1.0f) 316 317 bool playing; // Audio buffer state: AUDIO_PLAYING 318 bool paused; // Audio buffer state: AUDIO_PAUSED 319 bool looping; // Audio buffer looping, default to true for AudioStreams 320 int usage; // Audio buffer usage mode: STATIC or STREAM 321 322 bool isSubBufferProcessed[2]; // SubBuffer processed (virtual double buffer) 323 unsigned int sizeInFrames; // Total buffer size in frames 324 unsigned int frameCursorPos; // Frame cursor position 325 unsigned int framesProcessed; // Total frames processed in this buffer (required for play timing) 326 327 unsigned char *data; // Data buffer, on music stream keeps filling 328 329 rAudioBuffer *next; // Next audio buffer on the list 330 rAudioBuffer *prev; // Previous audio buffer on the list 331 }; 332 333 // Audio processor struct 334 // NOTE: Useful to apply effects to an AudioBuffer 335 struct rAudioProcessor { 336 AudioCallback process; // Processor callback function 337 rAudioProcessor *next; // Next audio processor on the list 338 rAudioProcessor *prev; // Previous audio processor on the list 339 }; 340 341 #define AudioBuffer rAudioBuffer // HACK: To avoid CoreAudio (macOS) symbol collision 342 343 // Audio data context 344 typedef struct AudioData { 345 struct { 346 ma_context context; // miniaudio context data 347 ma_device device; // miniaudio device 348 ma_mutex lock; // miniaudio mutex lock 349 bool isReady; // Check if audio device is ready 350 size_t pcmBufferSize; // Pre-allocated buffer size 351 void *pcmBuffer; // Pre-allocated buffer to read audio data from file/memory 352 } System; 353 struct { 354 AudioBuffer *first; // Pointer to first AudioBuffer in the list 355 AudioBuffer *last; // Pointer to last AudioBuffer in the list 356 int defaultSize; // Default audio buffer size for audio streams 357 } Buffer; 358 struct { 359 unsigned int poolCounter; // AudioBuffer pointers pool counter 360 AudioBuffer *pool[MAX_AUDIO_BUFFER_POOL_CHANNELS]; // Multichannel AudioBuffer pointers pool 361 unsigned int channels[MAX_AUDIO_BUFFER_POOL_CHANNELS]; // AudioBuffer pool channels 362 } MultiChannel; 363 } AudioData; 364 365 //---------------------------------------------------------------------------------- 366 // Global Variables Definition 367 //---------------------------------------------------------------------------------- 368 static AudioData AUDIO = { // Global AUDIO context 369 370 // NOTE: Music buffer size is defined by number of samples, independent of sample size and channels number 371 // After some math, considering a sampleRate of 48000, a buffer refill rate of 1/60 seconds and a 372 // standard double-buffering system, a 4096 samples buffer has been chosen, it should be enough 373 // In case of music-stalls, just increase this number 374 .Buffer.defaultSize = 0 375 }; 376 377 //---------------------------------------------------------------------------------- 378 // Module specific Functions Declaration 379 //---------------------------------------------------------------------------------- 380 static void OnLog(void *pUserData, ma_uint32 level, const char *pMessage); 381 static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const void *pFramesInput, ma_uint32 frameCount); 382 static void MixAudioFrames(float *framesOut, const float *framesIn, ma_uint32 frameCount, AudioBuffer *buffer); 383 384 #if defined(RAUDIO_STANDALONE) 385 static bool IsFileExtension(const char *fileName, const char *ext); // Check file extension 386 static const char *GetFileExtension(const char *fileName); // Get pointer to extension for a filename string (includes the dot: .png) 387 388 static unsigned char *LoadFileData(const char *fileName, unsigned int *bytesRead); // Load file data as byte array (read) 389 static bool SaveFileData(const char *fileName, void *data, unsigned int bytesToWrite); // Save data to file from byte array (write) 390 static bool SaveFileText(const char *fileName, char *text); // Save text data to file (write), string must be '\0' terminated 391 #endif 392 393 //---------------------------------------------------------------------------------- 394 // AudioBuffer management functions declaration 395 // NOTE: Those functions are not exposed by raylib... for the moment 396 //---------------------------------------------------------------------------------- 397 AudioBuffer *LoadAudioBuffer(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 sizeInFrames, int usage); 398 void UnloadAudioBuffer(AudioBuffer *buffer); 399 400 bool IsAudioBufferPlaying(AudioBuffer *buffer); 401 void PlayAudioBuffer(AudioBuffer *buffer); 402 void StopAudioBuffer(AudioBuffer *buffer); 403 void PauseAudioBuffer(AudioBuffer *buffer); 404 void ResumeAudioBuffer(AudioBuffer *buffer); 405 void SetAudioBufferVolume(AudioBuffer *buffer, float volume); 406 void SetAudioBufferPitch(AudioBuffer *buffer, float pitch); 407 void SetAudioBufferPan(AudioBuffer *buffer, float pan); 408 void TrackAudioBuffer(AudioBuffer *buffer); 409 void UntrackAudioBuffer(AudioBuffer *buffer); 410 411 //---------------------------------------------------------------------------------- 412 // Module Functions Definition - Audio Device initialization and Closing 413 //---------------------------------------------------------------------------------- 414 // Initialize audio device 415 void InitAudioDevice(void) 416 { 417 // Init audio context 418 ma_context_config ctxConfig = ma_context_config_init(); 419 ma_log_callback_init(OnLog, NULL); 420 421 ma_result result = ma_context_init(NULL, 0, &ctxConfig, &AUDIO.System.context); 422 if (result != MA_SUCCESS) 423 { 424 TRACELOG(LOG_WARNING, "AUDIO: Failed to initialize context"); 425 return; 426 } 427 428 // Init audio device 429 // NOTE: Using the default device. Format is floating point because it simplifies mixing. 430 ma_device_config config = ma_device_config_init(ma_device_type_playback); 431 config.playback.pDeviceID = NULL; // NULL for the default playback AUDIO.System.device. 432 config.playback.format = AUDIO_DEVICE_FORMAT; 433 config.playback.channels = AUDIO_DEVICE_CHANNELS; 434 config.capture.pDeviceID = NULL; // NULL for the default capture AUDIO.System.device. 435 config.capture.format = ma_format_s16; 436 config.capture.channels = 1; 437 config.sampleRate = AUDIO_DEVICE_SAMPLE_RATE; 438 config.dataCallback = OnSendAudioDataToDevice; 439 config.pUserData = NULL; 440 441 result = ma_device_init(&AUDIO.System.context, &config, &AUDIO.System.device); 442 if (result != MA_SUCCESS) 443 { 444 TRACELOG(LOG_WARNING, "AUDIO: Failed to initialize playback device"); 445 ma_context_uninit(&AUDIO.System.context); 446 return; 447 } 448 449 // Keep the device running the whole time. May want to consider doing something a bit smarter and only have the device running 450 // while there's at least one sound being played. 451 result = ma_device_start(&AUDIO.System.device); 452 if (result != MA_SUCCESS) 453 { 454 TRACELOG(LOG_WARNING, "AUDIO: Failed to start playback device"); 455 ma_device_uninit(&AUDIO.System.device); 456 ma_context_uninit(&AUDIO.System.context); 457 return; 458 } 459 460 // Mixing happens on a seperate thread which means we need to synchronize. I'm using a mutex here to make things simple, but may 461 // want to look at something a bit smarter later on to keep everything real-time, if that's necessary. 462 if (ma_mutex_init(&AUDIO.System.lock) != MA_SUCCESS) 463 { 464 TRACELOG(LOG_WARNING, "AUDIO: Failed to create mutex for mixing"); 465 ma_device_uninit(&AUDIO.System.device); 466 ma_context_uninit(&AUDIO.System.context); 467 return; 468 } 469 470 // Init dummy audio buffers pool for multichannel sound playing 471 for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++) 472 { 473 // WARNING: An empty audio buffer is created (data = 0) and added to list, AudioBuffer data is filled on PlaySoundMulti() 474 AUDIO.MultiChannel.pool[i] = LoadAudioBuffer(AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO.System.device.sampleRate, 0, AUDIO_BUFFER_USAGE_STATIC); 475 } 476 477 TRACELOG(LOG_INFO, "AUDIO: Device initialized successfully"); 478 TRACELOG(LOG_INFO, " > Backend: miniaudio / %s", ma_get_backend_name(AUDIO.System.context.backend)); 479 TRACELOG(LOG_INFO, " > Format: %s -> %s", ma_get_format_name(AUDIO.System.device.playback.format), ma_get_format_name(AUDIO.System.device.playback.internalFormat)); 480 TRACELOG(LOG_INFO, " > Channels: %d -> %d", AUDIO.System.device.playback.channels, AUDIO.System.device.playback.internalChannels); 481 TRACELOG(LOG_INFO, " > Sample rate: %d -> %d", AUDIO.System.device.sampleRate, AUDIO.System.device.playback.internalSampleRate); 482 TRACELOG(LOG_INFO, " > Periods size: %d", AUDIO.System.device.playback.internalPeriodSizeInFrames*AUDIO.System.device.playback.internalPeriods); 483 484 AUDIO.System.isReady = true; 485 } 486 487 // Close the audio device for all contexts 488 void CloseAudioDevice(void) 489 { 490 if (AUDIO.System.isReady) 491 { 492 // Unload dummy audio buffers pool 493 // WARNING: They can be pointing to already unloaded data 494 for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++) 495 { 496 //UnloadAudioBuffer(AUDIO.MultiChannel.pool[i]); 497 if (AUDIO.MultiChannel.pool[i] != NULL) 498 { 499 ma_data_converter_uninit(&AUDIO.MultiChannel.pool[i]->converter, NULL); 500 UntrackAudioBuffer(AUDIO.MultiChannel.pool[i]); 501 //RL_FREE(buffer->data); // Already unloaded by UnloadSound() 502 RL_FREE(AUDIO.MultiChannel.pool[i]); 503 } 504 } 505 506 ma_mutex_uninit(&AUDIO.System.lock); 507 ma_device_uninit(&AUDIO.System.device); 508 ma_context_uninit(&AUDIO.System.context); 509 510 AUDIO.System.isReady = false; 511 RL_FREE(AUDIO.System.pcmBuffer); 512 AUDIO.System.pcmBuffer = NULL; 513 AUDIO.System.pcmBufferSize = 0; 514 515 TRACELOG(LOG_INFO, "AUDIO: Device closed successfully"); 516 } 517 else TRACELOG(LOG_WARNING, "AUDIO: Device could not be closed, not currently initialized"); 518 } 519 520 // Check if device has been initialized successfully 521 bool IsAudioDeviceReady(void) 522 { 523 return AUDIO.System.isReady; 524 } 525 526 // Set master volume (listener) 527 void SetMasterVolume(float volume) 528 { 529 ma_device_set_master_volume(&AUDIO.System.device, volume); 530 } 531 532 //---------------------------------------------------------------------------------- 533 // Module Functions Definition - Audio Buffer management 534 //---------------------------------------------------------------------------------- 535 536 // Initialize a new audio buffer (filled with silence) 537 AudioBuffer *LoadAudioBuffer(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 sizeInFrames, int usage) 538 { 539 AudioBuffer *audioBuffer = (AudioBuffer *)RL_CALLOC(1, sizeof(AudioBuffer)); 540 541 if (audioBuffer == NULL) 542 { 543 TRACELOG(LOG_WARNING, "AUDIO: Failed to allocate memory for buffer"); 544 return NULL; 545 } 546 547 if (sizeInFrames > 0) audioBuffer->data = RL_CALLOC(sizeInFrames*channels*ma_get_bytes_per_sample(format), 1); 548 549 // Audio data runs through a format converter 550 ma_data_converter_config converterConfig = ma_data_converter_config_init(format, AUDIO_DEVICE_FORMAT, channels, AUDIO_DEVICE_CHANNELS, sampleRate, AUDIO.System.device.sampleRate); 551 converterConfig.allowDynamicSampleRate = true; 552 553 ma_result result = ma_data_converter_init(&converterConfig, NULL, &audioBuffer->converter); 554 555 if (result != MA_SUCCESS) 556 { 557 TRACELOG(LOG_WARNING, "AUDIO: Failed to create data conversion pipeline"); 558 RL_FREE(audioBuffer); 559 return NULL; 560 } 561 562 // Init audio buffer values 563 audioBuffer->volume = 1.0f; 564 audioBuffer->pitch = 1.0f; 565 audioBuffer->pan = 0.5f; 566 567 audioBuffer->callback = NULL; 568 audioBuffer->processor = NULL; 569 570 audioBuffer->playing = false; 571 audioBuffer->paused = false; 572 audioBuffer->looping = false; 573 574 audioBuffer->usage = usage; 575 audioBuffer->frameCursorPos = 0; 576 audioBuffer->sizeInFrames = sizeInFrames; 577 578 // Buffers should be marked as processed by default so that a call to 579 // UpdateAudioStream() immediately after initialization works correctly 580 audioBuffer->isSubBufferProcessed[0] = true; 581 audioBuffer->isSubBufferProcessed[1] = true; 582 583 // Track audio buffer to linked list next position 584 TrackAudioBuffer(audioBuffer); 585 586 return audioBuffer; 587 } 588 589 // Delete an audio buffer 590 void UnloadAudioBuffer(AudioBuffer *buffer) 591 { 592 if (buffer != NULL) 593 { 594 ma_data_converter_uninit(&buffer->converter, NULL); 595 UntrackAudioBuffer(buffer); 596 RL_FREE(buffer->data); 597 RL_FREE(buffer); 598 } 599 } 600 601 // Check if an audio buffer is playing 602 bool IsAudioBufferPlaying(AudioBuffer *buffer) 603 { 604 bool result = false; 605 606 if (buffer != NULL) result = (buffer->playing && !buffer->paused); 607 608 return result; 609 } 610 611 // Play an audio buffer 612 // NOTE: Buffer is restarted to the start. 613 // Use PauseAudioBuffer() and ResumeAudioBuffer() if the playback position should be maintained. 614 void PlayAudioBuffer(AudioBuffer *buffer) 615 { 616 if (buffer != NULL) 617 { 618 buffer->playing = true; 619 buffer->paused = false; 620 buffer->frameCursorPos = 0; 621 } 622 } 623 624 // Stop an audio buffer 625 void StopAudioBuffer(AudioBuffer *buffer) 626 { 627 if (buffer != NULL) 628 { 629 if (IsAudioBufferPlaying(buffer)) 630 { 631 buffer->playing = false; 632 buffer->paused = false; 633 buffer->frameCursorPos = 0; 634 buffer->framesProcessed = 0; 635 buffer->isSubBufferProcessed[0] = true; 636 buffer->isSubBufferProcessed[1] = true; 637 } 638 } 639 } 640 641 // Pause an audio buffer 642 void PauseAudioBuffer(AudioBuffer *buffer) 643 { 644 if (buffer != NULL) buffer->paused = true; 645 } 646 647 // Resume an audio buffer 648 void ResumeAudioBuffer(AudioBuffer *buffer) 649 { 650 if (buffer != NULL) buffer->paused = false; 651 } 652 653 // Set volume for an audio buffer 654 void SetAudioBufferVolume(AudioBuffer *buffer, float volume) 655 { 656 if (buffer != NULL) buffer->volume = volume; 657 } 658 659 // Set pitch for an audio buffer 660 void SetAudioBufferPitch(AudioBuffer *buffer, float pitch) 661 { 662 if ((buffer != NULL) && (pitch > 0.0f)) 663 { 664 // Pitching is just an adjustment of the sample rate. 665 // Note that this changes the duration of the sound: 666 // - higher pitches will make the sound faster 667 // - lower pitches make it slower 668 ma_uint32 outputSampleRate = (ma_uint32)((float)buffer->converter.sampleRateOut/pitch); 669 ma_data_converter_set_rate(&buffer->converter, buffer->converter.sampleRateIn, outputSampleRate); 670 671 buffer->pitch = pitch; 672 } 673 } 674 675 // Set pan for an audio buffer 676 void SetAudioBufferPan(AudioBuffer *buffer, float pan) 677 { 678 if (pan < 0.0f) pan = 0.0f; 679 else if (pan > 1.0f) pan = 1.0f; 680 681 if (buffer != NULL) buffer->pan = pan; 682 } 683 684 // Track audio buffer to linked list next position 685 void TrackAudioBuffer(AudioBuffer *buffer) 686 { 687 ma_mutex_lock(&AUDIO.System.lock); 688 { 689 if (AUDIO.Buffer.first == NULL) AUDIO.Buffer.first = buffer; 690 else 691 { 692 AUDIO.Buffer.last->next = buffer; 693 buffer->prev = AUDIO.Buffer.last; 694 } 695 696 AUDIO.Buffer.last = buffer; 697 } 698 ma_mutex_unlock(&AUDIO.System.lock); 699 } 700 701 // Untrack audio buffer from linked list 702 void UntrackAudioBuffer(AudioBuffer *buffer) 703 { 704 ma_mutex_lock(&AUDIO.System.lock); 705 { 706 if (buffer->prev == NULL) AUDIO.Buffer.first = buffer->next; 707 else buffer->prev->next = buffer->next; 708 709 if (buffer->next == NULL) AUDIO.Buffer.last = buffer->prev; 710 else buffer->next->prev = buffer->prev; 711 712 buffer->prev = NULL; 713 buffer->next = NULL; 714 } 715 ma_mutex_unlock(&AUDIO.System.lock); 716 } 717 718 //---------------------------------------------------------------------------------- 719 // Module Functions Definition - Sounds loading and playing (.WAV) 720 //---------------------------------------------------------------------------------- 721 722 // Load wave data from file 723 Wave LoadWave(const char *fileName) 724 { 725 Wave wave = { 0 }; 726 727 // Loading file to memory 728 unsigned int fileSize = 0; 729 unsigned char *fileData = LoadFileData(fileName, &fileSize); 730 731 // Loading wave from memory data 732 if (fileData != NULL) wave = LoadWaveFromMemory(GetFileExtension(fileName), fileData, fileSize); 733 734 RL_FREE(fileData); 735 736 return wave; 737 } 738 739 // Load wave from memory buffer, fileType refers to extension: i.e. ".wav" 740 // WARNING: File extension must be provided in lower-case 741 Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int dataSize) 742 { 743 Wave wave = { 0 }; 744 745 if (false) { } 746 #if defined(SUPPORT_FILEFORMAT_WAV) 747 else if (strcmp(fileType, ".wav") == 0) 748 { 749 drwav wav = { 0 }; 750 bool success = drwav_init_memory(&wav, fileData, dataSize, NULL); 751 752 if (success) 753 { 754 wave.frameCount = (unsigned int)wav.totalPCMFrameCount; 755 wave.sampleRate = wav.sampleRate; 756 wave.sampleSize = 16; 757 wave.channels = wav.channels; 758 wave.data = (short *)RL_MALLOC(wave.frameCount*wave.channels*sizeof(short)); 759 760 // NOTE: We are forcing conversion to 16bit sample size on reading 761 drwav_read_pcm_frames_s16(&wav, wav.totalPCMFrameCount, wave.data); 762 } 763 else TRACELOG(LOG_WARNING, "WAVE: Failed to load WAV data"); 764 765 drwav_uninit(&wav); 766 } 767 #endif 768 #if defined(SUPPORT_FILEFORMAT_OGG) 769 else if (strcmp(fileType, ".ogg") == 0) 770 { 771 stb_vorbis *oggData = stb_vorbis_open_memory((unsigned char *)fileData, dataSize, NULL, NULL); 772 773 if (oggData != NULL) 774 { 775 stb_vorbis_info info = stb_vorbis_get_info(oggData); 776 777 wave.sampleRate = info.sample_rate; 778 wave.sampleSize = 16; // By default, ogg data is 16 bit per sample (short) 779 wave.channels = info.channels; 780 wave.frameCount = (unsigned int)stb_vorbis_stream_length_in_samples(oggData); // NOTE: It returns frames! 781 wave.data = (short *)RL_MALLOC(wave.frameCount*wave.channels*sizeof(short)); 782 783 // NOTE: Get the number of samples to process (be careful! we ask for number of shorts, not bytes!) 784 stb_vorbis_get_samples_short_interleaved(oggData, info.channels, (short *)wave.data, wave.frameCount*wave.channels); 785 stb_vorbis_close(oggData); 786 } 787 else TRACELOG(LOG_WARNING, "WAVE: Failed to load OGG data"); 788 } 789 #endif 790 #if defined(SUPPORT_FILEFORMAT_FLAC) 791 else if (strcmp(fileType, ".flac") == 0) 792 { 793 unsigned long long int totalFrameCount = 0; 794 795 // NOTE: We are forcing conversion to 16bit sample size on reading 796 wave.data = drflac_open_memory_and_read_pcm_frames_s16(fileData, dataSize, &wave.channels, &wave.sampleRate, &totalFrameCount, NULL); 797 wave.sampleSize = 16; 798 799 if (wave.data != NULL) wave.frameCount = (unsigned int)totalFrameCount; 800 else TRACELOG(LOG_WARNING, "WAVE: Failed to load FLAC data"); 801 } 802 #endif 803 #if defined(SUPPORT_FILEFORMAT_MP3) 804 else if (strcmp(fileType, ".mp3") == 0) 805 { 806 drmp3_config config = { 0 }; 807 unsigned long long int totalFrameCount = 0; 808 809 // NOTE: We are forcing conversion to 32bit float sample size on reading 810 wave.data = drmp3_open_memory_and_read_pcm_frames_f32(fileData, dataSize, &config, &totalFrameCount, NULL); 811 wave.sampleSize = 32; 812 813 if (wave.data != NULL) 814 { 815 wave.channels = config.channels; 816 wave.sampleRate = config.sampleRate; 817 wave.frameCount = (int)totalFrameCount; 818 } 819 else TRACELOG(LOG_WARNING, "WAVE: Failed to load MP3 data"); 820 821 } 822 #endif 823 else TRACELOG(LOG_WARNING, "WAVE: Data format not supported"); 824 825 TRACELOG(LOG_INFO, "WAVE: Data loaded successfully (%i Hz, %i bit, %i channels)", wave.sampleRate, wave.sampleSize, wave.channels); 826 827 return wave; 828 } 829 830 // Load sound from file 831 // NOTE: The entire file is loaded to memory to be played (no-streaming) 832 Sound LoadSound(const char *fileName) 833 { 834 Wave wave = LoadWave(fileName); 835 836 Sound sound = LoadSoundFromWave(wave); 837 838 UnloadWave(wave); // Sound is loaded, we can unload wave 839 840 return sound; 841 } 842 843 // Load sound from wave data 844 // NOTE: Wave data must be unallocated manually 845 Sound LoadSoundFromWave(Wave wave) 846 { 847 Sound sound = { 0 }; 848 849 if (wave.data != NULL) 850 { 851 // When using miniaudio we need to do our own mixing. 852 // To simplify this we need convert the format of each sound to be consistent with 853 // the format used to open the playback AUDIO.System.device. We can do this two ways: 854 // 855 // 1) Convert the whole sound in one go at load time (here). 856 // 2) Convert the audio data in chunks at mixing time. 857 // 858 // First option has been selected, format conversion is done on the loading stage. 859 // The downside is that it uses more memory if the original sound is u8 or s16. 860 ma_format formatIn = ((wave.sampleSize == 8)? ma_format_u8 : ((wave.sampleSize == 16)? ma_format_s16 : ma_format_f32)); 861 ma_uint32 frameCountIn = wave.frameCount; 862 863 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); 864 if (frameCount == 0) TRACELOG(LOG_WARNING, "SOUND: Failed to get frame count for format conversion"); 865 866 AudioBuffer *audioBuffer = LoadAudioBuffer(AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO.System.device.sampleRate, frameCount, AUDIO_BUFFER_USAGE_STATIC); 867 if (audioBuffer == NULL) 868 { 869 TRACELOG(LOG_WARNING, "SOUND: Failed to create buffer"); 870 return sound; // early return to avoid dereferencing the audioBuffer null pointer 871 } 872 873 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); 874 if (frameCount == 0) TRACELOG(LOG_WARNING, "SOUND: Failed format conversion"); 875 876 sound.frameCount = frameCount; 877 sound.stream.sampleRate = AUDIO.System.device.sampleRate; 878 sound.stream.sampleSize = 32; 879 sound.stream.channels = AUDIO_DEVICE_CHANNELS; 880 sound.stream.buffer = audioBuffer; 881 } 882 883 return sound; 884 } 885 886 // Unload wave data 887 void UnloadWave(Wave wave) 888 { 889 RL_FREE(wave.data); 890 //TRACELOG(LOG_INFO, "WAVE: Unloaded wave data from RAM"); 891 } 892 893 // Unload sound 894 void UnloadSound(Sound sound) 895 { 896 UnloadAudioBuffer(sound.stream.buffer); 897 //TRACELOG(LOG_INFO, "SOUND: Unloaded sound data from RAM"); 898 } 899 900 // Update sound buffer with new data 901 void UpdateSound(Sound sound, const void *data, int sampleCount) 902 { 903 if (sound.stream.buffer != NULL) 904 { 905 StopAudioBuffer(sound.stream.buffer); 906 907 // TODO: May want to lock/unlock this since this data buffer is read at mixing time 908 memcpy(sound.stream.buffer->data, data, sampleCount*ma_get_bytes_per_frame(sound.stream.buffer->converter.formatIn, sound.stream.buffer->converter.channelsIn)); 909 } 910 } 911 912 // Export wave data to file 913 bool ExportWave(Wave wave, const char *fileName) 914 { 915 bool success = false; 916 917 if (false) { } 918 #if defined(SUPPORT_FILEFORMAT_WAV) 919 else if (IsFileExtension(fileName, ".wav")) 920 { 921 drwav wav = { 0 }; 922 drwav_data_format format = { 0 }; 923 format.container = drwav_container_riff; 924 if (wave.sampleSize == 32) format.format = DR_WAVE_FORMAT_IEEE_FLOAT; 925 else format.format = DR_WAVE_FORMAT_PCM; 926 format.channels = wave.channels; 927 format.sampleRate = wave.sampleRate; 928 format.bitsPerSample = wave.sampleSize; 929 930 void *fileData = NULL; 931 size_t fileDataSize = 0; 932 success = drwav_init_memory_write(&wav, &fileData, &fileDataSize, &format, NULL); 933 if (success) success = (int)drwav_write_pcm_frames(&wav, wave.frameCount, wave.data); 934 drwav_result result = drwav_uninit(&wav); 935 936 if (result == DRWAV_SUCCESS) success = SaveFileData(fileName, (unsigned char *)fileData, (unsigned int)fileDataSize); 937 938 drwav_free(fileData, NULL); 939 } 940 #endif 941 else if (IsFileExtension(fileName, ".raw")) 942 { 943 // Export raw sample data (without header) 944 // NOTE: It's up to the user to track wave parameters 945 success = SaveFileData(fileName, wave.data, wave.frameCount*wave.channels*wave.sampleSize/8); 946 } 947 948 if (success) TRACELOG(LOG_INFO, "FILEIO: [%s] Wave data exported successfully", fileName); 949 else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export wave data", fileName); 950 951 return success; 952 } 953 954 // Export wave sample data to code (.h) 955 bool ExportWaveAsCode(Wave wave, const char *fileName) 956 { 957 bool success = false; 958 959 #ifndef TEXT_BYTES_PER_LINE 960 #define TEXT_BYTES_PER_LINE 20 961 #endif 962 963 int waveDataSize = wave.frameCount*wave.channels*wave.sampleSize/8; 964 965 // NOTE: Text data buffer size is estimated considering wave data size in bytes 966 // and requiring 6 char bytes for every byte: "0x00, " 967 char *txtData = (char *)RL_CALLOC(waveDataSize*6 + 2000, sizeof(char)); 968 969 int byteCount = 0; 970 byteCount += sprintf(txtData + byteCount, "\n//////////////////////////////////////////////////////////////////////////////////\n"); 971 byteCount += sprintf(txtData + byteCount, "// //\n"); 972 byteCount += sprintf(txtData + byteCount, "// WaveAsCode exporter v1.1 - Wave data exported as an array of bytes //\n"); 973 byteCount += sprintf(txtData + byteCount, "// //\n"); 974 byteCount += sprintf(txtData + byteCount, "// more info and bugs-report: github.com/raysan5/raylib //\n"); 975 byteCount += sprintf(txtData + byteCount, "// feedback and support: ray[at]raylib.com //\n"); 976 byteCount += sprintf(txtData + byteCount, "// //\n"); 977 byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2018-2022 Ramon Santamaria (@raysan5) //\n"); 978 byteCount += sprintf(txtData + byteCount, "// //\n"); 979 byteCount += sprintf(txtData + byteCount, "//////////////////////////////////////////////////////////////////////////////////\n\n"); 980 981 char fileNameLower[256] = { 0 }; 982 char fileNameUpper[256] = { 0 }; 983 for (int i = 0; fileName[i] != '.'; i++) { fileNameLower[i] = fileName[i]; } // Get filename without extension 984 for (int i = 0; fileNameLower[i] != '\0'; i++) if (fileNameLower[i] >= 'a' && fileNameLower[i] <= 'z') { fileNameUpper[i] = fileNameLower[i] - 32; } 985 986 byteCount += sprintf(txtData + byteCount, "// Wave data information\n"); 987 byteCount += sprintf(txtData + byteCount, "#define %s_FRAME_COUNT %u\n", fileNameUpper, wave.frameCount); 988 byteCount += sprintf(txtData + byteCount, "#define %s_SAMPLE_RATE %u\n", fileNameUpper, wave.sampleRate); 989 byteCount += sprintf(txtData + byteCount, "#define %s_SAMPLE_SIZE %u\n", fileNameUpper, wave.sampleSize); 990 byteCount += sprintf(txtData + byteCount, "#define %s_CHANNELS %u\n\n", fileNameUpper, wave.channels); 991 992 // Write wave data as an array of values 993 // Wave data is exported as byte array for 8/16bit and float array for 32bit float data 994 // NOTE: Frame data exported is channel-interlaced: frame01[sampleChannel1, sampleChannel2, ...], frame02[], frame03[] 995 if (wave.sampleSize == 32) 996 { 997 byteCount += sprintf(txtData + byteCount, "static float %sData[%i] = {\n", fileNameLower, waveDataSize/4); 998 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]); 999 byteCount += sprintf(txtData + byteCount, "%.4ff };\n", ((float *)wave.data)[waveDataSize/4 - 1]); 1000 } 1001 else 1002 { 1003 byteCount += sprintf(txtData + byteCount, "static unsigned char %sData[%i] = { ", fileNameLower, waveDataSize); 1004 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]); 1005 byteCount += sprintf(txtData + byteCount, "0x%x };\n", ((unsigned char *)wave.data)[waveDataSize - 1]); 1006 } 1007 1008 // NOTE: Text data length exported is determined by '\0' (NULL) character 1009 success = SaveFileText(fileName, txtData); 1010 1011 RL_FREE(txtData); 1012 1013 if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Wave as code exported successfully", fileName); 1014 else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export wave as code", fileName); 1015 1016 return success; 1017 } 1018 1019 // Play a sound 1020 void PlaySound(Sound sound) 1021 { 1022 PlayAudioBuffer(sound.stream.buffer); 1023 } 1024 1025 // Play a sound in the multichannel buffer pool 1026 void PlaySoundMulti(Sound sound) 1027 { 1028 int index = -1; 1029 unsigned int oldAge = 0; 1030 int oldIndex = -1; 1031 1032 // find the first non playing pool entry 1033 for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++) 1034 { 1035 if (AUDIO.MultiChannel.channels[i] > oldAge) 1036 { 1037 oldAge = AUDIO.MultiChannel.channels[i]; 1038 oldIndex = i; 1039 } 1040 1041 if (!IsAudioBufferPlaying(AUDIO.MultiChannel.pool[i])) 1042 { 1043 index = i; 1044 break; 1045 } 1046 } 1047 1048 // If no none playing pool members can be index choose the oldest 1049 if (index == -1) 1050 { 1051 TRACELOG(LOG_WARNING, "SOUND: Buffer pool is already full, count: %i", AUDIO.MultiChannel.poolCounter); 1052 1053 if (oldIndex == -1) 1054 { 1055 // Shouldn't be able to get here... but just in case something odd happens! 1056 TRACELOG(LOG_WARNING, "SOUND: Buffer pool could not determine oldest buffer not playing sound"); 1057 return; 1058 } 1059 1060 index = oldIndex; 1061 1062 // Just in case... 1063 StopAudioBuffer(AUDIO.MultiChannel.pool[index]); 1064 } 1065 1066 // Experimentally mutex lock doesn't seem to be needed this makes sense 1067 // as pool[index] isn't playing and the only stuff we're copying 1068 // shouldn't be changing... 1069 1070 AUDIO.MultiChannel.channels[index] = AUDIO.MultiChannel.poolCounter; 1071 AUDIO.MultiChannel.poolCounter++; 1072 1073 SetAudioBufferVolume(AUDIO.MultiChannel.pool[index], sound.stream.buffer->volume); 1074 SetAudioBufferPitch(AUDIO.MultiChannel.pool[index], sound.stream.buffer->pitch); 1075 SetAudioBufferPan(AUDIO.MultiChannel.pool[index], sound.stream.buffer->pan); 1076 1077 AUDIO.MultiChannel.pool[index]->looping = sound.stream.buffer->looping; 1078 AUDIO.MultiChannel.pool[index]->usage = sound.stream.buffer->usage; 1079 AUDIO.MultiChannel.pool[index]->isSubBufferProcessed[0] = false; 1080 AUDIO.MultiChannel.pool[index]->isSubBufferProcessed[1] = false; 1081 AUDIO.MultiChannel.pool[index]->sizeInFrames = sound.stream.buffer->sizeInFrames; 1082 1083 AUDIO.MultiChannel.pool[index]->data = sound.stream.buffer->data; // Fill dummy track with data for playing 1084 1085 PlayAudioBuffer(AUDIO.MultiChannel.pool[index]); 1086 } 1087 1088 // Stop any sound played with PlaySoundMulti() 1089 void StopSoundMulti(void) 1090 { 1091 for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++) StopAudioBuffer(AUDIO.MultiChannel.pool[i]); 1092 } 1093 1094 // Get number of sounds playing in the multichannel buffer pool 1095 int GetSoundsPlaying(void) 1096 { 1097 int counter = 0; 1098 1099 for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++) 1100 { 1101 if (IsAudioBufferPlaying(AUDIO.MultiChannel.pool[i])) counter++; 1102 } 1103 1104 return counter; 1105 } 1106 1107 // Pause a sound 1108 void PauseSound(Sound sound) 1109 { 1110 PauseAudioBuffer(sound.stream.buffer); 1111 } 1112 1113 // Resume a paused sound 1114 void ResumeSound(Sound sound) 1115 { 1116 ResumeAudioBuffer(sound.stream.buffer); 1117 } 1118 1119 // Stop reproducing a sound 1120 void StopSound(Sound sound) 1121 { 1122 StopAudioBuffer(sound.stream.buffer); 1123 } 1124 1125 // Check if a sound is playing 1126 bool IsSoundPlaying(Sound sound) 1127 { 1128 return IsAudioBufferPlaying(sound.stream.buffer); 1129 } 1130 1131 // Set volume for a sound 1132 void SetSoundVolume(Sound sound, float volume) 1133 { 1134 SetAudioBufferVolume(sound.stream.buffer, volume); 1135 } 1136 1137 // Set pitch for a sound 1138 void SetSoundPitch(Sound sound, float pitch) 1139 { 1140 SetAudioBufferPitch(sound.stream.buffer, pitch); 1141 } 1142 1143 // Set pan for a sound 1144 void SetSoundPan(Sound sound, float pan) 1145 { 1146 SetAudioBufferPan(sound.stream.buffer, pan); 1147 } 1148 1149 // Convert wave data to desired format 1150 void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels) 1151 { 1152 ma_format formatIn = ((wave->sampleSize == 8)? ma_format_u8 : ((wave->sampleSize == 16)? ma_format_s16 : ma_format_f32)); 1153 ma_format formatOut = ((sampleSize == 8)? ma_format_u8 : ((sampleSize == 16)? ma_format_s16 : ma_format_f32)); 1154 1155 ma_uint32 frameCountIn = wave->frameCount; 1156 ma_uint32 frameCount = (ma_uint32)ma_convert_frames(NULL, 0, formatOut, channels, sampleRate, NULL, frameCountIn, formatIn, wave->channels, wave->sampleRate); 1157 1158 if (frameCount == 0) 1159 { 1160 TRACELOG(LOG_WARNING, "WAVE: Failed to get frame count for format conversion"); 1161 return; 1162 } 1163 1164 void *data = RL_MALLOC(frameCount*channels*(sampleSize/8)); 1165 1166 frameCount = (ma_uint32)ma_convert_frames(data, frameCount, formatOut, channels, sampleRate, wave->data, frameCountIn, formatIn, wave->channels, wave->sampleRate); 1167 if (frameCount == 0) 1168 { 1169 TRACELOG(LOG_WARNING, "WAVE: Failed format conversion"); 1170 return; 1171 } 1172 1173 wave->frameCount = frameCount; 1174 wave->sampleSize = sampleSize; 1175 wave->sampleRate = sampleRate; 1176 wave->channels = channels; 1177 1178 RL_FREE(wave->data); 1179 wave->data = data; 1180 } 1181 1182 // Copy a wave to a new wave 1183 Wave WaveCopy(Wave wave) 1184 { 1185 Wave newWave = { 0 }; 1186 1187 newWave.data = RL_MALLOC(wave.frameCount*wave.channels*wave.sampleSize/8); 1188 1189 if (newWave.data != NULL) 1190 { 1191 // NOTE: Size must be provided in bytes 1192 memcpy(newWave.data, wave.data, wave.frameCount*wave.channels*wave.sampleSize/8); 1193 1194 newWave.frameCount = wave.frameCount; 1195 newWave.sampleRate = wave.sampleRate; 1196 newWave.sampleSize = wave.sampleSize; 1197 newWave.channels = wave.channels; 1198 } 1199 1200 return newWave; 1201 } 1202 1203 // Crop a wave to defined samples range 1204 // NOTE: Security check in case of out-of-range 1205 void WaveCrop(Wave *wave, int initSample, int finalSample) 1206 { 1207 if ((initSample >= 0) && (initSample < finalSample) && ((unsigned int)finalSample < (wave->frameCount*wave->channels))) 1208 { 1209 int sampleCount = finalSample - initSample; 1210 1211 void *data = RL_MALLOC(sampleCount*wave->sampleSize/8); 1212 1213 memcpy(data, (unsigned char *)wave->data + (initSample*wave->channels*wave->sampleSize/8), sampleCount*wave->sampleSize/8); 1214 1215 RL_FREE(wave->data); 1216 wave->data = data; 1217 } 1218 else TRACELOG(LOG_WARNING, "WAVE: Crop range out of bounds"); 1219 } 1220 1221 // Load samples data from wave as a floats array 1222 // NOTE 1: Returned sample values are normalized to range [-1..1] 1223 // NOTE 2: Sample data allocated should be freed with UnloadWaveSamples() 1224 float *LoadWaveSamples(Wave wave) 1225 { 1226 float *samples = (float *)RL_MALLOC(wave.frameCount*wave.channels*sizeof(float)); 1227 1228 // NOTE: sampleCount is the total number of interlaced samples (including channels) 1229 1230 for (unsigned int i = 0; i < wave.frameCount*wave.channels; i++) 1231 { 1232 if (wave.sampleSize == 8) samples[i] = (float)(((unsigned char *)wave.data)[i] - 127)/256.0f; 1233 else if (wave.sampleSize == 16) samples[i] = (float)(((short *)wave.data)[i])/32767.0f; 1234 else if (wave.sampleSize == 32) samples[i] = ((float *)wave.data)[i]; 1235 } 1236 1237 return samples; 1238 } 1239 1240 // Unload samples data loaded with LoadWaveSamples() 1241 void UnloadWaveSamples(float *samples) 1242 { 1243 RL_FREE(samples); 1244 } 1245 1246 //---------------------------------------------------------------------------------- 1247 // Module Functions Definition - Music loading and stream playing (.OGG) 1248 //---------------------------------------------------------------------------------- 1249 1250 // Load music stream from file 1251 Music LoadMusicStream(const char *fileName) 1252 { 1253 Music music = { 0 }; 1254 bool musicLoaded = false; 1255 1256 if (false) { } 1257 #if defined(SUPPORT_FILEFORMAT_WAV) 1258 else if (IsFileExtension(fileName, ".wav")) 1259 { 1260 drwav *ctxWav = RL_CALLOC(1, sizeof(drwav)); 1261 bool success = drwav_init_file(ctxWav, fileName, NULL); 1262 1263 music.ctxType = MUSIC_AUDIO_WAV; 1264 music.ctxData = ctxWav; 1265 1266 if (success) 1267 { 1268 int sampleSize = ctxWav->bitsPerSample; 1269 if (ctxWav->bitsPerSample == 24) sampleSize = 16; // Forcing conversion to s16 on UpdateMusicStream() 1270 1271 music.stream = LoadAudioStream(ctxWav->sampleRate, sampleSize, ctxWav->channels); 1272 music.frameCount = (unsigned int)ctxWav->totalPCMFrameCount; 1273 music.looping = true; // Looping enabled by default 1274 musicLoaded = true; 1275 } 1276 } 1277 #endif 1278 #if defined(SUPPORT_FILEFORMAT_OGG) 1279 else if (IsFileExtension(fileName, ".ogg")) 1280 { 1281 // Open ogg audio stream 1282 music.ctxType = MUSIC_AUDIO_OGG; 1283 music.ctxData = stb_vorbis_open_filename(fileName, NULL, NULL); 1284 1285 if (music.ctxData != NULL) 1286 { 1287 stb_vorbis_info info = stb_vorbis_get_info((stb_vorbis *)music.ctxData); // Get Ogg file info 1288 1289 // OGG bit rate defaults to 16 bit, it's enough for compressed format 1290 music.stream = LoadAudioStream(info.sample_rate, 16, info.channels); 1291 1292 // WARNING: It seems this function returns length in frames, not samples, so we multiply by channels 1293 music.frameCount = (unsigned int)stb_vorbis_stream_length_in_samples((stb_vorbis *)music.ctxData); 1294 music.looping = true; // Looping enabled by default 1295 musicLoaded = true; 1296 } 1297 } 1298 #endif 1299 #if defined(SUPPORT_FILEFORMAT_FLAC) 1300 else if (IsFileExtension(fileName, ".flac")) 1301 { 1302 music.ctxType = MUSIC_AUDIO_FLAC; 1303 music.ctxData = drflac_open_file(fileName, NULL); 1304 1305 if (music.ctxData != NULL) 1306 { 1307 drflac *ctxFlac = (drflac *)music.ctxData; 1308 1309 music.stream = LoadAudioStream(ctxFlac->sampleRate, ctxFlac->bitsPerSample, ctxFlac->channels); 1310 music.frameCount = (unsigned int)ctxFlac->totalPCMFrameCount; 1311 music.looping = true; // Looping enabled by default 1312 musicLoaded = true; 1313 } 1314 } 1315 #endif 1316 #if defined(SUPPORT_FILEFORMAT_MP3) 1317 else if (IsFileExtension(fileName, ".mp3")) 1318 { 1319 drmp3 *ctxMp3 = RL_CALLOC(1, sizeof(drmp3)); 1320 int result = drmp3_init_file(ctxMp3, fileName, NULL); 1321 1322 music.ctxType = MUSIC_AUDIO_MP3; 1323 music.ctxData = ctxMp3; 1324 1325 if (result > 0) 1326 { 1327 music.stream = LoadAudioStream(ctxMp3->sampleRate, 32, ctxMp3->channels); 1328 music.frameCount = (unsigned int)drmp3_get_pcm_frame_count(ctxMp3); 1329 music.looping = true; // Looping enabled by default 1330 musicLoaded = true; 1331 } 1332 } 1333 #endif 1334 #if defined(SUPPORT_FILEFORMAT_XM) 1335 else if (IsFileExtension(fileName, ".xm")) 1336 { 1337 jar_xm_context_t *ctxXm = NULL; 1338 int result = jar_xm_create_context_from_file(&ctxXm, AUDIO.System.device.sampleRate, fileName); 1339 1340 music.ctxType = MUSIC_MODULE_XM; 1341 music.ctxData = ctxXm; 1342 1343 if (result == 0) // XM AUDIO.System.context created successfully 1344 { 1345 jar_xm_set_max_loop_count(ctxXm, 0); // Set infinite number of loops 1346 1347 unsigned int bits = 32; 1348 if (AUDIO_DEVICE_FORMAT == ma_format_s16) bits = 16; 1349 else if (AUDIO_DEVICE_FORMAT == ma_format_u8) bits = 8; 1350 1351 // NOTE: Only stereo is supported for XM 1352 music.stream = LoadAudioStream(AUDIO.System.device.sampleRate, bits, AUDIO_DEVICE_CHANNELS); 1353 music.frameCount = (unsigned int)jar_xm_get_remaining_samples(ctxXm); // NOTE: Always 2 channels (stereo) 1354 music.looping = true; // Looping enabled by default 1355 jar_xm_reset(ctxXm); // make sure we start at the beginning of the song 1356 musicLoaded = true; 1357 } 1358 } 1359 #endif 1360 #if defined(SUPPORT_FILEFORMAT_MOD) 1361 else if (IsFileExtension(fileName, ".mod")) 1362 { 1363 jar_mod_context_t *ctxMod = RL_CALLOC(1, sizeof(jar_mod_context_t)); 1364 jar_mod_init(ctxMod); 1365 int result = jar_mod_load_file(ctxMod, fileName); 1366 1367 music.ctxType = MUSIC_MODULE_MOD; 1368 music.ctxData = ctxMod; 1369 1370 if (result > 0) 1371 { 1372 // NOTE: Only stereo is supported for MOD 1373 music.stream = LoadAudioStream(AUDIO.System.device.sampleRate, 16, AUDIO_DEVICE_CHANNELS); 1374 music.frameCount = (unsigned int)jar_mod_max_samples(ctxMod); // NOTE: Always 2 channels (stereo) 1375 music.looping = true; // Looping enabled by default 1376 musicLoaded = true; 1377 } 1378 } 1379 #endif 1380 else TRACELOG(LOG_WARNING, "STREAM: [%s] File format not supported", fileName); 1381 1382 if (!musicLoaded) 1383 { 1384 if (false) { } 1385 #if defined(SUPPORT_FILEFORMAT_WAV) 1386 else if (music.ctxType == MUSIC_AUDIO_WAV) drwav_uninit((drwav *)music.ctxData); 1387 #endif 1388 #if defined(SUPPORT_FILEFORMAT_OGG) 1389 else if (music.ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close((stb_vorbis *)music.ctxData); 1390 #endif 1391 #if defined(SUPPORT_FILEFORMAT_FLAC) 1392 else if (music.ctxType == MUSIC_AUDIO_FLAC) drflac_free((drflac *)music.ctxData, NULL); 1393 #endif 1394 #if defined(SUPPORT_FILEFORMAT_MP3) 1395 else if (music.ctxType == MUSIC_AUDIO_MP3) { drmp3_uninit((drmp3 *)music.ctxData); RL_FREE(music.ctxData); } 1396 #endif 1397 #if defined(SUPPORT_FILEFORMAT_XM) 1398 else if (music.ctxType == MUSIC_MODULE_XM) jar_xm_free_context((jar_xm_context_t *)music.ctxData); 1399 #endif 1400 #if defined(SUPPORT_FILEFORMAT_MOD) 1401 else if (music.ctxType == MUSIC_MODULE_MOD) { jar_mod_unload((jar_mod_context_t *)music.ctxData); RL_FREE(music.ctxData); } 1402 #endif 1403 1404 music.ctxData = NULL; 1405 TRACELOG(LOG_WARNING, "FILEIO: [%s] Music file could not be opened", fileName); 1406 } 1407 else 1408 { 1409 // Show some music stream info 1410 TRACELOG(LOG_INFO, "FILEIO: [%s] Music file loaded successfully", fileName); 1411 TRACELOG(LOG_INFO, " > Sample rate: %i Hz", music.stream.sampleRate); 1412 TRACELOG(LOG_INFO, " > Sample size: %i bits", music.stream.sampleSize); 1413 TRACELOG(LOG_INFO, " > Channels: %i (%s)", music.stream.channels, (music.stream.channels == 1)? "Mono" : (music.stream.channels == 2)? "Stereo" : "Multi"); 1414 TRACELOG(LOG_INFO, " > Total frames: %i", music.frameCount); 1415 } 1416 1417 return music; 1418 } 1419 1420 // Load music stream from memory buffer, fileType refers to extension: i.e. ".wav" 1421 // WARNING: File extension must be provided in lower-case 1422 Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, int dataSize) 1423 { 1424 Music music = { 0 }; 1425 bool musicLoaded = false; 1426 1427 if (false) { } 1428 #if defined(SUPPORT_FILEFORMAT_WAV) 1429 else if (strcmp(fileType, ".wav") == 0) 1430 { 1431 drwav *ctxWav = RL_CALLOC(1, sizeof(drwav)); 1432 1433 bool success = drwav_init_memory(ctxWav, (const void *)data, dataSize, NULL); 1434 1435 music.ctxType = MUSIC_AUDIO_WAV; 1436 music.ctxData = ctxWav; 1437 1438 if (success) 1439 { 1440 int sampleSize = ctxWav->bitsPerSample; 1441 if (ctxWav->bitsPerSample == 24) sampleSize = 16; // Forcing conversion to s16 on UpdateMusicStream() 1442 1443 music.stream = LoadAudioStream(ctxWav->sampleRate, sampleSize, ctxWav->channels); 1444 music.frameCount = (unsigned int)ctxWav->totalPCMFrameCount; 1445 music.looping = true; // Looping enabled by default 1446 musicLoaded = true; 1447 } 1448 } 1449 #endif 1450 #if defined(SUPPORT_FILEFORMAT_FLAC) 1451 else if (strcmp(fileType, ".flac") == 0) 1452 { 1453 music.ctxType = MUSIC_AUDIO_FLAC; 1454 music.ctxData = drflac_open_memory((const void*)data, dataSize, NULL); 1455 1456 if (music.ctxData != NULL) 1457 { 1458 drflac *ctxFlac = (drflac *)music.ctxData; 1459 1460 music.stream = LoadAudioStream(ctxFlac->sampleRate, ctxFlac->bitsPerSample, ctxFlac->channels); 1461 music.frameCount = (unsigned int)ctxFlac->totalPCMFrameCount; 1462 music.looping = true; // Looping enabled by default 1463 musicLoaded = true; 1464 } 1465 } 1466 #endif 1467 #if defined(SUPPORT_FILEFORMAT_MP3) 1468 else if (strcmp(fileType, ".mp3") == 0) 1469 { 1470 drmp3 *ctxMp3 = RL_CALLOC(1, sizeof(drmp3)); 1471 int success = drmp3_init_memory(ctxMp3, (const void*)data, dataSize, NULL); 1472 1473 music.ctxType = MUSIC_AUDIO_MP3; 1474 music.ctxData = ctxMp3; 1475 1476 if (success) 1477 { 1478 music.stream = LoadAudioStream(ctxMp3->sampleRate, 32, ctxMp3->channels); 1479 music.frameCount = (unsigned int)drmp3_get_pcm_frame_count(ctxMp3); 1480 music.looping = true; // Looping enabled by default 1481 musicLoaded = true; 1482 } 1483 } 1484 #endif 1485 #if defined(SUPPORT_FILEFORMAT_OGG) 1486 else if (strcmp(fileType, ".ogg") == 0) 1487 { 1488 // Open ogg audio stream 1489 music.ctxType = MUSIC_AUDIO_OGG; 1490 //music.ctxData = stb_vorbis_open_filename(fileName, NULL, NULL); 1491 music.ctxData = stb_vorbis_open_memory((const unsigned char *)data, dataSize, NULL, NULL); 1492 1493 if (music.ctxData != NULL) 1494 { 1495 stb_vorbis_info info = stb_vorbis_get_info((stb_vorbis *)music.ctxData); // Get Ogg file info 1496 1497 // OGG bit rate defaults to 16 bit, it's enough for compressed format 1498 music.stream = LoadAudioStream(info.sample_rate, 16, info.channels); 1499 1500 // WARNING: It seems this function returns length in frames, not samples, so we multiply by channels 1501 music.frameCount = (unsigned int)stb_vorbis_stream_length_in_samples((stb_vorbis *)music.ctxData); 1502 music.looping = true; // Looping enabled by default 1503 musicLoaded = true; 1504 } 1505 } 1506 #endif 1507 #if defined(SUPPORT_FILEFORMAT_XM) 1508 else if (strcmp(fileType, ".xm") == 0) 1509 { 1510 jar_xm_context_t *ctxXm = NULL; 1511 int result = jar_xm_create_context_safe(&ctxXm, (const char *)data, dataSize, AUDIO.System.device.sampleRate); 1512 if (result == 0) // XM AUDIO.System.context created successfully 1513 { 1514 music.ctxType = MUSIC_MODULE_XM; 1515 jar_xm_set_max_loop_count(ctxXm, 0); // Set infinite number of loops 1516 1517 unsigned int bits = 32; 1518 if (AUDIO_DEVICE_FORMAT == ma_format_s16) bits = 16; 1519 else if (AUDIO_DEVICE_FORMAT == ma_format_u8) bits = 8; 1520 1521 // NOTE: Only stereo is supported for XM 1522 music.stream = LoadAudioStream(AUDIO.System.device.sampleRate, bits, 2); 1523 music.frameCount = (unsigned int)jar_xm_get_remaining_samples(ctxXm); // NOTE: Always 2 channels (stereo) 1524 music.looping = true; // Looping enabled by default 1525 jar_xm_reset(ctxXm); // make sure we start at the beginning of the song 1526 1527 music.ctxData = ctxXm; 1528 musicLoaded = true; 1529 } 1530 } 1531 #endif 1532 #if defined(SUPPORT_FILEFORMAT_MOD) 1533 else if (strcmp(fileType, ".mod") == 0) 1534 { 1535 jar_mod_context_t *ctxMod = (jar_mod_context_t *)RL_MALLOC(sizeof(jar_mod_context_t)); 1536 int result = 0; 1537 1538 jar_mod_init(ctxMod); 1539 1540 // Copy data to allocated memory for default UnloadMusicStream 1541 unsigned char *newData = (unsigned char *)RL_MALLOC(dataSize); 1542 int it = dataSize/sizeof(unsigned char); 1543 for (int i = 0; i < it; i++) newData[i] = data[i]; 1544 1545 // Memory loaded version for jar_mod_load_file() 1546 if (dataSize && (dataSize < 32*1024*1024)) 1547 { 1548 ctxMod->modfilesize = dataSize; 1549 ctxMod->modfile = newData; 1550 if (jar_mod_load(ctxMod, (void *)ctxMod->modfile, dataSize)) result = dataSize; 1551 } 1552 1553 if (result > 0) 1554 { 1555 music.ctxType = MUSIC_MODULE_MOD; 1556 1557 // NOTE: Only stereo is supported for MOD 1558 music.stream = LoadAudioStream(AUDIO.System.device.sampleRate, 16, 2); 1559 music.frameCount = (unsigned int)jar_mod_max_samples(ctxMod); // NOTE: Always 2 channels (stereo) 1560 music.looping = true; // Looping enabled by default 1561 musicLoaded = true; 1562 1563 music.ctxData = ctxMod; 1564 musicLoaded = true; 1565 } 1566 } 1567 #endif 1568 else TRACELOG(LOG_WARNING, "STREAM: Data format not supported"); 1569 1570 if (!musicLoaded) 1571 { 1572 if (false) { } 1573 #if defined(SUPPORT_FILEFORMAT_WAV) 1574 else if (music.ctxType == MUSIC_AUDIO_WAV) drwav_uninit((drwav *)music.ctxData); 1575 #endif 1576 #if defined(SUPPORT_FILEFORMAT_FLAC) 1577 else if (music.ctxType == MUSIC_AUDIO_FLAC) drflac_free((drflac *)music.ctxData, NULL); 1578 #endif 1579 #if defined(SUPPORT_FILEFORMAT_MP3) 1580 else if (music.ctxType == MUSIC_AUDIO_MP3) { drmp3_uninit((drmp3 *)music.ctxData); RL_FREE(music.ctxData); } 1581 #endif 1582 #if defined(SUPPORT_FILEFORMAT_OGG) 1583 else if (music.ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close((stb_vorbis *)music.ctxData); 1584 #endif 1585 #if defined(SUPPORT_FILEFORMAT_XM) 1586 else if (music.ctxType == MUSIC_MODULE_XM) jar_xm_free_context((jar_xm_context_t *)music.ctxData); 1587 #endif 1588 #if defined(SUPPORT_FILEFORMAT_MOD) 1589 else if (music.ctxType == MUSIC_MODULE_MOD) { jar_mod_unload((jar_mod_context_t *)music.ctxData); RL_FREE(music.ctxData); } 1590 #endif 1591 1592 music.ctxData = NULL; 1593 TRACELOG(LOG_WARNING, "FILEIO: Music data could not be loaded"); 1594 } 1595 else 1596 { 1597 // Show some music stream info 1598 TRACELOG(LOG_INFO, "FILEIO: Music data loaded successfully"); 1599 TRACELOG(LOG_INFO, " > Sample rate: %i Hz", music.stream.sampleRate); 1600 TRACELOG(LOG_INFO, " > Sample size: %i bits", music.stream.sampleSize); 1601 TRACELOG(LOG_INFO, " > Channels: %i (%s)", music.stream.channels, (music.stream.channels == 1)? "Mono" : (music.stream.channels == 2)? "Stereo" : "Multi"); 1602 TRACELOG(LOG_INFO, " > Total frames: %i", music.frameCount); 1603 } 1604 1605 return music; 1606 } 1607 1608 // Unload music stream 1609 void UnloadMusicStream(Music music) 1610 { 1611 UnloadAudioStream(music.stream); 1612 1613 if (music.ctxData != NULL) 1614 { 1615 if (false) { } 1616 #if defined(SUPPORT_FILEFORMAT_WAV) 1617 else if (music.ctxType == MUSIC_AUDIO_WAV) drwav_uninit((drwav *)music.ctxData); 1618 #endif 1619 #if defined(SUPPORT_FILEFORMAT_OGG) 1620 else if (music.ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close((stb_vorbis *)music.ctxData); 1621 #endif 1622 #if defined(SUPPORT_FILEFORMAT_FLAC) 1623 else if (music.ctxType == MUSIC_AUDIO_FLAC) drflac_free((drflac *)music.ctxData, NULL); 1624 #endif 1625 #if defined(SUPPORT_FILEFORMAT_MP3) 1626 else if (music.ctxType == MUSIC_AUDIO_MP3) { drmp3_uninit((drmp3 *)music.ctxData); RL_FREE(music.ctxData); } 1627 #endif 1628 #if defined(SUPPORT_FILEFORMAT_XM) 1629 else if (music.ctxType == MUSIC_MODULE_XM) jar_xm_free_context((jar_xm_context_t *)music.ctxData); 1630 #endif 1631 #if defined(SUPPORT_FILEFORMAT_MOD) 1632 else if (music.ctxType == MUSIC_MODULE_MOD) { jar_mod_unload((jar_mod_context_t *)music.ctxData); RL_FREE(music.ctxData); } 1633 #endif 1634 } 1635 } 1636 1637 // Start music playing (open stream) 1638 void PlayMusicStream(Music music) 1639 { 1640 if (music.stream.buffer != NULL) 1641 { 1642 // For music streams, we need to make sure we maintain the frame cursor position 1643 // This is a hack for this section of code in UpdateMusicStream() 1644 // NOTE: In case window is minimized, music stream is stopped, just make sure to 1645 // play again on window restore: if (IsMusicStreamPlaying(music)) PlayMusicStream(music); 1646 ma_uint32 frameCursorPos = music.stream.buffer->frameCursorPos; 1647 PlayAudioStream(music.stream); // WARNING: This resets the cursor position. 1648 music.stream.buffer->frameCursorPos = frameCursorPos; 1649 } 1650 } 1651 1652 // Pause music playing 1653 void PauseMusicStream(Music music) 1654 { 1655 PauseAudioStream(music.stream); 1656 } 1657 1658 // Resume music playing 1659 void ResumeMusicStream(Music music) 1660 { 1661 ResumeAudioStream(music.stream); 1662 } 1663 1664 // Stop music playing (close stream) 1665 void StopMusicStream(Music music) 1666 { 1667 StopAudioStream(music.stream); 1668 1669 switch (music.ctxType) 1670 { 1671 #if defined(SUPPORT_FILEFORMAT_WAV) 1672 case MUSIC_AUDIO_WAV: drwav_seek_to_first_pcm_frame((drwav *)music.ctxData); break; 1673 #endif 1674 #if defined(SUPPORT_FILEFORMAT_OGG) 1675 case MUSIC_AUDIO_OGG: stb_vorbis_seek_start((stb_vorbis *)music.ctxData); break; 1676 #endif 1677 #if defined(SUPPORT_FILEFORMAT_FLAC) 1678 case MUSIC_AUDIO_FLAC: drflac__seek_to_first_frame((drflac *)music.ctxData); break; 1679 #endif 1680 #if defined(SUPPORT_FILEFORMAT_MP3) 1681 case MUSIC_AUDIO_MP3: drmp3_seek_to_start_of_stream((drmp3 *)music.ctxData); break; 1682 #endif 1683 #if defined(SUPPORT_FILEFORMAT_XM) 1684 case MUSIC_MODULE_XM: jar_xm_reset((jar_xm_context_t *)music.ctxData); break; 1685 #endif 1686 #if defined(SUPPORT_FILEFORMAT_MOD) 1687 case MUSIC_MODULE_MOD: jar_mod_seek_start((jar_mod_context_t *)music.ctxData); break; 1688 #endif 1689 default: break; 1690 } 1691 } 1692 1693 // Seek music to a certain position (in seconds) 1694 void SeekMusicStream(Music music, float position) 1695 { 1696 // Seeking is not supported in module formats 1697 if ((music.ctxType == MUSIC_MODULE_XM) || (music.ctxType == MUSIC_MODULE_MOD)) return; 1698 1699 unsigned int positionInFrames = (unsigned int)(position*music.stream.sampleRate); 1700 1701 switch (music.ctxType) 1702 { 1703 #if defined(SUPPORT_FILEFORMAT_WAV) 1704 case MUSIC_AUDIO_WAV: drwav_seek_to_pcm_frame((drwav *)music.ctxData, positionInFrames); break; 1705 #endif 1706 #if defined(SUPPORT_FILEFORMAT_OGG) 1707 case MUSIC_AUDIO_OGG: stb_vorbis_seek_frame((stb_vorbis *)music.ctxData, positionInFrames); break; 1708 #endif 1709 #if defined(SUPPORT_FILEFORMAT_FLAC) 1710 case MUSIC_AUDIO_FLAC: drflac_seek_to_pcm_frame((drflac *)music.ctxData, positionInFrames); break; 1711 #endif 1712 #if defined(SUPPORT_FILEFORMAT_MP3) 1713 case MUSIC_AUDIO_MP3: drmp3_seek_to_pcm_frame((drmp3 *)music.ctxData, positionInFrames); break; 1714 #endif 1715 default: break; 1716 } 1717 1718 music.stream.buffer->framesProcessed = positionInFrames; 1719 } 1720 1721 // Update (re-fill) music buffers if data already processed 1722 void UpdateMusicStream(Music music) 1723 { 1724 if (music.stream.buffer == NULL) return; 1725 1726 unsigned int subBufferSizeInFrames = music.stream.buffer->sizeInFrames/2; 1727 1728 // On first call of this function we lazily pre-allocated a temp buffer to read audio files/memory data in 1729 int frameSize = music.stream.channels*music.stream.sampleSize/8; 1730 unsigned int pcmSize = subBufferSizeInFrames*frameSize; 1731 if (AUDIO.System.pcmBufferSize < pcmSize) 1732 { 1733 RL_FREE(AUDIO.System.pcmBuffer); 1734 AUDIO.System.pcmBuffer = RL_CALLOC(1, pcmSize); 1735 AUDIO.System.pcmBufferSize = pcmSize; 1736 } 1737 1738 // Check both sub-buffers to check if they require refilling 1739 for (int i = 0; i < 2; i++) 1740 { 1741 if ((music.stream.buffer != NULL) && !music.stream.buffer->isSubBufferProcessed[i]) continue; // No refilling required, move to next sub-buffer 1742 1743 unsigned int framesLeft = music.frameCount - music.stream.buffer->framesProcessed; // Frames left to be processed 1744 unsigned int framesToStream = 0; // Total frames to be streamed 1745 if ((framesLeft >= subBufferSizeInFrames) || music.looping) framesToStream = subBufferSizeInFrames; 1746 else framesToStream = framesLeft; 1747 1748 int frameCountStillNeeded = framesToStream; 1749 int frameCountRedTotal = 0; 1750 switch (music.ctxType) 1751 { 1752 #if defined(SUPPORT_FILEFORMAT_WAV) 1753 case MUSIC_AUDIO_WAV: 1754 { 1755 if (music.stream.sampleSize == 16) 1756 { 1757 while (true) 1758 { 1759 int frameCountRed = drwav_read_pcm_frames_s16((drwav *)music.ctxData, frameCountStillNeeded, (short *)((char *)AUDIO.System.pcmBuffer + frameCountRedTotal*frameSize)); 1760 frameCountRedTotal += frameCountRed; 1761 frameCountStillNeeded -= frameCountRed; 1762 if (frameCountStillNeeded == 0) break; 1763 else drwav_seek_to_first_pcm_frame((drwav *)music.ctxData); 1764 } 1765 } 1766 else if (music.stream.sampleSize == 32) 1767 { 1768 while (true) 1769 { 1770 int frameCountRed = drwav_read_pcm_frames_f32((drwav *)music.ctxData, frameCountStillNeeded, (float *)((char *)AUDIO.System.pcmBuffer + frameCountRedTotal*frameSize)); 1771 frameCountRedTotal += frameCountRed; 1772 frameCountStillNeeded -= frameCountRed; 1773 if (frameCountStillNeeded == 0) break; 1774 else drwav_seek_to_first_pcm_frame((drwav *)music.ctxData); 1775 } 1776 } 1777 } break; 1778 #endif 1779 #if defined(SUPPORT_FILEFORMAT_OGG) 1780 case MUSIC_AUDIO_OGG: 1781 { 1782 while (true) 1783 { 1784 int frameCountRed = stb_vorbis_get_samples_short_interleaved((stb_vorbis *)music.ctxData, music.stream.channels, (short *)((char *)AUDIO.System.pcmBuffer + frameCountRedTotal*frameSize), frameCountStillNeeded*music.stream.channels); 1785 frameCountRedTotal += frameCountRed; 1786 frameCountStillNeeded -= frameCountRed; 1787 if (frameCountStillNeeded == 0) break; 1788 else stb_vorbis_seek_start((stb_vorbis *)music.ctxData); 1789 } 1790 } break; 1791 #endif 1792 #if defined(SUPPORT_FILEFORMAT_FLAC) 1793 case MUSIC_AUDIO_FLAC: 1794 { 1795 while (true) 1796 { 1797 int frameCountRed = drflac_read_pcm_frames_s16((drflac *)music.ctxData, frameCountStillNeeded, (short *)((char *)AUDIO.System.pcmBuffer + frameCountRedTotal*frameSize)); 1798 frameCountRedTotal += frameCountRed; 1799 frameCountStillNeeded -= frameCountRed; 1800 if (frameCountStillNeeded == 0) break; 1801 else drflac__seek_to_first_frame((drflac *)music.ctxData); 1802 } 1803 } break; 1804 #endif 1805 #if defined(SUPPORT_FILEFORMAT_MP3) 1806 case MUSIC_AUDIO_MP3: 1807 { 1808 while (true) 1809 { 1810 int frameCountRed = drmp3_read_pcm_frames_f32((drmp3 *)music.ctxData, frameCountStillNeeded, (float *)((char *)AUDIO.System.pcmBuffer + frameCountRedTotal*frameSize)); 1811 frameCountRedTotal += frameCountRed; 1812 frameCountStillNeeded -= frameCountRed; 1813 if (frameCountStillNeeded == 0) break; 1814 else drmp3_seek_to_start_of_stream((drmp3 *)music.ctxData); 1815 } 1816 } break; 1817 #endif 1818 #if defined(SUPPORT_FILEFORMAT_XM) 1819 case MUSIC_MODULE_XM: 1820 { 1821 // NOTE: Internally we consider 2 channels generation, so sampleCount/2 1822 if (AUDIO_DEVICE_FORMAT == ma_format_f32) jar_xm_generate_samples((jar_xm_context_t *)music.ctxData, (float *)AUDIO.System.pcmBuffer, framesToStream); 1823 else if (AUDIO_DEVICE_FORMAT == ma_format_s16) jar_xm_generate_samples_16bit((jar_xm_context_t *)music.ctxData, (short *)AUDIO.System.pcmBuffer, framesToStream); 1824 else if (AUDIO_DEVICE_FORMAT == ma_format_u8) jar_xm_generate_samples_8bit((jar_xm_context_t *)music.ctxData, (char *)AUDIO.System.pcmBuffer, framesToStream); 1825 1826 //jar_xm_reset((jar_xm_context_t *)music.ctxData); 1827 1828 } break; 1829 #endif 1830 #if defined(SUPPORT_FILEFORMAT_MOD) 1831 case MUSIC_MODULE_MOD: 1832 { 1833 // NOTE: 3rd parameter (nbsample) specify the number of stereo 16bits samples you want, so sampleCount/2 1834 jar_mod_fillbuffer((jar_mod_context_t *)music.ctxData, (short *)AUDIO.System.pcmBuffer, framesToStream, 0); 1835 1836 //jar_mod_seek_start((jar_mod_context_t *)music.ctxData); 1837 1838 } break; 1839 #endif 1840 default: break; 1841 } 1842 1843 UpdateAudioStream(music.stream, AUDIO.System.pcmBuffer, framesToStream); 1844 1845 music.stream.buffer->framesProcessed = music.stream.buffer->framesProcessed%music.frameCount; 1846 1847 if (framesLeft <= subBufferSizeInFrames) 1848 { 1849 if (!music.looping) 1850 { 1851 // Streaming is ending, we filled latest frames from input 1852 StopMusicStream(music); 1853 return; 1854 } 1855 } 1856 } 1857 1858 // NOTE: In case window is minimized, music stream is stopped, 1859 // just make sure to play again on window restore 1860 if (IsMusicStreamPlaying(music)) PlayMusicStream(music); 1861 } 1862 1863 // Check if any music is playing 1864 bool IsMusicStreamPlaying(Music music) 1865 { 1866 return IsAudioStreamPlaying(music.stream); 1867 } 1868 1869 // Set volume for music 1870 void SetMusicVolume(Music music, float volume) 1871 { 1872 SetAudioStreamVolume(music.stream, volume); 1873 } 1874 1875 // Set pitch for music 1876 void SetMusicPitch(Music music, float pitch) 1877 { 1878 SetAudioBufferPitch(music.stream.buffer, pitch); 1879 } 1880 1881 // Set pan for a music 1882 void SetMusicPan(Music music, float pan) 1883 { 1884 SetAudioBufferPan(music.stream.buffer, pan); 1885 } 1886 1887 // Get music time length (in seconds) 1888 float GetMusicTimeLength(Music music) 1889 { 1890 float totalSeconds = 0.0f; 1891 1892 totalSeconds = (float)music.frameCount/music.stream.sampleRate; 1893 1894 return totalSeconds; 1895 } 1896 1897 // Get current music time played (in seconds) 1898 float GetMusicTimePlayed(Music music) 1899 { 1900 float secondsPlayed = 0.0f; 1901 if (music.stream.buffer != NULL) 1902 { 1903 #if defined(SUPPORT_FILEFORMAT_XM) 1904 if (music.ctxType == MUSIC_MODULE_XM) 1905 { 1906 uint64_t framesPlayed = 0; 1907 1908 jar_xm_get_position(music.ctxData, NULL, NULL, NULL, &framesPlayed); 1909 secondsPlayed = (float)framesPlayed/music.stream.sampleRate; 1910 } 1911 else 1912 #endif 1913 { 1914 //ma_uint32 frameSizeInBytes = ma_get_bytes_per_sample(music.stream.buffer->dsp.formatConverterIn.config.formatIn)*music.stream.buffer->dsp.formatConverterIn.config.channels; 1915 int framesProcessed = (int)music.stream.buffer->framesProcessed; 1916 int subBufferSize = (int)music.stream.buffer->sizeInFrames/2; 1917 int framesInFirstBuffer = music.stream.buffer->isSubBufferProcessed[0]? 0 : subBufferSize; 1918 int framesInSecondBuffer = music.stream.buffer->isSubBufferProcessed[1]? 0 : subBufferSize; 1919 int framesSentToMix = music.stream.buffer->frameCursorPos%subBufferSize; 1920 int framesPlayed = (framesProcessed - framesInFirstBuffer - framesInSecondBuffer + framesSentToMix)%(int)music.frameCount; 1921 if (framesPlayed < 0) framesPlayed += music.frameCount; 1922 secondsPlayed = (float)framesPlayed/music.stream.sampleRate; 1923 } 1924 } 1925 1926 return secondsPlayed; 1927 } 1928 1929 // Load audio stream (to stream audio pcm data) 1930 AudioStream LoadAudioStream(unsigned int sampleRate, unsigned int sampleSize, unsigned int channels) 1931 { 1932 AudioStream stream = { 0 }; 1933 1934 stream.sampleRate = sampleRate; 1935 stream.sampleSize = sampleSize; 1936 stream.channels = channels; 1937 1938 ma_format formatIn = ((stream.sampleSize == 8)? ma_format_u8 : ((stream.sampleSize == 16)? ma_format_s16 : ma_format_f32)); 1939 1940 // The size of a streaming buffer must be at least double the size of a period 1941 unsigned int periodSize = AUDIO.System.device.playback.internalPeriodSizeInFrames; 1942 1943 // If the buffer is not set, compute one that would give us a buffer good enough for a decent frame rate 1944 unsigned int subBufferSize = (AUDIO.Buffer.defaultSize == 0)? AUDIO.System.device.sampleRate/30 : AUDIO.Buffer.defaultSize; 1945 1946 if (subBufferSize < periodSize) subBufferSize = periodSize; 1947 1948 // Create a double audio buffer of defined size 1949 stream.buffer = LoadAudioBuffer(formatIn, stream.channels, stream.sampleRate, subBufferSize*2, AUDIO_BUFFER_USAGE_STREAM); 1950 1951 if (stream.buffer != NULL) 1952 { 1953 stream.buffer->looping = true; // Always loop for streaming buffers 1954 TRACELOG(LOG_INFO, "STREAM: Initialized successfully (%i Hz, %i bit, %s)", stream.sampleRate, stream.sampleSize, (stream.channels == 1)? "Mono" : "Stereo"); 1955 } 1956 else TRACELOG(LOG_WARNING, "STREAM: Failed to load audio buffer, stream could not be created"); 1957 1958 return stream; 1959 } 1960 1961 // Unload audio stream and free memory 1962 void UnloadAudioStream(AudioStream stream) 1963 { 1964 UnloadAudioBuffer(stream.buffer); 1965 1966 TRACELOG(LOG_INFO, "STREAM: Unloaded audio stream data from RAM"); 1967 } 1968 1969 // Update audio stream buffers with data 1970 // NOTE 1: Only updates one buffer of the stream source: unqueue -> update -> queue 1971 // NOTE 2: To unqueue a buffer it needs to be processed: IsAudioStreamProcessed() 1972 void UpdateAudioStream(AudioStream stream, const void *data, int frameCount) 1973 { 1974 if (stream.buffer != NULL) 1975 { 1976 if (stream.buffer->isSubBufferProcessed[0] || stream.buffer->isSubBufferProcessed[1]) 1977 { 1978 ma_uint32 subBufferToUpdate = 0; 1979 1980 if (stream.buffer->isSubBufferProcessed[0] && stream.buffer->isSubBufferProcessed[1]) 1981 { 1982 // Both buffers are available for updating. 1983 // Update the first one and make sure the cursor is moved back to the front. 1984 subBufferToUpdate = 0; 1985 stream.buffer->frameCursorPos = 0; 1986 } 1987 else 1988 { 1989 // Just update whichever sub-buffer is processed. 1990 subBufferToUpdate = (stream.buffer->isSubBufferProcessed[0])? 0 : 1; 1991 } 1992 1993 ma_uint32 subBufferSizeInFrames = stream.buffer->sizeInFrames/2; 1994 unsigned char *subBuffer = stream.buffer->data + ((subBufferSizeInFrames*stream.channels*(stream.sampleSize/8))*subBufferToUpdate); 1995 1996 // Total frames processed in buffer is always the complete size, filled with 0 if required 1997 stream.buffer->framesProcessed += subBufferSizeInFrames; 1998 1999 // Does this API expect a whole buffer to be updated in one go? 2000 // Assuming so, but if not will need to change this logic. 2001 if (subBufferSizeInFrames >= (ma_uint32)frameCount) 2002 { 2003 ma_uint32 framesToWrite = (ma_uint32)frameCount; 2004 2005 ma_uint32 bytesToWrite = framesToWrite*stream.channels*(stream.sampleSize/8); 2006 memcpy(subBuffer, data, bytesToWrite); 2007 2008 // Any leftover frames should be filled with zeros. 2009 ma_uint32 leftoverFrameCount = subBufferSizeInFrames - framesToWrite; 2010 2011 if (leftoverFrameCount > 0) memset(subBuffer + bytesToWrite, 0, leftoverFrameCount*stream.channels*(stream.sampleSize/8)); 2012 2013 stream.buffer->isSubBufferProcessed[subBufferToUpdate] = false; 2014 } 2015 else TRACELOG(LOG_WARNING, "STREAM: Attempting to write too many frames to buffer"); 2016 } 2017 else TRACELOG(LOG_WARNING, "STREAM: Buffer not available for updating"); 2018 } 2019 } 2020 2021 // Check if any audio stream buffers requires refill 2022 bool IsAudioStreamProcessed(AudioStream stream) 2023 { 2024 if (stream.buffer == NULL) return false; 2025 2026 return (stream.buffer->isSubBufferProcessed[0] || stream.buffer->isSubBufferProcessed[1]); 2027 } 2028 2029 // Play audio stream 2030 void PlayAudioStream(AudioStream stream) 2031 { 2032 PlayAudioBuffer(stream.buffer); 2033 } 2034 2035 // Play audio stream 2036 void PauseAudioStream(AudioStream stream) 2037 { 2038 PauseAudioBuffer(stream.buffer); 2039 } 2040 2041 // Resume audio stream playing 2042 void ResumeAudioStream(AudioStream stream) 2043 { 2044 ResumeAudioBuffer(stream.buffer); 2045 } 2046 2047 // Check if audio stream is playing. 2048 bool IsAudioStreamPlaying(AudioStream stream) 2049 { 2050 return IsAudioBufferPlaying(stream.buffer); 2051 } 2052 2053 // Stop audio stream 2054 void StopAudioStream(AudioStream stream) 2055 { 2056 StopAudioBuffer(stream.buffer); 2057 } 2058 2059 // Set volume for audio stream (1.0 is max level) 2060 void SetAudioStreamVolume(AudioStream stream, float volume) 2061 { 2062 SetAudioBufferVolume(stream.buffer, volume); 2063 } 2064 2065 // Set pitch for audio stream (1.0 is base level) 2066 void SetAudioStreamPitch(AudioStream stream, float pitch) 2067 { 2068 SetAudioBufferPitch(stream.buffer, pitch); 2069 } 2070 2071 // Set pan for audio stream 2072 void SetAudioStreamPan(AudioStream stream, float pan) 2073 { 2074 SetAudioBufferPan(stream.buffer, pan); 2075 } 2076 2077 // Default size for new audio streams 2078 void SetAudioStreamBufferSizeDefault(int size) 2079 { 2080 AUDIO.Buffer.defaultSize = size; 2081 } 2082 2083 // Audio thread callback to request new data 2084 void SetAudioStreamCallback(AudioStream stream, AudioCallback callback) 2085 { 2086 if (stream.buffer != NULL) stream.buffer->callback = callback; 2087 } 2088 2089 // Add processor to audio stream. Contrary to buffers, the order of processors is important. 2090 // The new processor must be added at the end. As there aren't supposed to be a lot of processors attached to 2091 // a given stream, we iterate through the list to find the end. That way we don't need a pointer to the last element. 2092 void AttachAudioStreamProcessor(AudioStream stream, AudioCallback process) 2093 { 2094 ma_mutex_lock(&AUDIO.System.lock); 2095 2096 rAudioProcessor *processor = (rAudioProcessor *)RL_CALLOC(1, sizeof(rAudioProcessor)); 2097 processor->process = process; 2098 2099 rAudioProcessor *last = stream.buffer->processor; 2100 2101 while (last && last->next) 2102 { 2103 last = last->next; 2104 } 2105 if (last) 2106 { 2107 processor->prev = last; 2108 last->next = processor; 2109 } 2110 else stream.buffer->processor = processor; 2111 2112 ma_mutex_unlock(&AUDIO.System.lock); 2113 } 2114 2115 void DetachAudioStreamProcessor(AudioStream stream, AudioCallback process) 2116 { 2117 ma_mutex_lock(&AUDIO.System.lock); 2118 2119 rAudioProcessor *processor = stream.buffer->processor; 2120 2121 while (processor) 2122 { 2123 rAudioProcessor *next = processor->next; 2124 rAudioProcessor *prev = processor->prev; 2125 2126 if (processor->process == process) 2127 { 2128 if (stream.buffer->processor == processor) stream.buffer->processor = next; 2129 if (prev) prev->next = next; 2130 if (next) next->prev = prev; 2131 2132 RL_FREE(processor); 2133 } 2134 2135 processor = next; 2136 } 2137 2138 ma_mutex_unlock(&AUDIO.System.lock); 2139 } 2140 2141 //---------------------------------------------------------------------------------- 2142 // Module specific Functions Definition 2143 //---------------------------------------------------------------------------------- 2144 2145 // Log callback function 2146 static void OnLog(void *pUserData, ma_uint32 level, const char *pMessage) 2147 { 2148 TRACELOG(LOG_WARNING, "miniaudio: %s", pMessage); // All log messages from miniaudio are errors 2149 } 2150 2151 // Reads audio data from an AudioBuffer object in internal format. 2152 static ma_uint32 ReadAudioBufferFramesInInternalFormat(AudioBuffer *audioBuffer, void *framesOut, ma_uint32 frameCount) 2153 { 2154 // Using audio buffer callback 2155 if (audioBuffer->callback) 2156 { 2157 audioBuffer->callback(framesOut, frameCount); 2158 audioBuffer->framesProcessed += frameCount; 2159 2160 return frameCount; 2161 } 2162 2163 ma_uint32 subBufferSizeInFrames = (audioBuffer->sizeInFrames > 1)? audioBuffer->sizeInFrames/2 : audioBuffer->sizeInFrames; 2164 ma_uint32 currentSubBufferIndex = audioBuffer->frameCursorPos/subBufferSizeInFrames; 2165 2166 if (currentSubBufferIndex > 1) return 0; 2167 2168 // Another thread can update the processed state of buffers so 2169 // we just take a copy here to try and avoid potential synchronization problems 2170 bool isSubBufferProcessed[2] = { 0 }; 2171 isSubBufferProcessed[0] = audioBuffer->isSubBufferProcessed[0]; 2172 isSubBufferProcessed[1] = audioBuffer->isSubBufferProcessed[1]; 2173 2174 ma_uint32 frameSizeInBytes = ma_get_bytes_per_frame(audioBuffer->converter.formatIn, audioBuffer->converter.channelsIn); 2175 2176 // Fill out every frame until we find a buffer that's marked as processed. Then fill the remainder with 0 2177 ma_uint32 framesRead = 0; 2178 while (1) 2179 { 2180 // We break from this loop differently depending on the buffer's usage 2181 // - For static buffers, we simply fill as much data as we can 2182 // - For streaming buffers we only fill the halves of the buffer that are processed 2183 // Unprocessed halves must keep their audio data in-tact 2184 if (audioBuffer->usage == AUDIO_BUFFER_USAGE_STATIC) 2185 { 2186 if (framesRead >= frameCount) break; 2187 } 2188 else 2189 { 2190 if (isSubBufferProcessed[currentSubBufferIndex]) break; 2191 } 2192 2193 ma_uint32 totalFramesRemaining = (frameCount - framesRead); 2194 if (totalFramesRemaining == 0) break; 2195 2196 ma_uint32 framesRemainingInOutputBuffer; 2197 if (audioBuffer->usage == AUDIO_BUFFER_USAGE_STATIC) 2198 { 2199 framesRemainingInOutputBuffer = audioBuffer->sizeInFrames - audioBuffer->frameCursorPos; 2200 } 2201 else 2202 { 2203 ma_uint32 firstFrameIndexOfThisSubBuffer = subBufferSizeInFrames*currentSubBufferIndex; 2204 framesRemainingInOutputBuffer = subBufferSizeInFrames - (audioBuffer->frameCursorPos - firstFrameIndexOfThisSubBuffer); 2205 } 2206 2207 ma_uint32 framesToRead = totalFramesRemaining; 2208 if (framesToRead > framesRemainingInOutputBuffer) framesToRead = framesRemainingInOutputBuffer; 2209 2210 memcpy((unsigned char *)framesOut + (framesRead*frameSizeInBytes), audioBuffer->data + (audioBuffer->frameCursorPos*frameSizeInBytes), framesToRead*frameSizeInBytes); 2211 audioBuffer->frameCursorPos = (audioBuffer->frameCursorPos + framesToRead)%audioBuffer->sizeInFrames; 2212 framesRead += framesToRead; 2213 2214 // If we've read to the end of the buffer, mark it as processed 2215 if (framesToRead == framesRemainingInOutputBuffer) 2216 { 2217 audioBuffer->isSubBufferProcessed[currentSubBufferIndex] = true; 2218 isSubBufferProcessed[currentSubBufferIndex] = true; 2219 2220 currentSubBufferIndex = (currentSubBufferIndex + 1)%2; 2221 2222 // We need to break from this loop if we're not looping 2223 if (!audioBuffer->looping) 2224 { 2225 StopAudioBuffer(audioBuffer); 2226 break; 2227 } 2228 } 2229 } 2230 2231 // Zero-fill excess 2232 ma_uint32 totalFramesRemaining = (frameCount - framesRead); 2233 if (totalFramesRemaining > 0) 2234 { 2235 memset((unsigned char *)framesOut + (framesRead*frameSizeInBytes), 0, totalFramesRemaining*frameSizeInBytes); 2236 2237 // For static buffers we can fill the remaining frames with silence for safety, but we don't want 2238 // to report those frames as "read". The reason for this is that the caller uses the return value 2239 // to know whether or not a non-looping sound has finished playback. 2240 if (audioBuffer->usage != AUDIO_BUFFER_USAGE_STATIC) framesRead += totalFramesRemaining; 2241 } 2242 2243 return framesRead; 2244 } 2245 2246 // Reads audio data from an AudioBuffer object in device format. Returned data will be in a format appropriate for mixing. 2247 static ma_uint32 ReadAudioBufferFramesInMixingFormat(AudioBuffer *audioBuffer, float *framesOut, ma_uint32 frameCount) 2248 { 2249 // What's going on here is that we're continuously converting data from the AudioBuffer's internal format to the mixing format, which 2250 // should be defined by the output format of the data converter. We do this until frameCount frames have been output. The important 2251 // detail to remember here is that we never, ever attempt to read more input data than is required for the specified number of output 2252 // frames. This can be achieved with ma_data_converter_get_required_input_frame_count(). 2253 ma_uint8 inputBuffer[4096] = { 0 }; 2254 ma_uint32 inputBufferFrameCap = sizeof(inputBuffer)/ma_get_bytes_per_frame(audioBuffer->converter.formatIn, audioBuffer->converter.channelsIn); 2255 2256 ma_uint32 totalOutputFramesProcessed = 0; 2257 while (totalOutputFramesProcessed < frameCount) 2258 { 2259 ma_uint64 outputFramesToProcessThisIteration = frameCount - totalOutputFramesProcessed; 2260 ma_uint64 inputFramesToProcessThisIteration = 0; 2261 2262 (void)ma_data_converter_get_required_input_frame_count(&audioBuffer->converter, outputFramesToProcessThisIteration, &inputFramesToProcessThisIteration); 2263 if (inputFramesToProcessThisIteration > inputBufferFrameCap) 2264 { 2265 inputFramesToProcessThisIteration = inputBufferFrameCap; 2266 } 2267 2268 float *runningFramesOut = framesOut + (totalOutputFramesProcessed*audioBuffer->converter.channelsOut); 2269 2270 /* At this point we can convert the data to our mixing format. */ 2271 ma_uint64 inputFramesProcessedThisIteration = ReadAudioBufferFramesInInternalFormat(audioBuffer, inputBuffer, (ma_uint32)inputFramesToProcessThisIteration); /* Safe cast. */ 2272 ma_uint64 outputFramesProcessedThisIteration = outputFramesToProcessThisIteration; 2273 ma_data_converter_process_pcm_frames(&audioBuffer->converter, inputBuffer, &inputFramesProcessedThisIteration, runningFramesOut, &outputFramesProcessedThisIteration); 2274 2275 totalOutputFramesProcessed += (ma_uint32)outputFramesProcessedThisIteration; /* Safe cast. */ 2276 2277 if (inputFramesProcessedThisIteration < inputFramesToProcessThisIteration) 2278 { 2279 break; /* Ran out of input data. */ 2280 } 2281 2282 /* 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. */ 2283 if (inputFramesProcessedThisIteration == 0 && outputFramesProcessedThisIteration == 0) 2284 { 2285 break; 2286 } 2287 } 2288 2289 return totalOutputFramesProcessed; 2290 } 2291 2292 2293 // Sending audio data to device callback function 2294 // This function will be called when miniaudio needs more data 2295 // NOTE: All the mixing takes place here 2296 static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const void *pFramesInput, ma_uint32 frameCount) 2297 { 2298 (void)pDevice; 2299 2300 // Mixing is basically just an accumulation, we need to initialize the output buffer to 0 2301 memset(pFramesOut, 0, frameCount*pDevice->playback.channels*ma_get_bytes_per_sample(pDevice->playback.format)); 2302 2303 // Using a mutex here for thread-safety which makes things not real-time 2304 // This is unlikely to be necessary for this project, but may want to consider how you might want to avoid this 2305 ma_mutex_lock(&AUDIO.System.lock); 2306 { 2307 for (AudioBuffer *audioBuffer = AUDIO.Buffer.first; audioBuffer != NULL; audioBuffer = audioBuffer->next) 2308 { 2309 // Ignore stopped or paused sounds 2310 if (!audioBuffer->playing || audioBuffer->paused) continue; 2311 2312 ma_uint32 framesRead = 0; 2313 2314 while (1) 2315 { 2316 if (framesRead >= frameCount) break; 2317 2318 // Just read as much data as we can from the stream 2319 ma_uint32 framesToRead = (frameCount - framesRead); 2320 2321 while (framesToRead > 0) 2322 { 2323 float tempBuffer[1024] = { 0 }; // Frames for stereo 2324 2325 ma_uint32 framesToReadRightNow = framesToRead; 2326 if (framesToReadRightNow > sizeof(tempBuffer)/sizeof(tempBuffer[0])/AUDIO_DEVICE_CHANNELS) 2327 { 2328 framesToReadRightNow = sizeof(tempBuffer)/sizeof(tempBuffer[0])/AUDIO_DEVICE_CHANNELS; 2329 } 2330 2331 ma_uint32 framesJustRead = ReadAudioBufferFramesInMixingFormat(audioBuffer, tempBuffer, framesToReadRightNow); 2332 if (framesJustRead > 0) 2333 { 2334 float *framesOut = (float *)pFramesOut + (framesRead*AUDIO.System.device.playback.channels); 2335 float *framesIn = tempBuffer; 2336 2337 // Apply processors chain if defined 2338 rAudioProcessor *processor = audioBuffer->processor; 2339 while (processor) 2340 { 2341 processor->process(framesIn, framesJustRead); 2342 processor = processor->next; 2343 } 2344 2345 MixAudioFrames(framesOut, framesIn, framesJustRead, audioBuffer); 2346 2347 framesToRead -= framesJustRead; 2348 framesRead += framesJustRead; 2349 } 2350 2351 if (!audioBuffer->playing) 2352 { 2353 framesRead = frameCount; 2354 break; 2355 } 2356 2357 // If we weren't able to read all the frames we requested, break 2358 if (framesJustRead < framesToReadRightNow) 2359 { 2360 if (!audioBuffer->looping) 2361 { 2362 StopAudioBuffer(audioBuffer); 2363 break; 2364 } 2365 else 2366 { 2367 // Should never get here, but just for safety, 2368 // move the cursor position back to the start and continue the loop 2369 audioBuffer->frameCursorPos = 0; 2370 continue; 2371 } 2372 } 2373 } 2374 2375 // If for some reason we weren't able to read every frame we'll need to break from the loop 2376 // Not doing this could theoretically put us into an infinite loop 2377 if (framesToRead > 0) break; 2378 } 2379 } 2380 } 2381 2382 ma_mutex_unlock(&AUDIO.System.lock); 2383 } 2384 2385 // Main mixing function, pretty simple in this project, just an accumulation 2386 // NOTE: framesOut is both an input and an output, it is initially filled with zeros outside of this function 2387 static void MixAudioFrames(float *framesOut, const float *framesIn, ma_uint32 frameCount, AudioBuffer *buffer) 2388 { 2389 const float localVolume = buffer->volume; 2390 const ma_uint32 channels = AUDIO.System.device.playback.channels; 2391 2392 if (channels == 2) // We consider panning 2393 { 2394 const float left = buffer->pan; 2395 const float right = 1.0f - left; 2396 2397 // Fast sine approximation in [0..1] for pan law: y = 0.5f*x*(3 - x*x); 2398 const float levels[2] = { localVolume*0.5f*left*(3.0f - left*left), localVolume*0.5f*right*(3.0f - right*right) }; 2399 2400 float *frameOut = framesOut; 2401 const float *frameIn = framesIn; 2402 2403 for (ma_uint32 frame = 0; frame < frameCount; frame++) 2404 { 2405 frameOut[0] += (frameIn[0]*levels[0]); 2406 frameOut[1] += (frameIn[1]*levels[1]); 2407 2408 frameOut += 2; 2409 frameIn += 2; 2410 } 2411 } 2412 else // We do not consider panning 2413 { 2414 for (ma_uint32 frame = 0; frame < frameCount; frame++) 2415 { 2416 for (ma_uint32 c = 0; c < channels; c++) 2417 { 2418 float *frameOut = framesOut + (frame*channels); 2419 const float *frameIn = framesIn + (frame*channels); 2420 2421 // Output accumulates input multiplied by volume to provided output (usually 0) 2422 frameOut[c] += (frameIn[c]*localVolume); 2423 } 2424 } 2425 } 2426 } 2427 2428 // Some required functions for audio standalone module version 2429 #if defined(RAUDIO_STANDALONE) 2430 // Check file extension 2431 static bool IsFileExtension(const char *fileName, const char *ext) 2432 { 2433 bool result = false; 2434 const char *fileExt; 2435 2436 if ((fileExt = strrchr(fileName, '.')) != NULL) 2437 { 2438 if (strcmp(fileExt, ext) == 0) result = true; 2439 } 2440 2441 return result; 2442 } 2443 2444 // Get pointer to extension for a filename string (includes the dot: .png) 2445 static const char *GetFileExtension(const char *fileName) 2446 { 2447 const char *dot = strrchr(fileName, '.'); 2448 2449 if (!dot || dot == fileName) return NULL; 2450 2451 return dot; 2452 } 2453 2454 // Load data from file into a buffer 2455 static unsigned char *LoadFileData(const char *fileName, unsigned int *bytesRead) 2456 { 2457 unsigned char *data = NULL; 2458 *bytesRead = 0; 2459 2460 if (fileName != NULL) 2461 { 2462 FILE *file = fopen(fileName, "rb"); 2463 2464 if (file != NULL) 2465 { 2466 // WARNING: On binary streams SEEK_END could not be found, 2467 // using fseek() and ftell() could not work in some (rare) cases 2468 fseek(file, 0, SEEK_END); 2469 int size = ftell(file); 2470 fseek(file, 0, SEEK_SET); 2471 2472 if (size > 0) 2473 { 2474 data = (unsigned char *)RL_MALLOC(size*sizeof(unsigned char)); 2475 2476 // NOTE: fread() returns number of read elements instead of bytes, so we read [1 byte, size elements] 2477 unsigned int count = (unsigned int)fread(data, sizeof(unsigned char), size, file); 2478 *bytesRead = count; 2479 2480 if (count != size) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially loaded", fileName); 2481 else TRACELOG(LOG_INFO, "FILEIO: [%s] File loaded successfully", fileName); 2482 } 2483 else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to read file", fileName); 2484 2485 fclose(file); 2486 } 2487 else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open file", fileName); 2488 } 2489 else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid"); 2490 2491 return data; 2492 } 2493 2494 // Save data to file from buffer 2495 static bool SaveFileData(const char *fileName, void *data, unsigned int bytesToWrite) 2496 { 2497 if (fileName != NULL) 2498 { 2499 FILE *file = fopen(fileName, "wb"); 2500 2501 if (file != NULL) 2502 { 2503 unsigned int count = (unsigned int)fwrite(data, sizeof(unsigned char), bytesToWrite, file); 2504 2505 if (count == 0) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to write file", fileName); 2506 else if (count != bytesToWrite) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially written", fileName); 2507 else TRACELOG(LOG_INFO, "FILEIO: [%s] File saved successfully", fileName); 2508 2509 fclose(file); 2510 } 2511 else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open file", fileName); 2512 } 2513 else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid"); 2514 } 2515 2516 // Save text data to file (write), string must be '\0' terminated 2517 static bool SaveFileText(const char *fileName, char *text) 2518 { 2519 if (fileName != NULL) 2520 { 2521 FILE *file = fopen(fileName, "wt"); 2522 2523 if (file != NULL) 2524 { 2525 int count = fprintf(file, "%s", text); 2526 2527 if (count == 0) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to write text file", fileName); 2528 else TRACELOG(LOG_INFO, "FILEIO: [%s] Text file saved successfully", fileName); 2529 2530 fclose(file); 2531 } 2532 else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open text file", fileName); 2533 } 2534 else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid"); 2535 } 2536 #endif 2537 2538 #undef AudioBuffer 2539 2540 #endif // SUPPORT_MODULE_RAUDIO