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