UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
wav_reader.c
Go to the documentation of this file.
1
5/*
6 * Copyright (c) 2013-2022 CESNET, z. s. p. o.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, is permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * 3. Neither the name of CESNET nor the names of its contributors may be
21 * used to endorse or promote products derived from this software without
22 * specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
26 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
27 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
28 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
29 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
34 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
35 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
37
38#ifdef HAVE_CONFIG_H
39#include "config.h"
40#include "config_unix.h"
41#include "config_win32.h"
42#endif // HAVE_CONFIG_H
43
44#include <inttypes.h>
45#include <limits.h>
46#include <stdbool.h>
47#include <stdint.h>
48
49#include "audio/utils.h"
50#include "audio/wav_reader.h"
51#include "debug.h"
52#include "utils/macros.h"
53#include "utils/misc.h"
54
55#define MOD_NAME "[WAV reader] "
56#define WAV_MAX_BIT_DEPTH 64
57#define WAV_MAX_CHANNELS 128
58
59#define READ_N(buf, len) \
60 if (fread(buf, len, 1, wav_file) != 1) {\
61 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Read error: %s.\n", ug_strerror(errno));\
62 return WAV_HDR_PARSE_READ_ERROR;\
63 }
64
65#define GUID_PCM { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 }
66
67static int read_fmt_chunk(FILE *wav_file, struct wav_metadata *metadata, size_t chunk_size)
68{
69 if (chunk_size != 16 && chunk_size != 18 && chunk_size != 40) {
70 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Expected fmt chunk size 16, 18 or 40, %zd given.\n", chunk_size);
72 }
73
74 uint16_t format;
75 READ_N(&format, 2);
76 if (format != 0x0001 && format != 0xFFFE) {
77 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Expected format 0x0001, 0x%04d given.\n", format);
79 }
80
81 uint16_t ch_count;
82 READ_N(&ch_count, 2);
83 if (ch_count == 0 || ch_count > WAV_MAX_CHANNELS) {
85 }
86 metadata->ch_count = ch_count;
87
88 uint32_t sample_rate;
90 if (sample_rate > 192000) {
92 }
93 metadata->sample_rate = sample_rate;
94
95 uint32_t avg_bytes_per_sec;
96 READ_N(&avg_bytes_per_sec, sizeof(avg_bytes_per_sec));
97
98 uint16_t block_align_offset;
99 READ_N(&block_align_offset, sizeof(block_align_offset));
100
101 uint16_t bits_per_sample;
102 READ_N(&bits_per_sample, sizeof(bits_per_sample));
103 if (bits_per_sample == 0 || bits_per_sample > WAV_MAX_BIT_DEPTH) {
105 }
106 metadata->bits_per_sample = bits_per_sample;
107
108 if (chunk_size >= 18) {
109 uint16_t ext_size;
110 READ_N(&ext_size, 2);
111 if (ext_size != chunk_size - 18) {
112 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unexpected ext size %" PRIu16 ", remaining chunk size %zu.\n", ext_size, chunk_size - 18);
114 }
115 if (ext_size == 22) {
116 READ_N(&metadata->valid_bits, sizeof metadata->valid_bits);
117 READ_N(&metadata->channel_mask, sizeof metadata->channel_mask);
118 char buffer[16];
119 READ_N(buffer, 16);
120 const char guid[] = GUID_PCM;
121 if (memcmp(guid, buffer, 16) != 0) {
122 log_msg(LOG_LEVEL_ERROR, MOD_NAME "GUID is not PCM!\n");
124 }
125 format = 0x0001;
126 }
127 }
128
129 if (format == 0xFFFE) {
130 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Subtype GUID not found!\n");
132 }
133
134 return WAV_HDR_PARSE_OK;
135}
136
137static _Bool is_member(const char *needle, const char **haystack) {
138 while (*haystack != NULL) {
139 if (strncmp(needle, *haystack, 4) == 0) {
140 return 1;
141 }
142 haystack++;
143 }
144 return 0;
145}
146
147static int fskip(FILE *f, long off) {
148 if (_fseeki64(f, off, SEEK_CUR) == 0) {
149 return 1;
150 }
151 while (off-- > 0) {
152 if (getc(f) == EOF) {
153 return -1;
154 }
155 }
156 return 1;
157}
158
161static _Bool process_data_chunk(FILE *wav_file, uint32_t chunk_size, struct wav_metadata *metadata, _Bool found_ds64_chunk) {
162 if (!found_ds64_chunk) { // RF64 has this chunk size always -1, value from ds64 is used instead
163 metadata->data_size = chunk_size;
164 }
165 metadata->data_offset = _ftelli64(wav_file);
166 if (metadata->data_size != UINT32_MAX || metadata->data_offset == -1) {
167 return 1;
168 }
169
170 // for UINT32_MAX deduce the length from file length (as FFmpeg does)
171 if (_fseeki64(wav_file, 0, SEEK_END) != 0) {
172 return 1;
173 }
174 int64_t data_end = _ftelli64(wav_file);
175 if (data_end == -1) {
176 ug_perror(MOD_NAME "cannot get length of file");
177 return 0;
178 }
179 if (_fseeki64(wav_file, metadata->data_offset, SEEK_SET) != 0) {
180 ug_perror(MOD_NAME "cannot seek back to data chunk");
181 return 0;
182 }
183 metadata->data_size = data_end - metadata->data_offset;
184 return 1;
185}
186
187static _Bool read_ds64(FILE *wav_file, uint32_t chunk_size, struct wav_metadata *metadata) {
188 if (chunk_size < 28) {
189 log_msg(LOG_LEVEL_ERROR, MOD_NAME "ds64 chunk size must be at least 28 but %" PRIu32 " was presented!\n", chunk_size);
190 return 0;
191 }
192 uint64_t tmp = 0;
193 READ_N(&tmp, 8); // RF64 chunk size - unused
194 READ_N(&tmp, 8);
195 assert(tmp <= LLONG_MAX);
196 metadata->data_size = tmp;
197 READ_N(&tmp, 8); // dummy - should be ignored
198 uint32_t table_count = 0;
199 const size_t table_sz = (sizeof(uint32_t) /*ID*/ + sizeof(uint64_t) /*size*/);
200 READ_N(&table_count, 4); // number of table entries for non-'data' chunks
201 assert(table_sz * table_count <= MIN(sizeof(uint32_t), sizeof(long))); // check overflows
202 if (chunk_size - 28 != table_sz * table_count) {
203 log_msg(LOG_LEVEL_ERROR, MOD_NAME "incorrect table count %" PRIu32 " given, ds64 chunk len %" PRIu32 "\n",
204 table_count, chunk_size);
205 return 0;
206 }
207 if (!fskip(wav_file, (long)(table_count * table_sz))) { // just skip the table contents
208 ug_perror(MOD_NAME "ds64 - cannot skip");
209 return 0;
210 }
211 return 1;
212}
213
214#define CHECK(cmd, retval) if (cmd == -1) return retval;
215int read_wav_header(FILE *wav_file, struct wav_metadata *metadata)
216{
217 char buffer[16];
218 uint32_t chunk_size;
219 rewind(wav_file);
220
221 _Bool rf64 = 0;
222 READ_N(buffer, 4);
223 if (strncmp(buffer, "RF64", 4) == 0 || strncmp(buffer, "BW64", 4) == 0) {
224 log_msg(LOG_LEVEL_VERBOSE, MOD_NAME "Using %4.4s WAV file.\n", buffer);
225 rf64 = 1;
226 } else if (strncmp(buffer, "RIFF", 4) != 0) {
227 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Expected RIFF or RF64/BW64 chunk, %.4s given.\n", buffer);
229 }
230
231 // this is the length of the rest of the file, we may ignore
232 READ_N(&chunk_size, 4);
233
234 READ_N(buffer, 4);
235 if (strncmp(buffer, "WAVE", 4) != 0) {
236 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Expected WAVE chunk, %.4s given.\n", buffer);
238 }
239
240 bool found_data_chunk = false;
241 bool found_ds64_chunk = false;
242 bool found_fmt_chunk = false;
243 while (fread(buffer, 4, 1, wav_file) == 1) {
244 READ_N(&chunk_size, 4);
245 if (strncmp(buffer, "data", 4) == 0) {
246 if (!process_data_chunk(wav_file, chunk_size, metadata, found_ds64_chunk)) {
248 }
249 found_data_chunk = true;
250 break;
251 }
252 if (strncmp(buffer, "ds64", 4) == 0) {
253 if (!read_ds64(wav_file, chunk_size, metadata)) {
255 }
256 found_ds64_chunk = true;
257 } else if (strncmp(buffer, "fmt ", 4) == 0) {
258 found_fmt_chunk = true;
259 int rc = read_fmt_chunk(wav_file, metadata, chunk_size);
260 if (rc != WAV_HDR_PARSE_OK) {
261 return rc;
262 }
263 } else { // other tags
264 const char *known_tags[] = { "JUNK", "LIST", "id3 ", NULL }; // "olym" ?
265 int level = is_member(buffer, known_tags) ? LOG_LEVEL_VERBOSE : LOG_LEVEL_WARNING;
266 log_msg(level, MOD_NAME "Skipping chunk \"%4.4s\" sized %" PRIu32 " B!\n", buffer, chunk_size);
267 CHECK(fskip(wav_file, chunk_size), WAV_HDR_PARSE_READ_ERROR);
268 }
269 }
270
271 if (!found_data_chunk) {
272 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Data chunk not found!\n");
274 }
275
276 if (!found_fmt_chunk) {
277 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Fmt chunk not found!\n");
279 }
280
281 if (rf64 && !found_ds64_chunk) { // buggy, but we may try to continue witn implicit data size
282 log_msg(LOG_LEVEL_WARNING, MOD_NAME "Broken WAV - a RF64 file detected but no ds64 chunk found!\n");
283 }
284
285 log_msg(LOG_LEVEL_VERBOSE, MOD_NAME "File parsed correctly - length %lld bytes, data offset %lld.\n",
286 metadata->data_size, metadata->data_offset);
287
288 return WAV_HDR_PARSE_OK;
289}
290
291size_t wav_read(void *buffer, size_t sample_count, FILE *wav_file, struct wav_metadata *metadata)
292{
293 size_t samples = fread(buffer, metadata->ch_count * metadata->bits_per_sample / 8, sample_count, wav_file);
294 if (metadata->bits_per_sample == 8) {
295 signed2unsigned(buffer, buffer, samples * metadata->ch_count * metadata->bits_per_sample / 8);
296 }
297 return samples;
298}
299
300const char *get_wav_error(int errcode)
301{
302 switch(errcode) {
303 case WAV_HDR_PARSE_OK:
304 return "Wav header OK";
306 return "Premature end of WAV file";
308 return "Wav header in wrong format";
310 return "Wav not in PCM";
312 return "Wrong/unsupported value in header";
313 default:
314 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unknown error code %d passed!\n", errcode);
315 return "Unknown error";
316 }
317}
318
319int wav_seek(FILE *wav_file, long offset, int whence, struct wav_metadata *metadata)
320{
321 if (whence == SEEK_CUR || whence == SEEK_END) {
322 return fseek(wav_file, offset, whence);
323 }
324 assert(whence == SEEK_SET);
325 return fseek(wav_file, metadata->data_offset + offset, SEEK_SET);
326}
327
int int int sample_rate
#define _fseeki64
#define _ftelli64
static __device__ const float offset
Definition cuda_dxt.cu:139
void log_msg(int level, const char *format,...)
Definition debug.cpp:130
#define LOG_LEVEL_ERROR
general errors
Definition debug.h:59
#define LOG_LEVEL_VERBOSE
Definition debug.h:63
#define LOG_LEVEL_WARNING
less severe errors
Definition debug.h:60
#define MIN(a, b)
Definition macros.h:63
void ug_perror(const char *s)
Definition misc.cpp:213
unsigned int ch_count
Definition wav_reader.h:47
unsigned int sample_rate
Definition wav_reader.h:48
long long data_offset
offset from beginning, -1 if unknown (typically unseekable)
Definition wav_reader.h:53
uint32_t channel_mask
Definition wav_reader.h:51
unsigned int bits_per_sample
Definition wav_reader.h:49
long long data_size
size of data, -1 if unknown
Definition wav_reader.h:54
uint16_t valid_bits
Definition wav_reader.h:50
void signed2unsigned(char *out, const char *in, int in_len)
Definition utils.cpp:515
int wav_seek(FILE *wav_file, long offset, int whence, struct wav_metadata *metadata)
same as fseek but WAV header presence is respected (for SEEK SET)
Definition wav_reader.c:319
#define WAV_MAX_BIT_DEPTH
Definition wav_reader.c:56
static int fskip(FILE *f, long off)
Definition wav_reader.c:147
#define READ_N(buf, len)
Definition wav_reader.c:59
static _Bool read_ds64(FILE *wav_file, uint32_t chunk_size, struct wav_metadata *metadata)
Definition wav_reader.c:187
static _Bool process_data_chunk(FILE *wav_file, uint32_t chunk_size, struct wav_metadata *metadata, _Bool found_ds64_chunk)
Definition wav_reader.c:161
#define GUID_PCM
Definition wav_reader.c:65
const char * get_wav_error(int errcode)
Definition wav_reader.c:300
size_t wav_read(void *buffer, size_t sample_count, FILE *wav_file, struct wav_metadata *metadata)
Definition wav_reader.c:291
static _Bool is_member(const char *needle, const char **haystack)
Definition wav_reader.c:137
#define MOD_NAME
Definition wav_reader.c:55
#define CHECK(cmd, retval)
Definition wav_reader.c:214
int read_wav_header(FILE *wav_file, struct wav_metadata *metadata)
Definition wav_reader.c:215
static int read_fmt_chunk(FILE *wav_file, struct wav_metadata *metadata, size_t chunk_size)
Definition wav_reader.c:67
#define WAV_MAX_CHANNELS
Definition wav_reader.c:57
#define WAV_HDR_PARSE_WRONG_FORMAT
Definition wav_reader.h:59
#define WAV_HDR_PARSE_READ_ERROR
Definition wav_reader.h:58
#define WAV_HDR_PARSE_OK
Definition wav_reader.h:57
#define WAV_HDR_PARSE_INVALID_PARAM
Definition wav_reader.h:61
#define WAV_HDR_PARSE_NOT_PCM
Definition wav_reader.h:60