GCC Code Coverage Report


Directory: ./
File: submodules/raylib/src/external/qoaplay.c
Date: 2023-09-29 04:53:15
Exec Total Coverage
Lines: 0 80 0.0%
Branches: 0 32 0.0%

Line Branch Exec Source
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 }
279