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