summaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorRay <[email protected]>2023-03-06 12:33:49 +0100
committerRay <[email protected]>2023-03-06 12:33:49 +0100
commit68ee0bb8dd43074574dab08d836eaeba58bd0351 (patch)
tree190c3087db693edfe9ab0922ffa3fd37959975de /src
parentd3f5bc40436ef45aee2cc353f985d4ad5fc57bbd (diff)
downloadraylib-68ee0bb8dd43074574dab08d836eaeba58bd0351.tar.gz
raylib-68ee0bb8dd43074574dab08d836eaeba58bd0351.zip
ADDED: QOA music streaming (with auxiliar lib)
Some format tweaks
Diffstat (limited to 'src')
-rw-r--r--src/external/qoaplay.c278
-rw-r--r--src/raudio.c157
2 files changed, 357 insertions, 78 deletions
diff --git a/src/external/qoaplay.c b/src/external/qoaplay.c
new file mode 100644
index 00000000..b4dc09c7
--- /dev/null
+++ b/src/external/qoaplay.c
@@ -0,0 +1,278 @@
+/*******************************************************************************************
+*
+* qoaplay - QOA stream playing helper functions
+*
+* qoaplay is a tiny abstraction to read and decode a QOA file "on the fly".
+* It reads and decodes one frame at a time with minimal memory requirements.
+* qoaplay also provides some functions to seek to a specific frame.
+*
+* LICENSE: MIT License
+*
+* Copyright (c) 2023 Dominic Szablewski (@phoboslab), reviewed by Ramon Santamaria (@raysan5)
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy
+* of this software and associated documentation files (the "Software"), to deal
+* in the Software without restriction, including without limitation the rights
+* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+* copies of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in all
+* copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+* SOFTWARE.
+*
+**********************************************************************************************/
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+// QOA streaming data descriptor
+typedef struct {
+ qoa_desc info; // QOA descriptor data
+
+ FILE *file; // QOA file to read, if NULL, using memory buffer -> file_data
+ unsigned char *file_data; // QOA file data on memory
+ unsigned int file_data_size; // QOA file data on memory size
+ unsigned int file_data_offset; // QOA file data on memory offset for next read
+
+ unsigned int first_frame_pos; // First frame position (after QOA header, required for offset)
+ unsigned int sample_position; // Current streaming sample position
+
+ unsigned char *buffer; // Buffer used to read samples from file/memory (used on decoding)
+ unsigned int buffer_len; // Buffer length to read samples for streaming
+
+ short *sample_data; // Sample data decoded
+ unsigned int sample_data_len; // Sample data decoded length
+ unsigned int sample_data_pos; // Sample data decoded position
+
+} qoaplay_desc;
+
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+//----------------------------------------------------------------------------------
+
+#if defined(__cplusplus)
+extern "C" { // Prevents name mangling of functions
+#endif
+
+qoaplay_desc *qoaplay_open(char *path);
+qoaplay_desc *qoaplay_open_memory(const unsigned char *data, int data_size);
+void qoaplay_close(qoaplay_desc *qoa_ctx);
+
+void qoaplay_rewind(qoaplay_desc *qoa_ctx);
+void qoaplay_seek_frame(qoaplay_desc *qoa_ctx, int frame);
+unsigned int qoaplay_decode(qoaplay_desc *qoa_ctx, float *sample_data, int num_samples);
+unsigned int qoaplay_decode_frame(qoaplay_desc *qoa_ctx);
+double qoaplay_get_duration(qoaplay_desc *qoa_ctx);
+double qoaplay_get_time(qoaplay_desc *qoa_ctx);
+int qoaplay_get_frame(qoaplay_desc *qoa_ctx);
+
+#if defined(__cplusplus)
+} // Prevents name mangling of functions
+#endif
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition
+//----------------------------------------------------------------------------------
+
+// Open QOA file, keep FILE pointer to keep reading from file
+qoaplay_desc *qoaplay_open(char *path)
+{
+ FILE *file = fopen(path, "rb");
+ if (!file) return NULL;
+
+ // Read and decode the file header
+ unsigned char header[QOA_MIN_FILESIZE];
+ int read = fread(header, QOA_MIN_FILESIZE, 1, file);
+ if (!read) return NULL;
+
+ qoa_desc qoa;
+ unsigned int first_frame_pos = qoa_decode_header(header, QOA_MIN_FILESIZE, &qoa);
+ if (!first_frame_pos) return NULL;
+
+ // Rewind the file back to beginning of the first frame
+ fseek(file, first_frame_pos, SEEK_SET);
+
+ // Allocate one chunk of memory for the qoaplay_desc struct
+ // + the sample data for one frame
+ // + a buffer to hold one frame of encoded data
+ unsigned int buffer_size = qoa_max_frame_size(&qoa);
+ unsigned int sample_data_size = qoa.channels*QOA_FRAME_LEN*sizeof(short)*2;
+ qoaplay_desc *qoa_ctx = QOA_MALLOC(sizeof(qoaplay_desc) + buffer_size + sample_data_size);
+ memset(qoa_ctx, 0, sizeof(qoaplay_desc));
+
+ qoa_ctx->file = file;
+ qoa_ctx->file_data = NULL;
+ qoa_ctx->file_data_size = 0;
+ qoa_ctx->file_data_offset = 0;
+ qoa_ctx->first_frame_pos = first_frame_pos;
+
+ // Setup data pointers to previously allocated data
+ qoa_ctx->buffer = ((unsigned char *)qoa_ctx) + sizeof(qoaplay_desc);
+ qoa_ctx->sample_data = (short *)(((unsigned char *)qoa_ctx) + sizeof(qoaplay_desc) + buffer_size);
+
+ qoa_ctx->info.channels = qoa.channels;
+ qoa_ctx->info.samplerate = qoa.samplerate;
+ qoa_ctx->info.samples = qoa.samples;
+
+ return qoa_ctx;
+}
+
+// Open QOA file from memory, no FILE pointer required
+qoaplay_desc *qoaplay_open_memory(const unsigned char *data, int data_size)
+{
+ // Read and decode the file header
+ unsigned char header[QOA_MIN_FILESIZE];
+ memcpy(header, data, QOA_MIN_FILESIZE);
+
+ qoa_desc qoa;
+ unsigned int first_frame_pos = qoa_decode_header(header, QOA_MIN_FILESIZE, &qoa);
+ if (!first_frame_pos) return NULL;
+
+ // Allocate one chunk of memory for the qoaplay_desc struct
+ // + the sample data for one frame
+ // + a buffer to hold one frame of encoded data
+ unsigned int buffer_size = qoa_max_frame_size(&qoa);
+ unsigned int sample_data_size = qoa.channels*QOA_FRAME_LEN*sizeof(short)*2;
+ qoaplay_desc *qoa_ctx = QOA_MALLOC(sizeof(qoaplay_desc) + buffer_size + sample_data_size);
+ memset(qoa_ctx, 0, sizeof(qoaplay_desc));
+
+ qoa_ctx->file = NULL;
+
+ // Keep a copy of file data provided to be managed internally
+ qoa_ctx->file_data = (unsigned char *)QOA_MALLOC(data_size);
+ memcpy(qoa_ctx->file_data, data, data_size);
+ qoa_ctx->file_data_size = data_size;
+ qoa_ctx->file_data_offset = 0;
+ qoa_ctx->first_frame_pos = first_frame_pos;
+
+ // Setup data pointers to previously allocated data
+ qoa_ctx->buffer = ((unsigned char *)qoa_ctx) + sizeof(qoaplay_desc);
+ qoa_ctx->sample_data = (short *)(((unsigned char *)qoa_ctx) + sizeof(qoaplay_desc) + buffer_size);
+
+ qoa_ctx->info.channels = qoa.channels;
+ qoa_ctx->info.samplerate = qoa.samplerate;
+ qoa_ctx->info.samples = qoa.samples;
+
+ return qoa_ctx;
+}
+
+// Close QOA file (if open) and free internal memory
+void qoaplay_close(qoaplay_desc *qoa_ctx)
+{
+ if (qoa_ctx->file) fclose(qoa_ctx->file);
+
+ if ((qoa_ctx->file_data) && (qoa_ctx->file_data_size > 0))
+ {
+ QOA_FREE(qoa_ctx->file_data);
+ qoa_ctx->file_data_size = 0;
+ }
+
+ QOA_FREE(qoa_ctx);
+}
+
+// Decode one frame from QOA data
+unsigned int qoaplay_decode_frame(qoaplay_desc *qoa_ctx)
+{
+ if (qoa_ctx->file) qoa_ctx->buffer_len = fread(qoa_ctx->buffer, 1, qoa_max_frame_size(&qoa_ctx->info), qoa_ctx->file);
+ else
+ {
+ qoa_ctx->buffer_len = qoa_max_frame_size(&qoa_ctx->info);
+ memcpy(qoa_ctx->buffer, qoa_ctx->file_data + qoa_ctx->file_data_offset, qoa_ctx->buffer_len);
+ qoa_ctx->file_data_offset += qoa_ctx->buffer_len;
+ }
+
+ unsigned int frame_len;
+ qoa_decode_frame(qoa_ctx->buffer, qoa_ctx->buffer_len, &qoa_ctx->info, qoa_ctx->sample_data, &frame_len);
+ qoa_ctx->sample_data_pos = 0;
+ qoa_ctx->sample_data_len = frame_len;
+
+ return frame_len;
+}
+
+// Rewind QOA file or memory pointer to beginning
+void qoaplay_rewind(qoaplay_desc *qoa_ctx)
+{
+ if (qoa_ctx->file) fseek(qoa_ctx->file, qoa_ctx->first_frame_pos, SEEK_SET);
+ else qoa_ctx->file_data_offset = 0;
+
+ qoa_ctx->sample_position = 0;
+ qoa_ctx->sample_data_len = 0;
+ qoa_ctx->sample_data_pos = 0;
+}
+
+// Decode required QOA frames
+unsigned int qoaplay_decode(qoaplay_desc *qoa_ctx, float *sample_data, int num_samples)
+{
+ int src_index = qoa_ctx->sample_data_pos*qoa_ctx->info.channels;
+ int dst_index = 0;
+
+ for (int i = 0; i < num_samples; i++)
+ {
+ // Do we have to decode more samples?
+ if (qoa_ctx->sample_data_len - qoa_ctx->sample_data_pos == 0)
+ {
+ if (!qoaplay_decode_frame(qoa_ctx))
+ {
+ // Loop to the beginning
+ qoaplay_rewind(qoa_ctx);
+ qoaplay_decode_frame(qoa_ctx);
+ }
+
+ src_index = 0;
+ }
+
+ // Normalize to -1..1 floats and write to dest
+ for (int c = 0; c < qoa_ctx->info.channels; c++)
+ {
+ sample_data[dst_index++] = qoa_ctx->sample_data[src_index++]/32768.0;
+ }
+
+ qoa_ctx->sample_data_pos++;
+ qoa_ctx->sample_position++;
+ }
+
+ return num_samples;
+}
+
+// Get QOA total time duration in seconds
+double qoaplay_get_duration(qoaplay_desc *qoa_ctx)
+{
+ return (double)qoa_ctx->info.samples/(double)qoa_ctx->info.samplerate;
+}
+
+// Get QOA current time position in seconds
+double qoaplay_get_time(qoaplay_desc *qoa_ctx)
+{
+ return (double)qoa_ctx->sample_position/(double)qoa_ctx->info.samplerate;
+}
+
+// Get QOA current audio frame
+int qoaplay_get_frame(qoaplay_desc *qoa_ctx)
+{
+ return qoa_ctx->sample_position/QOA_FRAME_LEN;
+}
+
+// Seek QOA audio frame
+void qoaplay_seek_frame(qoaplay_desc *qoa_ctx, int frame)
+{
+ if (frame < 0) frame = 0;
+
+ if (frame > qoa_ctx->info.samples/QOA_FRAME_LEN) frame = qoa_ctx->info.samples/QOA_FRAME_LEN;
+
+ qoa_ctx->sample_position = frame*QOA_FRAME_LEN;
+ qoa_ctx->sample_data_len = 0;
+ qoa_ctx->sample_data_pos = 0;
+
+ unsigned int offset = qoa_ctx->first_frame_pos + frame*qoa_max_frame_size(&qoa_ctx->info);
+
+ if (qoa_ctx->file) fseek(qoa_ctx->file, offset, SEEK_SET);
+ else qoa_ctx->file_data_offset = offset;
+}
diff --git a/src/raudio.c b/src/raudio.c
index 207c0c49..03e4bcba 100644
--- a/src/raudio.c
+++ b/src/raudio.c
@@ -230,6 +230,7 @@ typedef struct tagBITMAPINFOHEADER {
#define QOA_IMPLEMENTATION
#include "external/qoa.h" // QOA loading and saving functions
+ #include "external/qoaplay.c" // QOA stream playing helper functions
#endif
#if defined(SUPPORT_FILEFORMAT_FLAC)
@@ -287,21 +288,6 @@ typedef struct tagBITMAPINFOHEADER {
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
-
-// Music context type
-// NOTE: Depends on data structure provided by the library
-// in charge of reading the different file types
-typedef enum {
- MUSIC_AUDIO_NONE = 0, // No audio context loaded
- MUSIC_AUDIO_WAV, // WAV audio context
- MUSIC_AUDIO_OGG, // OGG audio context
- MUSIC_AUDIO_FLAC, // FLAC audio context
- MUSIC_AUDIO_MP3, // MP3 audio context
- MUSIC_AUDIO_QOA, // QOA audio context
- MUSIC_MODULE_XM, // XM module audio context
- MUSIC_MODULE_MOD // MOD module audio context
-} MusicContextType;
-
#if defined(RAUDIO_STANDALONE)
// Trace log level
// NOTE: Organized by priority level
@@ -317,6 +303,20 @@ typedef enum {
} TraceLogLevel;
#endif
+// Music context type
+// NOTE: Depends on data structure provided by the library
+// in charge of reading the different file types
+typedef enum {
+ MUSIC_AUDIO_NONE = 0, // No audio context loaded
+ MUSIC_AUDIO_WAV, // WAV audio context
+ MUSIC_AUDIO_OGG, // OGG audio context
+ MUSIC_AUDIO_FLAC, // FLAC audio context
+ MUSIC_AUDIO_MP3, // MP3 audio context
+ MUSIC_AUDIO_QOA, // QOA audio context
+ MUSIC_MODULE_XM, // XM module audio context
+ MUSIC_MODULE_MOD // MOD module audio context
+} MusicContextType;
+
// NOTE: Different logic is used when feeding data to the playback device
// depending on whether data is streamed (Music vs Sound)
typedef enum {
@@ -1322,7 +1322,7 @@ void UnloadWaveSamples(float *samples)
}
//----------------------------------------------------------------------------------
-// Module Functions Definition - Music loading and stream playing (.OGG)
+// Module Functions Definition - Music loading and stream playing
//----------------------------------------------------------------------------------
// Load music stream from file
@@ -1395,21 +1395,16 @@ Music LoadMusicStream(const char *fileName)
#if defined(SUPPORT_FILEFORMAT_QOA)
else if (IsFileExtension(fileName, ".qoa"))
{
- qoa_desc *ctxQoa = RL_CALLOC(1, sizeof(qoa_desc));
-
- // TODO: QOA stream support: Init context from file
- int result = 0;
-
+ qoaplay_desc *ctxQoa = qoaplay_open(fileName);
music.ctxType = MUSIC_AUDIO_QOA;
music.ctxData = ctxQoa;
- if (result > 0)
+ if (ctxQoa->file != NULL)
{
- music.stream = LoadAudioStream(ctxQoa->samplerate, 16, ctxQoa->channels);
-
- // TODO: Read next frame(s) from QOA stream
- //music.frameCount = qoa_decode_frame(const unsigned char *bytes, unsigned int size, ctxQoa, short *sample_data, unsigned int *frame_len);
-
+ // NOTE: We are loading samples are 32bit float normalized data, so,
+ // we configure the output audio stream to also use float 32bit
+ music.stream = LoadAudioStream(ctxQoa->info.samplerate, 32, ctxQoa->info.channels);
+ music.frameCount = ctxQoa->info.samples;
music.looping = true; // Looping enabled by default
musicLoaded = true;
}
@@ -1594,21 +1589,16 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data,
#if defined(SUPPORT_FILEFORMAT_QOA)
else if (strcmp(fileType, ".qoa") == 0)
{
- qoa_desc *ctxQoa = RL_CALLOC(1, sizeof(qoa_desc));
-
- // TODO: Init QOA context data
- int result = 0;
-
+ qoaplay_desc *ctxQoa = qoaplay_open_memory(data, dataSize);
music.ctxType = MUSIC_AUDIO_QOA;
music.ctxData = ctxQoa;
- if (result > 0)
+ if ((ctxQoa->file_data != NULL) && (ctxQoa->file_data_size != 0))
{
- music.stream = LoadAudioStream(ctxQoa->samplerate, 16, ctxQoa->channels);
-
- // TODO: Read next frame(s) from QOA stream
- //music.frameCount = qoa_decode_frame(const unsigned char *bytes, unsigned int size, ctxQoa, short *sample_data, unsigned int *frame_len);
-
+ // NOTE: We are loading samples are 32bit float normalized data, so,
+ // we configure the output audio stream to also use float 32bit
+ music.stream = LoadAudioStream(ctxQoa->info.samplerate, 32, ctxQoa->info.channels);
+ music.frameCount = ctxQoa->info.samples;
music.looping = true; // Looping enabled by default
musicLoaded = true;
}
@@ -1697,27 +1687,27 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data,
if (!musicLoaded)
{
if (false) { }
- #if defined(SUPPORT_FILEFORMAT_WAV)
+#if defined(SUPPORT_FILEFORMAT_WAV)
else if (music.ctxType == MUSIC_AUDIO_WAV) drwav_uninit((drwav *)music.ctxData);
- #endif
- #if defined(SUPPORT_FILEFORMAT_OGG)
+#endif
+#if defined(SUPPORT_FILEFORMAT_OGG)
else if (music.ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close((stb_vorbis *)music.ctxData);
- #endif
- #if defined(SUPPORT_FILEFORMAT_MP3)
+#endif
+#if defined(SUPPORT_FILEFORMAT_MP3)
else if (music.ctxType == MUSIC_AUDIO_MP3) { drmp3_uninit((drmp3 *)music.ctxData); RL_FREE(music.ctxData); }
- #endif
- #if defined(SUPPORT_FILEFORMAT_QOA)
- else if (music.ctxType == MUSIC_AUDIO_QOA) { /*TODO: Release QOA context*/ RL_FREE(music.ctxData); }
- #endif
- #if defined(SUPPORT_FILEFORMAT_FLAC)
+#endif
+#if defined(SUPPORT_FILEFORMAT_QOA)
+ else if (music.ctxType == MUSIC_AUDIO_QOA) qoaplay_close((qoaplay_desc *)music.ctxData);
+#endif
+#if defined(SUPPORT_FILEFORMAT_FLAC)
else if (music.ctxType == MUSIC_AUDIO_FLAC) drflac_free((drflac *)music.ctxData, NULL);
- #endif
- #if defined(SUPPORT_FILEFORMAT_XM)
+#endif
+#if defined(SUPPORT_FILEFORMAT_XM)
else if (music.ctxType == MUSIC_MODULE_XM) jar_xm_free_context((jar_xm_context_t *)music.ctxData);
- #endif
- #if defined(SUPPORT_FILEFORMAT_MOD)
+#endif
+#if defined(SUPPORT_FILEFORMAT_MOD)
else if (music.ctxType == MUSIC_MODULE_MOD) { jar_mod_unload((jar_mod_context_t *)music.ctxData); RL_FREE(music.ctxData); }
- #endif
+#endif
music.ctxData = NULL;
TRACELOG(LOG_WARNING, "FILEIO: Music data could not be loaded");
@@ -1763,7 +1753,7 @@ void UnloadMusicStream(Music music)
else if (music.ctxType == MUSIC_AUDIO_MP3) { drmp3_uninit((drmp3 *)music.ctxData); RL_FREE(music.ctxData); }
#endif
#if defined(SUPPORT_FILEFORMAT_QOA)
- else if (music.ctxType == MUSIC_AUDIO_QOA) { /*TODO: Release QOA context*/ RL_FREE(music.ctxData); }
+ else if (music.ctxType == MUSIC_AUDIO_QOA) qoaplay_close((qoaplay_desc *)music.ctxData);
#endif
#if defined(SUPPORT_FILEFORMAT_FLAC)
else if (music.ctxType == MUSIC_AUDIO_FLAC) drflac_free((drflac *)music.ctxData, NULL);
@@ -1821,7 +1811,7 @@ void StopMusicStream(Music music)
case MUSIC_AUDIO_MP3: drmp3_seek_to_start_of_stream((drmp3 *)music.ctxData); break;
#endif
#if defined(SUPPORT_FILEFORMAT_QOA)
- case MUSIC_AUDIO_QOA: /*TODO: Restart QOA context to beginning*/ break;
+ case MUSIC_AUDIO_QOA: qoaplay_rewind((qoaplay_desc *)music.ctxData); break;
#endif
#if defined(SUPPORT_FILEFORMAT_FLAC)
case MUSIC_AUDIO_FLAC: drflac__seek_to_first_frame((drflac *)music.ctxData); break;
@@ -1856,7 +1846,7 @@ void SeekMusicStream(Music music, float position)
case MUSIC_AUDIO_MP3: drmp3_seek_to_pcm_frame((drmp3 *)music.ctxData, positionInFrames); break;
#endif
#if defined(SUPPORT_FILEFORMAT_QOA)
- case MUSIC_AUDIO_QOA: /*TODO: Seek to specific QOA frame*/ break;
+ case MUSIC_AUDIO_QOA: qoaplay_seek_frame((qoaplay_desc *)music.ctxData, positionInFrames); break;
#endif
#if defined(SUPPORT_FILEFORMAT_FLAC)
case MUSIC_AUDIO_FLAC: drflac_seek_to_pcm_frame((drflac *)music.ctxData, positionInFrames); break;
@@ -1892,11 +1882,13 @@ void UpdateMusicStream(Music music)
unsigned int framesLeft = music.frameCount - music.stream.buffer->framesProcessed; // Frames left to be processed
unsigned int framesToStream = 0; // Total frames to be streamed
+
if ((framesLeft >= subBufferSizeInFrames) || music.looping) framesToStream = subBufferSizeInFrames;
else framesToStream = framesLeft;
int frameCountStillNeeded = framesToStream;
- int frameCountRedTotal = 0;
+ int frameCountReadTotal = 0;
+
switch (music.ctxType)
{
#if defined(SUPPORT_FILEFORMAT_WAV)
@@ -1906,8 +1898,8 @@ void UpdateMusicStream(Music music)
{
while (true)
{
- int frameCountRed = (int)drwav_read_pcm_frames_s16((drwav *)music.ctxData, frameCountStillNeeded, (short *)((char *)AUDIO.System.pcmBuffer + frameCountRedTotal*frameSize));
- frameCountRedTotal += frameCountRed;
+ int frameCountRed = (int)drwav_read_pcm_frames_s16((drwav *)music.ctxData, frameCountStillNeeded, (short *)((char *)AUDIO.System.pcmBuffer + frameCountReadTotal*frameSize));
+ frameCountReadTotal += frameCountRed;
frameCountStillNeeded -= frameCountRed;
if (frameCountStillNeeded == 0) break;
else drwav_seek_to_first_pcm_frame((drwav *)music.ctxData);
@@ -1917,8 +1909,8 @@ void UpdateMusicStream(Music music)
{
while (true)
{
- int frameCountRed = (int)drwav_read_pcm_frames_f32((drwav *)music.ctxData, frameCountStillNeeded, (float *)((char *)AUDIO.System.pcmBuffer + frameCountRedTotal*frameSize));
- frameCountRedTotal += frameCountRed;
+ int frameCountRed = (int)drwav_read_pcm_frames_f32((drwav *)music.ctxData, frameCountStillNeeded, (float *)((char *)AUDIO.System.pcmBuffer + frameCountReadTotal*frameSize));
+ frameCountReadTotal += frameCountRed;
frameCountStillNeeded -= frameCountRed;
if (frameCountStillNeeded == 0) break;
else drwav_seek_to_first_pcm_frame((drwav *)music.ctxData);
@@ -1931,8 +1923,8 @@ void UpdateMusicStream(Music music)
{
while (true)
{
- int frameCountRed = stb_vorbis_get_samples_short_interleaved((stb_vorbis *)music.ctxData, music.stream.channels, (short *)((char *)AUDIO.System.pcmBuffer + frameCountRedTotal*frameSize), frameCountStillNeeded*music.stream.channels);
- frameCountRedTotal += frameCountRed;
+ int frameCountRed = stb_vorbis_get_samples_short_interleaved((stb_vorbis *)music.ctxData, music.stream.channels, (short *)((char *)AUDIO.System.pcmBuffer + frameCountReadTotal*frameSize), frameCountStillNeeded*music.stream.channels);
+ frameCountReadTotal += frameCountRed;
frameCountStillNeeded -= frameCountRed;
if (frameCountStillNeeded == 0) break;
else stb_vorbis_seek_start((stb_vorbis *)music.ctxData);
@@ -1944,9 +1936,9 @@ void UpdateMusicStream(Music music)
{
while (true)
{
- int frameCountRed = (int)drmp3_read_pcm_frames_f32((drmp3 *)music.ctxData, frameCountStillNeeded, (float *)((char *)AUDIO.System.pcmBuffer + frameCountRedTotal*frameSize));
- frameCountRedTotal += frameCountRed;
- frameCountStillNeeded -= frameCountRed;
+ int frameCountRead = (int)drmp3_read_pcm_frames_f32((drmp3 *)music.ctxData, frameCountStillNeeded, (float *)((char *)AUDIO.System.pcmBuffer + frameCountReadTotal*frameSize));
+ frameCountReadTotal += frameCountRead;
+ frameCountStillNeeded -= frameCountRead;
if (frameCountStillNeeded == 0) break;
else drmp3_seek_to_start_of_stream((drmp3 *)music.ctxData);
}
@@ -1955,7 +1947,18 @@ void UpdateMusicStream(Music music)
#if defined(SUPPORT_FILEFORMAT_QOA)
case MUSIC_AUDIO_QOA:
{
- // TODO: Read QOA required framecount to fill buffer to keep music playing
+ unsigned int frameCountRead = qoaplay_decode((qoaplay_desc *)music.ctxData, (float *)AUDIO.System.pcmBuffer, framesToStream);
+ frameCountReadTotal += frameCountRead;
+ /*
+ while (true)
+ {
+ int frameCountRead = (int)qoaplay_decode((qoaplay_desc *)music.ctxData, (float *)((char *)AUDIO.System.pcmBuffer + frameCountReadTotal*frameSize), frameCountStillNeeded);
+ frameCountReadTotal += frameCountRead;
+ frameCountStillNeeded -= frameCountRead;
+ if (frameCountStillNeeded == 0) break;
+ else qoaplay_rewind((qoaplay_desc *)music.ctxData);
+ }
+ */
} break;
#endif
#if defined(SUPPORT_FILEFORMAT_FLAC)
@@ -1964,8 +1967,8 @@ void UpdateMusicStream(Music music)
while (true)
{
int frameCountRed = drflac_read_pcm_frames_s16((drflac *)music.ctxData, frameCountStillNeeded, (short *)((char *)AUDIO.System.pcmBuffer + frameCountRedTotal*frameSize));
- frameCountRedTotal += frameCountRed;
- frameCountStillNeeded -= frameCountRed;
+ frameCountReadTotal += frameCountRead;
+ frameCountStillNeeded -= frameCountRead;
if (frameCountStillNeeded == 0) break;
else drflac__seek_to_first_frame((drflac *)music.ctxData);
}
@@ -1978,7 +1981,6 @@ void UpdateMusicStream(Music music)
if (AUDIO_DEVICE_FORMAT == ma_format_f32) jar_xm_generate_samples((jar_xm_context_t *)music.ctxData, (float *)AUDIO.System.pcmBuffer, framesToStream);
else if (AUDIO_DEVICE_FORMAT == ma_format_s16) jar_xm_generate_samples_16bit((jar_xm_context_t *)music.ctxData, (short *)AUDIO.System.pcmBuffer, framesToStream);
else if (AUDIO_DEVICE_FORMAT == ma_format_u8) jar_xm_generate_samples_8bit((jar_xm_context_t *)music.ctxData, (char *)AUDIO.System.pcmBuffer, framesToStream);
-
//jar_xm_reset((jar_xm_context_t *)music.ctxData);
} break;
@@ -1988,7 +1990,6 @@ void UpdateMusicStream(Music music)
{
// NOTE: 3rd parameter (nbsample) specify the number of stereo 16bits samples you want, so sampleCount/2
jar_mod_fillbuffer((jar_mod_context_t *)music.ctxData, (short *)AUDIO.System.pcmBuffer, framesToStream, 0);
-
//jar_mod_seek_start((jar_mod_context_t *)music.ctxData);
} break;
@@ -2056,7 +2057,7 @@ float GetMusicTimePlayed(Music music)
float secondsPlayed = 0.0f;
if (music.stream.buffer != NULL)
{
- #if defined(SUPPORT_FILEFORMAT_XM)
+#if defined(SUPPORT_FILEFORMAT_XM)
if (music.ctxType == MUSIC_MODULE_XM)
{
uint64_t framesPlayed = 0;
@@ -2065,7 +2066,7 @@ float GetMusicTimePlayed(Music music)
secondsPlayed = (float)framesPlayed/music.stream.sampleRate;
}
else
- #endif
+#endif
{
//ma_uint32 frameSizeInBytes = ma_get_bytes_per_sample(music.stream.buffer->dsp.formatConverterIn.config.formatIn)*music.stream.buffer->dsp.formatConverterIn.config.channels;
int framesProcessed = (int)music.stream.buffer->framesProcessed;
@@ -2115,7 +2116,7 @@ AudioStream LoadAudioStream(unsigned int sampleRate, unsigned int sampleSize, un
}
// Checks if an audio stream is ready
-RLAPI bool IsAudioStreamReady(AudioStream stream)
+bool IsAudioStreamReady(AudioStream stream)
{
return ((stream.buffer != NULL) && // Validate stream buffer
(stream.sampleRate > 0) && // Validate sample rate is supported
@@ -2277,6 +2278,7 @@ void AttachAudioStreamProcessor(AudioStream stream, AudioCallback process)
ma_mutex_unlock(&AUDIO.System.lock);
}
+// Remove processor from audio stream
void DetachAudioStreamProcessor(AudioStream stream, AudioCallback process)
{
ma_mutex_lock(&AUDIO.System.lock);
@@ -2304,9 +2306,8 @@ void DetachAudioStreamProcessor(AudioStream stream, AudioCallback process)
}
// Add processor to audio pipeline. Order of processors is important
-// Works the same way as {Attach,Detach}AudioStreamProcessor functions, except
-// these two work on the already mixed output just before sending it to the
-// sound hardware.
+// Works the same way as {Attach,Detach}AudioStreamProcessor() functions, except
+// these two work on the already mixed output just before sending it to the sound hardware
void AttachAudioMixedProcessor(AudioCallback process)
{
ma_mutex_lock(&AUDIO.System.lock);
@@ -2330,6 +2331,7 @@ void AttachAudioMixedProcessor(AudioCallback process)
ma_mutex_unlock(&AUDIO.System.lock);
}
+// Remove processor from audio pipeline
void DetachAudioMixedProcessor(AudioCallback process)
{
ma_mutex_lock(&AUDIO.System.lock);
@@ -2508,7 +2510,6 @@ static ma_uint32 ReadAudioBufferFramesInMixingFormat(AudioBuffer *audioBuffer, f
return totalOutputFramesProcessed;
}
-
// Sending audio data to device callback function
// This function will be called when miniaudio needs more data
// NOTE: All the mixing takes place here