1 /* $NetBSD: wav.c,v 1.13 2015/08/05 06:54:39 mrg Exp $ */ 2 3 /* 4 * Copyright (c) 2002, 2009 Matthew R. Green 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 /* 30 * WAV support for the audio tools; thanks go to the sox utility for 31 * clearing up issues with WAV files. 32 */ 33 #include <sys/cdefs.h> 34 35 #ifndef lint 36 __RCSID("$NetBSD: wav.c,v 1.13 2015/08/05 06:54:39 mrg Exp $"); 37 #endif 38 39 40 #include <sys/types.h> 41 #include <sys/audioio.h> 42 #include <sys/ioctl.h> 43 #include <sys/time.h> 44 45 #include <ctype.h> 46 #include <err.h> 47 #include <stdio.h> 48 #include <stdlib.h> 49 #include <string.h> 50 #include <stdint.h> 51 #include <unistd.h> 52 53 #include "libaudio.h" 54 #include "auconv.h" 55 56 static const struct { 57 int wenc; 58 const char *wname; 59 } wavencs[] = { 60 { WAVE_FORMAT_UNKNOWN, "Microsoft Official Unknown" }, 61 { WAVE_FORMAT_PCM, "Microsoft PCM" }, 62 { WAVE_FORMAT_ADPCM, "Microsoft ADPCM" }, 63 { WAVE_FORMAT_ALAW, "Microsoft A-law" }, 64 { WAVE_FORMAT_MULAW, "Microsoft mu-law" }, 65 { WAVE_FORMAT_OKI_ADPCM,"OKI ADPCM" }, 66 { WAVE_FORMAT_DIGISTD, "Digistd format" }, 67 { WAVE_FORMAT_DIGIFIX, "Digifix format" }, 68 { -1, "?Unknown?" }, 69 }; 70 71 const char * 72 wav_enc_from_val(int encoding) 73 { 74 int i; 75 76 for (i = 0; wavencs[i].wenc != -1; i++) 77 if (wavencs[i].wenc == encoding) 78 break; 79 return (wavencs[i].wname); 80 } 81 82 /* 83 * sample header is: 84 * 85 * RIFF\^@^C^@WAVEfmt ^P^@^@^@^A^@^B^@D<AC>^@^@^P<B1>^B^@^D^@^P^@data^@^@^C^@^@^@^@^@^@^@^@^@^@ 86 * 87 */ 88 /* 89 * WAV format helpers 90 */ 91 /* 92 * find a .wav header, etc. returns header length on success 93 */ 94 ssize_t 95 audio_wav_parse_hdr(void *hdr, size_t sz, u_int *enc, u_int *prec, 96 u_int *sample, u_int *channels, off_t *datasize) 97 { 98 char *where = hdr, *owhere; 99 wav_audioheaderpart part; 100 wav_audioheaderfmt fmt; 101 wav_audiohdrextensible ext; 102 char *end = (((char *)hdr) + sz); 103 u_int newenc, newprec; 104 u_int16_t fmttag; 105 static const char 106 strfmt[4] = "fmt ", 107 strRIFF[4] = "RIFF", 108 strWAVE[4] = "WAVE", 109 strdata[4] = "data"; 110 111 if (sz < 32) 112 return (AUDIO_ENOENT); 113 114 if (strncmp(where, strRIFF, sizeof strRIFF)) 115 return (AUDIO_ENOENT); 116 where += 8; 117 if (strncmp(where, strWAVE, sizeof strWAVE)) 118 return (AUDIO_ENOENT); 119 where += 4; 120 121 do { 122 memcpy(&part, where, sizeof part); 123 owhere = where; 124 where += getle32(part.len) + 8; 125 } while (where < end && strncmp(part.name, strfmt, sizeof strfmt)); 126 127 /* too short ? */ 128 if (where + sizeof fmt > end) 129 return (AUDIO_ESHORTHDR); 130 131 memcpy(&fmt, (owhere + 8), sizeof fmt); 132 133 fmttag = getle16(fmt.tag); 134 if (verbose) 135 printf("WAVE format tag: %x\n", fmttag); 136 137 if (fmttag == WAVE_FORMAT_EXTENSIBLE) { 138 if ((uintptr_t)(where - owhere) < sizeof(fmt) + sizeof(ext)) 139 return (AUDIO_ESHORTHDR); 140 memcpy(&ext, owhere + sizeof fmt, sizeof ext); 141 if (getle16(ext.len) < sizeof(ext) - sizeof(ext.len)) 142 return (AUDIO_ESHORTHDR); 143 fmttag = ext.sub_tag; 144 if (verbose) 145 printf("WAVE extensible sub tag: %x\n", fmttag); 146 } 147 148 switch (fmttag) { 149 case WAVE_FORMAT_UNKNOWN: 150 case IBM_FORMAT_MULAW: 151 case IBM_FORMAT_ALAW: 152 case IBM_FORMAT_ADPCM: 153 default: 154 return (AUDIO_EWAVUNSUPP); 155 156 case WAVE_FORMAT_PCM: 157 case WAVE_FORMAT_ADPCM: 158 case WAVE_FORMAT_OKI_ADPCM: 159 case WAVE_FORMAT_IMA_ADPCM: 160 case WAVE_FORMAT_DIGIFIX: 161 case WAVE_FORMAT_DIGISTD: 162 switch (getle16(fmt.bits_per_sample)) { 163 case 8: 164 newprec = 8; 165 break; 166 case 16: 167 newprec = 16; 168 break; 169 case 24: 170 newprec = 24; 171 break; 172 case 32: 173 newprec = 32; 174 break; 175 default: 176 return (AUDIO_EWAVBADPCM); 177 } 178 if (newprec == 8) 179 newenc = AUDIO_ENCODING_ULINEAR_LE; 180 else 181 newenc = AUDIO_ENCODING_SLINEAR_LE; 182 break; 183 case WAVE_FORMAT_ALAW: 184 newenc = AUDIO_ENCODING_ALAW; 185 newprec = 8; 186 break; 187 case WAVE_FORMAT_MULAW: 188 newenc = AUDIO_ENCODING_ULAW; 189 newprec = 8; 190 break; 191 } 192 193 do { 194 memcpy(&part, where, sizeof part); 195 owhere = where; 196 where += (getle32(part.len) + 8); 197 } while (where < end && strncmp(part.name, strdata, sizeof strdata)); 198 199 if ((where - getle32(part.len)) <= end) { 200 if (channels) 201 *channels = (u_int)getle16(fmt.channels); 202 if (sample) 203 *sample = getle32(fmt.sample_rate); 204 if (enc) 205 *enc = newenc; 206 if (prec) 207 *prec = newprec; 208 if (datasize) 209 *datasize = (off_t)getle32(part.len); 210 return (owhere - (char *)hdr + 8); 211 } 212 return (AUDIO_EWAVNODATA); 213 } 214 215 216 /* 217 * prepare a WAV header for writing; we fill in hdrp, lenp and leftp, 218 * and expect our caller (wav_write_header()) to use them. 219 */ 220 int 221 wav_prepare_header(struct track_info *ti, void **hdrp, size_t *lenp, int *leftp) 222 { 223 /* 224 * WAV header we write looks like this: 225 * 226 * bytes purpose 227 * 0-3 "RIFF" 228 * 4-7 file length (minus 8) 229 * 8-15 "WAVEfmt " 230 * 16-19 format size 231 * 20-21 format tag 232 * 22-23 number of channels 233 * 24-27 sample rate 234 * 28-31 average bytes per second 235 * 32-33 block alignment 236 * 34-35 bits per sample 237 * 238 * then for ULAW and ALAW outputs, we have an extended chunk size 239 * and a WAV "fact" to add: 240 * 241 * 36-37 length of extension (== 0) 242 * 38-41 "fact" 243 * 42-45 fact size 244 * 46-49 number of samples written 245 * 50-53 "data" 246 * 54-57 data length 247 * 58- raw audio data 248 * 249 * for PCM outputs we have just the data remaining: 250 * 251 * 36-39 "data" 252 * 40-43 data length 253 * 44- raw audio data 254 * 255 * RIFF\^@^C^@WAVEfmt ^P^@^@^@^A^@^B^@D<AC>^@^@^P<B1>^B^@^D^@^P^@data^@^@^C^@^@^@^@^@^@^@^@^@^@ 256 */ 257 static char wavheaderbuf[64]; 258 char *p = wavheaderbuf; 259 const char *riff = "RIFF", 260 *wavefmt = "WAVEfmt ", 261 *fact = "fact", 262 *data = "data"; 263 u_int32_t filelen, fmtsz, sps, abps, factsz = 4, nsample, datalen; 264 u_int16_t fmttag, nchan, align, extln = 0; 265 266 if (ti->header_info) 267 warnx("header information not supported for WAV"); 268 *leftp = 0; 269 270 switch (ti->precision) { 271 case 8: 272 break; 273 case 16: 274 break; 275 case 32: 276 break; 277 default: 278 { 279 static int warned = 0; 280 281 if (warned == 0) { 282 warnx("can not support precision of %d", ti->precision); 283 warned = 1; 284 } 285 } 286 return (-1); 287 } 288 289 switch (ti->encoding) { 290 case AUDIO_ENCODING_ULAW: 291 fmttag = WAVE_FORMAT_MULAW; 292 fmtsz = 18; 293 align = ti->channels; 294 break; 295 296 case AUDIO_ENCODING_ALAW: 297 fmttag = WAVE_FORMAT_ALAW; 298 fmtsz = 18; 299 align = ti->channels; 300 break; 301 302 /* 303 * we could try to support RIFX but it seems to be more portable 304 * to output little-endian data for WAV files. 305 */ 306 case AUDIO_ENCODING_ULINEAR_BE: 307 case AUDIO_ENCODING_SLINEAR_BE: 308 case AUDIO_ENCODING_ULINEAR_LE: 309 case AUDIO_ENCODING_SLINEAR_LE: 310 case AUDIO_ENCODING_PCM16: 311 312 #if BYTE_ORDER == LITTLE_ENDIAN 313 case AUDIO_ENCODING_ULINEAR: 314 case AUDIO_ENCODING_SLINEAR: 315 #endif 316 fmttag = WAVE_FORMAT_PCM; 317 fmtsz = 16; 318 align = ti->channels * (ti->precision / 8); 319 break; 320 321 default: 322 #if 0 // move into record.c, and maybe merge.c 323 { 324 static int warned = 0; 325 326 if (warned == 0) { 327 const char *s = wav_enc_from_val(ti->encoding); 328 329 if (s == NULL) 330 warnx("can not support encoding of %s", s); 331 else 332 warnx("can not support encoding of %d", ti->encoding); 333 warned = 1; 334 } 335 } 336 #endif 337 ti->format = AUDIO_FORMAT_NONE; 338 return (-1); 339 } 340 341 nchan = ti->channels; 342 sps = ti->sample_rate; 343 344 /* data length */ 345 if (ti->outfd == STDOUT_FILENO) 346 datalen = 0; 347 else if (ti->total_size != -1) 348 datalen = ti->total_size; 349 else 350 datalen = 0; 351 352 /* file length */ 353 filelen = 4 + (8 + fmtsz) + (8 + datalen); 354 if (fmttag != WAVE_FORMAT_PCM) 355 filelen += 8 + factsz; 356 357 abps = (double)align*ti->sample_rate / (double)1 + 0.5; 358 359 nsample = (datalen / ti->precision) / ti->sample_rate; 360 361 /* 362 * now we've calculated the info, write it out! 363 */ 364 #define put32(x) do { \ 365 u_int32_t _f; \ 366 putle32(_f, (x)); \ 367 memcpy(p, &_f, 4); \ 368 } while (0) 369 #define put16(x) do { \ 370 u_int16_t _f; \ 371 putle16(_f, (x)); \ 372 memcpy(p, &_f, 2); \ 373 } while (0) 374 memcpy(p, riff, 4); 375 p += 4; /* 4 */ 376 put32(filelen); 377 p += 4; /* 8 */ 378 memcpy(p, wavefmt, 8); 379 p += 8; /* 16 */ 380 put32(fmtsz); 381 p += 4; /* 20 */ 382 put16(fmttag); 383 p += 2; /* 22 */ 384 put16(nchan); 385 p += 2; /* 24 */ 386 put32(sps); 387 p += 4; /* 28 */ 388 put32(abps); 389 p += 4; /* 32 */ 390 put16(align); 391 p += 2; /* 34 */ 392 put16(ti->precision); 393 p += 2; /* 36 */ 394 /* NON PCM formats have an extended chunk; write it */ 395 if (fmttag != WAVE_FORMAT_PCM) { 396 put16(extln); 397 p += 2; /* 38 */ 398 memcpy(p, fact, 4); 399 p += 4; /* 42 */ 400 put32(factsz); 401 p += 4; /* 46 */ 402 put32(nsample); 403 p += 4; /* 50 */ 404 } 405 memcpy(p, data, 4); 406 p += 4; /* 40/54 */ 407 put32(datalen); 408 p += 4; /* 44/58 */ 409 #undef put32 410 #undef put16 411 412 *hdrp = wavheaderbuf; 413 *lenp = (p - wavheaderbuf); 414 415 return 0; 416 } 417 418 write_conv_func 419 wav_write_get_conv_func(struct track_info *ti) 420 { 421 write_conv_func conv_func = NULL; 422 423 switch (ti->encoding) { 424 425 /* 426 * we could try to support RIFX but it seems to be more portable 427 * to output little-endian data for WAV files. 428 */ 429 case AUDIO_ENCODING_ULINEAR_BE: 430 #if BYTE_ORDER == BIG_ENDIAN 431 case AUDIO_ENCODING_ULINEAR: 432 #endif 433 if (ti->precision == 16) 434 conv_func = change_sign16_swap_bytes_be; 435 else if (ti->precision == 32) 436 conv_func = change_sign32_swap_bytes_be; 437 break; 438 439 case AUDIO_ENCODING_SLINEAR_BE: 440 #if BYTE_ORDER == BIG_ENDIAN 441 case AUDIO_ENCODING_SLINEAR: 442 #endif 443 if (ti->precision == 8) 444 conv_func = change_sign8; 445 else if (ti->precision == 16) 446 conv_func = swap_bytes; 447 else if (ti->precision == 32) 448 conv_func = swap_bytes32; 449 break; 450 451 case AUDIO_ENCODING_ULINEAR_LE: 452 #if BYTE_ORDER == LITTLE_ENDIAN 453 case AUDIO_ENCODING_ULINEAR: 454 #endif 455 if (ti->precision == 16) 456 conv_func = change_sign16_le; 457 else if (ti->precision == 32) 458 conv_func = change_sign32_le; 459 break; 460 461 case AUDIO_ENCODING_SLINEAR_LE: 462 case AUDIO_ENCODING_PCM16: 463 #if BYTE_ORDER == LITTLE_ENDIAN 464 case AUDIO_ENCODING_SLINEAR: 465 #endif 466 if (ti->precision == 8) 467 conv_func = change_sign8; 468 break; 469 470 default: 471 ti->format = AUDIO_FORMAT_NONE; 472 } 473 474 return conv_func; 475 } 476