1 /*
2     TiMidity++ -- MIDI to WAVE converter and player
3     Copyright (C) 1999-2002 Masanao Izumo <mo@goice.co.jp>
4     Copyright (C) 1995 Tuukka Toivonen <tt@cgs.fi>
5 
6     nas_a.c - Copyright (C) 1999 Michael Haardt <michael@moria.de>
7 
8     This program is free software; you can redistribute it and/or modify
9     it under the terms of the GNU General Public License as published by
10     the Free Software Foundation; either version 2 of the License, or
11     (at your option) any later version.
12 
13     This program is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16     GNU General Public License for more details.
17 
18     You should have received a copy of the GNU General Public License
19     along with this program; if not, write to the Free Software
20     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21 
22     nas_a.c
23 
24     Functions to play sound on the Network Audio System
25 
26 */
27 
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif /* HAVE_CONFIG_H */
31 
32 #include <audio/audiolib.h>
33 #include <audio/soundlib.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 
37 #ifndef NO_STRING_H /* for memmove */
38 #include <string.h>
39 #else
40 #include <strings.h>
41 #endif
42 
43 #include "timidity.h"
44 #include "common.h"
45 #include "output.h"
46 #include "controls.h"
47 #include "timer.h"
48 #include "instrum.h"
49 #include "playmidi.h"
50 #include "miditrace.h"
51 
52 #define BUFFERED_SECS 3
53 #define LOW_WATER_PERCENT 40
54 
55 #ifdef LITTLE_ENDIAN
56 #define LINEAR16_FORMAT AuFormatLinearSigned16LSB
57 #else
58 #define LINEAR16_FORMAT AuFormatLinearSigned16MSB
59 #endif
60 
61 
62 #define dpm nas_play_mode
63 
64 static int opendevaudio(void);
65 static void closedevaudio(void);
66 static int writedevaudio(char *buf, int32 len);
67 static int ioctldevaudio(int request, void *arg);
68 
69 PlayMode dpm=
70 {
71   DEFAULT_RATE,
72   PE_16BIT|PE_SIGNED,
73   PF_PCM_STREAM|PF_CAN_TRACE,
74   -1,
75   {0},
76   "Network Audio Server", 'n',
77   (char*)0,
78   opendevaudio,
79   closedevaudio,
80   writedevaudio,
81   ioctldevaudio
82 };
83 
84 struct DevAudio
85 {
86   AuServer *aud;
87   AuFlowID flow;
88   char *data;
89   AuUint32 used;
90   AuUint32 capacity;
91   AuBool synced;
92   AuBool finished;
93 };
94 
95 static struct DevAudio fd;
96 static int32 output_counter, play_counter, reset_samples;
97 static double play_start_time;
98 
reset_sample_counters()99 static void reset_sample_counters()
100 {
101     output_counter = play_counter = reset_samples = 0;
102 }
103 
current_samples(void)104 static int32 current_samples(void)
105 {
106     double realtime, es;
107 
108     realtime = get_current_calender_time();
109     if(play_counter == 0)
110     {
111 	play_start_time = realtime;
112 	return reset_samples;
113     }
114     es = dpm.rate * (realtime - play_start_time);
115     if(es >= play_counter)
116     {
117 	/* out of play counter */
118 	reset_samples += play_counter;
119 	play_counter = 0;
120 	play_start_time = realtime;
121 	return reset_samples;
122     }
123     if(es < 0)
124 	return 0; /* for safety */
125     return (int32)es + reset_samples;
126 }
127 
play(struct DevAudio * fd,AuUint32 len)128 static void play(struct DevAudio *fd, AuUint32 len)
129 {
130   int count;
131 
132   count = len;
133   if(!(dpm.encoding & PE_MONO))
134       count /= 2;
135   if(dpm.encoding & PE_16BIT)
136       count /= 2;
137 
138   if (len<fd->used)
139   {
140     AuWriteElement(fd->aud,fd->flow,0,len,fd->data,AuFalse,(AuStatus*)0);
141     memmove(fd->data,fd->data+len,fd->used-=len);
142   }
143   else
144   {
145     AuWriteElement(fd->aud,fd->flow,0,fd->used,fd->data,len!=fd->used,(AuStatus*)0);
146     fd->used=0;
147   }
148   fd->synced=AuTrue;
149 
150   current_samples();	/* Update sample counter */
151   play_counter += count;
152 }
153 
nas_eventHandler(AuServer * aud,AuEvent * ev,AuEventHandlerRec * handler)154 static AuBool nas_eventHandler(AuServer *aud, AuEvent *ev, AuEventHandlerRec *handler)
155 {
156   struct DevAudio *fd=(struct DevAudio*)handler->data;
157 
158   switch (ev->type)
159   {
160     case AuEventTypeMonitorNotify:
161     {
162       fd->finished=AuTrue;
163       break;
164     }
165     case AuEventTypeElementNotify:
166     {
167       AuElementNotifyEvent *event=(AuElementNotifyEvent*)ev;
168 
169       switch (event->kind)
170       {
171         case AuElementNotifyKindLowWater:
172         {
173           play(fd,event->num_bytes);
174           break;
175         }
176         case AuElementNotifyKindState: switch (event->cur_state)
177         {
178           case AuStatePause:
179           {
180             if (event->reason!=AuReasonUser) play(fd,event->num_bytes);
181             break;
182           }
183           case AuStateStop:
184           {
185             fd->finished=AuTrue;
186             break;
187           }
188         }
189       }
190     }
191   }
192   return AuTrue;
193 }
194 
syncdevaudio(void)195 static void syncdevaudio(void)
196 {
197   AuEvent ev;
198 
199   while ((!fd.synced) && (!fd.finished))
200   {
201     AuNextEvent(fd.aud,AuTrue,&ev);
202     AuDispatchEvent(fd.aud,&ev);
203   }
204   fd.synced=AuFalse;
205 }
206 
opendevaudio(void)207 static int opendevaudio(void)
208 {
209   AuDeviceID device=AuNone;
210   AuElement elements[2];
211   AuUint32 buf_samples;
212   int i;
213 
214   /* Only supported PE_16BIT|PE_SIGNED yet */
215   dpm.encoding = validate_encoding(dpm.encoding,
216 				   PE_16BIT|PE_SIGNED,
217 				   PE_BYTESWAP|PE_ULAW|PE_ALAW);
218 
219   if (!(fd.aud = AuOpenServer((char*)0, 0, NULL, 0, NULL, NULL))) return -1;
220   fd.capacity = 0;
221 
222   for (i=0; i<AuServerNumDevices(fd.aud); ++i)
223   {
224     if
225     (
226       AuDeviceKind(AuServerDevice(fd.aud,i))==AuComponentKindPhysicalOutput
227       && AuDeviceNumTracks(AuServerDevice(fd.aud,i))==((dpm.encoding&PE_MONO)?1:2)
228     )
229     {
230       device=AuDeviceIdentifier(AuServerDevice(fd.aud,i));
231       break;
232     }
233   }
234   if (device==AuNone) return -1;
235   if (!(fd.flow=AuCreateFlow(fd.aud,NULL))) return -1;
236   buf_samples = dpm.rate*BUFFERED_SECS;
237   AuMakeElementImportClient(&elements[0],dpm.rate,LINEAR16_FORMAT,(dpm.encoding&PE_MONO)?1:2,AuTrue,buf_samples,(AuUint32) (buf_samples/100*LOW_WATER_PERCENT),0,NULL);
238   AuMakeElementExportDevice(&elements[1],0,device,dpm.rate,AuUnlimitedSamples,0,NULL);
239   AuSetElements(fd.aud,fd.flow,AuTrue,2,elements,NULL);
240   AuRegisterEventHandler(fd.aud,AuEventHandlerIDMask,0,fd.flow,nas_eventHandler,(AuPointer)&fd);
241   fd.capacity = buf_samples*((dpm.encoding&PE_MONO)?1:2)*AuSizeofFormat(LINEAR16_FORMAT);
242   if ((fd.data=malloc(fd.capacity))==(char*)0) return -1;
243   fd.used = 0;
244   fd.synced = AuFalse;
245   fd.finished = AuFalse;
246   AuStartFlow(fd.aud,fd.flow,NULL);
247   dpm.fd = 0;
248   reset_sample_counters();
249   return 0;
250 }
251 
closedevaudio(void)252 static void closedevaudio(void)
253 {
254   if(dpm.fd != -1)
255   {
256     AuStopFlow(fd.aud, fd.flow, NULL);
257     while (!fd.finished) syncdevaudio();
258     AuCloseServer(fd.aud);
259     free(fd.data);
260     dpm.fd = 0;
261   }
262 }
263 
writedevaudio(char * buf,int32 len)264 static int writedevaudio(char *buf, int32 len)
265 {
266   int done = 0;
267 
268   while ((fd.used+(len-done))>fd.capacity)
269   {
270     int stillFits=fd.capacity-fd.used;
271 
272     memcpy(fd.data+fd.used,buf+done,stillFits);
273     done+=stillFits;
274     fd.used+=stillFits;
275     syncdevaudio();
276   }
277   memcpy(fd.data+fd.used,buf+done,len-done);
278   fd.used+=(len-done);
279   output_counter += len;
280   return len;
281 }
282 
ioctldevaudio(int request,void * arg)283 static int ioctldevaudio(int request, void *arg)
284 {
285   switch (request)
286   {
287     case PM_REQ_DISCARD:
288       if(!output_counter)
289 	  return 0;
290       AuStopFlow(fd.aud,fd.flow,NULL);
291 
292       /* Restart playing is not work correctly.
293        * I don't know what I should do, so I re-open audio server.
294        */
295 #if 0
296       AuStartFlow(fd.aud,fd.flow,NULL);
297       reset_sample_counters();
298       return 0;
299 #else
300       AuCloseServer(fd.aud);
301       free(fd.data);
302       return opendevaudio();
303 #endif
304 
305     case PM_REQ_FLUSH:
306       reset_sample_counters();
307       return 0;
308 
309     case PM_REQ_GETSAMPLES:
310       if(play_counter)
311 	  *(int *)arg = current_samples();
312       else
313 	  *(int *)arg = reset_samples;
314       return 0;
315 
316     case PM_REQ_PLAY_START:
317       reset_sample_counters();
318       return 0;
319   }
320   return -1;
321 }
322