Merge branch 'master' into event-recording-enhancements
This commit is contained in:
commit
aa769ab1c4
BIN
examples/audio/resources/target.qoa
Normal file
BIN
examples/audio/resources/target.qoa
Normal file
Binary file not shown.
|
|
@ -15,6 +15,11 @@
|
||||||
|
|
||||||
#include "raylib.h"
|
#include "raylib.h"
|
||||||
|
|
||||||
|
#include <stdlib.h> // Required for: calloc(), free()
|
||||||
|
|
||||||
|
#define MAX_FILEPATH_RECORDED 4096
|
||||||
|
#define MAX_FILEPATH_SIZE 2048
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------
|
||||||
// Program main entry point
|
// Program main entry point
|
||||||
//------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------
|
||||||
|
|
@ -27,7 +32,14 @@ int main(void)
|
||||||
|
|
||||||
InitWindow(screenWidth, screenHeight, "raylib [core] example - drop files");
|
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
|
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
||||||
//--------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------
|
||||||
|
|
@ -39,11 +51,18 @@ int main(void)
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
if (IsFileDropped())
|
if (IsFileDropped())
|
||||||
{
|
{
|
||||||
// Is some files have been previously loaded, unload them
|
FilePathList droppedFiles = LoadDroppedFiles();
|
||||||
if (droppedFiles.count > 0) UnloadDroppedFiles(droppedFiles);
|
|
||||||
|
|
||||||
// Load new dropped files
|
for (int i = 0, offset = filePathCounter; i < droppedFiles.count; i++)
|
||||||
droppedFiles = LoadDroppedFiles();
|
{
|
||||||
|
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);
|
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
|
else
|
||||||
{
|
{
|
||||||
DrawText("Dropped files:", 100, 40, 20, DARKGRAY);
|
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));
|
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));
|
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();
|
EndDrawing();
|
||||||
|
|
@ -75,7 +94,10 @@ int main(void)
|
||||||
|
|
||||||
// De-Initialization
|
// 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
|
CloseWindow(); // Close window and OpenGL context
|
||||||
//--------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -214,7 +214,7 @@
|
||||||
#define SUPPORT_FILEFORMAT_WAV 1
|
#define SUPPORT_FILEFORMAT_WAV 1
|
||||||
#define SUPPORT_FILEFORMAT_OGG 1
|
#define SUPPORT_FILEFORMAT_OGG 1
|
||||||
#define SUPPORT_FILEFORMAT_MP3 1
|
#define SUPPORT_FILEFORMAT_MP3 1
|
||||||
//#define SUPPORT_FILEFORMAT_QOA 1
|
#define SUPPORT_FILEFORMAT_QOA 1
|
||||||
//#define SUPPORT_FILEFORMAT_FLAC 1
|
//#define SUPPORT_FILEFORMAT_FLAC 1
|
||||||
#define SUPPORT_FILEFORMAT_XM 1
|
#define SUPPORT_FILEFORMAT_XM 1
|
||||||
#define SUPPORT_FILEFORMAT_MOD 1
|
#define SUPPORT_FILEFORMAT_MOD 1
|
||||||
|
|
|
||||||
14
src/external/qoa.h
vendored
14
src/external/qoa.h
vendored
|
|
@ -22,12 +22,12 @@ not in the file header. A decoder may peek into the first frame of the file to
|
||||||
find these values.
|
find these values.
|
||||||
|
|
||||||
In a valid QOA file all frames have the same number of channels and the same
|
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.
|
be decided.
|
||||||
|
|
||||||
All values in a QOA file are BIG ENDIAN. Luckily, EVERYTHING in a QOA file,
|
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
|
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:
|
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
|
`sf_index` defines the scalefactor to use for this slice as an index into the
|
||||||
qoa_scalefactor_tab[16]
|
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[].
|
scalefactor and quantized by the qoa_quant_tab[].
|
||||||
|
|
||||||
In the decoder, a prediction of the next sample is computed by multiplying the
|
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
|
/* 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
|
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
|
value. This is mostly fine, since the qoa_div() function always rounds away
|
||||||
from zero. */
|
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
|
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
|
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
|
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.
|
scalefactor 2048 times the quant range of 8 we can encode residuals up to 2**14.
|
||||||
|
|
||||||
The scalefactor values are computed as:
|
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
|
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
|
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
|
This is all done with fixed point integers. Hence the right-shifts when updating
|
||||||
the weights and calculating the prediction. */
|
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 dequantized = qoa_dequant_tab[scalefactor][quantized];
|
||||||
int reconstructed = qoa_clamp(predicted + dequantized, -32768, 32767);
|
int reconstructed = qoa_clamp(predicted + dequantized, -32768, 32767);
|
||||||
|
|
||||||
int error = (sample - reconstructed);
|
long long error = (sample - reconstructed);
|
||||||
current_error += error * error;
|
current_error += error * error;
|
||||||
if (current_error > best_error) {
|
if (current_error > best_error) {
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
278
src/external/qoaplay.c
vendored
Normal file
278
src/external/qoaplay.c
vendored
Normal file
|
|
@ -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;
|
||||||
|
}
|
||||||
139
src/raudio.c
139
src/raudio.c
|
|
@ -168,6 +168,10 @@ typedef struct tagBITMAPINFOHEADER {
|
||||||
#define MA_NO_WAV
|
#define MA_NO_WAV
|
||||||
#define MA_NO_FLAC
|
#define MA_NO_FLAC
|
||||||
#define MA_NO_MP3
|
#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 MINIAUDIO_IMPLEMENTATION
|
||||||
//#define MA_DEBUG_OUTPUT
|
//#define MA_DEBUG_OUTPUT
|
||||||
#include "external/miniaudio.h" // Audio device initialization and management
|
#include "external/miniaudio.h" // Audio device initialization and management
|
||||||
|
|
@ -226,6 +230,7 @@ typedef struct tagBITMAPINFOHEADER {
|
||||||
|
|
||||||
#define QOA_IMPLEMENTATION
|
#define QOA_IMPLEMENTATION
|
||||||
#include "external/qoa.h" // QOA loading and saving functions
|
#include "external/qoa.h" // QOA loading and saving functions
|
||||||
|
#include "external/qoaplay.c" // QOA stream playing helper functions
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
||||||
|
|
@ -283,21 +288,6 @@ typedef struct tagBITMAPINFOHEADER {
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
// Types and Structures Definition
|
// 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)
|
#if defined(RAUDIO_STANDALONE)
|
||||||
// Trace log level
|
// Trace log level
|
||||||
// NOTE: Organized by priority level
|
// NOTE: Organized by priority level
|
||||||
|
|
@ -313,6 +303,20 @@ typedef enum {
|
||||||
} TraceLogLevel;
|
} TraceLogLevel;
|
||||||
#endif
|
#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
|
// NOTE: Different logic is used when feeding data to the playback device
|
||||||
// depending on whether data is streamed (Music vs Sound)
|
// depending on whether data is streamed (Music vs Sound)
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
|
@ -998,14 +1002,18 @@ bool ExportWave(Wave wave, const char *fileName)
|
||||||
#endif
|
#endif
|
||||||
#if defined(SUPPORT_FILEFORMAT_QOA)
|
#if defined(SUPPORT_FILEFORMAT_QOA)
|
||||||
else if (IsFileExtension(fileName, ".qoa"))
|
else if (IsFileExtension(fileName, ".qoa"))
|
||||||
|
{
|
||||||
|
if (wave.sampleSize == 16)
|
||||||
{
|
{
|
||||||
qoa_desc qoa = { 0 };
|
qoa_desc qoa = { 0 };
|
||||||
qoa.channels = wave.channels;
|
qoa.channels = wave.channels;
|
||||||
qoa.samplerate = wave.sampleRate;
|
qoa.samplerate = wave.sampleRate;
|
||||||
qoa.samples = wave.frameCount;
|
qoa.samples = wave.frameCount;
|
||||||
|
|
||||||
// TODO: Review wave.data format required for export
|
int bytesWritten = qoa_write(fileName, wave.data, &qoa);
|
||||||
success = 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
|
#endif
|
||||||
else if (IsFileExtension(fileName, ".raw"))
|
else if (IsFileExtension(fileName, ".raw"))
|
||||||
|
|
@ -1314,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
|
// Load music stream from file
|
||||||
|
|
@ -1387,21 +1395,16 @@ Music LoadMusicStream(const char *fileName)
|
||||||
#if defined(SUPPORT_FILEFORMAT_QOA)
|
#if defined(SUPPORT_FILEFORMAT_QOA)
|
||||||
else if (IsFileExtension(fileName, ".qoa"))
|
else if (IsFileExtension(fileName, ".qoa"))
|
||||||
{
|
{
|
||||||
qoa_desc *ctxQoa = RL_CALLOC(1, sizeof(qoa_desc));
|
qoaplay_desc *ctxQoa = qoaplay_open(fileName);
|
||||||
|
|
||||||
// TODO: QOA stream support: Init context from file
|
|
||||||
int result = 0;
|
|
||||||
|
|
||||||
music.ctxType = MUSIC_AUDIO_QOA;
|
music.ctxType = MUSIC_AUDIO_QOA;
|
||||||
music.ctxData = ctxQoa;
|
music.ctxData = ctxQoa;
|
||||||
|
|
||||||
if (result > 0)
|
if (ctxQoa->file != NULL)
|
||||||
{
|
{
|
||||||
music.stream = LoadAudioStream(ctxQoa->samplerate, 16, ctxQoa->channels);
|
// NOTE: We are loading samples are 32bit float normalized data, so,
|
||||||
|
// we configure the output audio stream to also use float 32bit
|
||||||
// TODO: Read next frame(s) from QOA stream
|
music.stream = LoadAudioStream(ctxQoa->info.samplerate, 32, ctxQoa->info.channels);
|
||||||
//music.frameCount = qoa_decode_frame(const unsigned char *bytes, unsigned int size, ctxQoa, short *sample_data, unsigned int *frame_len);
|
music.frameCount = ctxQoa->info.samples;
|
||||||
|
|
||||||
music.looping = true; // Looping enabled by default
|
music.looping = true; // Looping enabled by default
|
||||||
musicLoaded = true;
|
musicLoaded = true;
|
||||||
}
|
}
|
||||||
|
|
@ -1586,21 +1589,16 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data,
|
||||||
#if defined(SUPPORT_FILEFORMAT_QOA)
|
#if defined(SUPPORT_FILEFORMAT_QOA)
|
||||||
else if (strcmp(fileType, ".qoa") == 0)
|
else if (strcmp(fileType, ".qoa") == 0)
|
||||||
{
|
{
|
||||||
qoa_desc *ctxQoa = RL_CALLOC(1, sizeof(qoa_desc));
|
qoaplay_desc *ctxQoa = qoaplay_open_memory(data, dataSize);
|
||||||
|
|
||||||
// TODO: Init QOA context data
|
|
||||||
int result = 0;
|
|
||||||
|
|
||||||
music.ctxType = MUSIC_AUDIO_QOA;
|
music.ctxType = MUSIC_AUDIO_QOA;
|
||||||
music.ctxData = ctxQoa;
|
music.ctxData = ctxQoa;
|
||||||
|
|
||||||
if (result > 0)
|
if ((ctxQoa->file_data != NULL) && (ctxQoa->file_data_size != 0))
|
||||||
{
|
{
|
||||||
music.stream = LoadAudioStream(ctxQoa->samplerate, 16, ctxQoa->channels);
|
// NOTE: We are loading samples are 32bit float normalized data, so,
|
||||||
|
// we configure the output audio stream to also use float 32bit
|
||||||
// TODO: Read next frame(s) from QOA stream
|
music.stream = LoadAudioStream(ctxQoa->info.samplerate, 32, ctxQoa->info.channels);
|
||||||
//music.frameCount = qoa_decode_frame(const unsigned char *bytes, unsigned int size, ctxQoa, short *sample_data, unsigned int *frame_len);
|
music.frameCount = ctxQoa->info.samples;
|
||||||
|
|
||||||
music.looping = true; // Looping enabled by default
|
music.looping = true; // Looping enabled by default
|
||||||
musicLoaded = true;
|
musicLoaded = true;
|
||||||
}
|
}
|
||||||
|
|
@ -1699,7 +1697,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data,
|
||||||
else if (music.ctxType == MUSIC_AUDIO_MP3) { drmp3_uninit((drmp3 *)music.ctxData); RL_FREE(music.ctxData); }
|
else if (music.ctxType == MUSIC_AUDIO_MP3) { drmp3_uninit((drmp3 *)music.ctxData); RL_FREE(music.ctxData); }
|
||||||
#endif
|
#endif
|
||||||
#if defined(SUPPORT_FILEFORMAT_QOA)
|
#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
|
#endif
|
||||||
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
||||||
else if (music.ctxType == MUSIC_AUDIO_FLAC) drflac_free((drflac *)music.ctxData, NULL);
|
else if (music.ctxType == MUSIC_AUDIO_FLAC) drflac_free((drflac *)music.ctxData, NULL);
|
||||||
|
|
@ -1755,7 +1753,7 @@ void UnloadMusicStream(Music music)
|
||||||
else if (music.ctxType == MUSIC_AUDIO_MP3) { drmp3_uninit((drmp3 *)music.ctxData); RL_FREE(music.ctxData); }
|
else if (music.ctxType == MUSIC_AUDIO_MP3) { drmp3_uninit((drmp3 *)music.ctxData); RL_FREE(music.ctxData); }
|
||||||
#endif
|
#endif
|
||||||
#if defined(SUPPORT_FILEFORMAT_QOA)
|
#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
|
#endif
|
||||||
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
||||||
else if (music.ctxType == MUSIC_AUDIO_FLAC) drflac_free((drflac *)music.ctxData, NULL);
|
else if (music.ctxType == MUSIC_AUDIO_FLAC) drflac_free((drflac *)music.ctxData, NULL);
|
||||||
|
|
@ -1813,7 +1811,7 @@ void StopMusicStream(Music music)
|
||||||
case MUSIC_AUDIO_MP3: drmp3_seek_to_start_of_stream((drmp3 *)music.ctxData); break;
|
case MUSIC_AUDIO_MP3: drmp3_seek_to_start_of_stream((drmp3 *)music.ctxData); break;
|
||||||
#endif
|
#endif
|
||||||
#if defined(SUPPORT_FILEFORMAT_QOA)
|
#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
|
#endif
|
||||||
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
||||||
case MUSIC_AUDIO_FLAC: drflac__seek_to_first_frame((drflac *)music.ctxData); break;
|
case MUSIC_AUDIO_FLAC: drflac__seek_to_first_frame((drflac *)music.ctxData); break;
|
||||||
|
|
@ -1848,7 +1846,7 @@ void SeekMusicStream(Music music, float position)
|
||||||
case MUSIC_AUDIO_MP3: drmp3_seek_to_pcm_frame((drmp3 *)music.ctxData, positionInFrames); break;
|
case MUSIC_AUDIO_MP3: drmp3_seek_to_pcm_frame((drmp3 *)music.ctxData, positionInFrames); break;
|
||||||
#endif
|
#endif
|
||||||
#if defined(SUPPORT_FILEFORMAT_QOA)
|
#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
|
#endif
|
||||||
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
||||||
case MUSIC_AUDIO_FLAC: drflac_seek_to_pcm_frame((drflac *)music.ctxData, positionInFrames); break;
|
case MUSIC_AUDIO_FLAC: drflac_seek_to_pcm_frame((drflac *)music.ctxData, positionInFrames); break;
|
||||||
|
|
@ -1884,11 +1882,13 @@ void UpdateMusicStream(Music music)
|
||||||
|
|
||||||
unsigned int framesLeft = music.frameCount - music.stream.buffer->framesProcessed; // Frames left to be processed
|
unsigned int framesLeft = music.frameCount - music.stream.buffer->framesProcessed; // Frames left to be processed
|
||||||
unsigned int framesToStream = 0; // Total frames to be streamed
|
unsigned int framesToStream = 0; // Total frames to be streamed
|
||||||
|
|
||||||
if ((framesLeft >= subBufferSizeInFrames) || music.looping) framesToStream = subBufferSizeInFrames;
|
if ((framesLeft >= subBufferSizeInFrames) || music.looping) framesToStream = subBufferSizeInFrames;
|
||||||
else framesToStream = framesLeft;
|
else framesToStream = framesLeft;
|
||||||
|
|
||||||
int frameCountStillNeeded = framesToStream;
|
int frameCountStillNeeded = framesToStream;
|
||||||
int frameCountRedTotal = 0;
|
int frameCountReadTotal = 0;
|
||||||
|
|
||||||
switch (music.ctxType)
|
switch (music.ctxType)
|
||||||
{
|
{
|
||||||
#if defined(SUPPORT_FILEFORMAT_WAV)
|
#if defined(SUPPORT_FILEFORMAT_WAV)
|
||||||
|
|
@ -1898,8 +1898,8 @@ void UpdateMusicStream(Music music)
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
int frameCountRed = (int)drwav_read_pcm_frames_s16((drwav *)music.ctxData, frameCountStillNeeded, (short *)((char *)AUDIO.System.pcmBuffer + frameCountRedTotal*frameSize));
|
int frameCountRed = (int)drwav_read_pcm_frames_s16((drwav *)music.ctxData, frameCountStillNeeded, (short *)((char *)AUDIO.System.pcmBuffer + frameCountReadTotal*frameSize));
|
||||||
frameCountRedTotal += frameCountRed;
|
frameCountReadTotal += frameCountRed;
|
||||||
frameCountStillNeeded -= frameCountRed;
|
frameCountStillNeeded -= frameCountRed;
|
||||||
if (frameCountStillNeeded == 0) break;
|
if (frameCountStillNeeded == 0) break;
|
||||||
else drwav_seek_to_first_pcm_frame((drwav *)music.ctxData);
|
else drwav_seek_to_first_pcm_frame((drwav *)music.ctxData);
|
||||||
|
|
@ -1909,8 +1909,8 @@ void UpdateMusicStream(Music music)
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
int frameCountRed = (int)drwav_read_pcm_frames_f32((drwav *)music.ctxData, frameCountStillNeeded, (float *)((char *)AUDIO.System.pcmBuffer + frameCountRedTotal*frameSize));
|
int frameCountRed = (int)drwav_read_pcm_frames_f32((drwav *)music.ctxData, frameCountStillNeeded, (float *)((char *)AUDIO.System.pcmBuffer + frameCountReadTotal*frameSize));
|
||||||
frameCountRedTotal += frameCountRed;
|
frameCountReadTotal += frameCountRed;
|
||||||
frameCountStillNeeded -= frameCountRed;
|
frameCountStillNeeded -= frameCountRed;
|
||||||
if (frameCountStillNeeded == 0) break;
|
if (frameCountStillNeeded == 0) break;
|
||||||
else drwav_seek_to_first_pcm_frame((drwav *)music.ctxData);
|
else drwav_seek_to_first_pcm_frame((drwav *)music.ctxData);
|
||||||
|
|
@ -1923,8 +1923,8 @@ void UpdateMusicStream(Music music)
|
||||||
{
|
{
|
||||||
while (true)
|
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);
|
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);
|
||||||
frameCountRedTotal += frameCountRed;
|
frameCountReadTotal += frameCountRed;
|
||||||
frameCountStillNeeded -= frameCountRed;
|
frameCountStillNeeded -= frameCountRed;
|
||||||
if (frameCountStillNeeded == 0) break;
|
if (frameCountStillNeeded == 0) break;
|
||||||
else stb_vorbis_seek_start((stb_vorbis *)music.ctxData);
|
else stb_vorbis_seek_start((stb_vorbis *)music.ctxData);
|
||||||
|
|
@ -1936,9 +1936,9 @@ void UpdateMusicStream(Music music)
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
int frameCountRed = (int)drmp3_read_pcm_frames_f32((drmp3 *)music.ctxData, frameCountStillNeeded, (float *)((char *)AUDIO.System.pcmBuffer + frameCountRedTotal*frameSize));
|
int frameCountRead = (int)drmp3_read_pcm_frames_f32((drmp3 *)music.ctxData, frameCountStillNeeded, (float *)((char *)AUDIO.System.pcmBuffer + frameCountReadTotal*frameSize));
|
||||||
frameCountRedTotal += frameCountRed;
|
frameCountReadTotal += frameCountRead;
|
||||||
frameCountStillNeeded -= frameCountRed;
|
frameCountStillNeeded -= frameCountRead;
|
||||||
if (frameCountStillNeeded == 0) break;
|
if (frameCountStillNeeded == 0) break;
|
||||||
else drmp3_seek_to_start_of_stream((drmp3 *)music.ctxData);
|
else drmp3_seek_to_start_of_stream((drmp3 *)music.ctxData);
|
||||||
}
|
}
|
||||||
|
|
@ -1947,7 +1947,18 @@ void UpdateMusicStream(Music music)
|
||||||
#if defined(SUPPORT_FILEFORMAT_QOA)
|
#if defined(SUPPORT_FILEFORMAT_QOA)
|
||||||
case MUSIC_AUDIO_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;
|
} break;
|
||||||
#endif
|
#endif
|
||||||
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
||||||
|
|
@ -1955,9 +1966,9 @@ void UpdateMusicStream(Music music)
|
||||||
{
|
{
|
||||||
while (true)
|
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));
|
||||||
frameCountRedTotal += frameCountRed;
|
frameCountReadTotal += frameCountRead;
|
||||||
frameCountStillNeeded -= frameCountRed;
|
frameCountStillNeeded -= frameCountRead;
|
||||||
if (frameCountStillNeeded == 0) break;
|
if (frameCountStillNeeded == 0) break;
|
||||||
else drflac__seek_to_first_frame((drflac *)music.ctxData);
|
else drflac__seek_to_first_frame((drflac *)music.ctxData);
|
||||||
}
|
}
|
||||||
|
|
@ -1970,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);
|
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_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);
|
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);
|
//jar_xm_reset((jar_xm_context_t *)music.ctxData);
|
||||||
|
|
||||||
} break;
|
} break;
|
||||||
|
|
@ -1980,7 +1990,6 @@ void UpdateMusicStream(Music music)
|
||||||
{
|
{
|
||||||
// NOTE: 3rd parameter (nbsample) specify the number of stereo 16bits samples you want, so sampleCount/2
|
// 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_fillbuffer((jar_mod_context_t *)music.ctxData, (short *)AUDIO.System.pcmBuffer, framesToStream, 0);
|
||||||
|
|
||||||
//jar_mod_seek_start((jar_mod_context_t *)music.ctxData);
|
//jar_mod_seek_start((jar_mod_context_t *)music.ctxData);
|
||||||
|
|
||||||
} break;
|
} break;
|
||||||
|
|
@ -2107,7 +2116,7 @@ AudioStream LoadAudioStream(unsigned int sampleRate, unsigned int sampleSize, un
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks if an audio stream is ready
|
// Checks if an audio stream is ready
|
||||||
RLAPI bool IsAudioStreamReady(AudioStream stream)
|
bool IsAudioStreamReady(AudioStream stream)
|
||||||
{
|
{
|
||||||
return ((stream.buffer != NULL) && // Validate stream buffer
|
return ((stream.buffer != NULL) && // Validate stream buffer
|
||||||
(stream.sampleRate > 0) && // Validate sample rate is supported
|
(stream.sampleRate > 0) && // Validate sample rate is supported
|
||||||
|
|
@ -2269,6 +2278,7 @@ void AttachAudioStreamProcessor(AudioStream stream, AudioCallback process)
|
||||||
ma_mutex_unlock(&AUDIO.System.lock);
|
ma_mutex_unlock(&AUDIO.System.lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove processor from audio stream
|
||||||
void DetachAudioStreamProcessor(AudioStream stream, AudioCallback process)
|
void DetachAudioStreamProcessor(AudioStream stream, AudioCallback process)
|
||||||
{
|
{
|
||||||
ma_mutex_lock(&AUDIO.System.lock);
|
ma_mutex_lock(&AUDIO.System.lock);
|
||||||
|
|
@ -2296,9 +2306,8 @@ void DetachAudioStreamProcessor(AudioStream stream, AudioCallback process)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add processor to audio pipeline. Order of processors is important
|
// Add processor to audio pipeline. Order of processors is important
|
||||||
// Works the same way as {Attach,Detach}AudioStreamProcessor functions, except
|
// Works the same way as {Attach,Detach}AudioStreamProcessor() functions, except
|
||||||
// these two work on the already mixed output just before sending it to the
|
// these two work on the already mixed output just before sending it to the sound hardware
|
||||||
// sound hardware.
|
|
||||||
void AttachAudioMixedProcessor(AudioCallback process)
|
void AttachAudioMixedProcessor(AudioCallback process)
|
||||||
{
|
{
|
||||||
ma_mutex_lock(&AUDIO.System.lock);
|
ma_mutex_lock(&AUDIO.System.lock);
|
||||||
|
|
@ -2322,6 +2331,7 @@ void AttachAudioMixedProcessor(AudioCallback process)
|
||||||
ma_mutex_unlock(&AUDIO.System.lock);
|
ma_mutex_unlock(&AUDIO.System.lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove processor from audio pipeline
|
||||||
void DetachAudioMixedProcessor(AudioCallback process)
|
void DetachAudioMixedProcessor(AudioCallback process)
|
||||||
{
|
{
|
||||||
ma_mutex_lock(&AUDIO.System.lock);
|
ma_mutex_lock(&AUDIO.System.lock);
|
||||||
|
|
@ -2500,7 +2510,6 @@ static ma_uint32 ReadAudioBufferFramesInMixingFormat(AudioBuffer *audioBuffer, f
|
||||||
return totalOutputFramesProcessed;
|
return totalOutputFramesProcessed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Sending audio data to device callback function
|
// Sending audio data to device callback function
|
||||||
// This function will be called when miniaudio needs more data
|
// This function will be called when miniaudio needs more data
|
||||||
// NOTE: All the mixing takes place here
|
// NOTE: All the mixing takes place here
|
||||||
|
|
|
||||||
|
|
@ -1716,12 +1716,13 @@ void *GetWindowHandle(void)
|
||||||
// NOTE: Returned handle is: void *HWND (windows.h)
|
// NOTE: Returned handle is: void *HWND (windows.h)
|
||||||
return glfwGetWin32Window(CORE.Window.handle);
|
return glfwGetWin32Window(CORE.Window.handle);
|
||||||
#endif
|
#endif
|
||||||
#if defined(__linux__)
|
#if defined(PLATFORM_DESKTOP) && defined(__linux__)
|
||||||
// NOTE: Returned handle is: unsigned long Window (X.h)
|
// NOTE: Returned handle is: unsigned long Window (X.h)
|
||||||
// typedef unsigned long XID;
|
// typedef unsigned long XID;
|
||||||
// typedef XID Window;
|
// typedef XID Window;
|
||||||
//unsigned long id = (unsigned long)glfwGetX11Window(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
|
#endif
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
// NOTE: Returned handle is: (objc_object *)
|
// NOTE: Returned handle is: (objc_object *)
|
||||||
|
|
@ -5632,6 +5633,8 @@ static void CursorEnterCallback(GLFWwindow *window, int enter)
|
||||||
|
|
||||||
// GLFW3 Window Drop Callback, runs when drop files into window
|
// GLFW3 Window Drop Callback, runs when drop files into window
|
||||||
static void WindowDropCallback(GLFWwindow *window, int count, const char **paths)
|
static void WindowDropCallback(GLFWwindow *window, int count, const char **paths)
|
||||||
|
{
|
||||||
|
if (count > 0)
|
||||||
{
|
{
|
||||||
// In case previous dropped filepaths have not been freed, we free them
|
// In case previous dropped filepaths have not been freed, we free them
|
||||||
if (CORE.Window.dropFileCount > 0)
|
if (CORE.Window.dropFileCount > 0)
|
||||||
|
|
@ -5654,6 +5657,7 @@ static void WindowDropCallback(GLFWwindow *window, int count, const char **paths
|
||||||
strcpy(CORE.Window.dropFilepaths[i], paths[i]);
|
strcpy(CORE.Window.dropFilepaths[i], paths[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(PLATFORM_ANDROID)
|
#if defined(PLATFORM_ANDROID)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user