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 <AudioExtent.h>
30 #include <AudioList.h>
31 #include <AudioDebug.h>
32 
33 // class AudioList methods
34 
35 
36 // class AudioListEntry Constructor
37 AudioList::AudioListEntry::
38 AudioListEntry(
39 	Audio*		obj):			// audio object to point to
40 	aptr(0), next(0), prev(0)
41 {
42 	// A NULL object is only valid in dummy entries, such as list heads
43 	newptr(obj);
44 }
45 
46 // class AudioListEntry Destructor
47 AudioList::AudioListEntry::
48 ~AudioListEntry()
49 {
50 	newptr(0);
51 	if (next != 0) {
52 		next->prev = prev;
53 	}
54 	if (prev != 0) {
55 		prev->next = next;
56 	}
57 }
58 
59 // Set a new extent pointer in an AudioListEntry
60 void AudioList::AudioListEntry::
61 newptr(
62 	Audio*		newa)		// new object
63 {
64 	if (aptr != 0)
65 		aptr->Dereference();
66 	aptr = newa;
67 	if (aptr != 0)
68 		aptr->Reference();
69 }
70 
71 	// Link object into list
72 // Link in a new AudioListEntry
73 void AudioList::AudioListEntry::
74 link(
75 	AudioListEntry*	after)		// link after this one
76 {
77 	// Link object into list
78 	prev = after;
79 	next = after->next;
80 	after->next = this;
81 	if (next != 0)
82 		next->prev = this;
83 }
84 
85 // Split an AudioListEntry at the specified offset
86 void AudioList::AudioListEntry::
87 split(
88 	Double		pos)		// split offset
89 {
90 	AudioExtent*	e1;
91 	AudioExtent*	e2;
92 	AudioListEntry*	newp;
93 
94 	// Create two extents referencing this object
95 	e1 = new AudioExtent(aptr, 0., pos);
96 	e2 = new AudioExtent(aptr, pos, AUDIO_UNKNOWN_TIME);
97 
98 	// Set the current entry to the first extent and append the second
99 	newptr(e1);
100 	newp = new AudioListEntry(e2);
101 	newp->link(this);
102 }
103 
104 
105 // class AudioList Constructor
106 AudioList::
107 AudioList(
108 	const char  *local_name):		// name string
109 	Audio(local_name), head(0)
110 {
111 }
112 
113 // class AudioList Destructor
114 AudioList::
115 ~AudioList()
116 {
117 	// Delete all entries in the list
118 	while (first() != 0)
119 		delete first();
120 }
121 
122 // Get the first entry in the list
123 AudioList::AudioListEntry* AudioList::
124 first() const
125 {
126 	return (head.next);
127 }
128 
129 // Get the extent and offset corresponding to a given position
130 // Return FALSE if no extents in list or position is beyond eof
131 Boolean AudioList::
132 getposition(
133 	Double&			pos,		// target position (updated)
134 	AudioListEntry*&	ep) const	// returned extent pointer
135 {
136 	Double			length;
137 
138 	// Position must be specified
139 	if (Undefined(pos))
140 		return (FALSE);
141 
142 	// Get the first extent in the list
143 	ep = first();
144 	while (ep != 0) {
145 		// Get length of extent
146 		length = ep->aptr->GetLength();
147 		if (Undefined(length)) {
148 			// Can't determine sizes beyond this
149 			return (TRUE);
150 		}
151 		// If the remaining offset is inside the current extent
152 		if (length > pos)
153 			return (TRUE);
154 
155 		// Move on to the next extent
156 		pos -= length;
157 		ep = ep->next;
158 	}
159 	return (FALSE);
160 }
161 
162 // Get the total length of the audio list
163 Double AudioList::
164 GetLength() const
165 {
166 	AudioListEntry*	ep;
167 	Double		sum;
168 	Double		x;
169 
170 	for (sum = 0., ep = first(); ep != 0; ep = ep->next) {
171 		// Accumulate times for each extent
172 		// Indeterminate extents screw up the calculation
173 		x = ep->aptr->GetLength();
174 		if (Undefined(x))
175 			return (x);
176 		sum += x;
177 	}
178 	return (sum);
179 }
180 
181 // Construct a name for the list
182 char *AudioList::
183 GetName() const
184 {
185 	// XXX - construct a better name
186 	return (Audio::GetName());
187 }
188 
189 // Get the audio header for the current read position
190 AudioHdr AudioList::
191 GetHeader()
192 {
193 	return (GetHeader(ReadPosition()));
194 }
195 
196 // Get the audio header for the given position
197 AudioHdr AudioList::
198 GetHeader(
199 	Double		pos)		// position
200 {
201 	AudioListEntry*	ep;
202 
203 	// Get the extent pointer for the given position
204 	if (!getposition(pos, ep)) {
205 		AudioHdr	h;
206 
207 		if (pos != 0.) {
208 			PrintMsg(_MGET_(
209 			    "AudioHdr:GetHeader()...position is beyond eof"),
210 			    Warning);
211 			return (h);
212 		}
213 		if ((ep = first()) != 0)
214 			return (ep->aptr->GetHeader());
215 		return (h);
216 	}
217 	// Get the header for the proper offset in the extent
218 	return (ep->aptr->GetDHeader(pos));
219 }
220 
221 // Copy data from list into specified buffer.
222 // No data format translation takes place.
223 // The object's read position is not updated.
224 //
225 // Since list could contain extents of differing encodings,
226 // clients should always use GetHeader() in combination with ReadData()
227 AudioError AudioList::
228 ReadData(
229 	void*		buf,		// destination buffer address
230 	size_t&		len,		// buffer size (updated)
231 	Double&		pos)		// start position (updated)
232 {
233 	AudioListEntry*	ep;
234 	size_t		cnt;
235 	Double		off;
236 	Double		newpos;
237 	AudioError	err;
238 
239 	// Save buffer size
240 	cnt = len;
241 
242 	// Position must be valid
243 	if (Undefined(pos) || (pos < 0.) || ((int)cnt < 0))
244 		return (RaiseError(AUDIO_ERR_BADARG));
245 
246 	// Loop until data is returned or error
247 	// XXX - THIS IS WRONG!  THE HEADER COULD CHANGE!
248 	do {
249 		// Get the extent/offset for read position; clear return count
250 		len = 0;
251 		off = pos;
252 		if (!getposition(off, ep)) {
253 			err = AUDIO_EOF;
254 			err.sys = AUDIO_COPY_INPUT_EOF;
255 			return (err);
256 		}
257 
258 		// Save the offset and read some data
259 		newpos = off;
260 		len = cnt;
261 		err = ep->aptr->ReadData(buf, len, newpos);
262 
263 		// If no eof on this list entry, or no more data, we're done
264 		if ((err != AUDIO_EOF) || (err.sys != AUDIO_COPY_INPUT_EOF) ||
265 		    (ep->next == 0)) {
266 			break;
267 		}
268 
269 		// Advance to next list entry
270 		// XXX - Is this problemmatic, too?
271 		pos += ep->aptr->GetLength() - off;
272 	} while (TRUE);
273 
274 	// Update the byte count and position
275 	pos += (newpos - off);		// XXX - recalculate?
276 	return (err);
277 }
278 
279 // Write to AudioList is (currently) prohibited
280 AudioError AudioList::
281 WriteData(
282 	void*,				// destination buffer address
283 	size_t&		len,		// buffer size (updated)
284 	Double&)			// start position (updated)
285 {
286 	len = 0;
287 	return (RaiseError(AUDIO_ERR_NOEFFECT));
288 }
289 
290 // Insert an entry at the start
291 AudioError AudioList::
292 Insert(
293 	Audio*		obj)		// object to insert
294 {
295 	Double		pos;		// insertion offset, in seconds
296 
297 	return (Insert(obj, pos = 0.));
298 }
299 
300 // Insert an entry at a specified position
301 AudioError AudioList::
302 Insert(
303 	Audio*		obj,		// object to insert
304 	Double		pos)		// insertion offset, in seconds
305 {
306 	AudioListEntry	*ep;
307 	AudioListEntry	*prev;
308 
309 	// Find the insertion point
310 	if (first() == 0) {
311 		prev = &head;		// this is the first extent
312 	} else {
313 		if (!getposition(pos, prev)) {
314 			if (pos == 0.) {
315 				// Append extent to end of list
316 				return (Append(obj));
317 			} else {
318 				return (RaiseError(AUDIO_ERR_BADARG));
319 			}
320 		} else if (pos != 0.) {
321 			// The insertion is in an extent, split it in two
322 			prev->split(pos);
323 		} else {
324 			// Insert before the current position
325 			prev = prev->prev;
326 		}
327 	}
328 	// Create object and link into list
329 	ep = new AudioListEntry(obj);
330 	ep->link(prev);
331 
332 	return (AUDIO_SUCCESS);
333 }
334 
335 // Append an entry to a list
336 AudioError AudioList::
337 Append(
338 	Audio*		obj)		// object to append
339 {
340 	AudioListEntry	*ep;
341 	AudioListEntry	*prev;
342 
343 	// Find the last extent in the list
344 	for (prev = &head; prev->next != 0; prev = prev->next)
345 		continue;
346 
347 	// Create object and link into list
348 	ep = new AudioListEntry(obj);
349 	ep->link(prev);
350 	return (AUDIO_SUCCESS);
351 }
352 
353 // Copy routine for lists
354 AudioError AudioList::
355 AsyncCopy(
356 	Audio*		to,			// audio object to copy to
357 	Double&		frompos,		// input pos (updated)
358 	Double&		topos,			// output pos (updated)
359 	Double&		limit)			// amt to copy (updated)
360 {
361 	AudioListEntry*	ep;
362 	Double		svlim;
363 	Double		newpos;
364 	Double		off;
365 	AudioError	err;
366 
367 	svlim = limit;
368 	// Loop until data is returned or error
369 	// XXX - THIS IS WRONG!  THE HEADER COULD CHANGE!
370 	do {
371 		// Get the extent and offset for the read position
372 		off = frompos;
373 		if (!getposition(off, ep)) {
374 			// nothing written, limit should reflect this
375 			limit = 0.0;
376 			err = AUDIO_EOF;
377 			err.sys = AUDIO_COPY_INPUT_EOF;
378 			return (err);
379 		}
380 
381 		// Save the offset and do a copy
382 		newpos = off;
383 		limit = svlim;
384 		err = ep->aptr->AsyncCopy(to, newpos, topos, limit);
385 
386 		// If no eof on this list entry, or no more data, we're done
387 		if ((err != AUDIO_EOF) || (err.sys != AUDIO_COPY_INPUT_EOF) ||
388 		    (ep->next == 0)) {
389 			break;
390 		}
391 
392 		// Advance to next list entry
393 		// XXX - Is this problemmatic, too?
394 		frompos += ep->aptr->GetLength() - off;
395 	} while (TRUE);
396 
397 	// Update the byte count and  position
398 	frompos += (newpos - off); // XXX - recalculate?
399 	return (err);
400 }
401