xref: /illumos-gate/usr/src/cmd/audio/utilities/AudioBuffer.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) 1992-2001 by Sun Microsystems, Inc.
24  * All rights reserved.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <stdlib.h>
30 #include <memory.h>
31 #include "../include/AudioDebug.h"
32 #include "../include/AudioBuffer.h"
33 #include "../include/zmalloc.h"
34 
35 // class AudioBuffer methods
36 
37 // Constructor with optional hdr, size, and name arguments
38 AudioBuffer::
39 AudioBuffer(
40 	double		len,			// buffer length, in seconds
41 	const char	*local_name):			// name
42 	AudioStream(local_name), buflen(len), bufaddr(0), zflag(0), bufsize(0)
43 {
44 }
45 
46 // Destructor
47 AudioBuffer::
48 ~AudioBuffer()
49 {
50 	(void) SetSize(0.);		// deallocate the buffer
51 }
52 
53 // XXX - the following functions are good candidates for inlining
54 
55 // Return TRUE if the stream is 'open'
56 Boolean AudioBuffer::
57 opened() const
58 {
59 	// A buffer is open if it is allocated and has a valid header
60 	return (hdrset() && (GetAddress() != 0));
61 }
62 
63 #define	MIN_ZBUFFER	(8192 * 10)	// only for large buffers
64 
65 // Allocate buffer.  Size and header must be set.
66 AudioError AudioBuffer::
67 alloc()
68 {
69 	long		size;
70 	size_t		cnt;
71 	unsigned int	ncpy;
72 	void*		tmpbuf;
73 
74 	// this is going to be the size we're setting the buffer
75 	// to (buflen field). it's set by calling SetSize().
76 	size = GetHeader().Time_to_Bytes(GetSize());
77 
78 	// this is actual current size, in bytes, of the allocated
79 	// buffer (the bufsize field).
80 	cnt = GetByteCount();
81 
82 	AUDIO_DEBUG((5, "%d: AudioBuffer::alloc - change from %d to %d bytes\n",
83 	    getid(), cnt, size));
84 
85 	bufsize = 0;
86 
87 	if (size == 0) {
88 		// Zero size deletes the buffer
89 		if (bufaddr != 0) {
90 			if (zflag != 0) {
91 				AUDIO_DEBUG((5,
92 			    "%d: AudioBuffer::alloc - zfree mmapped buffer\n",
93 				    getid()));
94 				(void) zfree((char *)bufaddr);
95 			} else {
96 				AUDIO_DEBUG((5,
97 			    "%d: AudioBuffer::alloc - free malloc'd buffer\n",
98 				    getid()));
99 				(void) free((char *)bufaddr);
100 			}
101 			zflag = 0;
102 		}
103 		bufaddr = 0;
104 
105 	} else if (size < 0) {
106 		// Ridiculous size
107 		AUDIO_DEBUG((5, "%d: AudioBuffer::alloc - bad size\n",
108 		    getid()));
109 		return (RaiseError(AUDIO_ERR_BADARG));
110 
111 	} else if (bufaddr == 0) {
112 		// Allocate a new buffer
113 		if (size > MIN_ZBUFFER) {
114 			AUDIO_DEBUG((5,
115 			    "%d: AudioBuffer::alloc - zmalloc new buffer\n",
116 			    getid()));
117 			bufaddr = (void*) zmalloc((unsigned int)size);
118 			zflag = 1;
119 		} else {
120 			AUDIO_DEBUG((5,
121 			    "%d: AudioBuffer::alloc - malloc new buffer\n",
122 			    getid()));
123 			bufaddr = (void*) malloc((unsigned int)size);
124 			zflag = 0;
125 		}
126 		if (bufaddr == 0) {
127 			AUDIO_DEBUG((5,
128 			    "%d: AudioBuffer::alloc - buffer alloc failed\n",
129 			    getid()));
130 			return (RaiseError(AUDIO_UNIXERROR));
131 		}
132 	} else {
133 		// A buffer was already allocated.
134 		// Change its size, preserving as much data as possible.
135 		if ((cnt <= MIN_ZBUFFER) && (size <= MIN_ZBUFFER) &&
136 		    (zflag == 0)) {
137 			AUDIO_DEBUG((5,
138 			    "%d: AudioBuffer::alloc - realloc to change size\n",
139 			    getid()));
140 			bufaddr = (void*)
141 			    realloc((char *)bufaddr, (unsigned int)size);
142 		} else {
143 			AUDIO_DEBUG((5,
144 			    "%d: AudioBuffer::alloc - zmalloc new buffer\n",
145 			    getid()));
146 			tmpbuf = bufaddr;
147 			bufaddr = (void*) zmalloc((unsigned int)size);
148 
149 			// copy over as much of the old data as will fit
150 			if (bufaddr != 0) {
151 				ncpy = (cnt < size) ? (unsigned int)cnt :
152 					(unsigned int)size;
153 
154 				AUDIO_DEBUG((5,
155 			    "%d: AudioBuffer::alloc - trasnfer %d bytes\n",
156 				    getid(), ncpy));
157 				(void) memcpy(bufaddr, tmpbuf, ncpy);
158 			}
159 			if ((cnt > MIN_ZBUFFER) && (zflag != 0)) {
160 				AUDIO_DEBUG((5,
161 			    "%d: AudioBuffer::alloc - zfree old buffer\n",
162 				    getid()));
163 				(void) zfree((char *)tmpbuf);
164 			} else {
165 				AUDIO_DEBUG((5,
166 			    "%d: AudioBuffer::alloc - free old buffer\n",
167 				    getid()));
168 				(void) free((char *)tmpbuf);
169 			}
170 			zflag = 1;
171 		}
172 		if (bufaddr == 0) {
173 			return (RaiseError(AUDIO_UNIXERROR));
174 		}
175 	}
176 	bufsize = (size_t)size;
177 	return (AUDIO_SUCCESS);
178 }
179 
180 
181 // Return the buffer address
182 void* AudioBuffer::
183 GetAddress() const
184 {
185 	return (GetAddress(0.));
186 }
187 
188 // Return the buffer address at a given time offset
189 // Returns NULL if no buffer, or the position is not within the buffer.
190 void* AudioBuffer::
191 GetAddress(
192 	Double		pos) const
193 {
194 	char		*addr;
195 	AudioHdr	hdr_local;
196 	AudioHdr(AudioBuffer::*hfunc)()const;
197 
198 	addr = (char *)bufaddr;
199 	if ((addr == 0) || (pos < 0.) || (pos >= buflen))
200 		return (NULL);
201 
202 	// If no offset, it's ok if the header hasn't been set yet
203 	if (pos == 0.)
204 		return ((void*) addr);
205 
206 	// Get the header and make sure it's valid
207 	// This convoluted hfunc works around non-const function problems
208 	hfunc = (AudioHdr(AudioBuffer::*)() const)&AudioBuffer::GetHeader;
209 	hdr_local = (this->*hfunc)();
210 	if (hdr_local.Validate())
211 		return (NULL);
212 	addr += hdr_local.Time_to_Bytes(pos);
213 
214 	// One more validation, to be paranoid before handing out this address
215 	if (addr >= ((char *)bufaddr + bufsize))
216 		return (NULL);
217 	return ((void*) addr);
218 }
219 
220 // Return the buffer size, in bytes
221 // (as opposed to 'length' which indicates how much data is in the buffer)
222 size_t AudioBuffer::
223 GetByteCount() const
224 {
225 	return (bufsize);
226 }
227 
228 // Return the buffer size, in seconds
229 // (as opposed to 'length' which indicates how much data is in the buffer)
230 Double AudioBuffer::
231 GetSize() const
232 {
233 	return (buflen);
234 }
235 
236 // Set the buffer size, allocating the buffer as necessary
237 AudioError AudioBuffer::
238 SetSize(
239 	Double		len)			// new size, in seconds
240 {
241 	// If no change in size, do nothing
242 	if (len == buflen)
243 		return (AUDIO_SUCCESS);
244 
245 	// If header not set, store the size for later
246 	buflen = len;
247 	if (!hdrset()) {
248 		return (AUDIO_SUCCESS);
249 	}
250 
251 	// If shrinking buffer, note this
252 	if (buflen < GetLength())
253 		SetLength(buflen);
254 	return (alloc());
255 }
256 
257 // Set the data header
258 // If no buffer allocated, allocate one now (if size is set).
259 // If buffer allocated, fiddle the sizes to account for new header type.
260 AudioError AudioBuffer::
261 SetHeader(
262 	const AudioHdr& h)			// header to copy
263 {
264 	AudioError	err;
265 
266 	// Validate, then update the header
267 	err = h.Validate();
268 	if (err)
269 		return (RaiseError(err));
270 	(void) AudioStream::updateheader(h);
271 
272 	// If no size set, done for now
273 	if (buflen == 0.)
274 		return (AUDIO_SUCCESS);
275 
276 	// If no buffer allocated, allocate one now
277 	if (GetAddress() == 0)
278 		return (alloc());
279 
280 	// If buffer allocated, change size to match new header
281 	buflen = h.Bytes_to_Time(GetByteCount());
282 	return (AUDIO_SUCCESS);
283 }
284 
285 // Set the buffer length (ie, the amount of data written to the buffer)
286 void AudioBuffer::
287 SetLength(
288 	Double		len)			// new length
289 {
290 	if (!hdrset() || (len < 0.))		// no-op if not ready
291 		return;
292 	if (!opened() && (len > 0.))
293 		return;
294 
295 	if (Undefined(len) || (len > GetSize())) {
296 		// Limit to the size of the buffer
297 		setlength(GetSize());
298 	} else {
299 		setlength(len);
300 	}
301 }
302 
303 // Copy data from local buffer into specified buffer.
304 // No data format translation takes place.
305 // The object's read position is not updated.
306 AudioError AudioBuffer::
307 ReadData(
308 	void*		buf,		// destination buffer address
309 	size_t&		len,		// buffer length (updated)
310 	Double&		pos)		// start position (updated)
311 {
312 	off_t		resid;
313 	off_t		cnt;
314 	off_t		offset;
315 	AudioError	err;
316 
317 	// Copy length, zero return value
318 	cnt = (off_t)len;
319 	len = 0;
320 
321 	// Cannot read if buffer or header not valid
322 	if (!opened())
323 		return (RaiseError(AUDIO_ERR_NOEFFECT));
324 
325 	// Position must be valid
326 	if ((pos < 0.) || (cnt < 0))
327 		return (RaiseError(AUDIO_ERR_BADARG));
328 
329 	// If the starting offset is at or beyond EOF, return eof flag
330 	if (pos >= GetLength()) {
331 		err = AUDIO_EOF;
332 		err.sys = AUDIO_COPY_INPUT_EOF;
333 		return (err);
334 	}
335 
336 	// Limit transfer to remaining room in buffer
337 	offset = GetHeader().Time_to_Bytes(pos);
338 	resid = GetHeader().Time_to_Bytes(GetLength()) - offset;
339 	if (resid <= 0) {
340 		err = AUDIO_EOF;
341 		err.sys = AUDIO_COPY_INPUT_EOF;
342 		return (err);
343 	}
344 	if (cnt > resid)
345 		cnt = resid;
346 
347 	// Fix the alignment to make sure we're not splitting frames
348 	err = AUDIO_SUCCESS;
349 	if (GetHeader().Bytes_to_Bytes(cnt) > 0) {
350 		// Copy as much data as possible
351 		memcpy((char *)buf, (char *)((off_t)GetAddress() + offset),
352 		    (int)cnt);
353 	} else {
354 		err.sys = AUDIO_COPY_ZERO_LIMIT;
355 	}
356 
357 	// Return the updated transfer size and position
358 	len = (size_t)cnt;
359 	pos = GetHeader().Bytes_to_Time(offset + cnt);
360 
361 
362 	// Check to see if the endian is right.
363 	coerceEndian((unsigned char *)buf, len, localByteOrder());
364 
365 	return (err);
366 }
367 
368 // Copy data to local buffer from specified buffer.
369 // No data format translation takes place.
370 // The object's write position is not updated.
371 AudioError AudioBuffer::
372 WriteData(
373 	void*		buf,		// source buffer address
374 	size_t&		len,		// buffer length (updated)
375 	Double&		pos)		// start position (updated)
376 {
377 	off_t		resid;
378 	off_t		cnt;
379 	off_t		offset;
380 	AudioError	err;
381 
382 	// Copy length, zero return value
383 	cnt = (off_t)len;
384 	len = 0;
385 
386 	// Cannot write if buffer or header not valid
387 	if (!opened())
388 		return (RaiseError(AUDIO_ERR_NOEFFECT));
389 
390 	// Position must be valid
391 	if ((pos < 0.) || (cnt < 0))
392 		return (RaiseError(AUDIO_ERR_BADARG));
393 
394 	// If the starting offset beyond end of buffer, return short write flag
395 	if (pos >= GetSize()) {
396 		err = AUDIO_EOF;
397 		err.sys = AUDIO_COPY_OUTPUT_EOF;
398 		return (err);
399 	}
400 
401 	// Limit transfer to remaining room in buffer
402 	offset = GetHeader().Time_to_Bytes(pos);
403 	resid = (off_t)bufsize - offset;
404 	if (resid <= 0) {
405 		err = AUDIO_EOF;
406 		err.sys = AUDIO_COPY_OUTPUT_EOF;
407 		return (err);
408 	}
409 	if (cnt > resid)
410 		cnt = resid;
411 
412 	// Fix the alignment to make sure we're not splitting frames
413 	err = AUDIO_SUCCESS;
414 	if (GetHeader().Bytes_to_Bytes(cnt) > 0) {
415 		// Copy as much data as possible
416 		memcpy((char *)((off_t)GetAddress() + offset), (char *)buf,
417 		    (int)cnt);
418 	} else {
419 		err.sys = AUDIO_COPY_ZERO_LIMIT;
420 	}
421 
422 	// Return the updated transfer size and position
423 	len = (size_t)cnt;
424 	pos = GetHeader().Bytes_to_Time(offset + cnt);
425 
426 	// The end of a write to a buffer always becomes the buffer EOF
427 	setlength(pos);
428 	return (err);
429 }
430 
431 // AppendData is just like WriteData, except that it guarantees to extend
432 // the buffer if it is not big enough.
433 // The object's write position is not updated.
434 AudioError AudioBuffer::
435 AppendData(
436 	void*		buf,		// source buffer address
437 	size_t&		len,		// buffer length (updated)
438 	Double&		pos)		// start position (updated)
439 {
440 	Double		local_length;
441 	AudioError	err;
442 
443 	// Cannot write if header not valid
444 	if (!hdrset())
445 		return (RaiseError(AUDIO_ERR_NOEFFECT));
446 
447 	// Position must be valid
448 	if (pos < 0.)
449 		return (RaiseError(AUDIO_ERR_BADARG));
450 
451 	// If the ending offset is beyond end of buffer, extend it
452 	local_length = pos + GetHeader().Bytes_to_Time(len);
453 	if (local_length > GetSize()) {
454 		if (err = SetSize(local_length))
455 			return (err);
456 	}
457 	return (WriteData(buf, len, pos));
458 }
459 
460 // Copy routine to copy direct to destination
461 AudioError AudioBuffer::
462 AsyncCopy(
463 	Audio*		to,			// audio object to copy to
464 	Double&		frompos,
465 	Double&		topos,
466 	Double&		limit)
467 {
468 	caddr_t		bptr;
469 	size_t		cnt;
470 	size_t		svcnt;
471 	Double		svfrom;
472 	Double		svto;
473 	Double		lim;
474 	AudioHdr	tohdr;
475 	AudioError	err;
476 
477 	// Cannot write if buffer or header not valid
478 	if (!opened())
479 		return (RaiseError(AUDIO_ERR_NOEFFECT));
480 
481 	tohdr = to->GetHeader();
482 	if (limit < 0.)
483 		return (RaiseError(AUDIO_ERR_BADARG));
484 
485 	// Get maximum possible copy length
486 	svfrom = GetLength();
487 	if (frompos >= svfrom) {
488 		limit = 0.;
489 		err = AUDIO_EOF;
490 		err.sys = AUDIO_COPY_INPUT_EOF;
491 		return (err);
492 	}
493 	lim = svfrom - frompos;
494 	if (!Undefined(limit) && (limit < lim))
495 		lim = limit;
496 
497 	limit = 0.;
498 
499 	bptr = (caddr_t)GetAddress(frompos);
500 	if (bptr == 0) {
501 		err = AUDIO_EOF;
502 		err.sys = AUDIO_COPY_INPUT_EOF;
503 		return (err);
504 	}
505 	cnt = (size_t)GetHeader().Time_to_Bytes(lim);
506 	if (cnt == 0) {
507 		err = AUDIO_SUCCESS;
508 		err.sys = AUDIO_COPY_ZERO_LIMIT;
509 		return (err);
510 	}
511 
512 	// Add a bunch of paranoid checks
513 	svcnt = (size_t)GetAddress() + (size_t)GetByteCount();
514 	if ((bptr + cnt) > (caddr_t)svcnt) {
515 		// re-adjust cnt so it reads up to the end of file
516 		cnt = (size_t)((caddr_t)svcnt - bptr);
517 	}
518 	if (GetHeader().Bytes_to_Bytes(cnt) == 0) {
519 		err = AUDIO_EOF;
520 		err.sys = AUDIO_COPY_INPUT_EOF;
521 		return (err);
522 	}
523 
524 	// Write the data to the destination and update pointers/ctrs
525 	svfrom = frompos;
526 	svto = topos;
527 	svcnt = cnt;
528 	err = to->WriteData(bptr, cnt, topos);
529 	limit = topos - svto;
530 	frompos = svfrom + limit;
531 
532 	// Report short writes
533 	if (!err && (cnt < svcnt)) {
534 		err.sys = AUDIO_COPY_SHORT_OUTPUT;
535 	}
536 	return (err);
537 }
538