1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright (c) 1993-2001 by Sun Microsystems, Inc. 24 * All rights reserved. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <stdlib.h> 30 #include <stdio.h> 31 #include <stdarg.h> 32 #include <string.h> 33 #include <unistd.h> 34 #include <sys/types.h> 35 #include <sys/stat.h> 36 #include <sys/file.h> 37 #include <sys/param.h> 38 39 #include <convert.h> 40 41 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 42 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 43 #endif 44 45 const char *opt_string = "pf:o:i:FTD?"; 46 47 char *Stdin; 48 char *Stdout; 49 char *Suffix = (char *)".AUDCVTMP"; 50 51 char *progname; // program name 52 char *fake_argv[] = {(char *)"-", NULL}; // stdin with no args 53 54 extern char *optarg; 55 extern int optind; 56 57 int Statistics = 0; 58 int Debug = 0; 59 60 void init_header(AudioHdr&); 61 void usage(); 62 63 main( 64 int argc, 65 char *argv[]) 66 { 67 AudioUnixfile* ifp = NULL; // input & output audio objects 68 AudioUnixfile* ofp = NULL; 69 AudioHdr ihdr; // input/output headers 70 AudioHdr ohdr; 71 char *infile = NULL; // input/output file names 72 char *outfile = NULL; 73 char *realfile = NULL; 74 char *out_fmt = NULL; // output fmt string 75 AudioError err; // for error msgs 76 int c; // for getopt 77 int pflag = 0; // in place flag 78 int fflag = 0; // ignore header (force conversion) 79 int stdin_seen = 0; // already read stdin 80 int israw = 0; // once we've seen -i, it's raw data 81 format_type ofmt = F_SUN; // output format type 82 format_type ifmt = F_SUN; // expected input format type 83 format_type fmt = F_SUN; // actual input format type 84 off_t o_offset = 0; // output offset (ignored) 85 off_t i_offset = 0; // input offset 86 int i; 87 struct stat st; 88 89 setlocale(LC_ALL, ""); 90 (void) textdomain(TEXT_DOMAIN); 91 92 // basename of program 93 if (progname = strrchr(argv[0], '/')) { 94 progname++; 95 } else { 96 progname = argv[0]; 97 } 98 Stdin = MGET("(stdin)"); 99 Stdout = MGET("(stdout)"); 100 101 // init the input & output headers 102 init_header(ihdr); 103 init_header(ohdr); 104 105 // some conversions depend on invocation name. we'll create 106 // default input/output formats based on argv[0] that 107 // can be overridden by -o or -i options. 108 if (strcmp(progname, "ulaw2pcm") == 0) { 109 (void) parse_format((char *)"ulaw", ihdr, ifmt, i_offset); 110 (void) parse_format((char *)"pcm", ohdr, ofmt, o_offset); 111 } else if (strcmp(progname, "pcm2ulaw") == 0) { 112 (void) parse_format((char *)"pcm", ihdr, ifmt, i_offset); 113 (void) parse_format((char *)"ulaw", ohdr, ofmt, o_offset); 114 } else if (strcmp(progname, "adpcm_enc") == 0) { 115 (void) parse_format((char *)"ulaw", ihdr, ifmt, i_offset); 116 (void) parse_format((char *)"g721", ohdr, ofmt, o_offset); 117 } else if (strcmp(progname, "adpcm_dec") == 0) { 118 (void) parse_format((char *)"g721", ihdr, ifmt, i_offset); 119 (void) parse_format((char *)"ulaw", ohdr, ofmt, o_offset); 120 } else if (strcmp(progname, "raw2audio") == 0) { 121 (void) parse_format((char *)"ulaw", ihdr, ifmt, i_offset); 122 (void) parse_format((char *)"ulaw", ohdr, ofmt, o_offset); 123 israw++; 124 pflag++; 125 } else if (argc <= 1) { 126 // audioconvert with no arguments 127 usage(); 128 } 129 130 // now parse the rest of the arg's 131 while ((c = getopt(argc, argv, opt_string)) != -1) { 132 switch (c) { 133 #ifdef DEBUG 134 case 'D': 135 // enable debug messages 136 Debug++; 137 break; 138 #endif 139 case 'p': 140 // convert files in place 141 if (outfile != NULL) { 142 Err(MGET("can't use -p with -o\n")); 143 exit(1); 144 } 145 pflag++; 146 break; 147 case 'F': 148 // force treatment of audio files as raw files 149 // (ignore filehdr). 150 fflag++; 151 break; 152 case 'f': 153 // save format string to parse later, but verify now 154 out_fmt = optarg; 155 if (parse_format(out_fmt, ohdr, ofmt, o_offset) == -1) 156 exit(1); 157 if (o_offset != 0) { 158 Err(MGET("can't specify an offset with -f\n")); 159 exit(1); 160 } 161 break; 162 case 'o': 163 if (pflag) { 164 Err(MGET("can't use -o with -p\n")); 165 exit(1); 166 } 167 outfile = optarg; 168 break; 169 case 'i': 170 // if bogus input header, exit ... 171 if (parse_format(optarg, ihdr, ifmt, i_offset) == -1) { 172 exit(1); 173 } 174 israw++; 175 break; 176 default: 177 case '?': 178 usage(); 179 } 180 } 181 182 // XXX - should check argument consistency here.... 183 184 // If no args left, we're taking input from stdin. 185 // In this case, make argv point to a fake argv with "-" as a file 186 // name, and set optind and argc apropriately so we'll go through 187 // the loop below once. 188 if (optind >= argc) { 189 argv = fake_argv; 190 argc = 1; 191 optind = 0; 192 /* 193 * XXX - we turn off pflag if stdin is the only input file. 194 * this is kind of a hack. if invoked as raw2audio, pflag 195 * it turned on. if no files are given, we want to turn 196 * it off, otherwise we'll complain about using -p with 197 * stdin, which won't make sense if invoked as raw2audio. 198 * instead, just silently ignore. the message is still given 199 * and stdin is ignored if it's specified as one of several 200 * input files. 201 */ 202 pflag = 0; 203 } 204 205 // From this point on we're looking at file names or -i args 206 // for input format specs. 207 for (; optind < argc; optind++) { 208 // new input format spec. 209 if (strcmp(argv[optind], "-i") == 0) { 210 init_header(ihdr); 211 i_offset = 0; 212 ifmt = F_SUN; 213 // if bogus input header, exit ... 214 if (parse_format(argv[++optind], ihdr, ifmt, i_offset) 215 == -1) { 216 exit(1); 217 } 218 israw++; 219 } else if (strcmp(argv[optind], "-") == 0) { 220 // ignore stdin argument if in place 221 if (pflag) { 222 Err(MGET("can't use %s with -p flag\n"), 223 Stdin); 224 continue; 225 } 226 227 if (stdin_seen) { 228 Err(MGET("already used stdin for input\n")); 229 continue; 230 } else { 231 stdin_seen++; 232 } 233 234 infile = Stdin; 235 } else { 236 infile = argv[optind]; 237 } 238 239 // if no audio object returned, just continue to the next 240 // file. if a fatal error occurs, open_input_file() 241 // will exit the program. 242 ifp = 243 open_input_file(infile, ihdr, israw, fflag, i_offset, fmt); 244 if (!ifp) { 245 continue; 246 } 247 248 if ((err = ifp->Open()) != AUDIO_SUCCESS) { 249 Err(MGET("open error on input file %s - %s\n"), 250 infile, err.msg()); 251 exit(1); 252 } 253 ifp->Reference(); 254 255 // create the output file if not created yet, or if 256 // converting in place. ofp will be NULL only the first 257 // time through. use the header of the first input file 258 // to base the output format on - then create the output 259 // header w/the output format spec. 260 if ((ofp == NULL) && !pflag) { 261 262 ohdr = ifp->GetHeader(); 263 ohdr = ifp->GetHeader(); 264 ofmt = ifmt; 265 // just use input hdr if no output hdr spec 266 if (out_fmt) { 267 if (parse_format(out_fmt, ohdr, ofmt, o_offset) 268 == -1) { 269 exit(1); 270 } 271 } 272 273 // need to check before output is opened ... 274 if (verify_conversion(ifp->GetHeader(), ohdr) == -1) { 275 // XXX - bomb out or skip? 276 exit(3); 277 } 278 279 // Create the file and set the info string. 280 char *infoString; 281 int infoStringLen; 282 infoString = ifp->GetInfostring(infoStringLen); 283 ofp = create_output_file(outfile, ohdr, ofmt, 284 infoString); 285 286 } else if (pflag) { 287 288 // create new output header based on each input file 289 ohdr = ifp->GetHeader(); 290 ofmt = ifmt; 291 // just use input hdr if no output hdr spec 292 if (out_fmt) { 293 if (parse_format(out_fmt, ohdr, ofmt, o_offset) 294 == -1) { 295 exit(1); 296 } 297 } 298 299 // get the *real* path of the infile (follow sym-links), 300 // and the stat info. 301 realfile = infile; 302 get_realfile(realfile, &st); 303 304 // if the file is read-only, give up 305 if (access(realfile, W_OK)) { 306 // XXX - do we really want to exit? 307 perror(infile); 308 Err(MGET("cannot rewrite in place\n")); 309 exit(1); 310 } 311 312 // this is now the output file. 313 i = strlen(realfile) + strlen(Suffix) + 1; 314 outfile = (char *)malloc((unsigned)i); 315 if (outfile == NULL) { 316 Err(MGET("out of memory\n")); 317 exit(1); 318 } 319 (void) sprintf(outfile, "%s%s", realfile, Suffix); 320 321 // outfile will get re-assigned to a tmp file 322 if (verify_conversion(ifp->GetHeader(), ohdr) == -1) { 323 // XXX - bomb out or skip? 324 exit(3); 325 } 326 327 // If no conversion, just skip the file 328 if (noop_conversion(ifp->GetHeader(), ohdr, 329 fmt, ofmt, i_offset, o_offset)) { 330 if (Debug) 331 Err(MGET( 332 "%s: no-op conversion...skipping\n"), 333 infile); 334 continue; 335 } 336 337 // Get the input info string. 338 char *infoString; 339 int infoStringLen; 340 infoString = ifp->GetInfostring(infoStringLen); 341 ofp = create_output_file(outfile, ohdr, ofmt, 342 infoString); 343 } 344 345 // verify that it's a valid conversion by looking at the 346 // file headers. (this will be called twice for the first 347 // file if *not* converting in place. that's ok.... 348 if (!pflag && (verify_conversion(ifp->GetHeader(), ohdr) 349 == -1)) { 350 // XXX - bomb out or skip file if invalid conversion? 351 exit(3); 352 } 353 354 // do the conversion, if error, bomb out 355 if (do_convert(ifp, ofp) == -1) { 356 exit(4); 357 } 358 359 ifp->Close(); 360 ifp->Dereference(); 361 362 // if in place, finish up by renaming the outfile to 363 // back to the infile. 364 if (pflag) { 365 delete(ofp); // will close and deref, etc. 366 367 if (rename(outfile, realfile) < 0) { 368 perror(outfile); 369 Err(MGET("error renaming %s to %s"), 370 outfile, realfile); 371 exit(1); 372 } 373 /* Set the permissions to match the original */ 374 if (chmod(realfile, (int)st.st_mode) < 0) { 375 Err(MGET("WARNING: could not reset mode of")); 376 perror(realfile); 377 } 378 } 379 } 380 381 if (!pflag) { 382 delete(ofp); // close output file 383 } 384 385 exit(0); // outta here! 386 } 387 388 389 // initialize audio hdr to default val's 390 void 391 init_header( 392 AudioHdr& hdr) 393 { 394 hdr.encoding = NONE; 395 hdr.sample_rate = 0; 396 hdr.samples_per_unit = 0; 397 hdr.bytes_per_unit = 0; 398 hdr.channels = 0; 399 } 400 401 extern "C" { void _doprnt(char *, ...); } 402 403 // report a fatal error and exit 404 void 405 Err(char *format, ...) 406 { 407 va_list ap; 408 409 va_start(ap, format); 410 fprintf(stderr, "%s: ", progname); 411 _doprnt(format, ap, stderr); 412 fflush(stderr); 413 va_end(ap); 414 } 415 416 void 417 usage() 418 { 419 fprintf(stderr, MGET( 420 "Convert between audio file formats and data encodings -- usage:\n" 421 "\t%s [-pF] [-f outfmt] [-o outfile] [[-i infmt] [file ...]] ...\n" 422 "where:\n" 423 "\t-p\tConvert files in place\n" 424 "\t-F\tForce interpretation of -i (ignore existing file hdr)\n" 425 "\t-f\tOutput format description\n" 426 "\t-o\tOutput file (default: stdout)\n" 427 "\t-i\tInput format description\n" 428 "\tfile\tList of files to convert (default: stdin)\n\n" 429 "Format Description:\n" 430 "\tkeyword=value[,keyword=value...]\n" 431 "where:\n" 432 "\tKeywords:\tValues:\n" 433 "\trate\t\tSample Rate in samples/second\n" 434 "\tchannels\tNumber of interleaved channels\n" 435 "\tencoding\tAudio encoding. One of:\n" 436 "\t\t\t ulaw, alaw, g721, g723,\n" 437 "\t\t\t linear8, linear16, linear32\n" 438 "\t\t\t pcm (same as linear16)\n" 439 "\t\t\t voice (ulaw,mono,rate=8k)\n" 440 "\t\t\t cd (linear16,stereo,rate=44.1k)\n" 441 "\t\t\t dat (linear16,stereo,rate=48k)\n" 442 "\tformat\t\tFile format. One of:\n" 443 "\t\t\t sun, raw (no format)\n" 444 "\toffset\t\tByte offset (raw input only)\n"), 445 progname); 446 exit(1); 447 } 448