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 1989-2002 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * Miscellaneous audio-related operations. 29 */ 30 31 #include <stdio.h> 32 #include <string.h> 33 #include <math.h> 34 35 #include <libaudio_impl.h> 36 #include <audio_errno.h> 37 #include <audio_hdr.h> 38 39 /* 40 * Convert a byte count into a floating-point time value, in seconds, 41 * using the encoding specified in the given audio header structure. 42 * Note that the byte count is not the same as the offset in an audio file, 43 * since the size of the audio file header is not taken into account. 44 */ 45 double 46 audio_bytes_to_secs(Audio_hdr *hp, unsigned int cnt) 47 { 48 return ((double)cnt / 49 ((double)(hp->channels * hp->bytes_per_unit * hp->sample_rate) / 50 (double)hp->samples_per_unit)); 51 } 52 53 /* 54 * Convert a floating-point time value, in seconds, to a byte count for 55 * the audio encoding in the given audio header. Note that the byte count 56 * is not the same as the offset in an audio file, since the size of the 57 * audio file header is not taken into account. 58 */ 59 unsigned 60 audio_secs_to_bytes(Audio_hdr *hp, double sec) 61 { 62 unsigned offset; 63 64 offset = (unsigned)(0.5 + (sec * 65 ((double)(hp->channels * hp->bytes_per_unit * hp->sample_rate) / 66 (double)hp->samples_per_unit))); 67 68 /* Round down to the start of the nearest sample frame */ 69 offset -= (offset % (hp->bytes_per_unit * hp->channels)); 70 return (offset); 71 } 72 73 /* 74 * Convert an ASCII time value (hh:mm:ss.dd) into floating-point seconds. 75 * Returns value if successfully converted. Otherwise, returns HUGE_VAL. 76 * 77 * XXX - currently allows the ridiculous construct: 5.3E3:-47.3E-1:17.3 78 */ 79 double 80 audio_str_to_secs(char *str) 81 { 82 double val; 83 char *str2; 84 85 val = strtod(str, &str2); /* get first numeric field */ 86 if (str2 == str) 87 return (HUGE_VAL); 88 89 if (*str2 == ':') { /* that was hours (or minutes) */ 90 val *= 60.; 91 str = str2 + 1; 92 val += strtod(str, &str2); /* another field is required */ 93 if (str2 == str) 94 return (HUGE_VAL); 95 } 96 97 if (*str2 == ':') { /* converted hours and minutes */ 98 val *= 60.; 99 str = str2 + 1; 100 val += strtod(str, &str2); /* another field is required */ 101 if (str2 == str) 102 return (HUGE_VAL); 103 } 104 105 if (*str2 != '\0') 106 return (HUGE_VAL); 107 return (val); 108 } 109 110 /* 111 * Convert floating-point seconds into an ASCII time value (hh:mm:ss.dd). 112 * 113 * HUGE_VAL is converted to 0:00. 'Precision' specifies the maximum 114 * number of digits after the decimal point (-1 allows the max). 115 * 116 * Store the resulting string in the specified buffer (must be at least 117 * AUDIO_MAX_TIMEVAL bytes long). The string address is returned. 118 */ 119 char * 120 audio_secs_to_str(double sec, char *str, int precision) 121 { 122 char *p; 123 unsigned ovflow; 124 int hours; 125 double x; 126 char buf[64]; 127 128 if (sec == HUGE_VAL) { 129 (void) strcpy(str, "0:00"); 130 return (str); 131 } 132 133 /* Limit precision arg to reasonable value */ 134 if ((precision > 10) || (precision < 0)) 135 precision = 10; 136 137 /* If negative, write a minus sign and get on with it. */ 138 p = str; 139 if (sec < 0.) { 140 sec = -sec; 141 142 /* Round off within precision to avoid -.01 printing as -0:00 */ 143 (void) sprintf(buf, "%.*f", precision, sec); 144 (void) sscanf(buf, "%lf", &sec); 145 if (sec > 0.) 146 *p++ = '-'; 147 } 148 149 /* Round off within precision to avoid 1:59.999 printing as 1:60.00 */ 150 x = fmod(sec, 60.); 151 sec -= x; 152 (void) sprintf(buf, "%.*f", precision, x); 153 (void) sscanf(buf, "%lf", &x); 154 sec += x; 155 156 if (sec >= 60.) { 157 /* Extract minutes */ 158 ovflow = ((unsigned)sec) / 60; 159 sec -= (double)(ovflow * 60); 160 hours = (ovflow >= 60); 161 if (hours) { 162 /* convert hours */ 163 (void) sprintf(p, "%d:", ovflow / 60); 164 p = &p[strlen(p)]; 165 ovflow %= 60; 166 } 167 /* convert minutes (use two digits if hours printed) */ 168 (void) sprintf(p, "%0*d:", (hours ? 2 : 1), ovflow); 169 p = &p[strlen(p)]; 170 } else { 171 *p++ = '0'; 172 *p++ = ':'; 173 } 174 175 if (sec < 10.) 176 *p++ = '0'; 177 (void) sprintf(p, "%.*f", precision, sec); 178 return (str); 179 } 180 181 /* 182 * Compare the encoding fields of two audio headers. 183 * Return 0 if they are the same, 1 if they are the same except for 184 * sample rate, else -1. 185 */ 186 int 187 audio_cmp_hdr(Audio_hdr *h1, Audio_hdr *h2) 188 { 189 if ((h1->encoding != h2->encoding) || 190 (h1->bytes_per_unit != h2->bytes_per_unit) || 191 (h1->channels != h2->channels) || 192 (h1->samples_per_unit != h2->samples_per_unit)) 193 return (-1); 194 195 if (h1->sample_rate != h2->sample_rate) 196 return (1); 197 198 return (0); 199 } 200 201 /* 202 * Interpret the encoding information in the specified header 203 * and return an appropriate string in the supplied buffer. 204 * The buffer should contain at least AUDIO_MAX_ENCODE_INFO bytes. 205 * The returned string is something like: 206 * "stereo 16-bit linear PCM @ 44.1kHz" 207 * 208 * Returns AUDIO_ERR_BADHDR if the header cannot be interpreted. 209 */ 210 int 211 audio_enc_to_str(Audio_hdr *hdrp, char *str) 212 { 213 char *chan; 214 char *prec; 215 char *enc; 216 char cbuf[AUDIO_MAX_ENCODE_INFO]; 217 char pbuf[AUDIO_MAX_ENCODE_INFO]; 218 char sbuf[AUDIO_MAX_ENCODE_INFO]; 219 int err; 220 221 err = AUDIO_SUCCESS; 222 223 switch (hdrp->channels) { 224 case 0: 225 chan = "(zero channels?)"; 226 err = AUDIO_ERR_BADHDR; 227 break; 228 case 1: 229 chan = "mono"; break; 230 case 2: 231 chan = "stereo"; break; 232 case 4: 233 chan = "quad"; break; 234 default: 235 chan = pbuf; 236 (void) sprintf(cbuf, "%u-channel", hdrp->channels); break; 237 } 238 239 switch (hdrp->encoding) { 240 case AUDIO_ENCODING_ULAW: 241 enc = "u-law"; 242 goto pcm; 243 case AUDIO_ENCODING_ALAW: 244 enc = "A-law"; 245 goto pcm; 246 case AUDIO_ENCODING_LINEAR: 247 enc = "linear PCM"; 248 goto pcm; 249 case AUDIO_ENCODING_FLOAT: 250 enc = "floating-point"; 251 pcm: 252 if (hdrp->samples_per_unit != 1) 253 goto unknown; 254 prec = pbuf; 255 (void) sprintf(pbuf, "%u-bit", hdrp->bytes_per_unit * 8); 256 break; 257 258 default: 259 unknown: 260 err = AUDIO_ERR_ENCODING; 261 enc = "(unknown encoding?)"; 262 if (hdrp->samples_per_unit != 0) { 263 prec = pbuf; 264 (void) sprintf(pbuf, "%f-bit", 265 (double)(hdrp->bytes_per_unit * 8) / 266 (double)hdrp->samples_per_unit); 267 } else { 268 prec = "(unknown precision?)"; 269 err = AUDIO_ERR_BADHDR; 270 } 271 } 272 273 (void) sprintf(sbuf, "%.3fkHz", ((double)hdrp->sample_rate / 1000.)); 274 (void) sprintf(str, "%s %s %s @ %s", chan, prec, enc, sbuf); 275 return (err); 276 } 277