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