1*7c478bd9Sstevel@tonic-gate /* 2*7c478bd9Sstevel@tonic-gate * CDDL HEADER START 3*7c478bd9Sstevel@tonic-gate * 4*7c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*7c478bd9Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*7c478bd9Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*7c478bd9Sstevel@tonic-gate * with the License. 8*7c478bd9Sstevel@tonic-gate * 9*7c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*7c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*7c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 12*7c478bd9Sstevel@tonic-gate * and limitations under the License. 13*7c478bd9Sstevel@tonic-gate * 14*7c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*7c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*7c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*7c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*7c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*7c478bd9Sstevel@tonic-gate * 20*7c478bd9Sstevel@tonic-gate * CDDL HEADER END 21*7c478bd9Sstevel@tonic-gate */ 22*7c478bd9Sstevel@tonic-gate /* 23*7c478bd9Sstevel@tonic-gate * Copyright (c) 1993-2001 by Sun Microsystems, Inc. 24*7c478bd9Sstevel@tonic-gate * All rights reserved. 25*7c478bd9Sstevel@tonic-gate */ 26*7c478bd9Sstevel@tonic-gate 27*7c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*7c478bd9Sstevel@tonic-gate 29*7c478bd9Sstevel@tonic-gate #include <stdlib.h> 30*7c478bd9Sstevel@tonic-gate #include <stdio.h> 31*7c478bd9Sstevel@tonic-gate #include <string.h> 32*7c478bd9Sstevel@tonic-gate #include <ctype.h> 33*7c478bd9Sstevel@tonic-gate #include <math.h> 34*7c478bd9Sstevel@tonic-gate 35*7c478bd9Sstevel@tonic-gate #include <Audio.h> 36*7c478bd9Sstevel@tonic-gate #include <AudioHdr.h> 37*7c478bd9Sstevel@tonic-gate 38*7c478bd9Sstevel@tonic-gate #include <parse.h> 39*7c478bd9Sstevel@tonic-gate #include <convert.h> 40*7c478bd9Sstevel@tonic-gate 41*7c478bd9Sstevel@tonic-gate static struct keyword_table Keywords[] = { 42*7c478bd9Sstevel@tonic-gate (char *)"encoding", K_ENCODING, 43*7c478bd9Sstevel@tonic-gate (char *)"rate", K_RATE, 44*7c478bd9Sstevel@tonic-gate (char *)"channels", K_CHANNELS, 45*7c478bd9Sstevel@tonic-gate (char *)"offset", K_OFFSET, 46*7c478bd9Sstevel@tonic-gate (char *)"format", K_FORMAT, 47*7c478bd9Sstevel@tonic-gate NULL, K_NULL, 48*7c478bd9Sstevel@tonic-gate }; 49*7c478bd9Sstevel@tonic-gate 50*7c478bd9Sstevel@tonic-gate // Lookup the string in a keyword table. return the token associated with it. 51*7c478bd9Sstevel@tonic-gate keyword_type 52*7c478bd9Sstevel@tonic-gate do_lookup( 53*7c478bd9Sstevel@tonic-gate char *s, 54*7c478bd9Sstevel@tonic-gate struct keyword_table *kp) 55*7c478bd9Sstevel@tonic-gate { 56*7c478bd9Sstevel@tonic-gate struct keyword_table *tkp = NULL; 57*7c478bd9Sstevel@tonic-gate 58*7c478bd9Sstevel@tonic-gate for (; kp && kp->name; kp++) { 59*7c478bd9Sstevel@tonic-gate if (strncmp(s, kp->name, strlen(s)) == 0) { 60*7c478bd9Sstevel@tonic-gate // check if exact match 61*7c478bd9Sstevel@tonic-gate if (strlen(s) == strlen(kp->name)) { 62*7c478bd9Sstevel@tonic-gate return (kp->type); 63*7c478bd9Sstevel@tonic-gate } else { 64*7c478bd9Sstevel@tonic-gate // already have another partial match, so 65*7c478bd9Sstevel@tonic-gate // it's ambiguous 66*7c478bd9Sstevel@tonic-gate if (tkp) { 67*7c478bd9Sstevel@tonic-gate return (K_AMBIG); 68*7c478bd9Sstevel@tonic-gate } else { 69*7c478bd9Sstevel@tonic-gate tkp = kp; 70*7c478bd9Sstevel@tonic-gate } 71*7c478bd9Sstevel@tonic-gate } 72*7c478bd9Sstevel@tonic-gate } 73*7c478bd9Sstevel@tonic-gate } 74*7c478bd9Sstevel@tonic-gate 75*7c478bd9Sstevel@tonic-gate // at end of list. if there was a partial match, return it, if 76*7c478bd9Sstevel@tonic-gate // not, there's no match.... 77*7c478bd9Sstevel@tonic-gate if (tkp) { 78*7c478bd9Sstevel@tonic-gate return (tkp->type); 79*7c478bd9Sstevel@tonic-gate } else { 80*7c478bd9Sstevel@tonic-gate return (K_NULL); 81*7c478bd9Sstevel@tonic-gate } 82*7c478bd9Sstevel@tonic-gate } 83*7c478bd9Sstevel@tonic-gate 84*7c478bd9Sstevel@tonic-gate // Parse a file format specification 85*7c478bd9Sstevel@tonic-gate int 86*7c478bd9Sstevel@tonic-gate fileformat_parse( 87*7c478bd9Sstevel@tonic-gate char *val, 88*7c478bd9Sstevel@tonic-gate format_type& format) 89*7c478bd9Sstevel@tonic-gate { 90*7c478bd9Sstevel@tonic-gate // XXX - other formats later ... 91*7c478bd9Sstevel@tonic-gate if (strcasecmp(val, "sun") == 0) { 92*7c478bd9Sstevel@tonic-gate format = F_SUN; 93*7c478bd9Sstevel@tonic-gate } else if (strcasecmp(val, "raw") == 0) { 94*7c478bd9Sstevel@tonic-gate format = F_RAW; 95*7c478bd9Sstevel@tonic-gate } else if (strcasecmp(val, "aiff") == 0) { 96*7c478bd9Sstevel@tonic-gate Err(MGET("AIFF not yet supported\n")); 97*7c478bd9Sstevel@tonic-gate return (-1); 98*7c478bd9Sstevel@tonic-gate } else { 99*7c478bd9Sstevel@tonic-gate return (-1); 100*7c478bd9Sstevel@tonic-gate } 101*7c478bd9Sstevel@tonic-gate return (0); 102*7c478bd9Sstevel@tonic-gate } 103*7c478bd9Sstevel@tonic-gate 104*7c478bd9Sstevel@tonic-gate // Parse an audio format keyword 105*7c478bd9Sstevel@tonic-gate int 106*7c478bd9Sstevel@tonic-gate audioformat_parse( 107*7c478bd9Sstevel@tonic-gate char *val, 108*7c478bd9Sstevel@tonic-gate AudioHdr& hdr) 109*7c478bd9Sstevel@tonic-gate { 110*7c478bd9Sstevel@tonic-gate // check if it's "cd" or "dat" or "voice". 111*7c478bd9Sstevel@tonic-gate // these set the precision and encoding, etc. 112*7c478bd9Sstevel@tonic-gate if (strcasecmp(val, "dat") == 0) { 113*7c478bd9Sstevel@tonic-gate hdr.sample_rate = 48000; 114*7c478bd9Sstevel@tonic-gate hdr.channels = 2; 115*7c478bd9Sstevel@tonic-gate hdr.encoding = LINEAR; 116*7c478bd9Sstevel@tonic-gate hdr.samples_per_unit = 1; 117*7c478bd9Sstevel@tonic-gate hdr.bytes_per_unit = 2; 118*7c478bd9Sstevel@tonic-gate } else if (strcasecmp(val, "cd") == 0) { 119*7c478bd9Sstevel@tonic-gate hdr.sample_rate = 44100; 120*7c478bd9Sstevel@tonic-gate hdr.channels = 2; 121*7c478bd9Sstevel@tonic-gate hdr.encoding = LINEAR; 122*7c478bd9Sstevel@tonic-gate hdr.samples_per_unit = 1; 123*7c478bd9Sstevel@tonic-gate hdr.bytes_per_unit = 2; 124*7c478bd9Sstevel@tonic-gate } else if (strcasecmp(val, "voice") == 0) { 125*7c478bd9Sstevel@tonic-gate hdr.sample_rate = 8000; 126*7c478bd9Sstevel@tonic-gate hdr.channels = 1; 127*7c478bd9Sstevel@tonic-gate hdr.encoding = ULAW; 128*7c478bd9Sstevel@tonic-gate hdr.samples_per_unit = 1; 129*7c478bd9Sstevel@tonic-gate hdr.bytes_per_unit = 1; 130*7c478bd9Sstevel@tonic-gate } else { 131*7c478bd9Sstevel@tonic-gate return (-1); 132*7c478bd9Sstevel@tonic-gate } 133*7c478bd9Sstevel@tonic-gate return (0); 134*7c478bd9Sstevel@tonic-gate } 135*7c478bd9Sstevel@tonic-gate 136*7c478bd9Sstevel@tonic-gate // Parse a format spec and return an audio header that describes it. 137*7c478bd9Sstevel@tonic-gate // Format is in the form of: [keyword=]value[,[keyword=]value ...]. 138*7c478bd9Sstevel@tonic-gate int 139*7c478bd9Sstevel@tonic-gate parse_format( 140*7c478bd9Sstevel@tonic-gate char *s, 141*7c478bd9Sstevel@tonic-gate AudioHdr& hdr, 142*7c478bd9Sstevel@tonic-gate format_type& format, 143*7c478bd9Sstevel@tonic-gate off_t& offset) 144*7c478bd9Sstevel@tonic-gate { 145*7c478bd9Sstevel@tonic-gate char *cp; 146*7c478bd9Sstevel@tonic-gate char *buf; 147*7c478bd9Sstevel@tonic-gate char *key; 148*7c478bd9Sstevel@tonic-gate char *val; 149*7c478bd9Sstevel@tonic-gate char *cp2; 150*7c478bd9Sstevel@tonic-gate 151*7c478bd9Sstevel@tonic-gate offset = 0; 152*7c478bd9Sstevel@tonic-gate format = F_SUN; 153*7c478bd9Sstevel@tonic-gate 154*7c478bd9Sstevel@tonic-gate // if no string provided, just return ... 155*7c478bd9Sstevel@tonic-gate if (!(s && *s)) 156*7c478bd9Sstevel@tonic-gate return (0); 157*7c478bd9Sstevel@tonic-gate 158*7c478bd9Sstevel@tonic-gate // First off, try to parse it as a full format string 159*7c478bd9Sstevel@tonic-gate // (it would have to have been quoted). 160*7c478bd9Sstevel@tonic-gate // If this works, we're done. 161*7c478bd9Sstevel@tonic-gate if (hdr.FormatParse(s) == AUDIO_SUCCESS) { 162*7c478bd9Sstevel@tonic-gate return (0); 163*7c478bd9Sstevel@tonic-gate } 164*7c478bd9Sstevel@tonic-gate 165*7c478bd9Sstevel@tonic-gate buf = strdup(s); // save a copy of the string 166*7c478bd9Sstevel@tonic-gate 167*7c478bd9Sstevel@tonic-gate // XXX - bug alert: if someone has info="xxx,yyy", strtok will 168*7c478bd9Sstevel@tonic-gate // break unless we snarf properly snarf the info. punt for now, 169*7c478bd9Sstevel@tonic-gate // fix later (since no info supported yet).... 170*7c478bd9Sstevel@tonic-gate 171*7c478bd9Sstevel@tonic-gate for (cp = strtok(buf, ","); cp; cp = strtok(NULL, ",")) { 172*7c478bd9Sstevel@tonic-gate // Check if there's a '=' 173*7c478bd9Sstevel@tonic-gate // If so, left side is keyword, right side is value. 174*7c478bd9Sstevel@tonic-gate // If not, entire string is value. 175*7c478bd9Sstevel@tonic-gate if (cp2 = strchr(cp, '=')) { 176*7c478bd9Sstevel@tonic-gate *cp2++ = NULL; 177*7c478bd9Sstevel@tonic-gate key = cp; 178*7c478bd9Sstevel@tonic-gate val = cp2; 179*7c478bd9Sstevel@tonic-gate 180*7c478bd9Sstevel@tonic-gate // Look for the keyword 181*7c478bd9Sstevel@tonic-gate switch (do_lookup(key, Keywords)) { 182*7c478bd9Sstevel@tonic-gate case K_ENCODING: 183*7c478bd9Sstevel@tonic-gate if (hdr.EncodingParse(val)) { 184*7c478bd9Sstevel@tonic-gate Err(MGET( 185*7c478bd9Sstevel@tonic-gate "invalid encoding option: %s\n"), 186*7c478bd9Sstevel@tonic-gate val); 187*7c478bd9Sstevel@tonic-gate goto parse_error; 188*7c478bd9Sstevel@tonic-gate } 189*7c478bd9Sstevel@tonic-gate break; 190*7c478bd9Sstevel@tonic-gate case K_RATE: 191*7c478bd9Sstevel@tonic-gate if (hdr.RateParse(val)) { 192*7c478bd9Sstevel@tonic-gate Err(MGET("invalid sample rate: %s\n"), 193*7c478bd9Sstevel@tonic-gate val); 194*7c478bd9Sstevel@tonic-gate goto parse_error; 195*7c478bd9Sstevel@tonic-gate } 196*7c478bd9Sstevel@tonic-gate break; 197*7c478bd9Sstevel@tonic-gate case K_CHANNELS: 198*7c478bd9Sstevel@tonic-gate if (hdr.ChannelParse(val)) { 199*7c478bd9Sstevel@tonic-gate Err(MGET( 200*7c478bd9Sstevel@tonic-gate "invalid channels option: %s\n"), 201*7c478bd9Sstevel@tonic-gate val); 202*7c478bd9Sstevel@tonic-gate goto parse_error; 203*7c478bd9Sstevel@tonic-gate } 204*7c478bd9Sstevel@tonic-gate break; 205*7c478bd9Sstevel@tonic-gate case K_FORMAT: 206*7c478bd9Sstevel@tonic-gate if (fileformat_parse(val, format) < 0) { 207*7c478bd9Sstevel@tonic-gate Err(MGET("unknown format: %s\n"), val); 208*7c478bd9Sstevel@tonic-gate goto parse_error; 209*7c478bd9Sstevel@tonic-gate } 210*7c478bd9Sstevel@tonic-gate break; 211*7c478bd9Sstevel@tonic-gate case K_OFFSET: 212*7c478bd9Sstevel@tonic-gate offset = (off_t)atoi(val); 213*7c478bd9Sstevel@tonic-gate break; 214*7c478bd9Sstevel@tonic-gate case K_AMBIG: 215*7c478bd9Sstevel@tonic-gate Err(MGET("ambiguous keyword: %s\n"), key); 216*7c478bd9Sstevel@tonic-gate goto parse_error; 217*7c478bd9Sstevel@tonic-gate case K_NULL: 218*7c478bd9Sstevel@tonic-gate Err(MGET("null keyword: =%s\n"), val); 219*7c478bd9Sstevel@tonic-gate goto parse_error; 220*7c478bd9Sstevel@tonic-gate default: 221*7c478bd9Sstevel@tonic-gate Err(MGET("invalid keyword: %s\n"), key); 222*7c478bd9Sstevel@tonic-gate goto parse_error; 223*7c478bd9Sstevel@tonic-gate } 224*7c478bd9Sstevel@tonic-gate } else { 225*7c478bd9Sstevel@tonic-gate // No keyword, so try to intuit the value 226*7c478bd9Sstevel@tonic-gate // First try encoding, audio, and file format. 227*7c478bd9Sstevel@tonic-gate // If they fail, try sample rate and channels. 228*7c478bd9Sstevel@tonic-gate val = cp; 229*7c478bd9Sstevel@tonic-gate if (hdr.EncodingParse(val) && 230*7c478bd9Sstevel@tonic-gate (audioformat_parse(val, hdr) < 0) && 231*7c478bd9Sstevel@tonic-gate (fileformat_parse(val, format) < 0)) { 232*7c478bd9Sstevel@tonic-gate // If this looks like sample rate, make sure 233*7c478bd9Sstevel@tonic-gate // it is not ambiguous with channels 234*7c478bd9Sstevel@tonic-gate if (!hdr.RateParse(val)) { 235*7c478bd9Sstevel@tonic-gate if (hdr.sample_rate < 1000) { 236*7c478bd9Sstevel@tonic-gate int x; 237*7c478bd9Sstevel@tonic-gate char y[10]; 238*7c478bd9Sstevel@tonic-gate 239*7c478bd9Sstevel@tonic-gate if (sscanf(val, " %lf %9s", 240*7c478bd9Sstevel@tonic-gate &x, y) != 1) { 241*7c478bd9Sstevel@tonic-gate Err( 242*7c478bd9Sstevel@tonic-gate MGET("ambiguous numeric option: %s\n"), 243*7c478bd9Sstevel@tonic-gate val); 244*7c478bd9Sstevel@tonic-gate goto parse_error; 245*7c478bd9Sstevel@tonic-gate } 246*7c478bd9Sstevel@tonic-gate } 247*7c478bd9Sstevel@tonic-gate } else if (hdr.ChannelParse(val)) { 248*7c478bd9Sstevel@tonic-gate Err(MGET("invalid option value: %s\n"), 249*7c478bd9Sstevel@tonic-gate val); 250*7c478bd9Sstevel@tonic-gate goto parse_error; 251*7c478bd9Sstevel@tonic-gate } 252*7c478bd9Sstevel@tonic-gate } 253*7c478bd9Sstevel@tonic-gate } 254*7c478bd9Sstevel@tonic-gate } 255*7c478bd9Sstevel@tonic-gate free(buf); 256*7c478bd9Sstevel@tonic-gate return (0); 257*7c478bd9Sstevel@tonic-gate 258*7c478bd9Sstevel@tonic-gate parse_error: 259*7c478bd9Sstevel@tonic-gate free(buf); 260*7c478bd9Sstevel@tonic-gate return (-1); 261*7c478bd9Sstevel@tonic-gate } 262