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 <string.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <Audio.h>
31 #include <AudioDebug.h>
32 #include <AudioBuffer.h>
33
34 // class Audio methods
35
36
37 // Initialize monotonically increasing id counter
38 int
39 Audio::idctr = 0;
40
41 // Constructor
42 Audio::
Audio(const char * str)43 Audio(
44 const char *str): // name
45 id(++idctr), refcnt(0), readpos(0.), writepos(0.), errorfunc(0)
46 {
47 char *s;
48
49 s = (char *)((str == NULL) ? "" : str);
50 name = new char[strlen(s) + 1];
51 (void) strcpy(name, s);
52
53 #ifndef DEBUG
54 // errorfunc is always set if compiling DEBUG;
55 // otherwise, only if requested
56 if (GetDebug() > 0)
57 #endif
58 errorfunc = AudioStderrMsg;
59 PrintMsg(_MGET_("Audio object create"), InitMessage);
60 }
61
62 // Destructor
63 Audio::
~Audio()64 ~Audio()
65 {
66 // If there are outstanding references, there is a programming error
67 if (refcnt < 0) {
68 PrintMsg(_MGET_("Audio object multiple destroy"), InitFatal);
69 } else if (refcnt > 0) {
70 PrintMsg(_MGET_("Referenced Audio object destroyed"),
71 InitFatal);
72 } else {
73 refcnt = -1;
74 PrintMsg(_MGET_("Audio object destroy"), InitMessage);
75 }
76 delete name;
77 }
78
79 // Raise error code
80 AudioError Audio::
RaiseError(AudioError code,AudioSeverity sev,const char * msg) const81 RaiseError(
82 AudioError code, // error code
83 AudioSeverity sev, // error severity
84 const char *msg) const // additional message
85 {
86 if (code == AUDIO_SUCCESS)
87 return (code);
88
89 if (errorfunc != 0) {
90 // XXX - Userfunc return value ignored for now
91 (void) (*errorfunc)(this, code, sev, msg);
92 }
93 if ((sev == Fatal) || (sev == InitFatal))
94 abort();
95 return (code);
96 }
97
98 // Print out messages
99 void Audio::
PrintMsg(char * msg,AudioSeverity sev) const100 PrintMsg(
101 char *msg, // error message
102 AudioSeverity sev) const // error severity
103 {
104 if (errorfunc != 0) {
105 // XXX - Userfunc return value ignored for now
106 (void) (*errorfunc)(this, AUDIO_NOERROR, sev, msg);
107 }
108
109 if ((sev == Fatal) || (sev == InitFatal)) {
110 fprintf(stderr, _MGET_("** Fatal Error: %s\n"), msg);
111 abort();
112 }
113 }
114
115 // Increment reference count
116 void Audio::
Reference()117 Reference()
118 {
119 if (refcnt < 0) {
120 PrintMsg(_MGET_("Reference to destroyed Audio object"), Fatal);
121 } else {
122 refcnt++;
123 }
124 }
125
126 // Decrement reference count
127 void Audio::
Dereference()128 Dereference()
129 {
130 if (refcnt < 0) {
131 PrintMsg(_MGET_("Dereference of destroyed Audio object"),
132 Fatal);
133 } else if (refcnt == 0) {
134 PrintMsg(_MGET_("Audio object dereference underflow"), Fatal);
135 } else if (--refcnt == 0) { // If this was the last reference,
136 delete this; // blow the object away
137 }
138 }
139
140 // Reset the stored name
141 void Audio::
SetName(const char * str)142 SetName(
143 const char *str) // new name string
144 {
145 delete name;
146 name = new char[strlen(str) + 1];
147 (void) strcpy(name, str);
148 }
149
150
151 // Set the current read/write position pointer
152 Double Audio::
setpos(Double & pos,Double newpos,Whence w)153 setpos(
154 Double& pos, // field to update
155 Double newpos, // new position
156 Whence w) // Absolute || Relative || Relative_eof
157 {
158 if (w == Relative) // offset from current position
159 newpos += pos;
160 else if (w == Relative_eof) { // offset from end-of-file
161 if (!Undefined(GetLength()))
162 newpos += GetLength();
163 else
164 return (AUDIO_UNKNOWN_TIME);
165 }
166
167 // If seek before start of file, set to start of file
168 if (newpos < 0.)
169 newpos = 0.;
170 pos = newpos;
171 return (pos);
172 }
173
174 // Set a new read position
175 Double Audio::
SetReadPosition(Double pos,Whence w)176 SetReadPosition(
177 Double pos, // new position or offset
178 Whence w) // Absolute | Relative
179 {
180 return (setpos(readpos, pos, w));
181 }
182
183 // Set a new write position
184 Double Audio::
SetWritePosition(Double pos,Whence w)185 SetWritePosition(
186 Double pos, // new position or offset
187 Whence w) // Absolute | Relative
188 {
189 return (setpos(writepos, pos, w));
190 }
191
192 // Default read routine reads from the current position
193 AudioError Audio::
Read(void * buf,size_t & len)194 Read(
195 void* buf, // buffer address
196 size_t& len) // buffer length (updated)
197 {
198 // ReadData updates the position argument
199 return (ReadData(buf, len, readpos));
200 }
201
202 // Default write routine writes to the current position
203 AudioError Audio::
Write(void * buf,size_t & len)204 Write(
205 void* buf, // buffer address
206 size_t& len) // buffer length (updated)
207 {
208 // WriteData updates the position argument
209 return (WriteData(buf, len, writepos));
210 }
211
212 // Default append routine should be specialized, if the object is fixed-length
213 AudioError Audio::
AppendData(void * buf,size_t & len,Double & pos)214 AppendData(
215 void* buf, // buffer address
216 size_t& len, // buffer length (updated)
217 Double& pos) // write position (updated)
218 {
219 // The default action is just to write the data.
220 // Subclasses, like AudioBuffer, should specialize this method
221 // to extend the object, if necessary.
222 return (WriteData(buf, len, pos));
223 }
224
225 // Copy out to the specified audio object.
226 // Input and output positions default to the 'current' positions.
227 AudioError Audio::
Copy(Audio * to)228 Copy(
229 Audio* to) // audio object to copy to
230 {
231 Double frompos = AUDIO_UNKNOWN_TIME;
232 Double topos = AUDIO_UNKNOWN_TIME;
233 Double limit = AUDIO_UNKNOWN_TIME;
234
235 return (Copy(to, frompos, topos, limit));
236 }
237
238 // Default Copy out routine. Specify the destination audio object,
239 // and src/dest start offsets. limit is either the time to copy or
240 // AUDIO_UNKNOWN_TIME to copy to eof or error.
241 // frompos and topos are updated with the final positions.
242 // limit is updated with the amount of data actually copied.
243 AudioError Audio::
Copy(Audio * to,Double & frompos,Double & topos,Double & limit)244 Copy(
245 Audio* to, // audio object to copy to
246 Double& frompos,
247 Double& topos,
248 Double& limit)
249 {
250 Double len;
251 Double svpos;
252 AudioError err;
253
254 // If positions are Undefined, try to set them properly
255 if (Undefined(frompos))
256 frompos = ReadPosition();
257 if (Undefined(topos))
258 topos = to->WritePosition();
259
260 svpos = frompos;
261 do {
262 // Calculate remaining copy size
263 if (Undefined(limit)) {
264 len = limit;
265 } else {
266 len = limit - (frompos - svpos);
267 if (len < 0.)
268 len = 0.;
269 }
270 // Copy one segment
271 err = AsyncCopy(to, frompos, topos, len);
272 if (!err) {
273 switch (err.sys) {
274 default:
275 case 0:
276 break;
277
278 // XXX - What do we do with short writes?
279 // This routine is meant to block until all the
280 // data has been copied. So copies to a pipe or
281 // device should continue. However, copies to a
282 // buffer (or extent or list?) will never go any
283 // further.
284 // For now, punt and return immediately.
285 case AUDIO_COPY_SHORT_OUTPUT:
286 goto outofloop;
287
288 // If a zero-length transfer was requested, we're done
289 case AUDIO_COPY_ZERO_LIMIT:
290 goto outofloop;
291
292 // If the input would block, we're done
293 case AUDIO_COPY_SHORT_INPUT:
294 goto outofloop;
295 }
296 }
297 } while (err == AUDIO_SUCCESS);
298 outofloop:
299 // Calculate total transfer count
300 limit = frompos - svpos;
301
302 // Declare victory if anything was copied
303 if (limit > 0.)
304 return (AUDIO_SUCCESS);
305 return (err);
306 }
307
308 // Default Data Copy out routine. Like Copy(), but only does one segment.
309 // If either src or dest are set non-blocking, a partial transfer may occur.
310 // Returns AUDIO_SUCCESS on normal completion, regardless of how much data
311 // was actually transferred (err.sys: AUDIO_COPY_SHORT_INPUT if input would
312 // block; AUDIO_COPY_ZERO_LIMIT if a zero-length copy was requested).
313 // Returns AUDIO_SUCCESS (err.sys: AUDIO_COPY_SHORT_OUTPUT) if more data was
314 // read than could be copied out (eg, if there was a short write to a
315 // non-blocking output). Short writes result in the input pointer being
316 // backed up to the right place in the input stream.
317 // Returns AUDIO_EOF if input or output position beyond end-of-file.
318 //
319 // XXX - If the input cannot seek backwards, this routine will spin trying
320 // to finish writing all input data to the output. We need to keep
321 // partial data in a state structure.
322 AudioError Audio::
AsyncCopy(Audio * to,Double & frompos,Double & topos,Double & limit)323 AsyncCopy(
324 Audio* to, // audio object to copy to
325 Double& frompos,
326 Double& topos,
327 Double& limit)
328 {
329 caddr_t bptr;
330 size_t bufsiz;
331 size_t lim;
332 Double svfrom;
333 Double svto;
334 AudioBuffer* tob;
335 AudioHdr tohdr;
336 AudioError err;
337
338 // Validate basic arguments and state
339 tohdr = to->GetHeader();
340 err = tohdr.Validate();
341 if (err != AUDIO_SUCCESS)
342 return (err);
343 if (limit < 0.)
344 return (RaiseError(AUDIO_ERR_BADARG));
345 lim = (size_t)tohdr.Time_to_Bytes(limit);
346
347 // If the destination is an AudioBuffer, we can copy more directly
348 if (to->isBuffer()) {
349 tob = (AudioBuffer*) to;
350
351 // Get the buffer address at the starting offset
352 bptr = (caddr_t)tob->GetAddress(topos);
353 bufsiz = bptr - (caddr_t)tob->GetAddress();
354 if ((bptr == NULL) || (tob->GetByteCount() <= bufsiz)) {
355 limit = 0.;
356 err = AUDIO_EOF;
357 err.sys = AUDIO_COPY_OUTPUT_EOF;
358 return (err);
359 }
360 bufsiz = tob->GetByteCount() - bufsiz;
361
362 // Limit the data transfer by the limit argument
363 if (!Undefined(limit) && (lim < bufsiz))
364 bufsiz = lim;
365
366 // Read the data directly into buffer
367 (void) tohdr.Bytes_to_Bytes(bufsiz);
368 err = ReadData((void*) bptr, bufsiz, frompos);
369 limit = tohdr.Bytes_to_Time(bufsiz);
370 topos += limit;
371 tob->SetLength(topos);
372 return (err);
373 }
374
375 // XXX - temporary bogus implementation
376 // XXX - max transfer buf will be 2 seconds of data (1 sec for stereo)
377 if (tohdr.channels < 2) {
378 bufsiz = (size_t)tohdr.Time_to_Bytes(2.0);
379 } else {
380 bufsiz = (size_t)tohdr.Time_to_Bytes(1.0);
381 }
382 if (!Undefined(limit) && (lim < bufsiz))
383 bufsiz = lim;
384
385 limit = 0.;
386 if ((bptr = new char[bufsiz]) == NULL)
387 return (AUDIO_UNIXERROR);
388
389 svfrom = frompos;
390 err = ReadData((void*)bptr, bufsiz, frompos);
391 if (!err) {
392 svto = topos;
393 lim = bufsiz;
394 if (tohdr.Bytes_to_Bytes(bufsiz) != lim) {
395 AUDIO_DEBUG((1,
396 "Read returned a fraction of a sample frame?!\n"));
397 lim = bufsiz;
398 }
399 if (bufsiz > 0) {
400 err = to->WriteData(bptr, bufsiz, topos);
401 limit = topos - svto;
402
403 // If the write was short, back up the input pointer
404 if (bufsiz < lim) {
405 lim = bufsiz;
406 if (tohdr.Bytes_to_Bytes(bufsiz) != lim) {
407 AUDIO_DEBUG((1,
408 "Write returned a fraction of a sample frame?!\n"));
409 }
410 frompos = svfrom + limit;
411 if (!err)
412 err.sys = AUDIO_COPY_SHORT_OUTPUT;
413 }
414 }
415 }
416 delete[] bptr;
417 return (err);
418 }
419