1 /* $NetBSD: wav.c,v 1.10 2011/08/28 01:17:47 joerg 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.10 2011/08/28 01:17:47 joerg 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 52 #include "libaudio.h" 53 54 static const struct { 55 int wenc; 56 const char *wname; 57 } wavencs[] = { 58 { WAVE_FORMAT_UNKNOWN, "Microsoft Official Unknown" }, 59 { WAVE_FORMAT_PCM, "Microsoft PCM" }, 60 { WAVE_FORMAT_ADPCM, "Microsoft ADPCM" }, 61 { WAVE_FORMAT_ALAW, "Microsoft A-law" }, 62 { WAVE_FORMAT_MULAW, "Microsoft mu-law" }, 63 { WAVE_FORMAT_OKI_ADPCM,"OKI ADPCM" }, 64 { WAVE_FORMAT_DIGISTD, "Digistd format" }, 65 { WAVE_FORMAT_DIGIFIX, "Digifix format" }, 66 { -1, "?Unknown?" }, 67 }; 68 69 const char * 70 wav_enc_from_val(int encoding) 71 { 72 int i; 73 74 for (i = 0; wavencs[i].wenc != -1; i++) 75 if (wavencs[i].wenc == encoding) 76 break; 77 return (wavencs[i].wname); 78 } 79 80 /* 81 * sample header is: 82 * 83 * RIFF\^@^C^@WAVEfmt ^P^@^@^@^A^@^B^@D<AC>^@^@^P<B1>^B^@^D^@^P^@data^@^@^C^@^@^@^@^@^@^@^@^@^@ 84 * 85 */ 86 /* 87 * WAV format helpers 88 */ 89 /* 90 * find a .wav header, etc. returns header length on success 91 */ 92 ssize_t 93 audio_wav_parse_hdr(void *hdr, size_t sz, u_int *enc, u_int *prec, 94 u_int *sample, u_int *channels, size_t *datasize) 95 { 96 char *where = hdr, *owhere; 97 wav_audioheaderpart part; 98 wav_audioheaderfmt fmt; 99 wav_audiohdrextensible ext; 100 char *end = (((char *)hdr) + sz); 101 u_int newenc, newprec; 102 u_int16_t fmttag; 103 static const char 104 strfmt[4] = "fmt ", 105 strRIFF[4] = "RIFF", 106 strWAVE[4] = "WAVE", 107 strdata[4] = "data"; 108 109 if (sz < 32) 110 return (AUDIO_ENOENT); 111 112 if (strncmp(where, strRIFF, sizeof strRIFF)) 113 return (AUDIO_ENOENT); 114 where += 8; 115 if (strncmp(where, strWAVE, sizeof strWAVE)) 116 return (AUDIO_ENOENT); 117 where += 4; 118 119 do { 120 memcpy(&part, where, sizeof part); 121 owhere = where; 122 where += getle32(part.len) + 8; 123 } while (where < end && strncmp(part.name, strfmt, sizeof strfmt)); 124 125 /* too short ? */ 126 if (where + sizeof fmt > end) 127 return (AUDIO_ESHORTHDR); 128 129 memcpy(&fmt, (owhere + 8), sizeof fmt); 130 131 fmttag = getle16(fmt.tag); 132 if (verbose) 133 printf("WAVE format tag: %x\n", fmttag); 134 135 if (fmttag == WAVE_FORMAT_EXTENSIBLE) { 136 if ((uintptr_t)(where - owhere) < sizeof(fmt) + sizeof(ext)) 137 return (AUDIO_ESHORTHDR); 138 memcpy(&ext, owhere + sizeof fmt, sizeof ext); 139 if (getle16(ext.len) < sizeof(ext) - sizeof(ext.len)) 140 return (AUDIO_ESHORTHDR); 141 fmttag = ext.sub_tag; 142 if (verbose) 143 printf("WAVE extensible sub tag: %x\n", fmttag); 144 } 145 146 switch (fmttag) { 147 case WAVE_FORMAT_UNKNOWN: 148 case IBM_FORMAT_MULAW: 149 case IBM_FORMAT_ALAW: 150 case IBM_FORMAT_ADPCM: 151 default: 152 return (AUDIO_EWAVUNSUPP); 153 154 case WAVE_FORMAT_PCM: 155 case WAVE_FORMAT_ADPCM: 156 case WAVE_FORMAT_OKI_ADPCM: 157 case WAVE_FORMAT_IMA_ADPCM: 158 case WAVE_FORMAT_DIGIFIX: 159 case WAVE_FORMAT_DIGISTD: 160 switch (getle16(fmt.bits_per_sample)) { 161 case 8: 162 newprec = 8; 163 break; 164 case 16: 165 newprec = 16; 166 break; 167 case 24: 168 newprec = 24; 169 break; 170 case 32: 171 newprec = 32; 172 break; 173 default: 174 return (AUDIO_EWAVBADPCM); 175 } 176 if (newprec == 8) 177 newenc = AUDIO_ENCODING_ULINEAR_LE; 178 else 179 newenc = AUDIO_ENCODING_SLINEAR_LE; 180 break; 181 case WAVE_FORMAT_ALAW: 182 newenc = AUDIO_ENCODING_ALAW; 183 newprec = 8; 184 break; 185 case WAVE_FORMAT_MULAW: 186 newenc = AUDIO_ENCODING_ULAW; 187 newprec = 8; 188 break; 189 } 190 191 do { 192 memcpy(&part, where, sizeof part); 193 owhere = where; 194 where += (getle32(part.len) + 8); 195 } while (where < end && strncmp(part.name, strdata, sizeof strdata)); 196 197 if ((where - getle32(part.len)) <= end) { 198 if (channels) 199 *channels = (u_int)getle16(fmt.channels); 200 if (sample) 201 *sample = getle32(fmt.sample_rate); 202 if (enc) 203 *enc = newenc; 204 if (prec) 205 *prec = newprec; 206 if (datasize) 207 *datasize = (size_t)getle32(part.len); 208 return (owhere - (char *)hdr + 8); 209 } 210 return (AUDIO_EWAVNODATA); 211 } 212