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