1 /* $NetBSD: audio.c,v 1.12 2001/06/07 12:48:27 mrg Exp $ */ 2 3 /* 4 * Copyright (c) 1999 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 * 3. The name of the author may not be used to endorse or promote products 16 * derived from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 23 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 25 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 /* 32 * XXX this is slightly icky in places... 33 */ 34 35 #include <sys/types.h> 36 #include <sys/audioio.h> 37 #include <sys/ioctl.h> 38 #include <sys/time.h> 39 40 #include <ctype.h> 41 #include <err.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 46 #include "libaudio.h" 47 48 /* back and forth between encodings */ 49 struct { 50 char *ename; 51 int eno; 52 } encs[] = { 53 { AudioEmulaw, AUDIO_ENCODING_ULAW }, 54 { "ulaw", AUDIO_ENCODING_ULAW }, 55 { AudioEalaw, AUDIO_ENCODING_ALAW }, 56 { AudioEslinear, AUDIO_ENCODING_SLINEAR }, 57 { "linear", AUDIO_ENCODING_SLINEAR }, 58 { AudioEulinear, AUDIO_ENCODING_ULINEAR }, 59 { AudioEadpcm, AUDIO_ENCODING_ADPCM }, 60 { "ADPCM", AUDIO_ENCODING_ADPCM }, 61 { AudioEslinear_le, AUDIO_ENCODING_SLINEAR_LE }, 62 { "linear_le", AUDIO_ENCODING_SLINEAR_LE }, 63 { AudioEulinear_le, AUDIO_ENCODING_ULINEAR_LE }, 64 { AudioEslinear_be, AUDIO_ENCODING_SLINEAR_BE }, 65 { "linear_be", AUDIO_ENCODING_SLINEAR_BE }, 66 { AudioEulinear_be, AUDIO_ENCODING_ULINEAR_BE }, 67 { AudioEmpeg_l1_stream, AUDIO_ENCODING_MPEG_L1_STREAM }, 68 { AudioEmpeg_l1_packets,AUDIO_ENCODING_MPEG_L1_PACKETS }, 69 { AudioEmpeg_l1_system, AUDIO_ENCODING_MPEG_L1_SYSTEM }, 70 { AudioEmpeg_l2_stream, AUDIO_ENCODING_MPEG_L2_STREAM }, 71 { AudioEmpeg_l2_packets,AUDIO_ENCODING_MPEG_L2_PACKETS }, 72 { AudioEmpeg_l2_system, AUDIO_ENCODING_MPEG_L2_SYSTEM }, 73 { NULL, -1 } 74 }; 75 76 77 char * 78 audio_enc_from_val(val) 79 int val; 80 { 81 int i; 82 83 for (i = 0; encs[i].ename; i++) 84 if (encs[i].eno == val) 85 break; 86 return (encs[i].ename); 87 } 88 89 int 90 audio_enc_to_val(enc) 91 const char *enc; 92 { 93 int i; 94 95 for (i = 0; encs[i].ename; i++) 96 if (strcmp(encs[i].ename, enc) == 0) 97 break; 98 if (encs[i].ename) 99 return (encs[i].eno); 100 else 101 return (-1); 102 } 103 104 /* 105 * SunOS/NeXT .au format helpers 106 */ 107 struct { 108 int file_encoding; 109 int encoding; 110 int precision; 111 } file2sw_encodings[] = { 112 { AUDIO_FILE_ENCODING_MULAW_8, AUDIO_ENCODING_ULAW, 8 }, 113 { AUDIO_FILE_ENCODING_LINEAR_8, AUDIO_ENCODING_ULINEAR_BE, 8 }, 114 { AUDIO_FILE_ENCODING_LINEAR_16, AUDIO_ENCODING_ULINEAR_BE, 16 }, 115 { AUDIO_FILE_ENCODING_LINEAR_24, AUDIO_ENCODING_ULINEAR_BE, 24 }, 116 { AUDIO_FILE_ENCODING_LINEAR_32, AUDIO_ENCODING_ULINEAR_BE, 32 }, 117 #if 0 118 /* 119 * we should make some of these available. the, eg ultrasparc, port 120 * can use the VIS instructions (if available) do do some of these 121 * mpeg ones. 122 */ 123 { AUDIO_FILE_ENCODING_FLOAT, AUDIO_ENCODING_ULAW, 32 }, 124 { AUDIO_FILE_ENCODING_DOUBLE, AUDIO_ENCODING_ULAW, 64 }, 125 { AUDIO_FILE_ENCODING_ADPCM_G721, AUDIO_ENCODING_ULAW, 4 }, 126 { AUDIO_FILE_ENCODING_ADPCM_G722, AUDIO_ENCODING_ULAW, 0 }, 127 { AUDIO_FILE_ENCODING_ADPCM_G723_3, AUDIO_ENCODING_ULAW, 3 }, 128 { AUDIO_FILE_ENCODING_ADPCM_G723_5, AUDIO_ENCODING_ULAW, 5 }, 129 #endif 130 { AUDIO_FILE_ENCODING_ALAW_8, AUDIO_ENCODING_ALAW, 8 }, 131 { -1, -1 } 132 }; 133 134 int 135 audio_sun_to_encoding(sun_encoding, encp, precp) 136 int sun_encoding; 137 int *encp; 138 int *precp; 139 { 140 int i; 141 142 for (i = 0; file2sw_encodings[i].file_encoding != -1; i++) 143 if (file2sw_encodings[i].file_encoding == sun_encoding) { 144 *precp = file2sw_encodings[i].precision; 145 *encp = file2sw_encodings[i].encoding; 146 return (0); 147 } 148 return (1); 149 } 150 151 int 152 audio_encoding_to_sun(encoding, precision, sunep) 153 int encoding; 154 int precision; 155 int *sunep; 156 { 157 int i; 158 159 for (i = 0; file2sw_encodings[i].file_encoding != -1; i++) 160 if (file2sw_encodings[i].encoding == encoding && 161 file2sw_encodings[i].precision == precision) { 162 *sunep = file2sw_encodings[i].file_encoding; 163 return (0); 164 } 165 return (1); 166 } 167 168 /* 169 * sample header is: 170 * 171 * RIFF\^@^C^@WAVEfmt ^P^@^@^@^A^@^B^@D<AC>^@^@^P<B1>^B^@^D^@^P^@data^@^@^C^@^@^@^@^@^@^@^@^@^@ 172 * 173 */ 174 /* 175 * WAV format helpers 176 */ 177 /* 178 * find a .wav header, etc. returns header length on success 179 */ 180 size_t 181 audio_parse_wav_hdr(hdr, sz, enc, prec, sample, channels, datasize) 182 void *hdr; 183 size_t sz; 184 int *enc; 185 int *prec; 186 int *sample; 187 int *channels; 188 size_t *datasize; 189 { 190 char *where = hdr; 191 wav_audioheaderpart *part; 192 wav_audioheaderfmt *fmt; 193 char *end = (((char *)hdr) + sz); 194 int newenc, newprec; 195 196 if (sz < 32) 197 return (AUDIO_ENOENT); 198 199 if (strncmp(where, "RIFF", 4)) 200 return (AUDIO_ENOENT); 201 where += 8; 202 if (strncmp(where, "WAVE", 4)) 203 return (AUDIO_ENOENT); 204 where += 4; 205 206 do { 207 part = (wav_audioheaderpart *)where; 208 where += getle32(part->len) + 8; 209 } while (where < end && strncmp(part->name, "fmt ", 4)); 210 211 /* too short ? */ 212 if (where + 16 > end) 213 return (AUDIO_ESHORTHDR); 214 215 fmt = (wav_audioheaderfmt *)(part + 1); 216 217 #if 0 218 printf("fmt header is:\n\t%d\ttag\n\t%d\tchannels\n\t%d\tsample rate\n\t%d\tavg_bps\n\t%d\talignment\n\t%d\tbits per sample\n", getle16(fmt->tag), getle16(fmt->channels), getle32(fmt->sample_rate), getle32(fmt->avg_bps), getle16(fmt->alignment), getle16(fmt->bits_per_sample)); 219 #endif 220 221 switch (getle16(fmt->tag)) { 222 case WAVE_FORMAT_UNKNOWN: 223 case WAVE_FORMAT_ADPCM: 224 case WAVE_FORMAT_OKI_ADPCM: 225 case WAVE_FORMAT_DIGISTD: 226 case WAVE_FORMAT_DIGIFIX: 227 case IBM_FORMAT_MULAW: 228 case IBM_FORMAT_ALAW: 229 case IBM_FORMAT_ADPCM: 230 default: 231 return (AUDIO_EWAVUNSUPP); 232 233 case WAVE_FORMAT_PCM: 234 switch (getle16(fmt->bits_per_sample)) { 235 case 8: 236 newprec = 8; 237 break; 238 case 16: 239 newprec = 16; 240 break; 241 case 24: 242 newprec = 24; 243 break; 244 case 32: 245 newprec = 32; 246 break; 247 default: 248 return (AUDIO_EWAVBADPCM); 249 } 250 if (newprec == 8) 251 newenc = AUDIO_ENCODING_ULINEAR_LE; 252 else 253 newenc = AUDIO_ENCODING_SLINEAR_LE; 254 break; 255 case WAVE_FORMAT_ALAW: 256 newenc = AUDIO_ENCODING_ALAW; 257 newprec = 8; 258 break; 259 case WAVE_FORMAT_MULAW: 260 newenc = AUDIO_ENCODING_ULAW; 261 newprec = 8; 262 break; 263 } 264 265 do { 266 part = (wav_audioheaderpart *)where; 267 #if 0 268 printf("part `%c%c%c%c' len = %d\n", part->name[0], part->name[1], part->name[2], part->name[3], getle32(part->len)); 269 #endif 270 where += (getle32(part->len) + 8); 271 } while ((char *)where < end && strncmp(part->name, "data", 4)); 272 273 if ((where - getle32(part->len)) <= end) { 274 *channels = getle16(fmt->channels); 275 *sample = getle32(fmt->sample_rate); 276 *enc = newenc; 277 *prec = newprec; 278 if (datasize) 279 *datasize = (size_t)getle32(part->len); 280 part++; 281 return ((char *)part - (char *)hdr); 282 } 283 return (AUDIO_EWAVNODATA); 284 } 285 286 /* 287 * these belong elsewhere?? 288 */ 289 void 290 decode_int(arg, intp) 291 const char *arg; 292 int *intp; 293 { 294 char *ep; 295 int ret; 296 297 ret = strtoul(arg, &ep, 0); 298 299 if (ep[0] == '\0') { 300 *intp = ret; 301 return; 302 } 303 errx(1, "argument `%s' not a valid integer", arg); 304 } 305 306 void 307 decode_time(arg, tvp) 308 const char *arg; 309 struct timeval *tvp; 310 { 311 char *s, *colon, *dot; 312 char *copy = strdup(arg); 313 int first; 314 315 if (copy == NULL) 316 err(1, "could not allocate a copy of %s", arg); 317 318 tvp->tv_sec = tvp->tv_usec = 0; 319 s = copy; 320 321 /* handle [hh:]mm:ss.dd */ 322 if ((colon = strchr(s, ':'))) { 323 *colon++ = '\0'; 324 decode_int(s, &first); 325 tvp->tv_sec = first * 60; /* minutes */ 326 s = colon; 327 328 if ((colon = strchr(s, ':'))) { 329 *colon++ = '\0'; 330 decode_int(s, &first); 331 tvp->tv_sec *= 60; 332 tvp->tv_sec += first; /* minutes and hours */ 333 s = colon; 334 } 335 } 336 if ((dot = strchr(s, '.'))) { 337 int i, base = 100000; 338 339 *dot++ = '\0'; 340 341 for (i = 0; i < 6; i++, base /= 10) { 342 if (!dot[i]) 343 break; 344 if (!isdigit(dot[i])) 345 errx(1, "argument `%s' is not a value time specification", arg); 346 tvp->tv_usec += base * (dot[i] - '0'); 347 } 348 } 349 decode_int(s, &first); 350 tvp->tv_sec += first; 351 #if 0 352 printf("tvp->tv_sec = %ld, tvp->tv_usec = %ld\n", tvp->tv_sec, tvp->tv_usec); 353 #endif 354 355 free(copy); 356 } 357 358 /* 359 * decode a string into an encoding value. 360 */ 361 void 362 decode_encoding(arg, encp) 363 const char *arg; 364 int *encp; 365 { 366 size_t len; 367 int i; 368 369 len = strlen(arg); 370 for (i = 0; encs[i].ename; i++) 371 if (strncmp(encs[i].ename, arg, len) == 0) { 372 *encp = encs[i].eno; 373 return; 374 } 375 errx(1, "unknown encoding `%s'", arg); 376 } 377 378 const char *const audio_errlist[] = { 379 "error zero", /* nothing? */ 380 "no audio entry", /* AUDIO_ENOENT */ 381 "short header", /* AUDIO_ESHORTHDR */ 382 "unsupported WAV format", /* AUDIO_EWAVUNSUPP */ 383 "bad (unsupported) WAV PCM format", /* AUDIO_EWAVBADPCM */ 384 "no WAV audio data", /* AUDIO_EWAVNODATA */ 385 }; 386 387 const char * 388 audio_errstring(errval) 389 int errval; 390 { 391 392 errval = -errval; 393 if (errval < 1 || errval > AUDIO_MAXERRNO) 394 return "Invalid error"; 395 return audio_errlist[errval]; 396 } 397