1/**********************************************************************************************
2*
3* raudio - 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 RAUDIO_STANDALONE
16* Define to use the module as standalone library (independently of raylib).
17* Required types and functions are defined in the same module.
18*
19* #define SUPPORT_FILEFORMAT_WAV
20* #define SUPPORT_FILEFORMAT_OGG
21* #define SUPPORT_FILEFORMAT_XM
22* #define SUPPORT_FILEFORMAT_MOD
23* #define SUPPORT_FILEFORMAT_FLAC
24* #define SUPPORT_FILEFORMAT_MP3
25* Selected desired fileformats to be supported for loading. Some of those formats are
26* supported by default, to remove support, just comment unrequired #define in this module
27*
28* DEPENDENCIES:
29* miniaudio.h - Audio device management lib (https://github.com/dr-soft/miniaudio)
30* stb_vorbis.h - Ogg audio files loading (http://www.nothings.org/stb_vorbis/)
31* dr_mp3.h - MP3 audio file loading (https://github.com/mackron/dr_libs)
32* dr_flac.h - FLAC audio file loading (https://github.com/mackron/dr_libs)
33* jar_xm.h - XM module file loading
34* jar_mod.h - MOD audio file loading
35*
36* CONTRIBUTORS:
37* David Reid (github: @mackron) (Nov. 2017):
38* - Complete port to miniaudio library
39*
40* Joshua Reisenauer (github: @kd7tck) (2015)
41* - XM audio module support (jar_xm)
42* - MOD audio module support (jar_mod)
43* - Mixing channels support
44* - Raw audio context support
45*
46*
47* LICENSE: zlib/libpng
48*
49* Copyright (c) 2013-2020 Ramon Santamaria (@raysan5)
50*
51* This software is provided "as-is", without any express or implied warranty. In no event
52* will the authors be held liable for any damages arising from the use of this software.
53*
54* Permission is granted to anyone to use this software for any purpose, including commercial
55* applications, and to alter it and redistribute it freely, subject to the following restrictions:
56*
57* 1. The origin of this software must not be misrepresented; you must not claim that you
58* wrote the original software. If you use this software in a product, an acknowledgment
59* in the product documentation would be appreciated but is not required.
60*
61* 2. Altered source versions must be plainly marked as such, and must not be misrepresented
62* as being the original software.
63*
64* 3. This notice may not be removed or altered from any source distribution.
65*
66**********************************************************************************************/
67
68#if defined(RAUDIO_STANDALONE)
69 #include "raudio.h"
70 #include <stdarg.h> // Required for: va_list, va_start(), vfprintf(), va_end()
71#else
72 #include "raylib.h" // Declares module functions
73
74// Check if config flags have been externally provided on compilation line
75#if !defined(EXTERNAL_CONFIG_FLAGS)
76 #include "config.h" // Defines module configuration flags
77#endif
78 #include "utils.h" // Required for: fopen() Android mapping
79#endif
80
81#if defined(_WIN32)
82// To avoid conflicting windows.h symbols with raylib, some flags are defined
83// WARNING: Those flags avoid inclusion of some Win32 headers that could be required
84// by user at some point and won't be included...
85//-------------------------------------------------------------------------------------
86
87// If defined, the following flags inhibit definition of the indicated items.
88#define NOGDICAPMASKS // CC_*, LC_*, PC_*, CP_*, TC_*, RC_
89#define NOVIRTUALKEYCODES // VK_*
90#define NOWINMESSAGES // WM_*, EM_*, LB_*, CB_*
91#define NOWINSTYLES // WS_*, CS_*, ES_*, LBS_*, SBS_*, CBS_*
92#define NOSYSMETRICS // SM_*
93#define NOMENUS // MF_*
94#define NOICONS // IDI_*
95#define NOKEYSTATES // MK_*
96#define NOSYSCOMMANDS // SC_*
97#define NORASTEROPS // Binary and Tertiary raster ops
98#define NOSHOWWINDOW // SW_*
99#define OEMRESOURCE // OEM Resource values
100#define NOATOM // Atom Manager routines
101#define NOCLIPBOARD // Clipboard routines
102#define NOCOLOR // Screen colors
103#define NOCTLMGR // Control and Dialog routines
104#define NODRAWTEXT // DrawText() and DT_*
105#define NOGDI // All GDI defines and routines
106#define NOKERNEL // All KERNEL defines and routines
107#define NOUSER // All USER defines and routines
108//#define NONLS // All NLS defines and routines
109#define NOMB // MB_* and MessageBox()
110#define NOMEMMGR // GMEM_*, LMEM_*, GHND, LHND, associated routines
111#define NOMETAFILE // typedef METAFILEPICT
112#define NOMINMAX // Macros min(a,b) and max(a,b)
113#define NOMSG // typedef MSG and associated routines
114#define NOOPENFILE // OpenFile(), OemToAnsi, AnsiToOem, and OF_*
115#define NOSCROLL // SB_* and scrolling routines
116#define NOSERVICE // All Service Controller routines, SERVICE_ equates, etc.
117#define NOSOUND // Sound driver routines
118#define NOTEXTMETRIC // typedef TEXTMETRIC and associated routines
119#define NOWH // SetWindowsHook and WH_*
120#define NOWINOFFSETS // GWL_*, GCL_*, associated routines
121#define NOCOMM // COMM driver routines
122#define NOKANJI // Kanji support stuff.
123#define NOHELP // Help engine interface.
124#define NOPROFILER // Profiler interface.
125#define NODEFERWINDOWPOS // DeferWindowPos routines
126#define NOMCX // Modem Configuration Extensions
127
128// Type required before windows.h inclusion
129typedef struct tagMSG *LPMSG;
130
131#include <windows.h>
132
133// Type required by some unused function...
134typedef struct tagBITMAPINFOHEADER {
135 DWORD biSize;
136 LONG biWidth;
137 LONG biHeight;
138 WORD biPlanes;
139 WORD biBitCount;
140 DWORD biCompression;
141 DWORD biSizeImage;
142 LONG biXPelsPerMeter;
143 LONG biYPelsPerMeter;
144 DWORD biClrUsed;
145 DWORD biClrImportant;
146} BITMAPINFOHEADER, *PBITMAPINFOHEADER;
147
148#include <objbase.h>
149#include <mmreg.h>
150#include <mmsystem.h>
151
152// Some required types defined for MSVC/TinyC compiler
153#if defined(_MSC_VER) || defined(__TINYC__)
154 #include "propidl.h"
155#endif
156#endif
157
158#define MA_MALLOC RL_MALLOC
159#define MA_FREE RL_FREE
160
161#define MA_NO_JACK
162#define MINIAUDIO_IMPLEMENTATION
163#include "external/miniaudio.h" // miniaudio library
164#undef PlaySound // Win32 API: windows.h > mmsystem.h defines PlaySound macro
165
166#include <stdlib.h> // Required for: malloc(), free()
167#include <stdio.h> // Required for: FILE, fopen(), fclose(), fread()
168
169#if defined(RAUDIO_STANDALONE)
170 #include <string.h> // Required for: strcmp() [Used in IsFileExtension()]
171
172 #if !defined(TRACELOG)
173 #define TRACELOG(level, ...) (void)0
174 #endif
175#endif
176
177#if defined(SUPPORT_FILEFORMAT_OGG)
178 // TODO: Remap malloc()/free() calls to RL_MALLOC/RL_FREE
179
180 #define STB_VORBIS_IMPLEMENTATION
181 #include "external/stb_vorbis.h" // OGG loading functions
182#endif
183
184#if defined(SUPPORT_FILEFORMAT_XM)
185 #define JARXM_MALLOC RL_MALLOC
186 #define JARXM_FREE RL_FREE
187
188 #define JAR_XM_IMPLEMENTATION
189 #include "external/jar_xm.h" // XM loading functions
190#endif
191
192#if defined(SUPPORT_FILEFORMAT_MOD)
193 #define JARMOD_MALLOC RL_MALLOC
194 #define JARMOD_FREE RL_FREE
195
196 #define JAR_MOD_IMPLEMENTATION
197 #include "external/jar_mod.h" // MOD loading functions
198#endif
199
200#if defined(SUPPORT_FILEFORMAT_FLAC)
201 #define DRFLAC_MALLOC RL_MALLOC
202 #define DRFLAC_REALLOC RL_REALLOC
203 #define DRFLAC_FREE RL_FREE
204
205 #define DR_FLAC_IMPLEMENTATION
206 #define DR_FLAC_NO_WIN32_IO
207 #include "external/dr_flac.h" // FLAC loading functions
208#endif
209
210#if defined(SUPPORT_FILEFORMAT_MP3)
211 #define DRMP3_MALLOC RL_MALLOC
212 #define DRMP3_REALLOC RL_REALLOC
213 #define DRMP3_FREE RL_FREE
214
215 #define DR_MP3_IMPLEMENTATION
216 #include "external/dr_mp3.h" // MP3 loading functions
217#endif
218
219#if defined(_MSC_VER)
220 #undef bool
221#endif
222
223//----------------------------------------------------------------------------------
224// Defines and Macros
225//----------------------------------------------------------------------------------
226#define AUDIO_DEVICE_FORMAT ma_format_f32
227#define AUDIO_DEVICE_CHANNELS 2
228#define AUDIO_DEVICE_SAMPLE_RATE 44100
229
230#define MAX_AUDIO_BUFFER_POOL_CHANNELS 16
231
232//----------------------------------------------------------------------------------
233// Types and Structures Definition
234//----------------------------------------------------------------------------------
235
236// Music context type
237// NOTE: Depends on data structure provided by the library
238// in charge of reading the different file types
239typedef enum {
240 MUSIC_AUDIO_WAV = 0,
241 MUSIC_AUDIO_OGG,
242 MUSIC_AUDIO_FLAC,
243 MUSIC_AUDIO_MP3,
244 MUSIC_MODULE_XM,
245 MUSIC_MODULE_MOD
246} MusicContextType;
247
248#if defined(RAUDIO_STANDALONE)
249typedef enum {
250 LOG_ALL,
251 LOG_TRACE,
252 LOG_DEBUG,
253 LOG_INFO,
254 LOG_WARNING,
255 LOG_ERROR,
256 LOG_FATAL,
257 LOG_NONE
258} TraceLogType;
259#endif
260
261// NOTE: Different logic is used when feeding data to the playback device
262// depending on whether or not data is streamed (Music vs Sound)
263typedef enum {
264 AUDIO_BUFFER_USAGE_STATIC = 0,
265 AUDIO_BUFFER_USAGE_STREAM
266} AudioBufferUsage;
267
268// Audio buffer structure
269struct rAudioBuffer {
270 ma_data_converter converter; // Audio data converter
271
272 float volume; // Audio buffer volume
273 float pitch; // Audio buffer pitch
274
275 bool playing; // Audio buffer state: AUDIO_PLAYING
276 bool paused; // Audio buffer state: AUDIO_PAUSED
277 bool looping; // Audio buffer looping, always true for AudioStreams
278 int usage; // Audio buffer usage mode: STATIC or STREAM
279
280 bool isSubBufferProcessed[2]; // SubBuffer processed (virtual double buffer)
281 unsigned int sizeInFrames; // Total buffer size in frames
282 unsigned int frameCursorPos; // Frame cursor position
283 unsigned int totalFramesProcessed; // Total frames processed in this buffer (required for play timing)
284
285 unsigned char *data; // Data buffer, on music stream keeps filling
286
287 rAudioBuffer *next; // Next audio buffer on the list
288 rAudioBuffer *prev; // Previous audio buffer on the list
289};
290
291#define AudioBuffer rAudioBuffer // HACK: To avoid CoreAudio (macOS) symbol collision
292
293// Audio data context
294typedef struct AudioData {
295 struct {
296 ma_context context; // miniaudio context data
297 ma_device device; // miniaudio device
298 ma_mutex lock; // miniaudio mutex lock
299 bool isReady; // Check if audio device is ready
300 } System;
301 struct {
302 AudioBuffer *first; // Pointer to first AudioBuffer in the list
303 AudioBuffer *last; // Pointer to last AudioBuffer in the list
304 int defaultSize; // Default audio buffer size for audio streams
305 } Buffer;
306 struct {
307 AudioBuffer *pool[MAX_AUDIO_BUFFER_POOL_CHANNELS]; // Multichannel AudioBuffer pointers pool
308 unsigned int poolCounter; // AudioBuffer pointers pool counter
309 unsigned int channels[MAX_AUDIO_BUFFER_POOL_CHANNELS]; // AudioBuffer pool channels
310 } MultiChannel;
311} AudioData;
312
313//----------------------------------------------------------------------------------
314// Global Variables Definition
315//----------------------------------------------------------------------------------
316static AudioData AUDIO = { // Global AUDIO context
317
318 // NOTE: Music buffer size is defined by number of samples, independent of sample size and channels number
319 // After some math, considering a sampleRate of 48000, a buffer refill rate of 1/60 seconds and a
320 // standard double-buffering system, a 4096 samples buffer has been chosen, it should be enough
321 // In case of music-stalls, just increase this number
322 .Buffer.defaultSize = 4096
323};
324
325//----------------------------------------------------------------------------------
326// Module specific Functions Declaration
327//----------------------------------------------------------------------------------
328static void OnLog(ma_context *pContext, ma_device *pDevice, ma_uint32 logLevel, const char *message);
329static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const void *pFramesInput, ma_uint32 frameCount);
330static void MixAudioFrames(float *framesOut, const float *framesIn, ma_uint32 frameCount, float localVolume);
331
332static void InitAudioBufferPool(void); // Initialise the multichannel buffer pool
333static void CloseAudioBufferPool(void); // Close the audio buffers pool
334
335#if defined(SUPPORT_FILEFORMAT_WAV)
336static Wave LoadWAV(const char *fileName); // Load WAV file
337static int SaveWAV(Wave wave, const char *fileName); // Save wave data as WAV file
338#endif
339#if defined(SUPPORT_FILEFORMAT_OGG)
340static Wave LoadOGG(const char *fileName); // Load OGG file
341#endif
342#if defined(SUPPORT_FILEFORMAT_FLAC)
343static Wave LoadFLAC(const char *fileName); // Load FLAC file
344#endif
345#if defined(SUPPORT_FILEFORMAT_MP3)
346static Wave LoadMP3(const char *fileName); // Load MP3 file
347#endif
348
349#if defined(RAUDIO_STANDALONE)
350bool IsFileExtension(const char *fileName, const char *ext);// Check file extension
351void TraceLog(int msgType, const char *text, ...); // Show trace log messages (LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DEBUG)
352#endif
353
354//----------------------------------------------------------------------------------
355// AudioBuffer management functions declaration
356// NOTE: Those functions are not exposed by raylib... for the moment
357//----------------------------------------------------------------------------------
358AudioBuffer *LoadAudioBuffer(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 sizeInFrames, int usage);
359void UnloadAudioBuffer(AudioBuffer *buffer);
360
361bool IsAudioBufferPlaying(AudioBuffer *buffer);
362void PlayAudioBuffer(AudioBuffer *buffer);
363void StopAudioBuffer(AudioBuffer *buffer);
364void PauseAudioBuffer(AudioBuffer *buffer);
365void ResumeAudioBuffer(AudioBuffer *buffer);
366void SetAudioBufferVolume(AudioBuffer *buffer, float volume);
367void SetAudioBufferPitch(AudioBuffer *buffer, float pitch);
368void TrackAudioBuffer(AudioBuffer *buffer);
369void UntrackAudioBuffer(AudioBuffer *buffer);
370
371//----------------------------------------------------------------------------------
372// Module Functions Definition - Audio Device initialization and Closing
373//----------------------------------------------------------------------------------
374// Initialize audio device
375void InitAudioDevice(void)
376{
377 // TODO: Load AUDIO context memory dynamically?
378
379 // Init audio context
380 ma_context_config ctxConfig = ma_context_config_init();
381 ctxConfig.logCallback = OnLog;
382
383 ma_result result = ma_context_init(NULL, 0, &ctxConfig, &AUDIO.System.context);
384 if (result != MA_SUCCESS)
385 {
386 TRACELOG(LOG_ERROR, "AUDIO: Failed to initialize context");
387 return;
388 }
389
390 // Init audio device
391 // NOTE: Using the default device. Format is floating point because it simplifies mixing.
392 ma_device_config config = ma_device_config_init(ma_device_type_playback);
393 config.playback.pDeviceID = NULL; // NULL for the default playback AUDIO.System.device.
394 config.playback.format = AUDIO_DEVICE_FORMAT;
395 config.playback.channels = AUDIO_DEVICE_CHANNELS;
396 config.capture.pDeviceID = NULL; // NULL for the default capture AUDIO.System.device.
397 config.capture.format = ma_format_s16;
398 config.capture.channels = 1;
399 config.sampleRate = AUDIO_DEVICE_SAMPLE_RATE;
400 config.dataCallback = OnSendAudioDataToDevice;
401 config.pUserData = NULL;
402
403 result = ma_device_init(&AUDIO.System.context, &config, &AUDIO.System.device);
404 if (result != MA_SUCCESS)
405 {
406 TRACELOG(LOG_ERROR, "AUDIO: Failed to initialize playback device");
407 ma_context_uninit(&AUDIO.System.context);
408 return;
409 }
410
411 // Keep the device running the whole time. May want to consider doing something a bit smarter and only have the device running
412 // while there's at least one sound being played.
413 result = ma_device_start(&AUDIO.System.device);
414 if (result != MA_SUCCESS)
415 {
416 TRACELOG(LOG_ERROR, "AUDIO: Failed to start playback device");
417 ma_device_uninit(&AUDIO.System.device);
418 ma_context_uninit(&AUDIO.System.context);
419 return;
420 }
421
422 // Mixing happens on a seperate thread which means we need to synchronize. I'm using a mutex here to make things simple, but may
423 // want to look at something a bit smarter later on to keep everything real-time, if that's necessary.
424 if (ma_mutex_init(&AUDIO.System.context, &AUDIO.System.lock) != MA_SUCCESS)
425 {
426 TRACELOG(LOG_ERROR, "AUDIO: Failed to create mutex for mixing");
427 ma_device_uninit(&AUDIO.System.device);
428 ma_context_uninit(&AUDIO.System.context);
429 return;
430 }
431
432 TRACELOG(LOG_INFO, "AUDIO: Device initialized successfully");
433 TRACELOG(LOG_INFO, " > Backend: miniaudio / %s", ma_get_backend_name(AUDIO.System.context.backend));
434 TRACELOG(LOG_INFO, " > Format: %s -> %s", ma_get_format_name(AUDIO.System.device.playback.format), ma_get_format_name(AUDIO.System.device.playback.internalFormat));
435 TRACELOG(LOG_INFO, " > Channels: %d -> %d", AUDIO.System.device.playback.channels, AUDIO.System.device.playback.internalChannels);
436 TRACELOG(LOG_INFO, " > Sample rate: %d -> %d", AUDIO.System.device.sampleRate, AUDIO.System.device.playback.internalSampleRate);
437 TRACELOG(LOG_INFO, " > Periods size: %d", AUDIO.System.device.playback.internalPeriodSizeInFrames*AUDIO.System.device.playback.internalPeriods);
438
439 InitAudioBufferPool();
440
441 AUDIO.System.isReady = true;
442}
443
444// Close the audio device for all contexts
445void CloseAudioDevice(void)
446{
447 if (AUDIO.System.isReady)
448 {
449 ma_mutex_uninit(&AUDIO.System.lock);
450 ma_device_uninit(&AUDIO.System.device);
451 ma_context_uninit(&AUDIO.System.context);
452
453 CloseAudioBufferPool();
454
455 TRACELOG(LOG_INFO, "AUDIO: Device closed successfully");
456 }
457 else TRACELOG(LOG_WARNING, "AUDIO: Device could not be closed, not currently initialized");
458}
459
460// Check if device has been initialized successfully
461bool IsAudioDeviceReady(void)
462{
463 return AUDIO.System.isReady;
464}
465
466// Set master volume (listener)
467void SetMasterVolume(float volume)
468{
469 ma_device_set_master_volume(&AUDIO.System.device, volume);
470}
471
472//----------------------------------------------------------------------------------
473// Module Functions Definition - Audio Buffer management
474//----------------------------------------------------------------------------------
475
476// Initialize a new audio buffer (filled with silence)
477AudioBuffer *LoadAudioBuffer(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 sizeInFrames, int usage)
478{
479 AudioBuffer *audioBuffer = (AudioBuffer *)RL_CALLOC(1, sizeof(AudioBuffer));
480
481 if (audioBuffer == NULL)
482 {
483 TRACELOG(LOG_ERROR, "AUDIO: Failed to allocate memory for buffer");
484 return NULL;
485 }
486
487 audioBuffer->data = RL_CALLOC(sizeInFrames*channels*ma_get_bytes_per_sample(format), 1);
488
489 // Audio data runs through a format converter
490 ma_data_converter_config converterConfig = ma_data_converter_config_init(format, AUDIO_DEVICE_FORMAT, channels, AUDIO_DEVICE_CHANNELS, sampleRate, AUDIO_DEVICE_SAMPLE_RATE);
491 converterConfig.resampling.allowDynamicSampleRate = true; // Required for pitch shifting
492
493 ma_result result = ma_data_converter_init(&converterConfig, &audioBuffer->converter);
494
495 if (result != MA_SUCCESS)
496 {
497 TRACELOG(LOG_ERROR, "AUDIO: Failed to create data conversion pipeline");
498 RL_FREE(audioBuffer);
499 return NULL;
500 }
501
502 // Init audio buffer values
503 audioBuffer->volume = 1.0f;
504 audioBuffer->pitch = 1.0f;
505 audioBuffer->playing = false;
506 audioBuffer->paused = false;
507 audioBuffer->looping = false;
508 audioBuffer->usage = usage;
509 audioBuffer->frameCursorPos = 0;
510 audioBuffer->sizeInFrames = sizeInFrames;
511
512 // Buffers should be marked as processed by default so that a call to
513 // UpdateAudioStream() immediately after initialization works correctly
514 audioBuffer->isSubBufferProcessed[0] = true;
515 audioBuffer->isSubBufferProcessed[1] = true;
516
517 // Track audio buffer to linked list next position
518 TrackAudioBuffer(audioBuffer);
519
520 return audioBuffer;
521}
522
523// Delete an audio buffer
524void UnloadAudioBuffer(AudioBuffer *buffer)
525{
526 if (buffer != NULL)
527 {
528 ma_data_converter_uninit(&buffer->converter);
529 UntrackAudioBuffer(buffer);
530 RL_FREE(buffer->data);
531 RL_FREE(buffer);
532 }
533}
534
535// Check if an audio buffer is playing
536bool IsAudioBufferPlaying(AudioBuffer *buffer)
537{
538 bool result = false;
539
540 if (buffer != NULL) result = (buffer->playing && !buffer->paused);
541
542 return result;
543}
544
545// Play an audio buffer
546// NOTE: Buffer is restarted to the start.
547// Use PauseAudioBuffer() and ResumeAudioBuffer() if the playback position should be maintained.
548void PlayAudioBuffer(AudioBuffer *buffer)
549{
550 if (buffer != NULL)
551 {
552 buffer->playing = true;
553 buffer->paused = false;
554 buffer->frameCursorPos = 0;
555 }
556}
557
558// Stop an audio buffer
559void StopAudioBuffer(AudioBuffer *buffer)
560{
561 if (buffer != NULL)
562 {
563 if (IsAudioBufferPlaying(buffer))
564 {
565 buffer->playing = false;
566 buffer->paused = false;
567 buffer->frameCursorPos = 0;
568 buffer->totalFramesProcessed = 0;
569 buffer->isSubBufferProcessed[0] = true;
570 buffer->isSubBufferProcessed[1] = true;
571 }
572 }
573}
574
575// Pause an audio buffer
576void PauseAudioBuffer(AudioBuffer *buffer)
577{
578 if (buffer != NULL) buffer->paused = true;
579}
580
581// Resume an audio buffer
582void ResumeAudioBuffer(AudioBuffer *buffer)
583{
584 if (buffer != NULL) buffer->paused = false;
585}
586
587// Set volume for an audio buffer
588void SetAudioBufferVolume(AudioBuffer *buffer, float volume)
589{
590 if (buffer != NULL) buffer->volume = volume;
591}
592
593// Set pitch for an audio buffer
594void SetAudioBufferPitch(AudioBuffer *buffer, float pitch)
595{
596 if (buffer != NULL)
597 {
598 float pitchMul = pitch/buffer->pitch;
599
600 // Pitching is just an adjustment of the sample rate.
601 // Note that this changes the duration of the sound:
602 // - higher pitches will make the sound faster
603 // - lower pitches make it slower
604 ma_uint32 newOutputSampleRate = (ma_uint32)((float)buffer->converter.config.sampleRateOut/pitchMul);
605 buffer->pitch *= (float)buffer->converter.config.sampleRateOut/newOutputSampleRate;
606
607 ma_data_converter_set_rate(&buffer->converter, buffer->converter.config.sampleRateIn, newOutputSampleRate);
608 }
609}
610
611// Track audio buffer to linked list next position
612void TrackAudioBuffer(AudioBuffer *buffer)
613{
614 ma_mutex_lock(&AUDIO.System.lock);
615 {
616 if (AUDIO.Buffer.first == NULL) AUDIO.Buffer.first = buffer;
617 else
618 {
619 AUDIO.Buffer.last->next = buffer;
620 buffer->prev = AUDIO.Buffer.last;
621 }
622
623 AUDIO.Buffer.last = buffer;
624 }
625 ma_mutex_unlock(&AUDIO.System.lock);
626}
627
628// Untrack audio buffer from linked list
629void UntrackAudioBuffer(AudioBuffer *buffer)
630{
631 ma_mutex_lock(&AUDIO.System.lock);
632 {
633 if (buffer->prev == NULL) AUDIO.Buffer.first = buffer->next;
634 else buffer->prev->next = buffer->next;
635
636 if (buffer->next == NULL) AUDIO.Buffer.last = buffer->prev;
637 else buffer->next->prev = buffer->prev;
638
639 buffer->prev = NULL;
640 buffer->next = NULL;
641 }
642 ma_mutex_unlock(&AUDIO.System.lock);
643}
644
645//----------------------------------------------------------------------------------
646// Module Functions Definition - Sounds loading and playing (.WAV)
647//----------------------------------------------------------------------------------
648
649// Load wave data from file
650Wave LoadWave(const char *fileName)
651{
652 Wave wave = { 0 };
653
654 if (false) { }
655#if defined(SUPPORT_FILEFORMAT_WAV)
656 else if (IsFileExtension(fileName, ".wav")) wave = LoadWAV(fileName);
657#endif
658#if defined(SUPPORT_FILEFORMAT_OGG)
659 else if (IsFileExtension(fileName, ".ogg")) wave = LoadOGG(fileName);
660#endif
661#if defined(SUPPORT_FILEFORMAT_FLAC)
662 else if (IsFileExtension(fileName, ".flac")) wave = LoadFLAC(fileName);
663#endif
664#if defined(SUPPORT_FILEFORMAT_MP3)
665 else if (IsFileExtension(fileName, ".mp3")) wave = LoadMP3(fileName);
666#endif
667 else TRACELOG(LOG_WARNING, "FILEIO: [%s] File format not supported", fileName);
668
669 return wave;
670}
671
672// Load sound from file
673// NOTE: The entire file is loaded to memory to be played (no-streaming)
674Sound LoadSound(const char *fileName)
675{
676 Wave wave = LoadWave(fileName);
677
678 Sound sound = LoadSoundFromWave(wave);
679
680 UnloadWave(wave); // Sound is loaded, we can unload wave
681
682 return sound;
683}
684
685// Load sound from wave data
686// NOTE: Wave data must be unallocated manually
687Sound LoadSoundFromWave(Wave wave)
688{
689 Sound sound = { 0 };
690
691 if (wave.data != NULL)
692 {
693 // When using miniaudio we need to do our own mixing.
694 // To simplify this we need convert the format of each sound to be consistent with
695 // the format used to open the playback AUDIO.System.device. We can do this two ways:
696 //
697 // 1) Convert the whole sound in one go at load time (here).
698 // 2) Convert the audio data in chunks at mixing time.
699 //
700 // First option has been selected, format conversion is done on the loading stage.
701 // The downside is that it uses more memory if the original sound is u8 or s16.
702 ma_format formatIn = ((wave.sampleSize == 8)? ma_format_u8 : ((wave.sampleSize == 16)? ma_format_s16 : ma_format_f32));
703 ma_uint32 frameCountIn = wave.sampleCount/wave.channels;
704
705 ma_uint32 frameCount = (ma_uint32)ma_convert_frames(NULL, 0, AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO_DEVICE_SAMPLE_RATE, NULL, frameCountIn, formatIn, wave.channels, wave.sampleRate);
706 if (frameCount == 0) TRACELOG(LOG_WARNING, "SOUND: Failed to get frame count for format conversion");
707
708 AudioBuffer *audioBuffer = LoadAudioBuffer(AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO_DEVICE_SAMPLE_RATE, frameCount, AUDIO_BUFFER_USAGE_STATIC);
709 if (audioBuffer == NULL) TRACELOG(LOG_WARNING, "SOUND: Failed to create buffer");
710
711 frameCount = (ma_uint32)ma_convert_frames(audioBuffer->data, frameCount, AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO_DEVICE_SAMPLE_RATE, wave.data, frameCountIn, formatIn, wave.channels, wave.sampleRate);
712 if (frameCount == 0) TRACELOG(LOG_WARNING, "SOUND: Failed format conversion");
713
714 sound.sampleCount = frameCount*AUDIO_DEVICE_CHANNELS;
715 sound.stream.sampleRate = AUDIO_DEVICE_SAMPLE_RATE;
716 sound.stream.sampleSize = 32;
717 sound.stream.channels = AUDIO_DEVICE_CHANNELS;
718 sound.stream.buffer = audioBuffer;
719 }
720
721 return sound;
722}
723
724// Unload wave data
725void UnloadWave(Wave wave)
726{
727 if (wave.data != NULL) RL_FREE(wave.data);
728
729 TRACELOG(LOG_INFO, "WAVE: Unloaded wave data from RAM");
730}
731
732// Unload sound
733void UnloadSound(Sound sound)
734{
735 UnloadAudioBuffer(sound.stream.buffer);
736
737 TRACELOG(LOG_INFO, "WAVE: Unloaded sound data from RAM");
738}
739
740// Update sound buffer with new data
741void UpdateSound(Sound sound, const void *data, int samplesCount)
742{
743 if (sound.stream.buffer != NULL)
744 {
745 StopAudioBuffer(sound.stream.buffer);
746
747 // TODO: May want to lock/unlock this since this data buffer is read at mixing time
748 memcpy(sound.stream.buffer->data, data, samplesCount*ma_get_bytes_per_frame(sound.stream.buffer->converter.config.formatIn, sound.stream.buffer->converter.config.channelsIn));
749 }
750}
751
752// Export wave data to file
753void ExportWave(Wave wave, const char *fileName)
754{
755 bool success = false;
756
757 if (false) { }
758#if defined(SUPPORT_FILEFORMAT_WAV)
759 else if (IsFileExtension(fileName, ".wav")) success = SaveWAV(wave, fileName);
760#endif
761 else if (IsFileExtension(fileName, ".raw"))
762 {
763 // Export raw sample data (without header)
764 // NOTE: It's up to the user to track wave parameters
765 SaveFileData(fileName, wave.data, wave.sampleCount*wave.channels*wave.sampleSize/8);
766 success = true;
767 }
768
769 if (success) TRACELOG(LOG_INFO, "FILEIO: [%s] Wave data exported successfully", fileName);
770 else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export wave data", fileName);
771}
772
773// Export wave sample data to code (.h)
774void ExportWaveAsCode(Wave wave, const char *fileName)
775{
776 #define BYTES_TEXT_PER_LINE 20
777
778 char varFileName[256] = { 0 };
779 int dataSize = wave.sampleCount*wave.channels*wave.sampleSize/8;
780
781 FILE *txtFile = fopen(fileName, "wt");
782
783 if (txtFile != NULL)
784 {
785 fprintf(txtFile, "\n//////////////////////////////////////////////////////////////////////////////////\n");
786 fprintf(txtFile, "// //\n");
787 fprintf(txtFile, "// WaveAsCode exporter v1.0 - Wave data exported as an array of bytes //\n");
788 fprintf(txtFile, "// //\n");
789 fprintf(txtFile, "// more info and bugs-report: github.com/raysan5/raylib //\n");
790 fprintf(txtFile, "// feedback and support: ray[at]raylib.com //\n");
791 fprintf(txtFile, "// //\n");
792 fprintf(txtFile, "// Copyright (c) 2018 Ramon Santamaria (@raysan5) //\n");
793 fprintf(txtFile, "// //\n");
794 fprintf(txtFile, "//////////////////////////////////////////////////////////////////////////////////\n\n");
795
796#if !defined(RAUDIO_STANDALONE)
797 // Get file name from path and convert variable name to uppercase
798 strcpy(varFileName, GetFileNameWithoutExt(fileName));
799 for (int i = 0; varFileName[i] != '\0'; i++) if (varFileName[i] >= 'a' && varFileName[i] <= 'z') { varFileName[i] = varFileName[i] - 32; }
800#else
801 strcpy(varFileName, fileName);
802#endif
803
804 fprintf(txtFile, "// Wave data information\n");
805 fprintf(txtFile, "#define %s_SAMPLE_COUNT %u\n", varFileName, wave.sampleCount);
806 fprintf(txtFile, "#define %s_SAMPLE_RATE %u\n", varFileName, wave.sampleRate);
807 fprintf(txtFile, "#define %s_SAMPLE_SIZE %u\n", varFileName, wave.sampleSize);
808 fprintf(txtFile, "#define %s_CHANNELS %u\n\n", varFileName, wave.channels);
809
810 // Write byte data as hexadecimal text
811 fprintf(txtFile, "static unsigned char %s_DATA[%i] = { ", varFileName, dataSize);
812 for (int i = 0; i < dataSize - 1; i++) fprintf(txtFile, ((i%BYTES_TEXT_PER_LINE == 0)? "0x%x,\n" : "0x%x, "), ((unsigned char *)wave.data)[i]);
813 fprintf(txtFile, "0x%x };\n", ((unsigned char *)wave.data)[dataSize - 1]);
814
815 fclose(txtFile);
816 }
817}
818
819// Play a sound
820void PlaySound(Sound sound)
821{
822 PlayAudioBuffer(sound.stream.buffer);
823}
824
825// Play a sound in the multichannel buffer pool
826void PlaySoundMulti(Sound sound)
827{
828 int index = -1;
829 unsigned int oldAge = 0;
830 int oldIndex = -1;
831
832 // find the first non playing pool entry
833 for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++)
834 {
835 if (AUDIO.MultiChannel.channels[i] > oldAge)
836 {
837 oldAge = AUDIO.MultiChannel.channels[i];
838 oldIndex = i;
839 }
840
841 if (!IsAudioBufferPlaying(AUDIO.MultiChannel.pool[i]))
842 {
843 index = i;
844 break;
845 }
846 }
847
848 // If no none playing pool members can be index choose the oldest
849 if (index == -1)
850 {
851 TRACELOG(LOG_WARNING, "SOUND: Buffer pool is already full, count: %i", AUDIO.MultiChannel.poolCounter);
852
853 if (oldIndex == -1)
854 {
855 // Shouldn't be able to get here... but just in case something odd happens!
856 TRACELOG(LOG_WARNING, "SOUND: Buffer pool could not determine oldest buffer not playing sound");
857 return;
858 }
859
860 index = oldIndex;
861
862 // Just in case...
863 StopAudioBuffer(AUDIO.MultiChannel.pool[index]);
864 }
865
866 // Experimentally mutex lock doesn't seem to be needed this makes sense
867 // as pool[index] isn't playing and the only stuff we're copying
868 // shouldn't be changing...
869
870 AUDIO.MultiChannel.channels[index] = AUDIO.MultiChannel.poolCounter;
871 AUDIO.MultiChannel.poolCounter++;
872
873 AUDIO.MultiChannel.pool[index]->volume = sound.stream.buffer->volume;
874 AUDIO.MultiChannel.pool[index]->pitch = sound.stream.buffer->pitch;
875 AUDIO.MultiChannel.pool[index]->looping = sound.stream.buffer->looping;
876 AUDIO.MultiChannel.pool[index]->usage = sound.stream.buffer->usage;
877 AUDIO.MultiChannel.pool[index]->isSubBufferProcessed[0] = false;
878 AUDIO.MultiChannel.pool[index]->isSubBufferProcessed[1] = false;
879 AUDIO.MultiChannel.pool[index]->sizeInFrames = sound.stream.buffer->sizeInFrames;
880 AUDIO.MultiChannel.pool[index]->data = sound.stream.buffer->data;
881
882 PlayAudioBuffer(AUDIO.MultiChannel.pool[index]);
883}
884
885// Stop any sound played with PlaySoundMulti()
886void StopSoundMulti(void)
887{
888 for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++) StopAudioBuffer(AUDIO.MultiChannel.pool[i]);
889}
890
891// Get number of sounds playing in the multichannel buffer pool
892int GetSoundsPlaying(void)
893{
894 int counter = 0;
895
896 for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++)
897 {
898 if (IsAudioBufferPlaying(AUDIO.MultiChannel.pool[i])) counter++;
899 }
900
901 return counter;
902}
903
904// Pause a sound
905void PauseSound(Sound sound)
906{
907 PauseAudioBuffer(sound.stream.buffer);
908}
909
910// Resume a paused sound
911void ResumeSound(Sound sound)
912{
913 ResumeAudioBuffer(sound.stream.buffer);
914}
915
916// Stop reproducing a sound
917void StopSound(Sound sound)
918{
919 StopAudioBuffer(sound.stream.buffer);
920}
921
922// Check if a sound is playing
923bool IsSoundPlaying(Sound sound)
924{
925 return IsAudioBufferPlaying(sound.stream.buffer);
926}
927
928// Set volume for a sound
929void SetSoundVolume(Sound sound, float volume)
930{
931 SetAudioBufferVolume(sound.stream.buffer, volume);
932}
933
934// Set pitch for a sound
935void SetSoundPitch(Sound sound, float pitch)
936{
937 SetAudioBufferPitch(sound.stream.buffer, pitch);
938}
939
940// Convert wave data to desired format
941void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels)
942{
943 ma_format formatIn = ((wave->sampleSize == 8)? ma_format_u8 : ((wave->sampleSize == 16)? ma_format_s16 : ma_format_f32));
944 ma_format formatOut = (( sampleSize == 8)? ma_format_u8 : (( sampleSize == 16)? ma_format_s16 : ma_format_f32));
945
946 ma_uint32 frameCountIn = wave->sampleCount; // Is wave->sampleCount actually the frame count? That terminology needs to change, if so.
947
948 ma_uint32 frameCount = (ma_uint32)ma_convert_frames(NULL, 0, formatOut, channels, sampleRate, NULL, frameCountIn, formatIn, wave->channels, wave->sampleRate);
949 if (frameCount == 0)
950 {
951 TRACELOG(LOG_WARNING, "WAVE: Failed to get frame count for format conversion");
952 return;
953 }
954
955 void *data = RL_MALLOC(frameCount*channels*(sampleSize/8));
956
957 frameCount = (ma_uint32)ma_convert_frames(data, frameCount, formatOut, channels, sampleRate, wave->data, frameCountIn, formatIn, wave->channels, wave->sampleRate);
958 if (frameCount == 0)
959 {
960 TRACELOG(LOG_WARNING, "WAVE: Failed format conversion");
961 return;
962 }
963
964 wave->sampleCount = frameCount;
965 wave->sampleSize = sampleSize;
966 wave->sampleRate = sampleRate;
967 wave->channels = channels;
968 RL_FREE(wave->data);
969 wave->data = data;
970}
971
972// Copy a wave to a new wave
973Wave WaveCopy(Wave wave)
974{
975 Wave newWave = { 0 };
976
977 newWave.data = RL_MALLOC(wave.sampleCount*wave.sampleSize/8*wave.channels);
978
979 if (newWave.data != NULL)
980 {
981 // NOTE: Size must be provided in bytes
982 memcpy(newWave.data, wave.data, wave.sampleCount*wave.channels*wave.sampleSize/8);
983
984 newWave.sampleCount = wave.sampleCount;
985 newWave.sampleRate = wave.sampleRate;
986 newWave.sampleSize = wave.sampleSize;
987 newWave.channels = wave.channels;
988 }
989
990 return newWave;
991}
992
993// Crop a wave to defined samples range
994// NOTE: Security check in case of out-of-range
995void WaveCrop(Wave *wave, int initSample, int finalSample)
996{
997 if ((initSample >= 0) && (initSample < finalSample) &&
998 (finalSample > 0) && ((unsigned int)finalSample < wave->sampleCount))
999 {
1000 int sampleCount = finalSample - initSample;
1001
1002 void *data = RL_MALLOC(sampleCount*wave->sampleSize/8*wave->channels);
1003
1004 memcpy(data, (unsigned char *)wave->data + (initSample*wave->channels*wave->sampleSize/8), sampleCount*wave->channels*wave->sampleSize/8);
1005
1006 RL_FREE(wave->data);
1007 wave->data = data;
1008 }
1009 else TRACELOG(LOG_WARNING, "WAVE: Crop range out of bounds");
1010}
1011
1012// Get samples data from wave as a floats array
1013// NOTE: Returned sample values are normalized to range [-1..1]
1014float *GetWaveData(Wave wave)
1015{
1016 float *samples = (float *)RL_MALLOC(wave.sampleCount*wave.channels*sizeof(float));
1017
1018 for (unsigned int i = 0; i < wave.sampleCount; i++)
1019 {
1020 for (unsigned int j = 0; j < wave.channels; j++)
1021 {
1022 if (wave.sampleSize == 8) samples[wave.channels*i + j] = (float)(((unsigned char *)wave.data)[wave.channels*i + j] - 127)/256.0f;
1023 else if (wave.sampleSize == 16) samples[wave.channels*i + j] = (float)((short *)wave.data)[wave.channels*i + j]/32767.0f;
1024 else if (wave.sampleSize == 32) samples[wave.channels*i + j] = ((float *)wave.data)[wave.channels*i + j];
1025 }
1026 }
1027
1028 return samples;
1029}
1030
1031//----------------------------------------------------------------------------------
1032// Module Functions Definition - Music loading and stream playing (.OGG)
1033//----------------------------------------------------------------------------------
1034
1035// Load music stream from file
1036Music LoadMusicStream(const char *fileName)
1037{
1038 Music music = { 0 };
1039 bool musicLoaded = false;
1040
1041 if (false) { }
1042#if defined(SUPPORT_FILEFORMAT_OGG)
1043 else if (IsFileExtension(fileName, ".ogg"))
1044 {
1045 // Open ogg audio stream
1046 music.ctxData = stb_vorbis_open_filename(fileName, NULL, NULL);
1047
1048 if (music.ctxData != NULL)
1049 {
1050 music.ctxType = MUSIC_AUDIO_OGG;
1051 stb_vorbis_info info = stb_vorbis_get_info((stb_vorbis *)music.ctxData); // Get Ogg file info
1052
1053 // OGG bit rate defaults to 16 bit, it's enough for compressed format
1054 music.stream = InitAudioStream(info.sample_rate, 16, info.channels);
1055 music.sampleCount = (unsigned int)stb_vorbis_stream_length_in_samples((stb_vorbis *)music.ctxData)*info.channels;
1056 music.loopCount = 0; // Infinite loop by default
1057 musicLoaded = true;
1058 }
1059 }
1060#endif
1061#if defined(SUPPORT_FILEFORMAT_FLAC)
1062 else if (IsFileExtension(fileName, ".flac"))
1063 {
1064 music.ctxData = drflac_open_file(fileName);
1065
1066 if (music.ctxData != NULL)
1067 {
1068 music.ctxType = MUSIC_AUDIO_FLAC;
1069 drflac *ctxFlac = (drflac *)music.ctxData;
1070
1071 music.stream = InitAudioStream(ctxFlac->sampleRate, ctxFlac->bitsPerSample, ctxFlac->channels);
1072 music.sampleCount = (unsigned int)ctxFlac->totalSampleCount;
1073 music.loopCount = 0; // Infinite loop by default
1074 musicLoaded = true;
1075 }
1076 }
1077#endif
1078#if defined(SUPPORT_FILEFORMAT_MP3)
1079 else if (IsFileExtension(fileName, ".mp3"))
1080 {
1081 drmp3 *ctxMp3 = RL_MALLOC(sizeof(drmp3));
1082 music.ctxData = ctxMp3;
1083
1084 int result = drmp3_init_file(ctxMp3, fileName, NULL);
1085
1086 if (result > 0)
1087 {
1088 music.ctxType = MUSIC_AUDIO_MP3;
1089
1090 music.stream = InitAudioStream(ctxMp3->sampleRate, 32, ctxMp3->channels);
1091 music.sampleCount = (unsigned int)drmp3_get_pcm_frame_count(ctxMp3)*ctxMp3->channels;
1092 music.loopCount = 0; // Infinite loop by default
1093 musicLoaded = true;
1094 }
1095 }
1096#endif
1097#if defined(SUPPORT_FILEFORMAT_XM)
1098 else if (IsFileExtension(fileName, ".xm"))
1099 {
1100 jar_xm_context_t *ctxXm = NULL;
1101
1102 int result = jar_xm_create_context_from_file(&ctxXm, 48000, fileName);
1103
1104 if (result == 0) // XM AUDIO.System.context created successfully
1105 {
1106 music.ctxType = MUSIC_MODULE_XM;
1107 jar_xm_set_max_loop_count(ctxXm, 0); // Set infinite number of loops
1108
1109 // NOTE: Only stereo is supported for XM
1110 music.stream = InitAudioStream(48000, 16, 2);
1111 music.sampleCount = (unsigned int)jar_xm_get_remaining_samples(ctxXm)*2;
1112 music.loopCount = 0; // Infinite loop by default
1113 jar_xm_reset(ctxXm); // make sure we start at the beginning of the song
1114 musicLoaded = true;
1115
1116 music.ctxData = ctxXm;
1117 }
1118 }
1119#endif
1120#if defined(SUPPORT_FILEFORMAT_MOD)
1121 else if (IsFileExtension(fileName, ".mod"))
1122 {
1123 jar_mod_context_t *ctxMod = RL_MALLOC(sizeof(jar_mod_context_t));
1124
1125 jar_mod_init(ctxMod);
1126 int result = jar_mod_load_file(ctxMod, fileName);
1127
1128 if (result > 0)
1129 {
1130 music.ctxType = MUSIC_MODULE_MOD;
1131
1132 // NOTE: Only stereo is supported for MOD
1133 music.stream = InitAudioStream(48000, 16, 2);
1134 music.sampleCount = (unsigned int)jar_mod_max_samples(ctxMod)*2;
1135 music.loopCount = 0; // Infinite loop by default
1136 musicLoaded = true;
1137
1138 music.ctxData = ctxMod;
1139 }
1140 }
1141#endif
1142 else TRACELOG(LOG_WARNING, "STREAM: [%s] Fileformat not supported", fileName);
1143
1144 if (!musicLoaded)
1145 {
1146 if (false) { }
1147 #if defined(SUPPORT_FILEFORMAT_OGG)
1148 else if (music.ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close((stb_vorbis *)music.ctxData);
1149 #endif
1150 #if defined(SUPPORT_FILEFORMAT_FLAC)
1151 else if (music.ctxType == MUSIC_AUDIO_FLAC) drflac_free((drflac *)music.ctxData);
1152 #endif
1153 #if defined(SUPPORT_FILEFORMAT_MP3)
1154 else if (music.ctxType == MUSIC_AUDIO_MP3) { drmp3_uninit((drmp3 *)music.ctxData); RL_FREE(music.ctxData); }
1155 #endif
1156 #if defined(SUPPORT_FILEFORMAT_XM)
1157 else if (music.ctxType == MUSIC_MODULE_XM) jar_xm_free_context((jar_xm_context_t *)music.ctxData);
1158 #endif
1159 #if defined(SUPPORT_FILEFORMAT_MOD)
1160 else if (music.ctxType == MUSIC_MODULE_MOD) { jar_mod_unload((jar_mod_context_t *)music.ctxData); RL_FREE(music.ctxData); }
1161 #endif
1162
1163 TRACELOG(LOG_WARNING, "FILEIO: [%s] Music file could not be opened", fileName);
1164 }
1165 else
1166 {
1167 // Show some music stream info
1168 TRACELOG(LOG_INFO, "FILEIO: [%s] Music file successfully loaded:", fileName);
1169 TRACELOG(LOG_INFO, " > Total samples: %i", music.sampleCount);
1170 TRACELOG(LOG_INFO, " > Sample rate: %i Hz", music.stream.sampleRate);
1171 TRACELOG(LOG_INFO, " > Sample size: %i bits", music.stream.sampleSize);
1172 TRACELOG(LOG_INFO, " > Channels: %i (%s)", music.stream.channels, (music.stream.channels == 1)? "Mono" : (music.stream.channels == 2)? "Stereo" : "Multi");
1173 }
1174
1175 return music;
1176}
1177
1178// Unload music stream
1179void UnloadMusicStream(Music music)
1180{
1181 CloseAudioStream(music.stream);
1182
1183 if (false) { }
1184#if defined(SUPPORT_FILEFORMAT_OGG)
1185 else if (music.ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close((stb_vorbis *)music.ctxData);
1186#endif
1187#if defined(SUPPORT_FILEFORMAT_FLAC)
1188 else if (music.ctxType == MUSIC_AUDIO_FLAC) drflac_free((drflac *)music.ctxData);
1189#endif
1190#if defined(SUPPORT_FILEFORMAT_MP3)
1191 else if (music.ctxType == MUSIC_AUDIO_MP3) { drmp3_uninit((drmp3 *)music.ctxData); RL_FREE(music.ctxData); }
1192#endif
1193#if defined(SUPPORT_FILEFORMAT_XM)
1194 else if (music.ctxType == MUSIC_MODULE_XM) jar_xm_free_context((jar_xm_context_t *)music.ctxData);
1195#endif
1196#if defined(SUPPORT_FILEFORMAT_MOD)
1197 else if (music.ctxType == MUSIC_MODULE_MOD) { jar_mod_unload((jar_mod_context_t *)music.ctxData); RL_FREE(music.ctxData); }
1198#endif
1199}
1200
1201// Start music playing (open stream)
1202void PlayMusicStream(Music music)
1203{
1204 if (music.stream.buffer != NULL)
1205 {
1206 // For music streams, we need to make sure we maintain the frame cursor position
1207 // This is a hack for this section of code in UpdateMusicStream()
1208 // NOTE: In case window is minimized, music stream is stopped, just make sure to
1209 // play again on window restore: if (IsMusicPlaying(music)) PlayMusicStream(music);
1210 ma_uint32 frameCursorPos = music.stream.buffer->frameCursorPos;
1211 PlayAudioStream(music.stream); // WARNING: This resets the cursor position.
1212 music.stream.buffer->frameCursorPos = frameCursorPos;
1213 }
1214}
1215
1216// Pause music playing
1217void PauseMusicStream(Music music)
1218{
1219 PauseAudioStream(music.stream);
1220}
1221
1222// Resume music playing
1223void ResumeMusicStream(Music music)
1224{
1225 ResumeAudioStream(music.stream);
1226}
1227
1228// Stop music playing (close stream)
1229void StopMusicStream(Music music)
1230{
1231 StopAudioStream(music.stream);
1232
1233 switch (music.ctxType)
1234 {
1235#if defined(SUPPORT_FILEFORMAT_OGG)
1236 case MUSIC_AUDIO_OGG: stb_vorbis_seek_start((stb_vorbis *)music.ctxData); break;
1237#endif
1238#if defined(SUPPORT_FILEFORMAT_FLAC)
1239 case MUSIC_AUDIO_FLAC: drflac_seek_to_pcm_frame((drflac *)music.ctxData, 0); break;
1240#endif
1241#if defined(SUPPORT_FILEFORMAT_MP3)
1242 case MUSIC_AUDIO_MP3: drmp3_seek_to_pcm_frame((drmp3 *)music.ctxData, 0); break;
1243#endif
1244#if defined(SUPPORT_FILEFORMAT_XM)
1245 case MUSIC_MODULE_XM: jar_xm_reset((jar_xm_context_t *)music.ctxData); break;
1246#endif
1247#if defined(SUPPORT_FILEFORMAT_MOD)
1248 case MUSIC_MODULE_MOD: jar_mod_seek_start((jar_mod_context_t *)music.ctxData); break;
1249#endif
1250 default: break;
1251 }
1252}
1253
1254// Update (re-fill) music buffers if data already processed
1255void UpdateMusicStream(Music music)
1256{
1257 if (music.stream.buffer == NULL) return;
1258
1259 bool streamEnding = false;
1260
1261 unsigned int subBufferSizeInFrames = music.stream.buffer->sizeInFrames/2;
1262
1263 // NOTE: Using dynamic allocation because it could require more than 16KB
1264 void *pcm = RL_CALLOC(subBufferSizeInFrames*music.stream.channels*music.stream.sampleSize/8, 1);
1265
1266 int samplesCount = 0; // Total size of data streamed in L+R samples for xm floats, individual L or R for ogg shorts
1267
1268 // TODO: Get the sampleLeft using totalFramesProcessed... but first, get total frames processed correctly...
1269 //ma_uint32 frameSizeInBytes = ma_get_bytes_per_sample(music.stream.buffer->dsp.formatConverterIn.config.formatIn)*music.stream.buffer->dsp.formatConverterIn.config.channels;
1270 int sampleLeft = music.sampleCount - (music.stream.buffer->totalFramesProcessed*music.stream.channels);
1271
1272 while (IsAudioStreamProcessed(music.stream))
1273 {
1274 if ((sampleLeft/music.stream.channels) >= subBufferSizeInFrames) samplesCount = subBufferSizeInFrames*music.stream.channels;
1275 else samplesCount = sampleLeft;
1276
1277 switch (music.ctxType)
1278 {
1279 #if defined(SUPPORT_FILEFORMAT_OGG)
1280 case MUSIC_AUDIO_OGG:
1281 {
1282 // NOTE: Returns the number of samples to process (be careful! we ask for number of shorts!)
1283 stb_vorbis_get_samples_short_interleaved((stb_vorbis *)music.ctxData, music.stream.channels, (short *)pcm, samplesCount);
1284
1285 } break;
1286 #endif
1287 #if defined(SUPPORT_FILEFORMAT_FLAC)
1288 case MUSIC_AUDIO_FLAC:
1289 {
1290 // NOTE: Returns the number of samples to process (not required)
1291 drflac_read_pcm_frames_s16((drflac *)music.ctxData, samplesCount, (short *)pcm);
1292
1293 } break;
1294 #endif
1295 #if defined(SUPPORT_FILEFORMAT_MP3)
1296 case MUSIC_AUDIO_MP3:
1297 {
1298 // NOTE: samplesCount, actually refers to framesCount and returns the number of frames processed
1299 drmp3_read_pcm_frames_f32((drmp3 *)music.ctxData, samplesCount/music.stream.channels, (float *)pcm);
1300
1301 } break;
1302 #endif
1303 #if defined(SUPPORT_FILEFORMAT_XM)
1304 case MUSIC_MODULE_XM:
1305 {
1306 // NOTE: Internally this function considers 2 channels generation, so samplesCount/2
1307 jar_xm_generate_samples_16bit((jar_xm_context_t *)music.ctxData, (short *)pcm, samplesCount/2);
1308 } break;
1309 #endif
1310 #if defined(SUPPORT_FILEFORMAT_MOD)
1311 case MUSIC_MODULE_MOD:
1312 {
1313 // NOTE: 3rd parameter (nbsample) specify the number of stereo 16bits samples you want, so sampleCount/2
1314 jar_mod_fillbuffer((jar_mod_context_t *)music.ctxData, (short *)pcm, samplesCount/2, 0);
1315 } break;
1316 #endif
1317 default: break;
1318 }
1319
1320 UpdateAudioStream(music.stream, pcm, samplesCount);
1321
1322 if ((music.ctxType == MUSIC_MODULE_XM) || (music.ctxType == MUSIC_MODULE_MOD))
1323 {
1324 if (samplesCount > 1) sampleLeft -= samplesCount/2;
1325 else sampleLeft -= samplesCount;
1326 }
1327 else sampleLeft -= samplesCount;
1328
1329 if (sampleLeft <= 0)
1330 {
1331 streamEnding = true;
1332 break;
1333 }
1334 }
1335
1336 // Free allocated pcm data
1337 RL_FREE(pcm);
1338
1339 // Reset audio stream for looping
1340 if (streamEnding)
1341 {
1342 StopMusicStream(music); // Stop music (and reset)
1343
1344 // Decrease loopCount to stop when required
1345 if (music.loopCount > 1)
1346 {
1347 music.loopCount--; // Decrease loop count
1348 PlayMusicStream(music); // Play again
1349 }
1350 else if (music.loopCount == 0) PlayMusicStream(music);
1351 }
1352 else
1353 {
1354 // NOTE: In case window is minimized, music stream is stopped,
1355 // just make sure to play again on window restore
1356 if (IsMusicPlaying(music)) PlayMusicStream(music);
1357 }
1358}
1359
1360// Check if any music is playing
1361bool IsMusicPlaying(Music music)
1362{
1363 return IsAudioStreamPlaying(music.stream);
1364}
1365
1366// Set volume for music
1367void SetMusicVolume(Music music, float volume)
1368{
1369 SetAudioStreamVolume(music.stream, volume);
1370}
1371
1372// Set pitch for music
1373void SetMusicPitch(Music music, float pitch)
1374{
1375 SetAudioStreamPitch(music.stream, pitch);
1376}
1377
1378// Set music loop count (loop repeats)
1379// NOTE: If set to 0, means infinite loop
1380void SetMusicLoopCount(Music music, int count)
1381{
1382 music.loopCount = count;
1383}
1384
1385// Get music time length (in seconds)
1386float GetMusicTimeLength(Music music)
1387{
1388 float totalSeconds = 0.0f;
1389
1390 totalSeconds = (float)music.sampleCount/(music.stream.sampleRate*music.stream.channels);
1391
1392 return totalSeconds;
1393}
1394
1395// Get current music time played (in seconds)
1396float GetMusicTimePlayed(Music music)
1397{
1398 float secondsPlayed = 0.0f;
1399
1400 if (music.stream.buffer != NULL)
1401 {
1402 //ma_uint32 frameSizeInBytes = ma_get_bytes_per_sample(music.stream.buffer->dsp.formatConverterIn.config.formatIn)*music.stream.buffer->dsp.formatConverterIn.config.channels;
1403 unsigned int samplesPlayed = music.stream.buffer->totalFramesProcessed*music.stream.channels;
1404 secondsPlayed = (float)samplesPlayed / (music.stream.sampleRate*music.stream.channels);
1405 }
1406
1407 return secondsPlayed;
1408}
1409
1410// Init audio stream (to stream audio pcm data)
1411AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, unsigned int channels)
1412{
1413 AudioStream stream = { 0 };
1414
1415 stream.sampleRate = sampleRate;
1416 stream.sampleSize = sampleSize;
1417 stream.channels = channels;
1418
1419 ma_format formatIn = ((stream.sampleSize == 8)? ma_format_u8 : ((stream.sampleSize == 16)? ma_format_s16 : ma_format_f32));
1420
1421 // The size of a streaming buffer must be at least double the size of a period
1422 unsigned int periodSize = AUDIO.System.device.playback.internalPeriodSizeInFrames;
1423 unsigned int subBufferSize = AUDIO.Buffer.defaultSize; // Default buffer size (audio stream)
1424
1425 if (subBufferSize < periodSize) subBufferSize = periodSize;
1426
1427 // Create a double audio buffer of defined size
1428 stream.buffer = LoadAudioBuffer(formatIn, stream.channels, stream.sampleRate, subBufferSize*2, AUDIO_BUFFER_USAGE_STREAM);
1429
1430 if (stream.buffer != NULL)
1431 {
1432 stream.buffer->looping = true; // Always loop for streaming buffers
1433 TRACELOG(LOG_INFO, "STREAM: Initialized successfully (%i Hz, %i bit, %s)", stream.sampleRate, stream.sampleSize, (stream.channels == 1)? "Mono" : "Stereo");
1434 }
1435 else TRACELOG(LOG_WARNING, "STREAM: Failed to load audio buffer, stream could not be created");
1436
1437 return stream;
1438}
1439
1440// Close audio stream and free memory
1441void CloseAudioStream(AudioStream stream)
1442{
1443 UnloadAudioBuffer(stream.buffer);
1444
1445 TRACELOG(LOG_INFO, "STREAM: Unloaded audio stream data from RAM");
1446}
1447
1448// Update audio stream buffers with data
1449// NOTE 1: Only updates one buffer of the stream source: unqueue -> update -> queue
1450// NOTE 2: To unqueue a buffer it needs to be processed: IsAudioStreamProcessed()
1451void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount)
1452{
1453 if (stream.buffer != NULL)
1454 {
1455 if (stream.buffer->isSubBufferProcessed[0] || stream.buffer->isSubBufferProcessed[1])
1456 {
1457 ma_uint32 subBufferToUpdate = 0;
1458
1459 if (stream.buffer->isSubBufferProcessed[0] && stream.buffer->isSubBufferProcessed[1])
1460 {
1461 // Both buffers are available for updating.
1462 // Update the first one and make sure the cursor is moved back to the front.
1463 subBufferToUpdate = 0;
1464 stream.buffer->frameCursorPos = 0;
1465 }
1466 else
1467 {
1468 // Just update whichever sub-buffer is processed.
1469 subBufferToUpdate = (stream.buffer->isSubBufferProcessed[0])? 0 : 1;
1470 }
1471
1472 ma_uint32 subBufferSizeInFrames = stream.buffer->sizeInFrames/2;
1473 unsigned char *subBuffer = stream.buffer->data + ((subBufferSizeInFrames*stream.channels*(stream.sampleSize/8))*subBufferToUpdate);
1474
1475 // TODO: Get total frames processed on this buffer... DOES NOT WORK.
1476 stream.buffer->totalFramesProcessed += subBufferSizeInFrames;
1477
1478 // Does this API expect a whole buffer to be updated in one go?
1479 // Assuming so, but if not will need to change this logic.
1480 if (subBufferSizeInFrames >= (ma_uint32)samplesCount/stream.channels)
1481 {
1482 ma_uint32 framesToWrite = subBufferSizeInFrames;
1483
1484 if (framesToWrite > ((ma_uint32)samplesCount/stream.channels)) framesToWrite = (ma_uint32)samplesCount/stream.channels;
1485
1486 ma_uint32 bytesToWrite = framesToWrite*stream.channels*(stream.sampleSize/8);
1487 memcpy(subBuffer, data, bytesToWrite);
1488
1489 // Any leftover frames should be filled with zeros.
1490 ma_uint32 leftoverFrameCount = subBufferSizeInFrames - framesToWrite;
1491
1492 if (leftoverFrameCount > 0) memset(subBuffer + bytesToWrite, 0, leftoverFrameCount*stream.channels*(stream.sampleSize/8));
1493
1494 stream.buffer->isSubBufferProcessed[subBufferToUpdate] = false;
1495 }
1496 else TRACELOG(LOG_WARNING, "STREAM: Attempting to write too many frames to buffer");
1497 }
1498 else TRACELOG(LOG_WARNING, "STREAM: Buffer not available for updating");
1499 }
1500}
1501
1502// Check if any audio stream buffers requires refill
1503bool IsAudioStreamProcessed(AudioStream stream)
1504{
1505 if (stream.buffer == NULL) return false;
1506
1507 return (stream.buffer->isSubBufferProcessed[0] || stream.buffer->isSubBufferProcessed[1]);
1508}
1509
1510// Play audio stream
1511void PlayAudioStream(AudioStream stream)
1512{
1513 PlayAudioBuffer(stream.buffer);
1514}
1515
1516// Play audio stream
1517void PauseAudioStream(AudioStream stream)
1518{
1519 PauseAudioBuffer(stream.buffer);
1520}
1521
1522// Resume audio stream playing
1523void ResumeAudioStream(AudioStream stream)
1524{
1525 ResumeAudioBuffer(stream.buffer);
1526}
1527
1528// Check if audio stream is playing.
1529bool IsAudioStreamPlaying(AudioStream stream)
1530{
1531 return IsAudioBufferPlaying(stream.buffer);
1532}
1533
1534// Stop audio stream
1535void StopAudioStream(AudioStream stream)
1536{
1537 StopAudioBuffer(stream.buffer);
1538}
1539
1540// Set volume for audio stream (1.0 is max level)
1541void SetAudioStreamVolume(AudioStream stream, float volume)
1542{
1543 SetAudioBufferVolume(stream.buffer, volume);
1544}
1545
1546// Set pitch for audio stream (1.0 is base level)
1547void SetAudioStreamPitch(AudioStream stream, float pitch)
1548{
1549 SetAudioBufferPitch(stream.buffer, pitch);
1550}
1551
1552// Default size for new audio streams
1553void SetAudioStreamBufferSizeDefault(int size)
1554{
1555 AUDIO.Buffer.defaultSize = size;
1556}
1557
1558//----------------------------------------------------------------------------------
1559// Module specific Functions Definition
1560//----------------------------------------------------------------------------------
1561
1562// Log callback function
1563static void OnLog(ma_context *pContext, ma_device *pDevice, ma_uint32 logLevel, const char *message)
1564{
1565 (void)pContext;
1566 (void)pDevice;
1567
1568 TRACELOG(LOG_ERROR, "miniaudio: %s", message); // All log messages from miniaudio are errors
1569}
1570
1571// Reads audio data from an AudioBuffer object in internal format.
1572static ma_uint32 ReadAudioBufferFramesInInternalFormat(AudioBuffer *audioBuffer, void *framesOut, ma_uint32 frameCount)
1573{
1574 ma_uint32 subBufferSizeInFrames = (audioBuffer->sizeInFrames > 1)? audioBuffer->sizeInFrames/2 : audioBuffer->sizeInFrames;
1575 ma_uint32 currentSubBufferIndex = audioBuffer->frameCursorPos/subBufferSizeInFrames;
1576
1577 if (currentSubBufferIndex > 1) return 0;
1578
1579 // Another thread can update the processed state of buffers so
1580 // we just take a copy here to try and avoid potential synchronization problems
1581 bool isSubBufferProcessed[2];
1582 isSubBufferProcessed[0] = audioBuffer->isSubBufferProcessed[0];
1583 isSubBufferProcessed[1] = audioBuffer->isSubBufferProcessed[1];
1584
1585 ma_uint32 frameSizeInBytes = ma_get_bytes_per_frame(audioBuffer->converter.config.formatIn, audioBuffer->converter.config.channelsIn);
1586
1587 // Fill out every frame until we find a buffer that's marked as processed. Then fill the remainder with 0
1588 ma_uint32 framesRead = 0;
1589 while (1)
1590 {
1591 // We break from this loop differently depending on the buffer's usage
1592 // - For static buffers, we simply fill as much data as we can
1593 // - For streaming buffers we only fill the halves of the buffer that are processed
1594 // Unprocessed halves must keep their audio data in-tact
1595 if (audioBuffer->usage == AUDIO_BUFFER_USAGE_STATIC)
1596 {
1597 if (framesRead >= frameCount) break;
1598 }
1599 else
1600 {
1601 if (isSubBufferProcessed[currentSubBufferIndex]) break;
1602 }
1603
1604 ma_uint32 totalFramesRemaining = (frameCount - framesRead);
1605 if (totalFramesRemaining == 0) break;
1606
1607 ma_uint32 framesRemainingInOutputBuffer;
1608 if (audioBuffer->usage == AUDIO_BUFFER_USAGE_STATIC)
1609 {
1610 framesRemainingInOutputBuffer = audioBuffer->sizeInFrames - audioBuffer->frameCursorPos;
1611 }
1612 else
1613 {
1614 ma_uint32 firstFrameIndexOfThisSubBuffer = subBufferSizeInFrames*currentSubBufferIndex;
1615 framesRemainingInOutputBuffer = subBufferSizeInFrames - (audioBuffer->frameCursorPos - firstFrameIndexOfThisSubBuffer);
1616 }
1617
1618 ma_uint32 framesToRead = totalFramesRemaining;
1619 if (framesToRead > framesRemainingInOutputBuffer) framesToRead = framesRemainingInOutputBuffer;
1620
1621 memcpy((unsigned char *)framesOut + (framesRead*frameSizeInBytes), audioBuffer->data + (audioBuffer->frameCursorPos*frameSizeInBytes), framesToRead*frameSizeInBytes);
1622 audioBuffer->frameCursorPos = (audioBuffer->frameCursorPos + framesToRead)%audioBuffer->sizeInFrames;
1623 framesRead += framesToRead;
1624
1625 // If we've read to the end of the buffer, mark it as processed
1626 if (framesToRead == framesRemainingInOutputBuffer)
1627 {
1628 audioBuffer->isSubBufferProcessed[currentSubBufferIndex] = true;
1629 isSubBufferProcessed[currentSubBufferIndex] = true;
1630
1631 currentSubBufferIndex = (currentSubBufferIndex + 1)%2;
1632
1633 // We need to break from this loop if we're not looping
1634 if (!audioBuffer->looping)
1635 {
1636 StopAudioBuffer(audioBuffer);
1637 break;
1638 }
1639 }
1640 }
1641
1642 // Zero-fill excess
1643 ma_uint32 totalFramesRemaining = (frameCount - framesRead);
1644 if (totalFramesRemaining > 0)
1645 {
1646 memset((unsigned char *)framesOut + (framesRead*frameSizeInBytes), 0, totalFramesRemaining*frameSizeInBytes);
1647
1648 // For static buffers we can fill the remaining frames with silence for safety, but we don't want
1649 // to report those frames as "read". The reason for this is that the caller uses the return value
1650 // to know whether or not a non-looping sound has finished playback.
1651 if (audioBuffer->usage != AUDIO_BUFFER_USAGE_STATIC) framesRead += totalFramesRemaining;
1652 }
1653
1654 return framesRead;
1655}
1656
1657// Reads audio data from an AudioBuffer object in device format. Returned data will be in a format appropriate for mixing.
1658static ma_uint32 ReadAudioBufferFramesInMixingFormat(AudioBuffer *audioBuffer, float *framesOut, ma_uint32 frameCount)
1659{
1660 // What's going on here is that we're continuously converting data from the AudioBuffer's internal format to the mixing format, which
1661 // should be defined by the output format of the data converter. We do this until frameCount frames have been output. The important
1662 // detail to remember here is that we never, ever attempt to read more input data than is required for the specified number of output
1663 // frames. This can be achieved with ma_data_converter_get_required_input_frame_count().
1664 ma_uint8 inputBuffer[4096];
1665 ma_uint32 inputBufferFrameCap = sizeof(inputBuffer) / ma_get_bytes_per_frame(audioBuffer->converter.config.formatIn, audioBuffer->converter.config.channelsIn);
1666
1667 ma_uint32 totalOutputFramesProcessed = 0;
1668 while (totalOutputFramesProcessed < frameCount)
1669 {
1670 ma_uint64 outputFramesToProcessThisIteration = frameCount - totalOutputFramesProcessed;
1671
1672 ma_uint64 inputFramesToProcessThisIteration = ma_data_converter_get_required_input_frame_count(&audioBuffer->converter, outputFramesToProcessThisIteration);
1673 if (inputFramesToProcessThisIteration > inputBufferFrameCap)
1674 {
1675 inputFramesToProcessThisIteration = inputBufferFrameCap;
1676 }
1677
1678 float *runningFramesOut = framesOut + (totalOutputFramesProcessed * audioBuffer->converter.config.channelsOut);
1679
1680 /* At this point we can convert the data to our mixing format. */
1681 ma_uint64 inputFramesProcessedThisIteration = ReadAudioBufferFramesInInternalFormat(audioBuffer, inputBuffer, (ma_uint32)inputFramesToProcessThisIteration); /* Safe cast. */
1682 ma_uint64 outputFramesProcessedThisIteration = outputFramesToProcessThisIteration;
1683 ma_data_converter_process_pcm_frames(&audioBuffer->converter, inputBuffer, &inputFramesProcessedThisIteration, runningFramesOut, &outputFramesProcessedThisIteration);
1684
1685 totalOutputFramesProcessed += (ma_uint32)outputFramesProcessedThisIteration; /* Safe cast. */
1686
1687 if (inputFramesProcessedThisIteration < inputFramesToProcessThisIteration)
1688 {
1689 break; /* Ran out of input data. */
1690 }
1691
1692 /* 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. */
1693 if (inputFramesProcessedThisIteration == 0 && outputFramesProcessedThisIteration == 0)
1694 {
1695 break;
1696 }
1697 }
1698
1699 return totalOutputFramesProcessed;
1700}
1701
1702
1703// Sending audio data to device callback function
1704// NOTE: All the mixing takes place here
1705static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const void *pFramesInput, ma_uint32 frameCount)
1706{
1707 (void)pDevice;
1708
1709 // Mixing is basically just an accumulation, we need to initialize the output buffer to 0
1710 memset(pFramesOut, 0, frameCount*pDevice->playback.channels*ma_get_bytes_per_sample(pDevice->playback.format));
1711
1712 // Using a mutex here for thread-safety which makes things not real-time
1713 // This is unlikely to be necessary for this project, but may want to consider how you might want to avoid this
1714 ma_mutex_lock(&AUDIO.System.lock);
1715 {
1716 for (AudioBuffer *audioBuffer = AUDIO.Buffer.first; audioBuffer != NULL; audioBuffer = audioBuffer->next)
1717 {
1718 // Ignore stopped or paused sounds
1719 if (!audioBuffer->playing || audioBuffer->paused) continue;
1720
1721 ma_uint32 framesRead = 0;
1722
1723 while (1)
1724 {
1725 if (framesRead >= frameCount) break;
1726
1727 // Just read as much data as we can from the stream
1728 ma_uint32 framesToRead = (frameCount - framesRead);
1729
1730 while (framesToRead > 0)
1731 {
1732 float tempBuffer[1024]; // 512 frames for stereo
1733
1734 ma_uint32 framesToReadRightNow = framesToRead;
1735 if (framesToReadRightNow > sizeof(tempBuffer)/sizeof(tempBuffer[0])/AUDIO_DEVICE_CHANNELS)
1736 {
1737 framesToReadRightNow = sizeof(tempBuffer)/sizeof(tempBuffer[0])/AUDIO_DEVICE_CHANNELS;
1738 }
1739
1740 ma_uint32 framesJustRead = ReadAudioBufferFramesInMixingFormat(audioBuffer, tempBuffer, framesToReadRightNow);
1741 if (framesJustRead > 0)
1742 {
1743 float *framesOut = (float *)pFramesOut + (framesRead*AUDIO.System.device.playback.channels);
1744 float *framesIn = tempBuffer;
1745
1746 MixAudioFrames(framesOut, framesIn, framesJustRead, audioBuffer->volume);
1747
1748 framesToRead -= framesJustRead;
1749 framesRead += framesJustRead;
1750 }
1751
1752 if (!audioBuffer->playing)
1753 {
1754 framesRead = frameCount;
1755 break;
1756 }
1757
1758 // If we weren't able to read all the frames we requested, break
1759 if (framesJustRead < framesToReadRightNow)
1760 {
1761 if (!audioBuffer->looping)
1762 {
1763 StopAudioBuffer(audioBuffer);
1764 break;
1765 }
1766 else
1767 {
1768 // Should never get here, but just for safety,
1769 // move the cursor position back to the start and continue the loop
1770 audioBuffer->frameCursorPos = 0;
1771 continue;
1772 }
1773 }
1774 }
1775
1776 // If for some reason we weren't able to read every frame we'll need to break from the loop
1777 // Not doing this could theoretically put us into an infinite loop
1778 if (framesToRead > 0) break;
1779 }
1780 }
1781 }
1782
1783 ma_mutex_unlock(&AUDIO.System.lock);
1784}
1785
1786// This is the main mixing function. Mixing is pretty simple in this project - it's just an accumulation.
1787// NOTE: framesOut is both an input and an output. It will be initially filled with zeros outside of this function.
1788static void MixAudioFrames(float *framesOut, const float *framesIn, ma_uint32 frameCount, float localVolume)
1789{
1790 for (ma_uint32 iFrame = 0; iFrame < frameCount; ++iFrame)
1791 {
1792 for (ma_uint32 iChannel = 0; iChannel < AUDIO.System.device.playback.channels; ++iChannel)
1793 {
1794 float *frameOut = framesOut + (iFrame*AUDIO.System.device.playback.channels);
1795 const float *frameIn = framesIn + (iFrame*AUDIO.System.device.playback.channels);
1796
1797 frameOut[iChannel] += (frameIn[iChannel]*localVolume);
1798 }
1799 }
1800}
1801
1802// Initialise the multichannel buffer pool
1803static void InitAudioBufferPool(void)
1804{
1805 // Dummy buffers
1806 for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++)
1807 {
1808 AUDIO.MultiChannel.pool[i] = LoadAudioBuffer(AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO_DEVICE_SAMPLE_RATE, 0, AUDIO_BUFFER_USAGE_STATIC);
1809 }
1810
1811 // TODO: Verification required for log
1812 TRACELOG(LOG_INFO, "AUDIO: Multichannel pool size: %i", MAX_AUDIO_BUFFER_POOL_CHANNELS);
1813}
1814
1815// Close the audio buffers pool
1816static void CloseAudioBufferPool(void)
1817{
1818 for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++)
1819 {
1820 RL_FREE(AUDIO.MultiChannel.pool[i]->data);
1821 RL_FREE(AUDIO.MultiChannel.pool[i]);
1822 }
1823}
1824
1825#if defined(SUPPORT_FILEFORMAT_WAV)
1826// Load WAV file into Wave structure
1827static Wave LoadWAV(const char *fileName)
1828{
1829 // Basic WAV headers structs
1830 typedef struct {
1831 char chunkID[4];
1832 int chunkSize;
1833 char format[4];
1834 } WAVRiffHeader;
1835
1836 typedef struct {
1837 char subChunkID[4];
1838 int subChunkSize;
1839 short audioFormat;
1840 short numChannels;
1841 int sampleRate;
1842 int byteRate;
1843 short blockAlign;
1844 short bitsPerSample;
1845 } WAVFormat;
1846
1847 typedef struct {
1848 char subChunkID[4];
1849 int subChunkSize;
1850 } WAVData;
1851
1852 WAVRiffHeader wavRiffHeader = { 0 };
1853 WAVFormat wavFormat = { 0 };
1854 WAVData wavData = { 0 };
1855
1856 Wave wave = { 0 };
1857 FILE *wavFile = NULL;
1858
1859 wavFile = fopen(fileName, "rb");
1860
1861 if (wavFile == NULL)
1862 {
1863 TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open WAV file", fileName);
1864 wave.data = NULL;
1865 }
1866 else
1867 {
1868 // Read in the first chunk into the struct
1869 fread(&wavRiffHeader, sizeof(WAVRiffHeader), 1, wavFile);
1870
1871 // Check for RIFF and WAVE tags
1872 if ((wavRiffHeader.chunkID[0] != 'R') ||
1873 (wavRiffHeader.chunkID[1] != 'I') ||
1874 (wavRiffHeader.chunkID[2] != 'F') ||
1875 (wavRiffHeader.chunkID[3] != 'F') ||
1876 (wavRiffHeader.format[0] != 'W') ||
1877 (wavRiffHeader.format[1] != 'A') ||
1878 (wavRiffHeader.format[2] != 'V') ||
1879 (wavRiffHeader.format[3] != 'E'))
1880 {
1881 TRACELOG(LOG_WARNING, "WAVE: [%s] RIFF or WAVE header are not valid", fileName);
1882 }
1883 else
1884 {
1885 // Read in the 2nd chunk for the wave info
1886 fread(&wavFormat, sizeof(WAVFormat), 1, wavFile);
1887
1888 // Check for fmt tag
1889 if ((wavFormat.subChunkID[0] != 'f') || (wavFormat.subChunkID[1] != 'm') ||
1890 (wavFormat.subChunkID[2] != 't') || (wavFormat.subChunkID[3] != ' '))
1891 {
1892 TRACELOG(LOG_WARNING, "WAVE: [%s] Wave format header is not valid", fileName);
1893 }
1894 else
1895 {
1896 // Check for extra parameters;
1897 if (wavFormat.subChunkSize > 16) fseek(wavFile, sizeof(short), SEEK_CUR);
1898
1899 // Read in the the last byte of data before the sound file
1900 fread(&wavData, sizeof(WAVData), 1, wavFile);
1901
1902 // Check for data tag
1903 if ((wavData.subChunkID[0] != 'd') || (wavData.subChunkID[1] != 'a') ||
1904 (wavData.subChunkID[2] != 't') || (wavData.subChunkID[3] != 'a'))
1905 {
1906 TRACELOG(LOG_WARNING, "WAVE: [%s] Data header is not valid", fileName);
1907 }
1908 else
1909 {
1910 // Allocate memory for data
1911 wave.data = RL_MALLOC(wavData.subChunkSize);
1912
1913 // Read in the sound data into the soundData variable
1914 fread(wave.data, wavData.subChunkSize, 1, wavFile);
1915
1916 // Store wave parameters
1917 wave.sampleRate = wavFormat.sampleRate;
1918 wave.sampleSize = wavFormat.bitsPerSample;
1919 wave.channels = wavFormat.numChannels;
1920
1921 // NOTE: Only support 8 bit, 16 bit and 32 bit sample sizes
1922 if ((wave.sampleSize != 8) && (wave.sampleSize != 16) && (wave.sampleSize != 32))
1923 {
1924 TRACELOG(LOG_WARNING, "WAVE: [%s] Sample size (%ibit) not supported, converted to 16bit", fileName, wave.sampleSize);
1925 WaveFormat(&wave, wave.sampleRate, 16, wave.channels);
1926 }
1927
1928 // NOTE: Only support up to 2 channels (mono, stereo)
1929 if (wave.channels > 2)
1930 {
1931 WaveFormat(&wave, wave.sampleRate, wave.sampleSize, 2);
1932 TRACELOG(LOG_WARNING, "WAVE: [%s] Channels number (%i) not supported, converted to 2 channels", fileName, wave.channels);
1933 }
1934
1935 // NOTE: subChunkSize comes in bytes, we need to translate it to number of samples
1936 wave.sampleCount = (wavData.subChunkSize/(wave.sampleSize/8))/wave.channels;
1937
1938 TRACELOG(LOG_INFO, "WAVE: [%s] File loaded successfully (%i Hz, %i bit, %s)", fileName, wave.sampleRate, wave.sampleSize, (wave.channels == 1)? "Mono" : "Stereo");
1939 }
1940 }
1941 }
1942
1943 fclose(wavFile);
1944 }
1945
1946 return wave;
1947}
1948
1949// Save wave data as WAV file
1950static int SaveWAV(Wave wave, const char *fileName)
1951{
1952 int success = 0;
1953 int dataSize = wave.sampleCount*wave.channels*wave.sampleSize/8;
1954
1955 // Basic WAV headers structs
1956 typedef struct {
1957 char chunkID[4];
1958 int chunkSize;
1959 char format[4];
1960 } RiffHeader;
1961
1962 typedef struct {
1963 char subChunkID[4];
1964 int subChunkSize;
1965 short audioFormat;
1966 short numChannels;
1967 int sampleRate;
1968 int byteRate;
1969 short blockAlign;
1970 short bitsPerSample;
1971 } WaveFormat;
1972
1973 typedef struct {
1974 char subChunkID[4];
1975 int subChunkSize;
1976 } WaveData;
1977
1978 FILE *wavFile = fopen(fileName, "wb");
1979
1980 if (wavFile == NULL) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open audio file", fileName);
1981 else
1982 {
1983 RiffHeader riffHeader;
1984 WaveFormat waveFormat;
1985 WaveData waveData;
1986
1987 // Fill structs with data
1988 riffHeader.chunkID[0] = 'R';
1989 riffHeader.chunkID[1] = 'I';
1990 riffHeader.chunkID[2] = 'F';
1991 riffHeader.chunkID[3] = 'F';
1992 riffHeader.chunkSize = 44 - 4 + wave.sampleCount*wave.sampleSize/8;
1993 riffHeader.format[0] = 'W';
1994 riffHeader.format[1] = 'A';
1995 riffHeader.format[2] = 'V';
1996 riffHeader.format[3] = 'E';
1997
1998 waveFormat.subChunkID[0] = 'f';
1999 waveFormat.subChunkID[1] = 'm';
2000 waveFormat.subChunkID[2] = 't';
2001 waveFormat.subChunkID[3] = ' ';
2002 waveFormat.subChunkSize = 16;
2003 waveFormat.audioFormat = 1;
2004 waveFormat.numChannels = wave.channels;
2005 waveFormat.sampleRate = wave.sampleRate;
2006 waveFormat.byteRate = wave.sampleRate*wave.sampleSize/8;
2007 waveFormat.blockAlign = wave.sampleSize/8;
2008 waveFormat.bitsPerSample = wave.sampleSize;
2009
2010 waveData.subChunkID[0] = 'd';
2011 waveData.subChunkID[1] = 'a';
2012 waveData.subChunkID[2] = 't';
2013 waveData.subChunkID[3] = 'a';
2014 waveData.subChunkSize = dataSize;
2015
2016 fwrite(&riffHeader, sizeof(RiffHeader), 1, wavFile);
2017 fwrite(&waveFormat, sizeof(WaveFormat), 1, wavFile);
2018 fwrite(&waveData, sizeof(WaveData), 1, wavFile);
2019
2020 success = fwrite(wave.data, dataSize, 1, wavFile);
2021
2022 fclose(wavFile);
2023 }
2024
2025 // If all data has been written correctly to file, success = 1
2026 return success;
2027}
2028#endif
2029
2030#if defined(SUPPORT_FILEFORMAT_OGG)
2031// Load OGG file into Wave structure
2032// NOTE: Using stb_vorbis library
2033static Wave LoadOGG(const char *fileName)
2034{
2035 Wave wave = { 0 };
2036
2037 stb_vorbis *oggFile = stb_vorbis_open_filename(fileName, NULL, NULL);
2038
2039 if (oggFile == NULL) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open OGG file", fileName);
2040 else
2041 {
2042 stb_vorbis_info info = stb_vorbis_get_info(oggFile);
2043
2044 wave.sampleRate = info.sample_rate;
2045 wave.sampleSize = 16; // 16 bit per sample (short)
2046 wave.channels = info.channels;
2047 wave.sampleCount = (unsigned int)stb_vorbis_stream_length_in_samples(oggFile)*info.channels; // Independent by channel
2048
2049 float totalSeconds = stb_vorbis_stream_length_in_seconds(oggFile);
2050 if (totalSeconds > 10) TRACELOG(LOG_WARNING, "WAVE: [%s] Ogg audio length larger than 10 seconds (%f), that's a big file in memory, consider music streaming", fileName, totalSeconds);
2051
2052 wave.data = (short *)RL_MALLOC(wave.sampleCount*wave.channels*sizeof(short));
2053
2054 // NOTE: Returns the number of samples to process (be careful! we ask for number of shorts!)
2055 stb_vorbis_get_samples_short_interleaved(oggFile, info.channels, (short *)wave.data, wave.sampleCount*wave.channels);
2056 TRACELOG(LOG_INFO, "WAVE: [%s] OGG file loaded successfully (%i Hz, %i bit, %s)", fileName, wave.sampleRate, wave.sampleSize, (wave.channels == 1)? "Mono" : "Stereo");
2057
2058 stb_vorbis_close(oggFile);
2059 }
2060
2061 return wave;
2062}
2063#endif
2064
2065#if defined(SUPPORT_FILEFORMAT_FLAC)
2066// Load FLAC file into Wave structure
2067// NOTE: Using dr_flac library
2068static Wave LoadFLAC(const char *fileName)
2069{
2070 Wave wave = { 0 };
2071
2072 // Decode an entire FLAC file in one go
2073 unsigned long long int totalSampleCount = 0;
2074 wave.data = drflac_open_file_and_read_pcm_frames_s16(fileName, &wave.channels, &wave.sampleRate, &totalSampleCount);
2075
2076 if (wave.data == NULL) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to load FLAC data", fileName);
2077 else
2078 {
2079 wave.sampleCount = (unsigned int)totalSampleCount;
2080 wave.sampleSize = 16;
2081
2082 // NOTE: Only support up to 2 channels (mono, stereo)
2083 if (wave.channels > 2) TRACELOG(LOG_WARNING, "WAVE: [%s] FLAC channels number (%i) not supported", fileName, wave.channels);
2084
2085 TRACELOG(LOG_INFO, "WAVE: [%s] FLAC file loaded successfully (%i Hz, %i bit, %s)", fileName, wave.sampleRate, wave.sampleSize, (wave.channels == 1)? "Mono" : "Stereo");
2086 }
2087
2088 return wave;
2089}
2090#endif
2091
2092#if defined(SUPPORT_FILEFORMAT_MP3)
2093// Load MP3 file into Wave structure
2094// NOTE: Using dr_mp3 library
2095static Wave LoadMP3(const char *fileName)
2096{
2097 Wave wave = { 0 };
2098
2099 // Decode an entire MP3 file in one go
2100 unsigned long long int totalFrameCount = 0;
2101 drmp3_config config = { 0 };
2102 wave.data = drmp3_open_file_and_read_f32(fileName, &config, &totalFrameCount);
2103
2104 if (wave.data == NULL) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to load MP3 data", fileName);
2105 else
2106 {
2107 wave.channels = config.outputChannels;
2108 wave.sampleRate = config.outputSampleRate;
2109 wave.sampleCount = (int)totalFrameCount*wave.channels;
2110 wave.sampleSize = 32;
2111
2112 // NOTE: Only support up to 2 channels (mono, stereo)
2113 if (wave.channels > 2) TRACELOG(LOG_WARNING, "WAVE: [%s] MP3 channels number (%i) not supported", fileName, wave.channels);
2114
2115 TRACELOG(LOG_INFO, "WAVE: [%s] MP3 file loaded successfully (%i Hz, %i bit, %s)", fileName, wave.sampleRate, wave.sampleSize, (wave.channels == 1)? "Mono" : "Stereo");
2116 }
2117
2118 return wave;
2119}
2120#endif
2121
2122// Some required functions for audio standalone module version
2123#if defined(RAUDIO_STANDALONE)
2124// Check file extension
2125bool IsFileExtension(const char *fileName, const char *ext)
2126{
2127 bool result = false;
2128 const char *fileExt;
2129
2130 if ((fileExt = strrchr(fileName, '.')) != NULL)
2131 {
2132 if (strcmp(fileExt, ext) == 0) result = true;
2133 }
2134
2135 return result;
2136}
2137#endif
2138
2139#undef AudioBuffer
2140