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