1 /* $NetBSD: audio.c,v 1.9 1999/09/27 05:06:10 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_get_sun_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 /* 152 * sample header is: 153 * 154 * RIFF\^@^C^@WAVEfmt ^P^@^@^@^A^@^B^@D<AC>^@^@^P<B1>^B^@^D^@^P^@data^@^@^C^@^@^@^@^@^@^@^@^@^@ 155 * 156 */ 157 /* 158 * WAV format helpers 159 */ 160 /* 161 * find a .wav header, etc. returns header length on success 162 */ 163 size_t 164 audio_parse_wav_hdr(hdr, sz, enc, prec, sample, channels) 165 void *hdr; 166 size_t sz; 167 int *enc; 168 int *prec; 169 int *sample; 170 int *channels; 171 { 172 char *where = hdr; 173 wav_audioheaderpart *part; 174 wav_audioheaderfmt *fmt; 175 char *end = (((char *)hdr) + sz); 176 int newenc, newprec; 177 178 if (sz < 32) 179 return (AUDIO_ENOENT); 180 181 if (strncmp(where, "RIFF", 4)) 182 return (AUDIO_ENOENT); 183 where += 8; 184 if (strncmp(where, "WAVE", 4)) 185 return (AUDIO_ENOENT); 186 where += 4; 187 188 do { 189 part = (wav_audioheaderpart *)where; 190 where += getle32(part->len) + 8; 191 } while (where < end && strncmp(part->name, "fmt ", 4)); 192 193 /* too short ? */ 194 if (where + 16 > end) 195 return (AUDIO_ESHORTHDR); 196 197 fmt = (wav_audioheaderfmt *)(part + 1); 198 199 #if 0 200 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)); 201 #endif 202 203 switch (getle16(fmt->tag)) { 204 case WAVE_FORMAT_UNKNOWN: 205 case WAVE_FORMAT_ADPCM: 206 case WAVE_FORMAT_OKI_ADPCM: 207 case WAVE_FORMAT_DIGISTD: 208 case WAVE_FORMAT_DIGIFIX: 209 case IBM_FORMAT_MULAW: 210 case IBM_FORMAT_ALAW: 211 case IBM_FORMAT_ADPCM: 212 default: 213 return (AUDIO_EWAVUNSUPP); 214 215 case WAVE_FORMAT_PCM: 216 switch (getle16(fmt->bits_per_sample)) { 217 case 8: 218 newprec = 8; 219 break; 220 case 16: 221 newprec = 16; 222 break; 223 case 24: 224 newprec = 24; 225 break; 226 case 32: 227 newprec = 32; 228 break; 229 default: 230 return (AUDIO_EWAVBADPCM); 231 } 232 if (newprec == 8) 233 newenc = AUDIO_ENCODING_ULINEAR_LE; 234 else 235 newenc = AUDIO_ENCODING_SLINEAR_LE; 236 break; 237 case WAVE_FORMAT_ALAW: 238 newenc = AUDIO_ENCODING_ALAW; 239 newprec = 8; 240 break; 241 case WAVE_FORMAT_MULAW: 242 newenc = AUDIO_ENCODING_ULAW; 243 newprec = 8; 244 break; 245 } 246 247 do { 248 part = (wav_audioheaderpart *)where; 249 #if 0 250 printf("part `%c%c%c%c' len = %d\n", part->name[0], part->name[1], part->name[2], part->name[3], getle32(part->len)); 251 #endif 252 where += (getle32(part->len) + 8); 253 } while ((char *)where < end && strncmp(part->name, "data", 4)); 254 255 if ((where - getle32(part->len)) <= end) { 256 *channels = getle16(fmt->channels); 257 *sample = getle32(fmt->sample_rate); 258 *enc = newenc; 259 *prec = newprec; 260 part++; 261 return ((char *)part - (char *)hdr); 262 } 263 return (AUDIO_EWAVNODATA); 264 } 265 266 /* 267 * these belong elsewhere?? 268 */ 269 void 270 decode_int(arg, intp) 271 const char *arg; 272 int *intp; 273 { 274 char *ep; 275 int ret; 276 277 ret = strtoul(arg, &ep, 0); 278 279 if (ep[0] == '\0') { 280 *intp = ret; 281 return; 282 } 283 errx(1, "argument `%s' not a valid integer", arg); 284 } 285 286 void 287 decode_time(arg, tvp) 288 const char *arg; 289 struct timeval *tvp; 290 { 291 char *s, *colon, *dot; 292 char *copy = strdup(arg); 293 int first; 294 295 if (copy == NULL) 296 err(1, "could not allocate a copy of %s", arg); 297 298 tvp->tv_sec = tvp->tv_usec = 0; 299 s = copy; 300 301 /* handle [hh:]mm:ss.dd */ 302 if ((colon = strchr(s, ':'))) { 303 *colon++ = '\0'; 304 decode_int(s, &first); 305 tvp->tv_sec = first * 60; /* minutes */ 306 s = colon; 307 308 if ((colon = strchr(s, ':'))) { 309 *colon++ = '\0'; 310 decode_int(s, &first); 311 tvp->tv_sec += first; 312 tvp->tv_sec *= 60; /* minutes and hours */ 313 s = colon; 314 } 315 } 316 if ((dot = strchr(s, '.'))) { 317 int i, base = 100000; 318 319 *dot++ = '\0'; 320 321 for (i = 0; i < 6; i++, base /= 10) { 322 if (!dot[i]) 323 break; 324 if (!isdigit(dot[i])) 325 errx(1, "argument `%s' is not a value time specification", arg); 326 tvp->tv_usec += base * (dot[i] - '0'); 327 } 328 } 329 decode_int(s, &first); 330 tvp->tv_sec += first; 331 #if 0 332 printf("tvp->tv_sec = %ld, tvp->tv_usec = %ld\n", tvp->tv_sec, tvp->tv_usec); 333 #endif 334 335 free(copy); 336 } 337 338 /* 339 * decode a string into an encoding value. 340 */ 341 void 342 decode_encoding(arg, encp) 343 const char *arg; 344 int *encp; 345 { 346 size_t len; 347 int i; 348 349 len = strlen(arg); 350 for (i = 0; encs[i].ename; i++) 351 if (strncmp(encs[i].ename, arg, len) == 0) { 352 *encp = encs[i].eno; 353 return; 354 } 355 errx(1, "unknown encoding `%s'", arg); 356 } 357 358 const char *const audio_errlist[] = { 359 "error zero", /* nothing? */ 360 "no audio entry", /* AUDIO_ENOENT */ 361 "short header", /* AUDIO_ESHORTHDR */ 362 "unsupported WAV format", /* AUDIO_EWAVUNSUPP */ 363 "bad (unsupported) WAV PCM format", /* AUDIO_EWAVBADPCM */ 364 "no WAV audio data", /* AUDIO_EWAVNODATA */ 365 }; 366 367 const char * 368 audio_errstring(errval) 369 int errval; 370 { 371 372 errval = -errval; 373 if (errval < 1 || errval > AUDIO_MAXERRNO) 374 return "Invalid error"; 375 return audio_errlist[errval]; 376 } 377