From 6897b4359922ba5862eaaba159b24e734ff655ee Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 3 Mar 2023 20:13:35 +0100 Subject: [PATCH 01/13] REVIEWED: `core_drop_files` #2943 --- examples/core/core_drop_files.c | 44 ++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/examples/core/core_drop_files.c b/examples/core/core_drop_files.c index cfe03e196..b591cdea7 100644 --- a/examples/core/core_drop_files.c +++ b/examples/core/core_drop_files.c @@ -15,6 +15,11 @@ #include "raylib.h" +#include // Required for: calloc(), free() + +#define MAX_FILEPATH_RECORDED 4096 +#define MAX_FILEPATH_SIZE 2048 + //------------------------------------------------------------------------------------ // Program main entry point //------------------------------------------------------------------------------------ @@ -27,7 +32,14 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [core] example - drop files"); - FilePathList droppedFiles = { 0 }; + int filePathCounter = 0; + char *filePaths[MAX_FILEPATH_RECORDED] = { 0 }; // We will register a maximum of filepaths + + // Allocate space for the required file paths + for (int i = 0; i < MAX_FILEPATH_RECORDED; i++) + { + filePaths[i] = (char *)RL_CALLOC(MAX_FILEPATH_SIZE, 1); + } SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -39,11 +51,18 @@ int main(void) //---------------------------------------------------------------------------------- if (IsFileDropped()) { - // Is some files have been previously loaded, unload them - if (droppedFiles.count > 0) UnloadDroppedFiles(droppedFiles); - - // Load new dropped files - droppedFiles = LoadDroppedFiles(); + FilePathList droppedFiles = LoadDroppedFiles(); + + for (int i = 0, offset = filePathCounter; i < droppedFiles.count; i++) + { + if (filePathCounter < (MAX_FILEPATH_RECORDED - 1)) + { + TextCopy(filePaths[offset + i], droppedFiles.paths[i]); + filePathCounter++; + } + } + + UnloadDroppedFiles(droppedFiles); // Unload filepaths from memory } //---------------------------------------------------------------------------------- @@ -53,20 +72,20 @@ int main(void) ClearBackground(RAYWHITE); - if (droppedFiles.count == 0) DrawText("Drop your files to this window!", 100, 40, 20, DARKGRAY); + if (filePathCounter == 0) DrawText("Drop your files to this window!", 100, 40, 20, DARKGRAY); else { DrawText("Dropped files:", 100, 40, 20, DARKGRAY); - for (unsigned int i = 0; i < droppedFiles.count; i++) + for (unsigned int i = 0; i < filePathCounter; i++) { if (i%2 == 0) DrawRectangle(0, 85 + 40*i, screenWidth, 40, Fade(LIGHTGRAY, 0.5f)); else DrawRectangle(0, 85 + 40*i, screenWidth, 40, Fade(LIGHTGRAY, 0.3f)); - DrawText(droppedFiles.paths[i], 120, 100 + 40*i, 10, GRAY); + DrawText(filePaths[i], 120, 100 + 40*i, 10, GRAY); } - DrawText("Drop new files...", 100, 110 + 40*droppedFiles.count, 20, DARKGRAY); + DrawText("Drop new files...", 100, 110 + 40*filePathCounter, 20, DARKGRAY); } EndDrawing(); @@ -75,7 +94,10 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- - UnloadDroppedFiles(droppedFiles); // Unload files memory + for (int i = 0; i < MAX_FILEPATH_RECORDED; i++) + { + RL_FREE(filePaths[i]); // Free allocated memory for all filepaths + } CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- From 1611cd54e7f9a17bab14902ebe37bd8b2945449d Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 4 Mar 2023 18:56:04 +0100 Subject: [PATCH 02/13] REVIEWED: `GetWindowHandle()` #2938 --- src/rcore.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index 443ef8e3a..6f4fa2951 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1718,7 +1718,8 @@ void *GetWindowHandle(void) // typedef unsigned long XID; // typedef XID Window; //unsigned long id = (unsigned long)glfwGetX11Window(window); - return NULL; // TODO: Find a way to return value... cast to void *? + //return NULL; // TODO: Find a way to return value... cast to void *? + return (void *)CORE.Window.handle; #endif #if defined(__APPLE__) // NOTE: Returned handle is: (objc_object *) From 5492f52adccbde94db221c1a5c7351deea1dfeec Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Mar 2023 00:09:56 +0100 Subject: [PATCH 03/13] Change default threading model for COM objects It shouldn't matter much but it could avoid some conflicts with other libraries in the future (like `tinyfiledialogs`). --- src/raudio.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/raudio.c b/src/raudio.c index 04386167d..b4b8b64e3 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -168,6 +168,10 @@ typedef struct tagBITMAPINFOHEADER { #define MA_NO_WAV #define MA_NO_FLAC #define MA_NO_MP3 + +// Threading model: Default: [0] COINIT_MULTITHREADED: COM calls objects on any thread (free threading) +#define MA_COINIT_VALUE 2 // [2] COINIT_APARTMENTTHREADED: Each object has its own thread (apartment model) + #define MINIAUDIO_IMPLEMENTATION //#define MA_DEBUG_OUTPUT #include "external/miniaudio.h" // Audio device initialization and management From 0eeb499288b7c8f948588695e5927bfd07eabe07 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Mar 2023 11:17:47 +0100 Subject: [PATCH 04/13] Update qoa.h --- src/external/qoa.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/external/qoa.h b/src/external/qoa.h index 65752d1b7..59d90aded 100644 --- a/src/external/qoa.h +++ b/src/external/qoa.h @@ -22,12 +22,12 @@ not in the file header. A decoder may peek into the first frame of the file to find these values. In a valid QOA file all frames have the same number of channels and the same -samplerate. These restriction may be releaxed for streaming. This remains to +samplerate. These restrictions may be relaxed for streaming. This remains to be decided. All values in a QOA file are BIG ENDIAN. Luckily, EVERYTHING in a QOA file, including the headers, is 64 bit aligned, so it's possible to read files with -just a read_u64() that does the byte swapping if neccessary. +just a read_u64() that does the byte swapping if necessary. In pseudocode, the file layout is as follows: @@ -66,7 +66,7 @@ Wheras the 64bit qoa_slice_t is defined as follows: `sf_index` defines the scalefactor to use for this slice as an index into the qoa_scalefactor_tab[16] -`r00`--`r19` are the residuals for the individiual samples, divided by the +`r00`--`r19` are the residuals for the individual samples, divided by the scalefactor and quantized by the qoa_quant_tab[]. In the decoder, a prediction of the next sample is computed by multiplying the @@ -153,7 +153,7 @@ typedef unsigned long long qoa_uint64_t; /* The quant_tab provides an index into the dequant_tab for residuals in the -range of -8 .. 8. It maps this range to just 3bits and becommes less accurate at +range of -8 .. 8. It maps this range to just 3bits and becomes less accurate at the higher end. Note that the residual zero is identical to the lowest positive value. This is mostly fine, since the qoa_div() function always rounds away from zero. */ @@ -169,7 +169,7 @@ static int qoa_quant_tab[17] = { less accurate at the higher end. In theory, the highest scalefactor that we would need to encode the highest 16bit residual is (2**16)/8 = 8192. However we rely on the LMS filter to predict samples accurately enough that a maximum -residual of one quarter of the 16 bit range is high sufficent. I.e. with the +residual of one quarter of the 16 bit range is high sufficient. I.e. with the scalefactor 2048 times the quant range of 8 we can encode residuals up to 2**14. The scalefactor values are computed as: @@ -230,7 +230,7 @@ The next sample is predicted as the sum of (weight[i] * history[i]). The adjustment of the weights is done with a "Sign-Sign-LMS" that adds or subtracts the residual to each weight, based on the corresponding sample from -the history. This, suprisingly, is sufficent to get worthwhile predictions. +the history. This, surprisingly, is sufficient to get worthwhile predictions. This is all done with fixed point integers. Hence the right-shifts when updating the weights and calculating the prediction. */ @@ -369,7 +369,7 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned int dequantized = qoa_dequant_tab[scalefactor][quantized]; int reconstructed = qoa_clamp(predicted + dequantized, -32768, 32767); - int error = (sample - reconstructed); + long long error = (sample - reconstructed); current_error += error * error; if (current_error > best_error) { break; From ab14ad5d754f7bbccbf8845b8062c8ece018b3e0 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Mar 2023 11:26:37 +0100 Subject: [PATCH 05/13] Close issue #2949 --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index 6f4fa2951..4efb20179 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1713,7 +1713,7 @@ void *GetWindowHandle(void) // NOTE: Returned handle is: void *HWND (windows.h) return glfwGetWin32Window(CORE.Window.handle); #endif -#if defined(__linux__) +#if defined(__linux__) && !defined(PLATFORM_DRM) // NOTE: Returned handle is: unsigned long Window (X.h) // typedef unsigned long XID; // typedef XID Window; From 9614d3353b81b95adcfd01392c62326a8c512aee Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Mar 2023 13:49:18 +0100 Subject: [PATCH 06/13] REVIEWED: QOA audio file export --- src/raudio.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index b4b8b64e3..207c0c490 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -1003,13 +1003,17 @@ bool ExportWave(Wave wave, const char *fileName) #if defined(SUPPORT_FILEFORMAT_QOA) else if (IsFileExtension(fileName, ".qoa")) { - qoa_desc qoa = { 0 }; - qoa.channels = wave.channels; - qoa.samplerate = wave.sampleRate; - qoa.samples = wave.frameCount; + if (wave.sampleSize == 16) + { + qoa_desc qoa = { 0 }; + qoa.channels = wave.channels; + qoa.samplerate = wave.sampleRate; + qoa.samples = wave.frameCount; - // TODO: Review wave.data format required for export - success = qoa_write(fileName, wave.data, &qoa); + int bytesWritten = qoa_write(fileName, wave.data, &qoa); + if (bytesWritten > 0) success = true; + } + else TRACELOG(LOG_WARNING, "AUDIO: Wave data must be 16 bit per sample for QOA format export"); } #endif else if (IsFileExtension(fileName, ".raw")) From f4f6e25340080e149deea5f640976dd8f7452d33 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Mar 2023 14:07:02 +0100 Subject: [PATCH 07/13] Support QOA audio file format by default --- examples/audio/resources/target.qoa | Bin 0 -> 35648 bytes src/config.h | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 examples/audio/resources/target.qoa diff --git a/examples/audio/resources/target.qoa b/examples/audio/resources/target.qoa new file mode 100644 index 0000000000000000000000000000000000000000..d48c4f9f057083be25e82f944e6b08f5c6ffb5e6 GIT binary patch literal 35648 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 Oa$vL^7%d0LF9!e{3+BQA literal 0 HcmV?d00001 diff --git a/src/config.h b/src/config.h index d5f8e486c..de9f4641a 100644 --- a/src/config.h +++ b/src/config.h @@ -214,7 +214,7 @@ #define SUPPORT_FILEFORMAT_WAV 1 #define SUPPORT_FILEFORMAT_OGG 1 #define SUPPORT_FILEFORMAT_MP3 1 -//#define SUPPORT_FILEFORMAT_QOA 1 +#define SUPPORT_FILEFORMAT_QOA 1 //#define SUPPORT_FILEFORMAT_FLAC 1 #define SUPPORT_FILEFORMAT_XM 1 #define SUPPORT_FILEFORMAT_MOD 1 From d3f5bc40436ef45aee2cc353f985d4ad5fc57bbd Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Mar 2023 20:06:45 +0100 Subject: [PATCH 08/13] REVIEWED: `GetWindowHandle()` #2950 --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index 4efb20179..4f3d98b0f 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1713,7 +1713,7 @@ void *GetWindowHandle(void) // NOTE: Returned handle is: void *HWND (windows.h) return glfwGetWin32Window(CORE.Window.handle); #endif -#if defined(__linux__) && !defined(PLATFORM_DRM) +#if defined(PLATFORM_DESKTOP) && defined(__linux__) // NOTE: Returned handle is: unsigned long Window (X.h) // typedef unsigned long XID; // typedef XID Window; From 68ee0bb8dd43074574dab08d836eaeba58bd0351 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 6 Mar 2023 12:33:49 +0100 Subject: [PATCH 09/13] ADDED: QOA music streaming (with auxiliar lib) Some format tweaks --- src/external/qoaplay.c | 278 +++++++++++++++++++++++++++++++++++++++++ src/raudio.c | 157 +++++++++++------------ 2 files changed, 357 insertions(+), 78 deletions(-) create mode 100644 src/external/qoaplay.c diff --git a/src/external/qoaplay.c b/src/external/qoaplay.c new file mode 100644 index 000000000..b4dc09c7e --- /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 207c0c490..03e4bcbaa 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 From aae7ab64c7a6b4e7e040b21736710843086b35a0 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 6 Mar 2023 13:00:30 +0100 Subject: [PATCH 10/13] Update raudio.c --- src/raudio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raudio.c b/src/raudio.c index 03e4bcbaa..8bd9052c2 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -1966,7 +1966,7 @@ void UpdateMusicStream(Music music) { while (true) { - int frameCountRed = drflac_read_pcm_frames_s16((drflac *)music.ctxData, frameCountStillNeeded, (short *)((char *)AUDIO.System.pcmBuffer + frameCountRedTotal*frameSize)); + int frameCountRead = drflac_read_pcm_frames_s16((drflac *)music.ctxData, frameCountStillNeeded, (short *)((char *)AUDIO.System.pcmBuffer + frameCountReadTotal*frameSize)); frameCountReadTotal += frameCountRead; frameCountStillNeeded -= frameCountRead; if (frameCountStillNeeded == 0) break; From cf1ebada0e0c18a02d48f091091418ce7b020b1e Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 6 Mar 2023 13:15:13 +0100 Subject: [PATCH 11/13] Tweak `WindowDropCallback()` #2943 --- src/rcore.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 4f3d98b0f..071915cf7 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -5598,25 +5598,28 @@ static void CursorEnterCallback(GLFWwindow *window, int enter) // GLFW3 Window Drop Callback, runs when drop files into window static void WindowDropCallback(GLFWwindow *window, int count, const char **paths) { - // In case previous dropped filepaths have not been freed, we free them - if (CORE.Window.dropFileCount > 0) + if (count > 0) { - for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++) RL_FREE(CORE.Window.dropFilepaths[i]); + // In case previous dropped filepaths have not been freed, we free them + if (CORE.Window.dropFileCount > 0) + { + for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++) RL_FREE(CORE.Window.dropFilepaths[i]); - RL_FREE(CORE.Window.dropFilepaths); + RL_FREE(CORE.Window.dropFilepaths); - CORE.Window.dropFileCount = 0; - CORE.Window.dropFilepaths = NULL; - } + CORE.Window.dropFileCount = 0; + CORE.Window.dropFilepaths = NULL; + } - // WARNING: Paths are freed by GLFW when the callback returns, we must keep an internal copy - CORE.Window.dropFileCount = count; - CORE.Window.dropFilepaths = (char **)RL_CALLOC(CORE.Window.dropFileCount, sizeof(char *)); + // WARNING: Paths are freed by GLFW when the callback returns, we must keep an internal copy + CORE.Window.dropFileCount = count; + CORE.Window.dropFilepaths = (char **)RL_CALLOC(CORE.Window.dropFileCount, sizeof(char *)); - for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++) - { - CORE.Window.dropFilepaths[i] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char)); - strcpy(CORE.Window.dropFilepaths[i], paths[i]); + for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++) + { + CORE.Window.dropFilepaths[i] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char)); + strcpy(CORE.Window.dropFilepaths[i], paths[i]); + } } } #endif From 614e0518a7740bb900539e8c92517acaf052232c Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 6 Mar 2023 14:58:58 +0100 Subject: [PATCH 12/13] Remove trailing spaces --- examples/Makefile | 10 +++++----- examples/core/core_drop_files.c | 4 ++-- src/external/qoaplay.c | 32 ++++++++++++++++---------------- src/rcamera.h | 12 ++++++------ src/rcore.c | 6 +++--- src/rtextures.c | 10 +++++----- 6 files changed, 37 insertions(+), 37 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index 574975d23..0b524015f 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -297,17 +297,17 @@ ifeq ($(PLATFORM),PLATFORM_WEB) # --preload-file resources # specify a resources folder for data compilation # --source-map-base # allow debugging in browser with source map LDFLAGS += -s USE_GLFW=3 -s TOTAL_MEMORY=$(BUILD_WEB_HEAP_SIZE) -s FORCE_FILESYSTEM=1 - + # Build using asyncify ifeq ($(BUILD_WEB_ASYNCIFY),TRUE) LDFLAGS += -s ASYNCIFY endif - + # Add resources building if required ifeq ($(BUILD_WEB_RESOURCES),TRUE) LDFLAGS += --preload-file $(BUILD_WEB_RESOURCES_PATH) endif - + # Add debug mode flags if required ifeq ($(BUILD_MODE),DEBUG) LDFLAGS += -s ASSERTIONS=1 --profiling @@ -316,7 +316,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) # Define a custom shell .html and output extension LDFLAGS += --shell-file $(BUILD_WEB_SHELL) EXT = .html - + # NOTE: Simple raylib examples are compiled to be interpreter with asyncify, that way, # we can compile same code for ALL platforms with no change required, but, working on bigger # projects, code needs to be refactored to avoid a blocking while() loop, moving Update and Draw @@ -354,7 +354,7 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) ifeq ($(RAYLIB_LIBTYPE),SHARED) LDLIBS += -lc endif - + # NOTE: On ARM 32bit arch, miniaudio requires atomics library LDLIBS += -latomic endif diff --git a/examples/core/core_drop_files.c b/examples/core/core_drop_files.c index b591cdea7..9f6220393 100644 --- a/examples/core/core_drop_files.c +++ b/examples/core/core_drop_files.c @@ -34,7 +34,7 @@ int main(void) int filePathCounter = 0; char *filePaths[MAX_FILEPATH_RECORDED] = { 0 }; // We will register a maximum of filepaths - + // Allocate space for the required file paths for (int i = 0; i < MAX_FILEPATH_RECORDED; i++) { @@ -94,7 +94,7 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- - for (int i = 0; i < MAX_FILEPATH_RECORDED; i++) + for (int i = 0; i < MAX_FILEPATH_RECORDED; i++) { RL_FREE(filePaths[i]); // Free allocated memory for all filepaths } diff --git a/src/external/qoaplay.c b/src/external/qoaplay.c index b4dc09c7e..466d13319 100644 --- a/src/external/qoaplay.c +++ b/src/external/qoaplay.c @@ -7,19 +7,19 @@ * 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 @@ -121,7 +121,7 @@ qoaplay_desc *qoaplay_open(char *path) qoa_ctx->info.channels = qoa.channels; qoa_ctx->info.samplerate = qoa.samplerate; qoa_ctx->info.samples = qoa.samples; - + return qoa_ctx; } @@ -174,15 +174,15 @@ void qoaplay_close(qoaplay_desc *qoa_ctx) 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 + 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); @@ -193,14 +193,14 @@ unsigned int qoaplay_decode_frame(qoaplay_desc *qoa_ctx) 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); + 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; @@ -213,7 +213,7 @@ unsigned int qoaplay_decode(qoaplay_desc *qoa_ctx, float *sample_data, int num_s { 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? @@ -225,20 +225,20 @@ unsigned int qoaplay_decode(qoaplay_desc *qoa_ctx, float *sample_data, int num_s 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++) + 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; } @@ -272,7 +272,7 @@ void qoaplay_seek_frame(qoaplay_desc *qoa_ctx, int frame) 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/rcamera.h b/src/rcamera.h index 6aee26f38..c3a101f0b 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -160,7 +160,7 @@ Matrix GetCameraProjectionMatrix(Camera* camera, float aspect); // MatrixOrtho() // MatrixIdentity() -// raylib required functionality: +// raylib required functionality: // GetMouseDelta() // GetMouseWheelMove() // IsKeyDown() @@ -223,7 +223,7 @@ Vector3 GetCameraRight(Camera *camera) { Vector3 forward = GetCameraForward(camera); Vector3 up = GetCameraUp(camera); - + return Vector3CrossProduct(forward, up); } @@ -251,7 +251,7 @@ void CameraMoveForward(Camera *camera, float distance, bool moveInWorldPlane) void CameraMoveUp(Camera *camera, float distance) { Vector3 up = GetCameraUp(camera); - + // Scale by distance up = Vector3Scale(up, distance); @@ -410,7 +410,7 @@ Matrix GetCameraProjectionMatrix(Camera *camera, float aspect) return MatrixOrtho(-right, right, -top, top, CAMERA_CULL_DISTANCE_NEAR, CAMERA_CULL_DISTANCE_FAR); } - + return MatrixIdentity(); } @@ -425,7 +425,7 @@ void UpdateCamera(Camera *camera, int mode) bool rotateAroundTarget = ((mode == CAMERA_THIRD_PERSON) || (mode == CAMERA_ORBITAL)); bool lockView = ((mode == CAMERA_FIRST_PERSON) || (mode == CAMERA_THIRD_PERSON) || (mode == CAMERA_ORBITAL)); bool rotateUp = (mode == CAMERA_FREE); - + if (mode == CAMERA_ORBITAL) { // Orbital can just orbit @@ -446,7 +446,7 @@ void UpdateCamera(Camera *camera, int mode) CameraYaw(camera, -mousePositionDelta.x*CAMERA_MOUSE_MOVE_SENSITIVITY, rotateAroundTarget); CameraPitch(camera, -mousePositionDelta.y*CAMERA_MOUSE_MOVE_SENSITIVITY, lockView, rotateAroundTarget, rotateUp); - + // Camera movement if (IsKeyDown(KEY_W)) CameraMoveForward(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); if (IsKeyDown(KEY_A)) CameraMoveRight(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); diff --git a/src/rcore.c b/src/rcore.c index 071915cf7..f24ae012c 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1555,7 +1555,7 @@ void ClearWindowState(unsigned int flags) // Set icon for window (only PLATFORM_DESKTOP) // NOTE 1: Image must be in RGBA format, 8bit per channel -// NOTE 2: Image is scaled by the OS for all required sizes +// NOTE 2: Image is scaled by the OS for all required sizes void SetWindowIcon(Image image) { #if defined(PLATFORM_DESKTOP) @@ -1586,7 +1586,7 @@ void SetWindowIcon(Image image) // Set icon for window (multiple images, only PLATFORM_DESKTOP) // NOTE 1: Images must be in RGBA format, 8bit per channel // NOTE 2: The multiple images are used depending on provided sizes -// Standard Windows icon sizes: 256, 128, 96, 64, 48, 32, 24, 16 +// Standard Windows icon sizes: 256, 128, 96, 64, 48, 32, 24, 16 void SetWindowIcons(Image *images, int count) { #if defined(PLATFORM_DESKTOP) @@ -2580,7 +2580,7 @@ bool IsShaderReady(Shader shader) // The following locations are tried to be set automatically (locs[i] >= 0), // any of them can be checked for validation but the only mandatory one is, afaik, SHADER_LOC_VERTEX_POSITION // NOTE: Users can also setup manually their own attributes/uniforms and do not used the default raylib ones - + // Vertex shader attribute locations (default) // shader.locs[SHADER_LOC_VERTEX_POSITION] // Set by default internal shader // shader.locs[SHADER_LOC_VERTEX_TEXCOORD01] // Set by default internal shader diff --git a/src/rtextures.c b/src/rtextures.c index f9b5c862d..56b200ce8 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -320,7 +320,7 @@ Image LoadImageAnim(const char *fileName, int *frames) #else if (false) { } #endif - else + else { image = LoadImage(fileName); frameCount = 1; @@ -507,7 +507,7 @@ Image LoadImageFromScreen(void) bool IsImageReady(Image image) { return ((image.data != NULL) && // Validate pixel data available - (image.width > 0) && + (image.width > 0) && (image.height > 0) && // Validate image size (image.format > 0) && // Validate image format (image.mipmaps > 0)); // Validate image mipmaps (at least 1 for basic mipmap level) @@ -3340,10 +3340,10 @@ RenderTexture2D LoadRenderTexture(int width, int height) bool IsTextureReady(Texture2D texture) { // TODO: Validate maximum texture size supported by GPU? - + return ((texture.id > 0) && // Validate OpenGL id - (texture.width > 0) && - (texture.height > 0) && // Validate texture size + (texture.width > 0) && + (texture.height > 0) && // Validate texture size (texture.format > 0) && // Validate texture pixel format (texture.mipmaps > 0)); // Validate texture mipmaps (at least 1 for basic mipmap level) } From 8f7e2cd1791414f55e8442186b1b32557704f1b5 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 6 Mar 2023 15:05:57 +0100 Subject: [PATCH 13/13] Replace TABS by 4 spaces --- src/external/qoaplay.c | 222 ++++++++++++++++++++--------------------- 1 file changed, 111 insertions(+), 111 deletions(-) diff --git a/src/external/qoaplay.c b/src/external/qoaplay.c index 466d13319..549e9f778 100644 --- a/src/external/qoaplay.c +++ b/src/external/qoaplay.c @@ -35,22 +35,22 @@ //---------------------------------------------------------------------------------- // QOA streaming data descriptor typedef struct { - qoa_desc info; // QOA descriptor data - + 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 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 + 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 + 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; @@ -85,97 +85,97 @@ int qoaplay_get_frame(qoaplay_desc *qoa_ctx); // 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; + 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; + // 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; + 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); + // 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 + // 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; + 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); + // 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; + qoa_ctx->info.channels = qoa.channels; + qoa_ctx->info.samplerate = qoa.samplerate; + qoa_ctx->info.samples = qoa.samples; - return qoa_ctx; + 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); + // 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; + 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)); + // 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; + 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; + // 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); + // 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; + qoa_ctx->info.channels = qoa.channels; + qoa_ctx->info.samplerate = qoa.samplerate; + qoa_ctx->info.samples = qoa.samples; - return qoa_ctx; + 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) 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; - } + 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); + QOA_FREE(qoa_ctx); } // Decode one frame from QOA data @@ -189,90 +189,90 @@ unsigned int qoaplay_decode_frame(qoaplay_desc *qoa_ctx) 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; + 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; + 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); + 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; + 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; + int src_index = qoa_ctx->sample_data_pos*qoa_ctx->info.channels; + int dst_index = 0; - for (int i = 0; i < num_samples; i++) + 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) + // 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)) + if (!qoaplay_decode_frame(qoa_ctx)) { - // Loop to the beginning - qoaplay_rewind(qoa_ctx); - qoaplay_decode_frame(qoa_ctx); - } + // Loop to the beginning + qoaplay_rewind(qoa_ctx); + qoaplay_decode_frame(qoa_ctx); + } - src_index = 0; - } + src_index = 0; + } - // Normalize to -1..1 floats and write to dest - for (int c = 0; c < qoa_ctx->info.channels; c++) + // 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; - } + sample_data[dst_index++] = qoa_ctx->sample_data[src_index++]/32768.0; + } - qoa_ctx->sample_data_pos++; - qoa_ctx->sample_position++; - } + qoa_ctx->sample_data_pos++; + qoa_ctx->sample_position++; + } - return num_samples; + 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; + 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; + 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; + 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 < 0) frame = 0; - if (frame > qoa_ctx->info.samples/QOA_FRAME_LEN) frame = qoa_ctx->info.samples/QOA_FRAME_LEN; + 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; + 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); + 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); + if (qoa_ctx->file) fseek(qoa_ctx->file, offset, SEEK_SET); else qoa_ctx->file_data_offset = offset; }