1 /*
2  * Copyright 1993 Network Computing Devices, Inc.
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation, and that the name Network Computing Devices, Inc. not be
9  * used in advertising or publicity pertaining to distribution of this
10  * software without specific, written prior permission.
11  *
12  * THIS SOFTWARE IS PROVIDED 'AS-IS'.  NETWORK COMPUTING DEVICES, INC.,
13  * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT
14  * LIMITATION ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
15  * PARTICULAR PURPOSE, OR NONINFRINGEMENT.  IN NO EVENT SHALL NETWORK
16  * COMPUTING DEVICES, INC., BE LIABLE FOR ANY DAMAGES WHATSOEVER, INCLUDING
17  * SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES, INCLUDING LOSS OF USE, DATA,
18  * OR PROFITS, EVEN IF ADVISED OF THE POSSIBILITY THEREOF, AND REGARDLESS OF
19  * WHETHER IN AN ACTION IN CONTRACT, TORT OR NEGLIGENCE, ARISING OUT OF OR IN
20  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21  *
22  * $Id: soundlib.c 226 2006-08-31 00:46:14Z jon $
23  * $NCDId: @(#)soundlib.c,v 1.42 1995/11/28 23:21:17 greg Exp $
24  */
25 
26 #define _SOUNDLIB_C_
27 
28 #include <stdio.h>
29 #include <audio/Aos.h>
30 #include "Alibint.h"
31 #include "soundlib.h"
32 
33 unsigned int    AuSoundFileChunkSize = 10 * 1024,	/* in bytes */
34                 AuSoundPortDuration = 5,	/* in seconds */
35                 AuSoundPortLowWaterMark = 25,	/* in percent */
36                 AuSoundPortHighWaterMark = 75;	/* in percent */
37 AuBool          AuSoundRestartHardwarePauses = AuTrue;
38 
39 extern int      AuMonitorRate,
40                 AuMonitorFormat;
41 
42 AuBucketID
AuSoundCreateBucketFromFile(AuServer * aud,const char * filename,AuUint32 access,AuBucketAttributes ** ret_attr,AuStatus * ret_status)43 AuSoundCreateBucketFromFile(
44                             AuServer       *aud,
45                             const char *filename,
46                             AuUint32   access,
47                             AuBucketAttributes **ret_attr,
48                             AuStatus       *ret_status
49                             )
50 {
51     Sound           s;
52     AuBucketID      bucket;
53     AuFlowID        flow;
54     int             import;
55     char           *buf;
56     unsigned int    count;
57     AuString        desc;
58     AuBool          done = AuFalse;
59 
60 #ifdef DEBUG
61     fprintf(stderr, "AuSoundCreateBucketFromFile: starting.\n");
62 #endif
63 
64 
65     if (!(s = SoundOpenFileForReading(filename)))
66 	return AuNone;
67 
68     if (!(buf = (char *) Aumalloc(AuSoundFileChunkSize)))
69     {
70 	SoundCloseFile(s);
71 	return AuNone;
72     }
73 
74 
75     desc.type = AuStringLatin1;
76     desc.len = strlen(SoundComment(s));
77     desc.data = SoundComment(s);
78 
79     bucket = AuCreateBucket(aud, SoundDataFormat(s), SoundNumTracks(s), access,
80 			    SoundSampleRate(s), SoundNumSamples(s), &desc,
81 			    ret_status);
82 
83     if (bucket)
84     {
85 	/*
86 	 * Get an unclocked flow into a bucket.  We can do this operation
87 	 * synchronously, so we don't have to horse around with events.
88 	 */
89 	flow = AuGetScratchFlowToBucket(aud, bucket, &import, ret_status);
90 
91 	if (flow)
92 	{
93 	    int             numBytes = SoundNumBytes(s);
94 
95 	    while (!done)
96 	    {
97 		count = aumin(AuSoundFileChunkSize, numBytes);
98 		count = SoundReadFile(buf, count, s);
99 		numBytes -= count;
100 		done = !(count && numBytes);
101 
102 		(void) AuWriteElement(aud, flow, import, count, buf,
103 				      done, ret_status);
104 	    }
105 
106 	    AuReleaseScratchFlow(aud, flow, ret_status);
107 	}
108 
109 	if (ret_attr)
110 	    *ret_attr = AuGetBucketAttributes(aud, bucket, ret_status);
111     }
112 
113     Aufree(buf);
114     SoundCloseFile(s);
115 
116     return bucket;
117 }
118 
119 AuBool
AuSoundCreateFileFromBucket(AuServer * aud,const char * filename,int fileFormat,AuBucketID bucket,AuStatus * ret_status)120 AuSoundCreateFileFromBucket(
121                             AuServer       *aud,
122                             const char  *filename,
123                             int             fileFormat,
124                             AuBucketID      bucket,
125                             AuStatus       *ret_status
126                             )
127 {
128     AuBucketAttributes *ba;
129     AuFlowID        flow;
130     int             export;
131     AuBool          result = AuTrue;
132     char           *p;
133     AuUint32   size;
134     Sound           s;
135 
136     if (!(ba = AuGetBucketAttributes(aud, bucket, ret_status)))
137 	return AuFalse;
138 
139     size = AuBucketNumSamples(ba) * AuSizeofFormat(AuBucketFormat(ba)) *
140 	AuBucketNumTracks(ba);
141 
142     if (!(p = (char *) Aumalloc(AuSoundFileChunkSize)))
143     {
144 	AuFreeBucketAttributes(aud, 1, ba);
145 	return AuFalse;
146     }
147 
148     s = SoundCreate(fileFormat, AuBucketFormat(ba), AuBucketNumTracks(ba),
149 		    AuBucketSampleRate(ba), AuBucketNumSamples(ba),
150 		    AuBucketDescription(ba)->data);
151 
152     /* For now, assume that description is ISOLatin1 */
153 
154     if (!s || !SoundOpenFileForWriting(filename, s))
155     {
156 	if (s)
157 	    SoundDestroy(s);
158 
159 	Aufree(p);
160 	AuFreeBucketAttributes(aud, 1, ba);
161 	return AuFalse;
162     }
163 
164     /*
165      * Get an unclocked flow from a bucket.  We can do this operation
166      * synchronously, so we don't have to horse around with events.
167      */
168     flow = AuGetScratchFlowFromBucket(aud, bucket, &export, ret_status);
169 
170     if (flow)
171     {
172 	AuUint32   wanted = size,
173 	                got = 0;
174 
175 	while (wanted &&
176 	       (got = AuReadElement(aud, flow, export,
177 				    aumin(wanted, AuSoundFileChunkSize), p,
178 				    ret_status)))
179 	{
180 	    if (SoundWriteFile(p, got, s) != got)
181 	    {
182 		result = AuFalse;
183 		break;
184 	    }
185 
186 	    wanted -= got;
187 	}
188 
189 	AuReleaseScratchFlow(aud, flow, ret_status);
190     }
191 
192     free(p);
193     AuFreeBucketAttributes(aud, 1, ba);
194     SoundCloseFile(s);
195 
196     return result;
197 }
198 
199 AuBucketID
AuSoundCreateBucketFromData(AuServer * aud,Sound s,AuPointer data,AuUint32 access,AuBucketAttributes ** ret_attr,AuStatus * ret_status)200 AuSoundCreateBucketFromData(
201                             AuServer       *aud,
202                             Sound           s,
203                             AuPointer       data,
204                             AuUint32   access,
205                             AuBucketAttributes **ret_attr, /* RETURN */
206                             AuStatus       *ret_status
207                             )
208 {
209     AuBucketID      bucket;
210     AuFlowID        flow;
211     int             import;
212     AuString        desc;
213 
214 #ifdef DEBUG
215     fprintf(stderr, "AuSoundCreateBucketFromData: starting\n");
216 #endif
217 
218     if (SoundNumSamples(s) == SoundUnknownNumSamples)
219 	return AuNone;
220 
221     desc.type = AuStringLatin1;
222     desc.len = strlen(SoundComment(s));
223     desc.data = (char *) SoundComment(s);
224 
225     bucket = AuCreateBucket(aud, SoundDataFormat(s), SoundNumTracks(s), access,
226 			    SoundSampleRate(s), SoundNumSamples(s), &desc,
227 			    ret_status);
228 
229     if (bucket)
230     {
231 	/*
232 	 * Get an unclocked flow into a bucket.  We can do this operation
233 	 * synchronously, so we don't have to horse around with events.
234 	 */
235 	flow = AuGetScratchFlowToBucket(aud, bucket, &import, ret_status);
236 
237 	if (flow)
238 	{
239 	    (void) AuWriteElement(aud, flow, import, SoundNumBytes(s), data,
240 				  AuTrue, ret_status);
241 	    AuReleaseScratchFlow(aud, flow, ret_status);
242 	}
243 
244 	if (ret_attr)
245 	    *ret_attr = AuGetBucketAttributes(aud, bucket, ret_status);
246 
247     }
248 
249     return bucket;
250 }
251 
252 AuPointer
AuSoundCreateDataFromBucket(AuServer * aud,AuBucketID bucket,Sound * ps,AuStatus * ret_status)253 AuSoundCreateDataFromBucket(
254                             AuServer       *aud,
255                             AuBucketID      bucket,
256                             Sound          *ps,
257                             AuStatus       *ret_status
258                             )
259 {
260     AuBucketAttributes *ba;
261     AuFlowID        flow;
262     int             export;
263     char           *p;
264     AuInt32            nbytes;
265 
266     ba = AuGetBucketAttributes(aud, bucket, ret_status);
267 
268     if (!ba)
269 	return NULL;
270 
271     nbytes = AuBucketNumSamples(ba) * AuSizeofFormat(AuBucketFormat(ba)) *
272 	AuBucketNumTracks(ba);
273 
274     *ps = SoundCreate(SoundFileFormatNone, AuBucketFormat(ba),
275 		      AuBucketNumTracks(ba), AuBucketSampleRate(ba),
276 		      AuBucketNumSamples(ba), AuBucketDescription(ba)->data);
277 
278     if (!*ps)
279     {
280 	AuFreeBucketAttributes(aud, 1, ba);
281 	return NULL;
282     }
283 
284     p = (char *) Aumalloc(nbytes);
285 
286     if (!p)
287     {
288 	AuFreeBucketAttributes(aud, 1, ba);
289 	SoundDestroy(*ps);
290 	return NULL;
291     }
292 
293     /*
294      * Get an unclocked flow from a bucket.  We can do this operation
295      * synchronously, so we don't have to horse around with events.
296      */
297     flow = AuGetScratchFlowFromBucket(aud, bucket, &export, ret_status);
298 
299     if (flow)
300     {
301 	AuReadElement(aud, flow, export, nbytes, p, ret_status);
302 	AuReleaseScratchFlow(aud, flow, ret_status);
303     }
304 
305     AuFreeBucketAttributes(aud, 1, ba);
306     return (AuPointer) p;
307 }
308 
309 static void
sendData(AuServer * aud,AuSoundDataPtr priv,AuUint32 numBytes)310 sendData(
311          AuServer       *aud,
312          AuSoundDataPtr  priv,
313          AuUint32   numBytes
314          )
315 {
316     int             n = aumin(numBytes, priv->numBytes);
317 
318 #if DEBUG
319     fprintf(stderr, "sendData: n = %d numBytes = %d\n",
320 	    n, numBytes);
321 #endif
322 
323     if (n)
324     {
325 	AuWriteElement(aud, priv->flow, 0, n, priv->buf, n != numBytes, NULL);
326 	priv->numBytes -= n;
327 	priv->buf += n;
328     }
329     else
330       {
331 #if DEBUG
332 	fprintf(stderr, "sendData: n = %d numBytes = %d, EOF WRITE\n",
333 		n, numBytes);
334 #endif
335 
336 				/* JET - need to account for case where */
337 				/* the amount of data is equal to the import */
338 				/* size... ie: a read returns 0, and force */
339 				/* an EOF */
340 	AuWriteElement(aud, priv->flow, 0, 0, priv->buf, AuTrue, NULL);
341       }
342 
343 }
344 
345 static void
receiveData(AuServer * aud,AuSoundDataPtr priv,AuUint32 numBytes)346 receiveData(
347             AuServer       *aud,
348             AuSoundDataPtr  priv,
349             AuUint32   numBytes
350             )
351 {
352     AuUint32   n;
353 
354     n = AuReadElement(aud, priv->flow, 1, numBytes, priv->buf, NULL);
355     priv->buf += n;
356     priv->length += n;
357 }
358 
359 static void
sendFile(AuServer * aud,AuSoundDataPtr priv,AuUint32 numBytes)360 sendFile(
361          AuServer       *aud,
362          AuSoundDataPtr  priv,
363          AuUint32   numBytes
364          )
365 {
366     int             n;
367 
368 
369     if ((n = SoundReadFile(priv->buf, aumin(numBytes, priv->numBytes),
370 			   priv->s)) > 0)
371     {
372 #if DEBUG
373     fprintf(stderr, "sendFile: n = %d numBytes = %d\n",
374 	    n, numBytes);
375 #endif
376 
377 	AuWriteElement(aud, priv->flow, 0, n, priv->buf, n != numBytes, NULL);
378 	priv->numBytes -= n;
379     }
380     else
381       {
382 #if DEBUG
383 	fprintf(stderr, "sendFile: n = %d numBytes = %d, EOF WRITE\n",
384 		n, numBytes);
385 #endif
386 
387 				/* JET - need to account for case where */
388 				/* the amount of data is equal to the import */
389 				/* size... ie: a read returns 0, and force */
390 				/* an EOF */
391 	AuWriteElement(aud, priv->flow, 0, 0, priv->buf, AuTrue, NULL);
392       }
393 
394 }
395 
396 static void
receiveFile(AuServer * aud,AuSoundDataPtr priv,AuUint32 numBytes)397 receiveFile(
398             AuServer       *aud,
399             AuSoundDataPtr  priv,
400             AuUint32   numBytes
401             )
402 {
403     AuUint32   n;
404 
405     n = AuReadElement(aud, priv->flow, 1, numBytes, priv->buf, NULL);
406     SoundWriteFile(priv->buf, n, priv->s);
407 }
408 
409 static          AuBool
EventHandler(AuServer * aud,AuEvent * ev,AuEventHandlerRec * handler)410 EventHandler(
411              AuServer       *aud,
412              AuEvent        *ev,
413              AuEventHandlerRec *handler
414              )
415 {
416     AuSoundDataPtr  priv = (AuSoundDataPtr) handler->data;
417 
418     switch (ev->type)
419     {
420 	case AuEventTypeMonitorNotify:
421 	    if (priv->callback)
422 		(*priv->callback) (aud, handler, ev, priv->callback_data);
423 	    break;
424 	case AuEventTypeElementNotify:
425 	    {
426 		AuElementNotifyEvent *event = (AuElementNotifyEvent *) ev;
427 
428 		switch (event->kind)
429 		{
430 		    case AuElementNotifyKindHighWater:
431 		    case AuElementNotifyKindLowWater:
432 			(*priv->dataHandler) (aud, priv, event->num_bytes);
433 			break;
434 		    case AuElementNotifyKindState:
435 			switch (event->cur_state)
436 			{
437 			    case AuStateStop:
438 				if (priv->dataHandlerStop)
439 				    (*priv->dataHandlerStop) (aud, priv,
440 							  event->num_bytes);
441 
442 				if (priv->freeSound)
443 				    SoundCloseFile(priv->s);
444 
445 				if (priv->callback)
446 				    (*priv->callback) (aud, handler, ev,
447 						       priv->callback_data);
448 
449 				if (priv->loopCount &&
450 				    event->reason == AuReasonEOF)
451 				{
452 				    if (!--priv->loopCount)
453 					AuStopFlow(aud, priv->flow, NULL);
454 				    break;
455 				}
456 
457 				AuUnregisterEventHandler(aud, handler);
458 				AuReleaseScratchFlow(aud, priv->flow, NULL);
459 				Aufree(priv);
460 				break;
461 			    case AuStatePause:
462 				if (event->reason == AuReasonHardware)
463 				    if (AuSoundRestartHardwarePauses)
464 					AuStartFlow(aud, priv->flow, NULL);
465 				    else
466 					AuStopFlow(aud, priv->flow, NULL);
467 				else if (event->reason != AuReasonUser)
468 				    (*priv->dataHandler) (aud, priv,
469 							  event->num_bytes);
470 				break;
471 			}
472 			break;
473 		}
474 	    }
475     }
476 
477     return AuTrue;
478 }
479 
480 #define ABORT()								      \
481 {									      \
482     if (sound_data->freeSound)						      \
483         SoundDestroy(sound_data->s);					      \
484     Aufree(sound_data);							      \
485 									      \
486     return NULL;							      \
487 }
488 
489 /* play from any source */
490 AuEventHandlerRec *
AuSoundPlay(AuServer * aud,AuDeviceID device,AuFixedPoint volume,int mode,AuSoundDataPtr sound_data,AuFlowID * ret_flow,int * ret_mult_elem,int * ret_mon_elem,AuStatus * ret_status)491 AuSoundPlay(
492             AuServer       *aud,
493             AuDeviceID      device,
494             AuFixedPoint    volume,
495             int             mode,
496             AuSoundDataPtr  sound_data,
497             AuFlowID       *ret_flow,
498             int            *ret_mult_elem,
499             int            *ret_mon_elem,
500             AuStatus       *ret_status
501             )
502 {
503     AuElement       elements[4];
504     unsigned int    importSize,
505                     i;
506     AuEventHandlerRec *handler;
507     AuDeviceAttributes *d = NULL,
508                     da;
509 
510     /* calc the size of the import */
511     importSize = SoundSampleRate(sound_data->s) * AuSoundPortDuration;
512 
513     /* if no output device was specified, look for an appropriate one */
514     if (device == AuNone)
515     {
516 	for (i = 0; i < AuServerNumDevices(aud); i++)
517 	    if ((AuDeviceKind(AuServerDevice(aud, i)) ==
518 		 AuComponentKindPhysicalOutput) &&
519 	        AuDeviceNumTracks(AuServerDevice(aud, i)) ==
520                 SoundNumTracks(sound_data->s))
521 	    {
522 		device = AuDeviceIdentifier(AuServerDevice(aud, i));
523 		break;
524 	    }
525 	/* abort if we didn't find an appropriate device */
526 	if (device == AuNone)
527 	    ABORT();
528     }
529 
530     /* get and set the device attributes */
531     for (i = 0; i < AuServerNumDevices(aud); i++)
532 	if (AuDeviceIdentifier(AuServerDevice(aud, i)) == device)
533 	{
534 	    d = AuServerDevice(aud, i);
535 	    break;
536 	}
537 
538     if (!d)
539 	return NULL;
540 
541     if (mode != -1 && (AuDeviceChangableMask(d) & AuCompDeviceLineModeMask))
542     {
543 	AuDeviceLineMode(&da) = mode;
544 	AuSetDeviceAttributes(aud, device, AuCompDeviceLineModeMask, &da, NULL);
545     }
546 
547     /* create the flow */
548     if ((sound_data->flow = AuGetScratchFlow(aud, NULL)) == AuNone)
549 	ABORT();
550 
551     AuMakeElementImportClient(&elements[0], SoundSampleRate(sound_data->s),
552 			      SoundDataFormat(sound_data->s),
553 			      SoundNumTracks(sound_data->s),
554 			      AuTrue,
555 			      importSize,
556 			      importSize * AuSoundPortLowWaterMark / 100,
557 			      0, NULL);
558     AuMakeElementMultiplyConstant(&elements[1], 0, volume);
559     AuMakeElementExportDevice(&elements[2], 1, device,
560                               SoundSampleRate(sound_data->s),
561 			      AuUnlimitedSamples, 0, NULL);
562 
563     if (ret_mon_elem)
564     {
565 	i = 4;
566 	*ret_mon_elem = 3;
567 	AuMakeElementExportMonitor(&elements[3], 0, AuMonitorRate,
568 				   AuMonitorFormat,
569                                    SoundNumTracks(sound_data->s));
570     }
571     else
572 	i = 3;
573 
574     /* set up the flow */
575     AuSetElements(aud, sound_data->flow, AuTrue, i, elements, ret_status);
576 
577     /* set up the event handler */
578     if (!(handler = AuRegisterEventHandler(aud, AuEventHandlerIDMask,
579 					   0, sound_data->flow, EventHandler,
580 					   (AuPointer) sound_data)))
581     {
582 	AuReleaseScratchFlow(aud, sound_data->flow, ret_status);
583 	ABORT();
584     }
585 
586     /* start up the components */
587     AuStartFlow(aud, sound_data->flow, ret_status);
588 
589     if (ret_flow)
590 	*ret_flow = sound_data->flow;
591 
592     if (ret_mult_elem)
593 	*ret_mult_elem = 1;
594 
595     return handler;
596 }
597 
598 /* record to any destination */
599 AuEventHandlerRec *
AuSoundRecord(AuServer * aud,AuDeviceID device,AuFixedPoint gain,AuUint32 numSamples,int mode,AuSoundDataPtr sound_data,AuFlowID * ret_flow,int * ret_mult_elem,AuStatus * ret_status)600 AuSoundRecord(
601               AuServer       *aud,
602               AuDeviceID      device,
603               AuFixedPoint    gain,
604               AuUint32        numSamples,
605               int             mode,
606               AuSoundDataPtr  sound_data,
607               AuFlowID       *ret_flow,
608               int            *ret_mult_elem,
609               AuStatus       *ret_status
610               )
611 {
612     AuElementAction actions[1];
613     AuElement       elements[2];
614     unsigned int    exportSize,
615                     i,
616      		    mask = 0;
617     AuEventHandlerRec *handler;
618     AuDeviceAttributes *d = NULL,
619                     da;
620 
621     /* calc the size of the export */
622     exportSize = SoundSampleRate(sound_data->s) * AuSoundPortDuration;
623 
624     /* get and set the device attributes */
625     for (i = 0; i < AuServerNumDevices(aud); i++)
626 	if (AuDeviceIdentifier(AuServerDevice(aud, i)) == device)
627 	{
628 	    d = AuServerDevice(aud, i);
629 	    break;
630 	}
631 
632     if (!d)
633 	return NULL;
634 
635     if (AuDeviceChangableMask(d) & AuCompDeviceLineModeMask)
636     {
637 	AuDeviceLineMode(&da) = mode;
638 	mask |= AuCompDeviceLineModeMask;
639     }
640 
641     if (AuDeviceChangableMask(d) & AuCompDeviceGainMask)
642     {
643 	AuDeviceGain(&da) = gain;
644 	mask |= AuCompDeviceGainMask;
645     }
646 
647     AuSetDeviceAttributes(aud, device, mask, &da, NULL);
648 
649     /* create the flow */
650     if ((sound_data->flow = AuGetScratchFlow(aud, NULL)) == AuNone)
651         ABORT();
652 
653     AuMakeChangeStateAction(&actions[0], AuStateStop, AuStateAny, AuReasonEOF,
654 			    sound_data->flow, 1, AuStateStop);
655 
656     AuMakeElementImportDevice(&elements[0], SoundSampleRate(sound_data->s),
657                               device, numSamples, 1, actions);
658     AuMakeElementExportClient(&elements[1], 0, SoundSampleRate(sound_data->s),
659 			      SoundDataFormat(sound_data->s),
660 			      SoundNumTracks(sound_data->s),
661 			      AuTrue,
662 			      exportSize,
663 			      exportSize * AuSoundPortHighWaterMark / 100,
664 			      0, NULL);
665 
666     /* set up the flow */
667     AuSetElements(aud, sound_data->flow, AuTrue, 2, elements, ret_status);
668 
669     /* set up the event handler */
670     if (!(handler = AuRegisterEventHandler(aud, AuEventHandlerTypeMask |
671 					   AuEventHandlerIDMask,
672 					   AuEventTypeElementNotify,
673 					   sound_data->flow, EventHandler,
674                                            (AuPointer) sound_data)))
675     {
676 	AuReleaseScratchFlow(aud, sound_data->flow, ret_status);
677         ABORT();
678     }
679 
680     /* start up the components */
681     AuStartFlow(aud, sound_data->flow, ret_status);
682 
683     if (ret_flow)
684 	*ret_flow = sound_data->flow;
685 
686     if (ret_mult_elem)
687 	*ret_mult_elem = -1;	/* XXX - not implemented yet */
688 
689     return handler;
690 }
691 
692 /* ARGSUSED */
693 /* play a Sound file */
694 AuEventHandlerRec *
AuSoundPlayFromFile(AuServer * aud,const char * filename,AuDeviceID device,AuFixedPoint volume,AuSoundCallback callback,AuPointer callback_data,AuFlowID * ret_flow,int * ret_mult_elem,int * ret_mon_elem,AuStatus * ret_status)695 AuSoundPlayFromFile(
696                     AuServer       *aud,
697                     const char  *filename,
698                     AuDeviceID      device,
699                     AuFixedPoint    volume,
700                     AuSoundCallback callback,
701                     AuPointer       callback_data,
702                     AuFlowID       *ret_flow,
703                     int            *ret_mult_elem,
704                     int            *ret_mon_elem,
705                     AuStatus       *ret_status
706                     )
707 {
708     AuSoundDataPtr  priv;
709     unsigned int    importSize,
710                     bufSize;
711     Sound           s;
712 
713     /* open the file and get the header */
714     if (!(s = SoundOpenFileForReading(filename)))
715 	return NULL;
716 
717     /* calc the size of the import */
718     importSize = SoundSampleRate(s) * AuSoundPortDuration;
719     bufSize = importSize * SoundNumTracks(s) * SoundBytesPerSample(s);
720 
721     /* alloc private data */
722     if (!(priv = (AuSoundDataPtr) Aumalloc(PAD4(sizeof(AuSoundDataRec)) +
723                                            bufSize)))
724     {
725 	SoundDestroy(s);
726 	return NULL;
727     }
728 
729     priv->loopCount = 0;
730     priv->callback = callback;
731     priv->callback_data = callback_data;
732     priv->dataHandler = sendFile;
733     priv->dataHandlerStop = NULL;
734     priv->buf = ((char *) priv) + PAD4(sizeof(AuSoundDataRec));
735     priv->s = s;
736     priv->freeSound = AuTrue;
737     priv->numBytes = SoundNumBytes(s);
738 
739 #ifdef DEBUG
740     fprintf(stderr, "JET - AuSoundPlayFromFile: numBytes = %d\n"
741 	    "          importSize = %d bufSize = %d SoundBytesPerSample = %d\n",
742 	    priv->numBytes, importSize, bufSize, SoundBytesPerSample(s));
743 #endif
744 
745     return AuSoundPlay(aud, device, volume, -1, priv,
746                        ret_flow, ret_mult_elem, ret_mon_elem, ret_status);
747 }
748 
749 /* ARGSUSED */
750 /* record a Sound file */
751 AuEventHandlerRec *
AuSoundRecordToFile(AuServer * aud,const char * filename,AuDeviceID device,AuFixedPoint gain,AuSoundCallback callback,AuPointer callback_data,int mode,int fileFormat,char * comment,AuUint32 rate,int dataFormat,AuFlowID * ret_flow,int * ret_mult_elem,AuStatus * ret_status)752 AuSoundRecordToFile(
753                     AuServer       *aud,
754                     const char  *filename,
755                     AuDeviceID      device,
756                     AuFixedPoint    gain,
757                     AuSoundCallback callback,
758                     AuPointer       callback_data,
759                     int             mode,
760                     int             fileFormat,
761                     char           *comment,
762                     AuUint32   rate,
763                     int             dataFormat,
764                     AuFlowID       *ret_flow,
765                     int            *ret_mult_elem,
766                     AuStatus       *ret_status
767                     )
768 {
769     return AuSoundRecordToFileN(aud, filename, device, gain,
770 				AuUnlimitedSamples, callback, callback_data,
771                                 mode, fileFormat, comment, rate, dataFormat,
772                                 ret_flow, ret_mult_elem,
773 				ret_status);
774 }
775 
776 /* ARGSUSED */
777 /* record a Sound file */
778 AuEventHandlerRec *
AuSoundRecordToFileN(AuServer * aud,const char * filename,AuDeviceID device,AuFixedPoint gain,AuUint32 numSamples,AuSoundCallback callback,AuPointer callback_data,int mode,int fileFormat,char * comment,AuUint32 rate,int dataFormat,AuFlowID * ret_flow,int * ret_mult_elem,AuStatus * ret_status)779 AuSoundRecordToFileN(
780                      AuServer       *aud,
781                      const char  *filename,
782                      AuDeviceID      device,
783                      AuFixedPoint    gain,
784                      AuUint32 numSamples,
785                      AuSoundCallback callback,
786                      AuPointer       callback_data,
787                      int             mode,
788                      int fileFormat,
789                      char           *comment,
790                      AuUint32   rate,
791                      int dataFormat,
792                      AuFlowID       *ret_flow,
793                      int            *ret_mult_elem,
794                      AuStatus       *ret_status
795                      )
796 {
797     AuSoundDataPtr  priv;
798     unsigned int    exportSize,
799                     bufSize,
800                     i;
801     Sound           s;
802     AuDeviceAttributes *d = NULL;
803 
804     /* create sound information */
805     for (i = 0; i < AuServerNumDevices(aud); i++)
806 	if (AuDeviceIdentifier(AuServerDevice(aud, i)) == device)
807 	{
808 	    d = AuServerDevice(aud, i);
809 	    break;
810 	}
811 
812     if (!d)
813 	return NULL;
814 
815     s = SoundCreate(fileFormat, dataFormat, AuDeviceNumTracks(d), rate,
816 		    SoundUnknownNumSamples, comment);
817 
818     if (!s)
819 	return NULL;
820 
821     /* create the file */
822     if (!SoundOpenFileForWriting(filename, s))
823     {
824 	SoundDestroy(s);
825 	return NULL;
826     }
827 
828     /* calc the size of the export */
829     exportSize = SoundSampleRate(s) * AuSoundPortDuration;
830     bufSize = exportSize * SoundNumTracks(s) * SoundBytesPerSample(s);
831 
832     /* alloc private data */
833     if (!(priv = (AuSoundDataPtr) Aumalloc(PAD4(sizeof(AuSoundDataRec)) +
834                                            bufSize)))
835     {
836 	SoundDestroy(s);
837 	return NULL;
838     }
839 
840     priv->loopCount = 0;
841     priv->callback = callback;
842     priv->callback_data = callback_data;
843     priv->dataHandler = priv->dataHandlerStop = receiveFile;
844     priv->buf = ((char *) priv) + PAD4(sizeof(AuSoundDataRec));
845     priv->s = s;
846     priv->freeSound = AuTrue;
847 
848     return AuSoundRecord(aud, device, gain, numSamples, mode, priv, ret_flow,
849                          ret_mult_elem, ret_status);
850 }
851 
852 #define	VOL(volume)		((1 << 16) * (volume) / 100)
853 
854 /* ARGSUSED */
855 static void
sync_play_cb(AuServer * aud,AuEventHandlerRec * handler,AuEvent * ev,AuPointer data)856 sync_play_cb(
857              AuServer       *aud,
858              AuEventHandlerRec *handler,
859              AuEvent        *ev,
860              AuPointer       data
861              )
862 {
863     int            *d = (int *) data;
864 
865     *d = 1;
866 }
867 
868 AuBool
AuSoundPlaySynchronousFromFile(AuServer * aud,const char * fname,int volume)869 AuSoundPlaySynchronousFromFile(
870                                AuServer       *aud,
871                                const char  *fname,
872                                int             volume
873                                )
874 {
875     int             d = 0;
876     AuStatus        ret;
877     AuEvent         ev;
878 
879     if (!AuSoundPlayFromFile(aud, fname, AuNone, VOL(volume),
880 			     sync_play_cb, (AuPointer) &d, (AuFlowID *) NULL,
881 			     (int *) NULL, (int *) NULL, &ret))
882 	return AuFalse;		/* XXX do something with ret? */
883 
884     while (1)
885     {
886 	AuNextEvent(aud, AuTrue, &ev);
887 	AuDispatchEvent(aud, &ev);
888 
889 	if (d)
890 	    break;
891     }
892 
893     return AuTrue;
894 }
895 
896 /* ARGSUSED */
897 /* record into a bucket */
898 AuEventHandlerRec *
AuSoundRecordToBucket(AuServer * aud,AuBucketID bucket,AuDeviceID device,AuFixedPoint gain,AuSoundCallback callback,AuPointer callback_data,int mode,AuFlowID * ret_flow,int * ret_mult_elem,AuStatus * ret_status)899 AuSoundRecordToBucket(
900                       AuServer       *aud,
901                       AuBucketID      bucket,
902                       AuDeviceID      device,
903                       AuFixedPoint    gain,
904                       AuSoundCallback callback,
905                       AuPointer       callback_data,
906                       int             mode,
907                       AuFlowID       *ret_flow,
908                       int            *ret_mult_elem,
909                       AuStatus       *ret_status
910                       )
911 {
912     AuElement       elements[2];
913     AuSoundDataPtr  priv;
914     AuBucketAttributes *ba;
915     AuEventHandlerRec *handler;
916     AuDeviceAttributes da, *d;
917     unsigned int    i,
918     		    mask = 0;
919 
920     /* get the device attributes */
921     for (i = 0; i < AuServerNumDevices(aud); i++)
922 	if (AuDeviceIdentifier(AuServerDevice(aud, i)) == device)
923 	{
924 	    d = AuServerDevice(aud, i);
925 	    break;
926 	}
927 
928     if (!d || !(ba = AuGetBucketAttributes(aud, bucket, ret_status)))
929 	return NULL;
930 
931     /* alloc private data */
932     if (!(priv = (AuSoundDataPtr) Aumalloc(sizeof(AuSoundDataRec))))
933     {
934 	AuFreeBucketAttributes(aud, 1, ba);
935 	return NULL;
936     }
937 
938     priv->loopCount = 0;
939     priv->callback = callback;
940     priv->callback_data = callback_data;
941     priv->dataHandlerStop = NULL;
942     priv->s = NULL;
943     priv->freeSound = AuFalse;
944 
945     /* create the flow */
946     if ((priv->flow = AuGetScratchFlow(aud, NULL)) == AuNone)
947     {
948 	AuFreeBucketAttributes(aud, 1, ba);
949 	Aufree(priv);
950 	return NULL;
951     }
952 
953     AuMakeElementImportDevice(&elements[0], AuBucketSampleRate(ba), device,
954 			      AuUnlimitedSamples, 0, NULL);
955     AuMakeElementExportBucket(&elements[1], 0, bucket, AuUnlimitedSamples,
956 			      0, 0, NULL);
957 
958     /* set up the flow */
959     AuSetElements(aud, priv->flow, AuTrue, 2, elements, ret_status);
960 
961     /* set up the event handler */
962     if (!(handler = AuRegisterEventHandler(aud, AuEventHandlerTypeMask |
963 					   AuEventHandlerIDMask,
964 					   AuEventTypeElementNotify,
965 					   priv->flow, EventHandler, priv)))
966     {
967 	AuReleaseScratchFlow(aud, priv->flow, ret_status);
968 	AuFreeBucketAttributes(aud, 1, ba);
969 	Aufree(priv);
970 	return NULL;
971     }
972 
973     if (AuDeviceChangableMask(d) & AuCompDeviceLineModeMask)
974     {
975 	AuDeviceLineMode(&da) = mode;
976 	mask |= AuCompDeviceLineModeMask;
977     }
978 
979     if (AuDeviceChangableMask(d) & AuCompDeviceGainMask)
980     {
981 	AuDeviceGain(&da) = gain;
982 	mask |= AuCompDeviceGainMask;
983     }
984 
985     AuSetDeviceAttributes(aud, device, mask, &da, NULL);
986 
987     /* start up the components */
988     AuStartFlow(aud, priv->flow, ret_status);
989 
990     if (ret_flow)
991 	*ret_flow = priv->flow;
992 
993     if (ret_mult_elem)
994 	*ret_mult_elem = -1;	/* XXX - not implemented yet */
995 
996     AuFreeBucketAttributes(aud, 1, ba);
997     return handler;
998 }
999 
1000 /* play from a bucket */
1001 AuEventHandlerRec *
AuSoundPlayFromBucket(AuServer * aud,AuBucketID bucket,AuDeviceID device,AuFixedPoint volume,AuSoundCallback callback,AuPointer callback_data,int count,AuFlowID * ret_flow,int * ret_mult_elem,int * ret_mon_elem,AuStatus * ret_status)1002 AuSoundPlayFromBucket(
1003                       AuServer       *aud,
1004                       AuBucketID      bucket,
1005                       AuDeviceID      device,
1006                       AuFixedPoint    volume,
1007                       AuSoundCallback callback,
1008                       AuPointer       callback_data,
1009                       int             count,
1010                       AuFlowID       *ret_flow,
1011                       int            *ret_mult_elem,
1012                       int            *ret_mon_elem,
1013                       AuStatus       *ret_status
1014                       )
1015 {
1016     AuElement       elements[4];
1017     AuElementAction actions[4];
1018     AuSoundDataPtr  priv;
1019     unsigned int    i,
1020                     numActions = 0;
1021     AuBucketAttributes *ba;
1022     AuEventHandlerRec *handler;
1023 
1024     if (!(ba = AuGetBucketAttributes(aud, bucket, ret_status)))
1025 	return NULL;
1026 
1027     /* alloc private data */
1028     if (!(priv = (AuSoundDataPtr) Aumalloc(sizeof(AuSoundDataRec))))
1029     {
1030 	AuFreeBucketAttributes(aud, 1, ba);
1031 	return NULL;
1032     }
1033 
1034     priv->loopCount = 0;
1035     priv->callback = callback;
1036     priv->callback_data = callback_data;
1037     priv->dataHandlerStop = NULL;
1038     priv->s = NULL;
1039     priv->freeSound = AuFalse;
1040 
1041     /* if no ouput device was specified, look for an appropriate one */
1042     if (device == AuNone)
1043     {
1044 	for (i = 0; i < AuServerNumDevices(aud); i++)
1045 	    if ((AuDeviceKind(AuServerDevice(aud, i)) ==
1046 		 AuComponentKindPhysicalOutput) &&
1047 		AuDeviceNumTracks(AuServerDevice(aud, i)) ==
1048 		AuBucketNumTracks(ba))
1049 	    {
1050 		device = AuDeviceIdentifier(AuServerDevice(aud, i));
1051 		break;
1052 	    }
1053 
1054 	/* abort if we didn't find an appropriate device */
1055 	if (device == AuNone)
1056 	{
1057 	    AuFreeBucketAttributes(aud, 1, ba);
1058 	    Aufree(priv);
1059 	    return NULL;
1060 	}
1061     }
1062 
1063     /* create the flow */
1064     if ((priv->flow = AuGetScratchFlow(aud, NULL)) == AuNone)
1065     {
1066 	AuFreeBucketAttributes(aud, 1, ba);
1067 	Aufree(priv);
1068 	return NULL;
1069     }
1070 
1071     if (count > 1)
1072     {
1073 	AuMakeSendNotifyAction(&actions[0], AuStatePause, AuStateAny,
1074 			       AuReasonAny);
1075 	AuMakeSendNotifyAction(&actions[1], AuStateStop, AuStateAny,
1076 			       AuReasonAny);
1077 	AuMakeChangeStateAction(&actions[2], AuStateStop, AuStateAny,
1078 				AuReasonUser, priv->flow, AuElementAll,
1079 				AuStateStop);
1080 	AuMakeChangeStateAction(&actions[3], AuStateStop, AuStateAny,
1081 				AuReasonEOF, priv->flow, 0, AuStateStart);
1082 
1083 	priv->loopCount = count;
1084 	numActions = 4;
1085     }
1086 
1087     AuMakeElementImportBucket(&elements[0], AuBucketSampleRate(ba), bucket,
1088 			      AuUnlimitedSamples, 0, numActions, actions);
1089     AuMakeElementMultiplyConstant(&elements[1], 0, volume);
1090     AuMakeElementExportDevice(&elements[2], 1, device, AuBucketSampleRate(ba),
1091 			      AuUnlimitedSamples, 0, NULL);
1092 
1093     if (ret_mon_elem)
1094     {
1095 	i = 4;
1096 	*ret_mon_elem = 3;
1097 	AuMakeElementExportMonitor(&elements[3], 0, AuMonitorRate,
1098 				   AuMonitorFormat, AuBucketNumTracks(ba));
1099     }
1100     else
1101 	i = 3;
1102 
1103     /* set up the flow */
1104     AuSetElements(aud, priv->flow, AuTrue, i, elements, ret_status);
1105 
1106     /* set up the event handler */
1107     if (!(handler = AuRegisterEventHandler(aud, AuEventHandlerIDMask,
1108 					   0, priv->flow, EventHandler,
1109 					   (AuPointer) priv)))
1110     {
1111 	AuReleaseScratchFlow(aud, priv->flow, ret_status);
1112 	AuFreeBucketAttributes(aud, 1, ba);
1113 	Aufree(priv);
1114 	return NULL;
1115     }
1116 
1117     /* start up the components */
1118     AuStartFlow(aud, priv->flow, ret_status);
1119 
1120     if (ret_flow)
1121 	*ret_flow = priv->flow;
1122 
1123     if (ret_mult_elem)
1124 	*ret_mult_elem = 1;
1125 
1126     AuFreeBucketAttributes(aud, 1, ba);
1127     return handler;
1128 }
1129 
1130 /* play from data in memory */
1131 AuEventHandlerRec *
AuSoundPlayFromData(AuServer * aud,Sound s,AuPointer data,AuDeviceID device,AuFixedPoint volume,AuSoundCallback callback,AuPointer callback_data,AuFlowID * ret_flow,int * ret_mult_elem,int * ret_mon_elem,AuStatus * ret_status)1132 AuSoundPlayFromData(
1133                     AuServer       *aud,
1134                     Sound           s,
1135                     AuPointer       data,
1136                     AuDeviceID      device,
1137                     AuFixedPoint    volume,
1138                     AuSoundCallback callback,
1139                     AuPointer       callback_data,
1140                     AuFlowID       *ret_flow,
1141                     int            *ret_mult_elem,
1142                     int            *ret_mon_elem,
1143                     AuStatus       *ret_status
1144                     )
1145 {
1146     AuSoundDataPtr  priv;
1147 
1148     /* alloc private data */
1149     if (!(priv = (AuSoundDataPtr) Aumalloc(sizeof(AuSoundDataRec))))
1150 	return NULL;
1151 
1152     priv->loopCount = 0;
1153     priv->callback = callback;
1154     priv->callback_data = callback_data;
1155     priv->dataHandler = sendData;
1156     priv->dataHandlerStop = NULL;
1157     priv->buf = (char *) data;
1158     priv->s = s;
1159     priv->freeSound = AuFalse;
1160     priv->numBytes = SoundNumBytes(s);
1161 
1162     return AuSoundPlay(aud, device, volume, -1, priv,
1163                        ret_flow, ret_mult_elem, ret_mon_elem, ret_status);
1164 }
1165 
1166 /* record into data in memory */
1167 AuEventHandlerRec *
AuSoundRecordToData(AuServer * aud,Sound s,AuPointer data,AuDeviceID device,AuFixedPoint gain,AuSoundCallback callback,AuPointer callback_data,int mode,AuFlowID * ret_flow,int * ret_mult_elem,AuStatus * ret_status)1168 AuSoundRecordToData(
1169                     AuServer       *aud,
1170                     Sound           s,
1171                     AuPointer       data,
1172                     AuDeviceID      device,
1173                     AuFixedPoint    gain,
1174                     AuSoundCallback callback,
1175                     AuPointer       callback_data,
1176                     int             mode,
1177                     AuFlowID       *ret_flow,
1178                     int            *ret_mult_elem,
1179                     AuStatus       *ret_status
1180                     )
1181 {
1182     AuSoundDataPtr  priv;
1183 
1184     if (!(priv = (AuSoundDataPtr) Aumalloc(sizeof(AuSoundDataRec))))
1185 	return NULL;
1186 
1187     priv->loopCount = 0;
1188     priv->length = 0;
1189     priv->callback = callback;
1190     priv->callback_data = callback_data;
1191     priv->dataHandler = priv->dataHandlerStop = receiveData;
1192     priv->buf = (char *) data;
1193     priv->s = s;
1194     priv->freeSound = AuFalse;
1195 
1196     return AuSoundRecord(aud, device, gain, SoundNumSamples(s), mode, priv,
1197                          ret_flow, ret_mult_elem, ret_status);
1198 }
1199 
1200 /* length of data recorded to memory */
1201 /*ARGSUSED*/
1202 AuUint32
AuSoundRecordToDataLength(AuEventHandlerRec * handler)1203 AuSoundRecordToDataLength(AuEventHandlerRec *handler)
1204 {
1205     AuSoundDataPtr  priv = (AuSoundDataPtr) handler->data;
1206 
1207     return priv->length;
1208 }
1209