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