minesweeper

A minewseeper implementation to play around with Hare and Raylib
git clone https://git.tronto.net/minesweeper
Download | Log | Files | Refs | README | LICENSE

qoaplay.c (10503B)


      1 /*******************************************************************************************
      2 *
      3 *   qoaplay - QOA stream playing helper functions
      4 *
      5 *   qoaplay is a tiny abstraction to read and decode a QOA file "on the fly".
      6 *   It reads and decodes one frame at a time with minimal memory requirements.
      7 *   qoaplay also provides some functions to seek to a specific frame.
      8 *
      9 *   LICENSE: MIT License
     10 *
     11 *   Copyright (c) 2023 Dominic Szablewski (@phoboslab), reviewed by Ramon Santamaria (@raysan5)
     12 *
     13 *   Permission is hereby granted, free of charge, to any person obtaining a copy
     14 *   of this software and associated documentation files (the "Software"), to deal
     15 *   in the Software without restriction, including without limitation the rights
     16 *   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     17 *   copies of the Software, and to permit persons to whom the Software is
     18 *   furnished to do so, subject to the following conditions:
     19 *
     20 *   The above copyright notice and this permission notice shall be included in all
     21 *   copies or substantial portions of the Software.
     22 *
     23 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     24 *   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     25 *   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     26 *   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     27 *   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     28 *   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     29 *   SOFTWARE.
     30 *
     31 **********************************************************************************************/
     32 
     33 //----------------------------------------------------------------------------------
     34 // Types and Structures Definition
     35 //----------------------------------------------------------------------------------
     36 // QOA streaming data descriptor
     37 typedef struct {
     38     qoa_desc info;                  // QOA descriptor data
     39 
     40     FILE *file;                     // QOA file to read, if NULL, using memory buffer -> file_data
     41     unsigned char *file_data;       // QOA file data on memory
     42     unsigned int file_data_size;    // QOA file data on memory size
     43     unsigned int file_data_offset;  // QOA file data on memory offset for next read
     44 
     45     unsigned int first_frame_pos;   // First frame position (after QOA header, required for offset)
     46     unsigned int sample_position;   // Current streaming sample position
     47 
     48     unsigned char *buffer;          // Buffer used to read samples from file/memory (used on decoding)
     49     unsigned int buffer_len;        // Buffer length to read samples for streaming
     50 
     51     short *sample_data;             // Sample data decoded
     52     unsigned int sample_data_len;   // Sample data decoded length
     53     unsigned int sample_data_pos;   // Sample data decoded position
     54 
     55 } qoaplay_desc;
     56 
     57 //----------------------------------------------------------------------------------
     58 // Module Functions Declaration
     59 //----------------------------------------------------------------------------------
     60 
     61 #if defined(__cplusplus)
     62 extern "C" {            // Prevents name mangling of functions
     63 #endif
     64 
     65 qoaplay_desc *qoaplay_open(const char *path);
     66 qoaplay_desc *qoaplay_open_memory(const unsigned char *data, int data_size);
     67 void qoaplay_close(qoaplay_desc *qoa_ctx);
     68 
     69 void qoaplay_rewind(qoaplay_desc *qoa_ctx);
     70 void qoaplay_seek_frame(qoaplay_desc *qoa_ctx, int frame);
     71 unsigned int qoaplay_decode(qoaplay_desc *qoa_ctx, float *sample_data, int num_samples);
     72 unsigned int qoaplay_decode_frame(qoaplay_desc *qoa_ctx);
     73 double qoaplay_get_duration(qoaplay_desc *qoa_ctx);
     74 double qoaplay_get_time(qoaplay_desc *qoa_ctx);
     75 int qoaplay_get_frame(qoaplay_desc *qoa_ctx);
     76 
     77 #if defined(__cplusplus)
     78 }            // Prevents name mangling of functions
     79 #endif
     80 
     81 //----------------------------------------------------------------------------------
     82 // Module Functions Definition
     83 //----------------------------------------------------------------------------------
     84 
     85 // Open QOA file, keep FILE pointer to keep reading from file
     86 qoaplay_desc *qoaplay_open(const char *path)
     87 {
     88     FILE *file = fopen(path, "rb");
     89     if (!file) return NULL;
     90 
     91     // Read and decode the file header
     92     unsigned char header[QOA_MIN_FILESIZE];
     93     int read = fread(header, QOA_MIN_FILESIZE, 1, file);
     94     if (!read) return NULL;
     95 
     96     qoa_desc qoa;
     97     unsigned int first_frame_pos = qoa_decode_header(header, QOA_MIN_FILESIZE, &qoa);
     98     if (!first_frame_pos) return NULL;
     99 
    100     // Rewind the file back to beginning of the first frame
    101     fseek(file, first_frame_pos, SEEK_SET);
    102 
    103     // Allocate one chunk of memory for the qoaplay_desc struct
    104     // + the sample data for one frame
    105     // + a buffer to hold one frame of encoded data
    106     unsigned int buffer_size = qoa_max_frame_size(&qoa);
    107     unsigned int sample_data_size = qoa.channels*QOA_FRAME_LEN*sizeof(short)*2;
    108     qoaplay_desc *qoa_ctx = QOA_MALLOC(sizeof(qoaplay_desc) + buffer_size + sample_data_size);
    109     memset(qoa_ctx, 0, sizeof(qoaplay_desc));
    110 
    111     qoa_ctx->file = file;
    112     qoa_ctx->file_data = NULL;
    113     qoa_ctx->file_data_size = 0;
    114     qoa_ctx->file_data_offset = 0;
    115     qoa_ctx->first_frame_pos = first_frame_pos;
    116 
    117     // Setup data pointers to previously allocated data
    118     qoa_ctx->buffer = ((unsigned char *)qoa_ctx) + sizeof(qoaplay_desc);
    119     qoa_ctx->sample_data = (short *)(((unsigned char *)qoa_ctx) + sizeof(qoaplay_desc) + buffer_size);
    120 
    121     qoa_ctx->info.channels = qoa.channels;
    122     qoa_ctx->info.samplerate = qoa.samplerate;
    123     qoa_ctx->info.samples = qoa.samples;
    124 
    125     return qoa_ctx;
    126 }
    127 
    128 // Open QOA file from memory, no FILE pointer required
    129 qoaplay_desc *qoaplay_open_memory(const unsigned char *data, int data_size)
    130 {
    131     // Read and decode the file header
    132     unsigned char header[QOA_MIN_FILESIZE];
    133     memcpy(header, data, QOA_MIN_FILESIZE);
    134 
    135     qoa_desc qoa;
    136     unsigned int first_frame_pos = qoa_decode_header(header, QOA_MIN_FILESIZE, &qoa);
    137     if (!first_frame_pos) return NULL;
    138 
    139     // Allocate one chunk of memory for the qoaplay_desc struct
    140     // + the sample data for one frame
    141     // + a buffer to hold one frame of encoded data
    142     unsigned int buffer_size = qoa_max_frame_size(&qoa);
    143     unsigned int sample_data_size = qoa.channels*QOA_FRAME_LEN*sizeof(short)*2;
    144     qoaplay_desc *qoa_ctx = QOA_MALLOC(sizeof(qoaplay_desc) + buffer_size + sample_data_size);
    145     memset(qoa_ctx, 0, sizeof(qoaplay_desc));
    146 
    147     qoa_ctx->file = NULL;
    148 
    149     // Keep a copy of file data provided to be managed internally
    150     qoa_ctx->file_data = (unsigned char *)QOA_MALLOC(data_size);
    151     memcpy(qoa_ctx->file_data, data, data_size);
    152     qoa_ctx->file_data_size = data_size;
    153     qoa_ctx->file_data_offset = 0;
    154     qoa_ctx->first_frame_pos = first_frame_pos;
    155 
    156     // Setup data pointers to previously allocated data
    157     qoa_ctx->buffer = ((unsigned char *)qoa_ctx) + sizeof(qoaplay_desc);
    158     qoa_ctx->sample_data = (short *)(((unsigned char *)qoa_ctx) + sizeof(qoaplay_desc) + buffer_size);
    159 
    160     qoa_ctx->info.channels = qoa.channels;
    161     qoa_ctx->info.samplerate = qoa.samplerate;
    162     qoa_ctx->info.samples = qoa.samples;
    163 
    164     return qoa_ctx;
    165 }
    166 
    167 // Close QOA file (if open) and free internal memory
    168 void qoaplay_close(qoaplay_desc *qoa_ctx)
    169 {
    170     if (qoa_ctx->file) fclose(qoa_ctx->file);
    171 
    172     if ((qoa_ctx->file_data) && (qoa_ctx->file_data_size > 0))
    173     {
    174         QOA_FREE(qoa_ctx->file_data);
    175         qoa_ctx->file_data_size = 0;
    176     }
    177 
    178     QOA_FREE(qoa_ctx);
    179 }
    180 
    181 // Decode one frame from QOA data
    182 unsigned int qoaplay_decode_frame(qoaplay_desc *qoa_ctx)
    183 {
    184     if (qoa_ctx->file) qoa_ctx->buffer_len = fread(qoa_ctx->buffer, 1, qoa_max_frame_size(&qoa_ctx->info), qoa_ctx->file);
    185     else
    186     {
    187         qoa_ctx->buffer_len = qoa_max_frame_size(&qoa_ctx->info);
    188         memcpy(qoa_ctx->buffer, qoa_ctx->file_data + qoa_ctx->file_data_offset, qoa_ctx->buffer_len);
    189         qoa_ctx->file_data_offset += qoa_ctx->buffer_len;
    190     }
    191 
    192     unsigned int frame_len;
    193     qoa_decode_frame(qoa_ctx->buffer, qoa_ctx->buffer_len, &qoa_ctx->info, qoa_ctx->sample_data, &frame_len);
    194     qoa_ctx->sample_data_pos = 0;
    195     qoa_ctx->sample_data_len = frame_len;
    196 
    197     return frame_len;
    198 }
    199 
    200 // Rewind QOA file or memory pointer to beginning
    201 void qoaplay_rewind(qoaplay_desc *qoa_ctx)
    202 {
    203     if (qoa_ctx->file) fseek(qoa_ctx->file, qoa_ctx->first_frame_pos, SEEK_SET);
    204     else qoa_ctx->file_data_offset = 0;
    205 
    206     qoa_ctx->sample_position = 0;
    207     qoa_ctx->sample_data_len = 0;
    208     qoa_ctx->sample_data_pos = 0;
    209 }
    210 
    211 // Decode required QOA frames
    212 unsigned int qoaplay_decode(qoaplay_desc *qoa_ctx, float *sample_data, int num_samples)
    213 {
    214     int src_index = qoa_ctx->sample_data_pos*qoa_ctx->info.channels;
    215     int dst_index = 0;
    216 
    217     for (int i = 0; i < num_samples; i++)
    218     {
    219         // Do we have to decode more samples?
    220         if (qoa_ctx->sample_data_len - qoa_ctx->sample_data_pos == 0)
    221         {
    222             if (!qoaplay_decode_frame(qoa_ctx))
    223             {
    224                 // Loop to the beginning
    225                 qoaplay_rewind(qoa_ctx);
    226                 qoaplay_decode_frame(qoa_ctx);
    227             }
    228 
    229             src_index = 0;
    230         }
    231 
    232         // Normalize to -1..1 floats and write to dest
    233         for (int c = 0; c < qoa_ctx->info.channels; c++)
    234         {
    235             sample_data[dst_index++] = qoa_ctx->sample_data[src_index++]/32768.0;
    236         }
    237 
    238         qoa_ctx->sample_data_pos++;
    239         qoa_ctx->sample_position++;
    240     }
    241 
    242     return num_samples;
    243 }
    244 
    245 // Get QOA total time duration in seconds
    246 double qoaplay_get_duration(qoaplay_desc *qoa_ctx)
    247 {
    248     return (double)qoa_ctx->info.samples/(double)qoa_ctx->info.samplerate;
    249 }
    250 
    251 // Get QOA current time position in seconds
    252 double qoaplay_get_time(qoaplay_desc *qoa_ctx)
    253 {
    254     return (double)qoa_ctx->sample_position/(double)qoa_ctx->info.samplerate;
    255 }
    256 
    257 // Get QOA current audio frame
    258 int qoaplay_get_frame(qoaplay_desc *qoa_ctx)
    259 {
    260     return qoa_ctx->sample_position/QOA_FRAME_LEN;
    261 }
    262 
    263 // Seek QOA audio frame
    264 void qoaplay_seek_frame(qoaplay_desc *qoa_ctx, int frame)
    265 {
    266     if (frame < 0) frame = 0;
    267 
    268     if (frame > qoa_ctx->info.samples/QOA_FRAME_LEN) frame = qoa_ctx->info.samples/QOA_FRAME_LEN;
    269 
    270     qoa_ctx->sample_position = frame*QOA_FRAME_LEN;
    271     qoa_ctx->sample_data_len = 0;
    272     qoa_ctx->sample_data_pos = 0;
    273 
    274     unsigned int offset = qoa_ctx->first_frame_pos + frame*qoa_max_frame_size(&qoa_ctx->info);
    275 
    276     if (qoa_ctx->file) fseek(qoa_ctx->file, offset, SEEK_SET);
    277     else qoa_ctx->file_data_offset = offset;
    278 }