xref: /illumos-gate/usr/src/cmd/audio/utilities/Audio.cc (revision 9164a50bf932130cbb5097a16f6986873ce0e6e5)
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 	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