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