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