xref: /illumos-gate/usr/src/cmd/audio/utilities/Audio.cc (revision 533affcbc7fc4d0c8132976ea454aaa715fe2307)
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::
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::
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::
81 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::
100 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::
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::
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::
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::
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::
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::
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::
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::
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::
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::
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::
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::
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 	if (err = tohdr.Validate())
341 		return (err);
342 	if (limit < 0.)
343 		return (RaiseError(AUDIO_ERR_BADARG));
344 	lim = (size_t)tohdr.Time_to_Bytes(limit);
345 
346 	// If the destination is an AudioBuffer, we can copy more directly
347 	if (to->isBuffer()) {
348 		tob = (AudioBuffer*) to;
349 
350 		// Get the buffer address at the starting offset
351 		bptr = (caddr_t)tob->GetAddress(topos);
352 		bufsiz = bptr - (caddr_t)tob->GetAddress();
353 		if ((bptr == NULL) || (tob->GetByteCount() <= bufsiz)) {
354 			limit = 0.;
355 			err = AUDIO_EOF;
356 			err.sys = AUDIO_COPY_OUTPUT_EOF;
357 			return (err);
358 		}
359 		bufsiz = tob->GetByteCount() - bufsiz;
360 
361 		// Limit the data transfer by the limit argument
362 		if (!Undefined(limit) && (lim < bufsiz))
363 			bufsiz = lim;
364 
365 		// Read the data directly into buffer
366 		(void) tohdr.Bytes_to_Bytes(bufsiz);
367 		err = ReadData((void*) bptr, bufsiz, frompos);
368 		limit = tohdr.Bytes_to_Time(bufsiz);
369 		topos += limit;
370 		tob->SetLength(topos);
371 		return (err);
372 	}
373 
374 	// XXX - temporary bogus implementation
375 	// XXX - max transfer buf will be 2 seconds of data (1 sec for stereo)
376 	if (tohdr.channels < 2) {
377 		bufsiz = (size_t)tohdr.Time_to_Bytes(2.0);
378 	} else {
379 		bufsiz = (size_t)tohdr.Time_to_Bytes(1.0);
380 	}
381 	if (!Undefined(limit) && (lim < bufsiz))
382 		bufsiz = lim;
383 
384 	limit = 0.;
385 	if ((bptr = new char[bufsiz]) == NULL)
386 		return (AUDIO_UNIXERROR);
387 
388 	svfrom = frompos;
389 	err = ReadData((void*)bptr, bufsiz, frompos);
390 	if (!err) {
391 		svto = topos;
392 		lim = bufsiz;
393 		if (tohdr.Bytes_to_Bytes(bufsiz) != lim) {
394 			AUDIO_DEBUG((1,
395 			    "Read returned a fraction of a sample frame?!\n"));
396 			lim = bufsiz;
397 		}
398 		if (bufsiz > 0) {
399 			err = to->WriteData(bptr, bufsiz, topos);
400 			limit = topos - svto;
401 
402 			// If the write was short, back up the input pointer
403 			if (bufsiz < lim) {
404 				lim = bufsiz;
405 				if (tohdr.Bytes_to_Bytes(bufsiz) != lim) {
406 					AUDIO_DEBUG((1,
407 		    "Write returned a fraction of a sample frame?!\n"));
408 				}
409 				frompos = svfrom + limit;
410 				if (!err)
411 					err.sys = AUDIO_COPY_SHORT_OUTPUT;
412 			}
413 		}
414 	}
415 	delete[] bptr;
416 	return (err);
417 }
418