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