1 /* $NetBSD: wav.c,v 1.9 2009/06/18 02:37:27 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.9 2009/06/18 02:37:27 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 52 #include "libaudio.h" 53 54 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 extern int verbose; 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(hdr, sz, enc, prec, sample, channels, datasize) 96 void *hdr; 97 size_t sz; 98 u_int *enc; 99 u_int *prec; 100 u_int *sample; 101 u_int *channels; 102 size_t *datasize; 103 { 104 char *where = hdr, *owhere; 105 wav_audioheaderpart part; 106 wav_audioheaderfmt fmt; 107 wav_audiohdrextensible ext; 108 char *end = (((char *)hdr) + sz); 109 u_int newenc, newprec; 110 u_int16_t fmttag; 111 static const char 112 strfmt[4] = "fmt ", 113 strRIFF[4] = "RIFF", 114 strWAVE[4] = "WAVE", 115 strdata[4] = "data"; 116 117 if (sz < 32) 118 return (AUDIO_ENOENT); 119 120 if (strncmp(where, strRIFF, sizeof strRIFF)) 121 return (AUDIO_ENOENT); 122 where += 8; 123 if (strncmp(where, strWAVE, sizeof strWAVE)) 124 return (AUDIO_ENOENT); 125 where += 4; 126 127 do { 128 memcpy(&part, where, sizeof part); 129 owhere = where; 130 where += getle32(part.len) + 8; 131 } while (where < end && strncmp(part.name, strfmt, sizeof strfmt)); 132 133 /* too short ? */ 134 if (where + sizeof fmt > end) 135 return (AUDIO_ESHORTHDR); 136 137 memcpy(&fmt, (owhere + 8), sizeof fmt); 138 139 fmttag = getle16(fmt.tag); 140 if (verbose) 141 printf("WAVE format tag: %x\n", fmttag); 142 143 if (fmttag == WAVE_FORMAT_EXTENSIBLE) { 144 if ((uintptr_t)(where - owhere) < sizeof(fmt) + sizeof(ext)) 145 return (AUDIO_ESHORTHDR); 146 memcpy(&ext, owhere + sizeof fmt, sizeof ext); 147 if (getle16(ext.len) < sizeof(ext) - sizeof(ext.len)) 148 return (AUDIO_ESHORTHDR); 149 fmttag = ext.sub_tag; 150 if (verbose) 151 printf("WAVE extensible sub tag: %x\n", fmttag); 152 } 153 154 switch (fmttag) { 155 case WAVE_FORMAT_UNKNOWN: 156 case IBM_FORMAT_MULAW: 157 case IBM_FORMAT_ALAW: 158 case IBM_FORMAT_ADPCM: 159 default: 160 return (AUDIO_EWAVUNSUPP); 161 162 case WAVE_FORMAT_PCM: 163 case WAVE_FORMAT_ADPCM: 164 case WAVE_FORMAT_OKI_ADPCM: 165 case WAVE_FORMAT_IMA_ADPCM: 166 case WAVE_FORMAT_DIGIFIX: 167 case WAVE_FORMAT_DIGISTD: 168 switch (getle16(fmt.bits_per_sample)) { 169 case 8: 170 newprec = 8; 171 break; 172 case 16: 173 newprec = 16; 174 break; 175 case 24: 176 newprec = 24; 177 break; 178 case 32: 179 newprec = 32; 180 break; 181 default: 182 return (AUDIO_EWAVBADPCM); 183 } 184 if (newprec == 8) 185 newenc = AUDIO_ENCODING_ULINEAR_LE; 186 else 187 newenc = AUDIO_ENCODING_SLINEAR_LE; 188 break; 189 case WAVE_FORMAT_ALAW: 190 newenc = AUDIO_ENCODING_ALAW; 191 newprec = 8; 192 break; 193 case WAVE_FORMAT_MULAW: 194 newenc = AUDIO_ENCODING_ULAW; 195 newprec = 8; 196 break; 197 } 198 199 do { 200 memcpy(&part, where, sizeof part); 201 owhere = where; 202 where += (getle32(part.len) + 8); 203 } while (where < end && strncmp(part.name, strdata, sizeof strdata)); 204 205 if ((where - getle32(part.len)) <= end) { 206 if (channels) 207 *channels = (u_int)getle16(fmt.channels); 208 if (sample) 209 *sample = getle32(fmt.sample_rate); 210 if (enc) 211 *enc = newenc; 212 if (prec) 213 *prec = newprec; 214 if (datasize) 215 *datasize = (size_t)getle32(part.len); 216 return (owhere - (char *)hdr + 8); 217 } 218 return (AUDIO_EWAVNODATA); 219 } 220