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
38AudioBuffer::
39AudioBuffer(
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
47AudioBuffer::
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'
56Boolean AudioBuffer::
57opened() 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.
66AudioError AudioBuffer::
67alloc()
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
182void* AudioBuffer::
183GetAddress() 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.
190void* AudioBuffer::
191GetAddress(
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)
222size_t AudioBuffer::
223GetByteCount() 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)
230Double AudioBuffer::
231GetSize() const
232{
233	return (buflen);
234}
235
236// Set the buffer size, allocating the buffer as necessary
237AudioError AudioBuffer::
238SetSize(
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.
260AudioError AudioBuffer::
261SetHeader(
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)
286void AudioBuffer::
287SetLength(
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.
306AudioError AudioBuffer::
307ReadData(
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.
371AudioError AudioBuffer::
372WriteData(
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.
434AudioError AudioBuffer::
435AppendData(
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
461AudioError AudioBuffer::
462AsyncCopy(
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