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 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
20
21 sun_a.c - Functions to play sound on a Sun and NetBSD /dev/audio.
22 Written by Masanao Izumo <mo@goice.co.jp>
23 */
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif /* HAVE_CONFIG_H */
28 #include <stdio.h>
29 #include <stdlib.h>
30 #ifndef NO_STRING_H
31 #include <string.h>
32 #endif
33 #include <fcntl.h>
34 #include <sys/ioctl.h>
35 #include <sys/types.h>
36 #include <sys/filio.h>
37 #include <sys/stat.h>
38 #include <signal.h>
39 #include <unistd.h>
40
41 #ifdef HAVE_STROPTS_H
42 #include <stropts.h>
43 #endif /* HAVE_STROPTS_H */
44
45 #if defined(HAVE_SYS_AUDIOIO_H)
46 #include <sys/audioio.h>
47 #elif defined(HAVE_SUN_AUDIOIO_H)
48 #include <sun/audioio.h>
49 #endif
50
51 #include "timidity.h"
52 #include "common.h"
53 #include "aenc.h"
54 #include "output.h"
55 #include "controls.h"
56
57 #if defined(__NetBSD__) || defined(__OpenBSD__)
58 #ifdef LITTLE_ENDIAN
59 #define AUDIO_LINEAR_TAG AUDIO_ENCODING_SLINEAR_LE
60 #else
61 #define AUDIO_LINEAR_TAG AUDIO_ENCODING_SLINEAR_BE
62 #endif
63 #else /* Sun */
64 #define AUDIO_LINEAR_TAG AUDIO_ENCODING_LINEAR
65 #endif
66
67 #ifdef LITTLE_ENDIAN
68 #define SUNAUDIO_AENC_SIGWORD AENC_SIGWORDL
69 #else
70 #define SUNAUDIO_AENC_SIGWORD AENC_SIGWORDB
71 #endif
72
73 #ifndef __NetBSD__
74 #define AUDIO_DEV "/dev/audio"
75 #else
76 #define AUDIO_DEV "/dev/sound"
77 #endif
78 #define AUDIO_CTLDEV "/dev/audioctl"
79
80
81 static int open_output(void); /* 0=success, 1=warning, -1=fatal error */
82 static void close_output(void);
83 static int output_data(char *buf, int32 bytes);
84 static int acntl(int request, void *arg);
85 static int output_counter, play_samples_offset;
86
87 /* export the playback mode */
88 #define dpm sun_play_mode
89 PlayMode dpm = {
90 DEFAULT_RATE, PE_16BIT|PE_SIGNED,
91 PF_PCM_STREAM|PF_CAN_TRACE,
92 -1,
93 {0,0,0,0,0}, /* no extra parameters so far */
94
95 #if defined(sun)
96 "Sun audio device",
97 #elif defined(__NetBSD__)
98 "NetBSD audio device",
99 #elif defined(__OpenBSD__)
100 "OpenBSD audio device",
101 #else
102 AUDIO_DEV,
103 #endif
104
105 'd',
106 AUDIO_DEV,
107 open_output,
108 close_output,
109 output_data,
110 acntl
111 };
112 static int audioctl_fd = -1;
113
sun_audio_encoding(int enc)114 static int sun_audio_encoding(int enc)
115 {
116 if(enc & PE_ULAW)
117 return AUDIO_ENCODING_ULAW;
118 if(enc & PE_ALAW)
119 return AUDIO_ENCODING_ALAW;
120 #ifdef AUDIO_ENCODING_LINEAR8
121 if(!(enc & PE_16BIT))
122 return AUDIO_ENCODING_LINEAR8;
123 #else
124 if(!(enc & PE_16BIT))
125 return 105;
126 #endif /* AUDIO_ENCODING_LINEAR8 */
127 return AUDIO_LINEAR_TAG;
128 }
129
sun_audio_getinfo(audio_info_t * auinfo)130 static int sun_audio_getinfo(audio_info_t *auinfo)
131 {
132 AUDIO_INITINFO(auinfo);
133 return ioctl(audioctl_fd, AUDIO_GETINFO, auinfo);
134 }
135
sun_audio_setinfo(audio_info_t * auinfo)136 static int sun_audio_setinfo(audio_info_t *auinfo)
137 {
138 return ioctl(dpm.fd, AUDIO_SETINFO, auinfo);
139 }
140
open_output(void)141 static int open_output(void)
142 {
143 int include_enc = 0, exclude_enc = PE_BYTESWAP;
144 struct stat sb;
145 audio_info_t auinfo;
146 char *audio_dev, *audio_ctl_dev, *tmp_audio;
147
148
149 /* See if the AUDIODEV environment variable is defined, and set the
150 audio device accordingly - Lalit Chhabra 23/Oct/2001 */
151 if((audio_dev = getenv("AUDIODEV")) != NULL)
152 {
153 dpm.id_name = safe_malloc(strlen(audio_dev)+1);
154 dpm.name = safe_malloc(strlen(audio_dev)+1);
155 strcpy(dpm.name, audio_dev);
156 strcpy(dpm.id_name, audio_dev);
157
158 tmp_audio = safe_malloc(strlen(audio_dev) + 3);
159 audio_ctl_dev = safe_malloc(strlen(audio_dev) + 3);
160
161 strcpy(tmp_audio, audio_dev);
162 strcpy(audio_ctl_dev, strcat(tmp_audio, "ctl"));
163 }
164 else
165 {
166 audio_ctl_dev = safe_malloc(strlen(AUDIO_CTLDEV) + 3);
167 strcpy(audio_ctl_dev, AUDIO_CTLDEV);
168 }
169
170 output_counter = play_samples_offset = 0;
171
172 /* Open the audio device */
173 if((audioctl_fd = open(audio_ctl_dev, O_RDWR)) < 0)
174 {
175 ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
176 "%s: %s", audio_ctl_dev, strerror(errno));
177 return -1;
178 }
179
180 /* ############## */
181 #if 0
182 if((dpm.fd = open(dpm.name, O_WRONLY | O_NDELAY)) == -1)
183 {
184 ctl->cmsg(CMSG_WARNING, VERB_NORMAL,
185 "%s: %s", dpm.name, strerror(errno));
186 if(errno == EBUSY)
187 {
188 if((dpm.fd = open(dpm.name, O_WRONLY)) == -1)
189 {
190 ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
191 "%s: %s", dpm.name, strerror(errno));
192 close_output();
193 return -1;
194 }
195 }
196 }
197 #endif
198
199 if((dpm.fd = open(dpm.name, O_WRONLY)) == -1)
200 {
201 ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
202 "%s: %s", dpm.name, strerror(errno));
203 close_output();
204 return -1;
205 }
206
207 if(stat(dpm.name, &sb) < 0)
208 {
209 ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
210 "%s: %s", dpm.name, strerror(errno));
211 close_output();
212 return -1;
213 }
214
215 if(!S_ISCHR(sb.st_mode))
216 {
217 ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
218 "%s: Not a audio device", dpm.name);
219 close_output();
220 return -1;
221 }
222
223 if(sun_audio_getinfo(&auinfo) < 0)
224 {
225 /* from Francesco Zanichelli's */
226 /* If it doesn't give info, it probably won't take requests
227 either. Assume it's an old device that does 8kHz uLaw only.
228
229 Disclaimer: I don't know squat about the various Sun audio
230 devices, so if this is not what we should do, I'll gladly
231 accept modifications. */
232 ctl->cmsg(CMSG_WARNING, VERB_NORMAL, "Cannot inquire %s", dpm.name);
233 include_enc = PE_ULAW|PE_ALAW|PE_MONO;
234 exclude_enc = PE_SIGNED|PE_16BIT|PE_BYTESWAP;
235 dpm.encoding = validate_encoding(dpm.encoding,
236 include_enc, exclude_enc);
237 if(dpm.rate != 8000)
238 ctl->cmsg(CMSG_WARNING, VERB_VERBOSE,
239 "Sample rate is changed %d to 8000",
240 dpm.rate);
241 dpm.rate = 8000;
242 return 1;
243 }
244
245 if(!(dpm.encoding & PE_16BIT))
246 exclude_enc |= PE_SIGNED; /* Always unsigned */
247 dpm.encoding = validate_encoding(dpm.encoding, include_enc, exclude_enc);
248
249 AUDIO_INITINFO(&auinfo);
250 auinfo.play.sample_rate = dpm.rate;
251 auinfo.play.channels = (dpm.encoding & PE_MONO) ? 1 : 2;
252 auinfo.play.encoding = sun_audio_encoding(dpm.encoding);
253 auinfo.play.precision = (dpm.encoding & PE_16BIT) ? 16 : 8;
254
255 if(sun_audio_setinfo(&auinfo) == -1)
256 {
257 ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
258 "rate=%d, channels=%d, precision=%d, encoding=%s",
259 auinfo.play.sample_rate, auinfo.play.channels,
260 auinfo.play.precision, output_encoding_string(dpm.encoding));
261 close_output();
262 return -1;
263 }
264
265 return 0;
266 }
267
close_output(void)268 static void close_output(void)
269 {
270 if(dpm.fd != -1)
271 {
272 close(dpm.fd);
273 dpm.fd = -1;
274 }
275 if(audioctl_fd != -1)
276 {
277 close(audioctl_fd);
278 audioctl_fd = -1;
279 }
280 }
281
output_data(char * buff,int32 nbytes)282 int output_data(char *buff, int32 nbytes)
283 {
284 int n;
285
286 retry_write:
287 n = write(dpm.fd, buff, nbytes);
288
289 if(n < 0)
290 {
291 ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: %s",
292 dpm.name, strerror(errno));
293 if(errno == EINTR || errno == EAGAIN)
294 goto retry_write;
295 return -1;
296 }
297
298 output_counter += n;
299
300 return n;
301 }
302
303
304 #if !defined(I_FLUSH) || !defined(FLUSHW)
305 #if defined(AUDIO_FLUSH) /* BSD extension */
sun_discard_playing(void)306 static int sun_discard_playing(void)
307 {
308 if(ioctl(dpm.fd, AUDIO_FLUSH, NULL) < 0)
309 {
310 ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: (ioctl) %s",
311 dpm.name, strerror(errno));
312 return -1;
313 }
314 return 0;
315 }
316 #else
null_proc()317 static void null_proc(){}
sun_discard_playing(void)318 static int sun_discard_playing(void)
319 {
320 void (* orig_alarm_handler)();
321
322 orig_alarm_handler = signal(SIGALRM, null_proc);
323 ualarm(10000, 10000);
324 close_output();
325 ualarm(0, 0);
326 signal(SIGALRM, orig_alarm_handler);
327 return open_output();
328 }
329 #endif /* AUDIO_FLASH */
330 #else
sun_discard_playing(void)331 static int sun_discard_playing(void)
332 {
333 if(ioctl(dpm.fd, I_FLUSH, FLUSHW) < 0)
334 {
335 ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: (ioctl) %s",
336 dpm.name, strerror(errno));
337 return -1;
338 }
339 return 0;
340 }
341 #endif
342
acntl(int request,void * arg)343 static int acntl(int request, void *arg)
344 {
345 audio_info_t auinfo;
346 int i;
347
348 switch(request)
349 {
350 case PM_REQ_GETQSIZ:
351 if(ioctl(audioctl_fd, AUDIO_GETINFO, &auinfo) < 0)
352 return -1;
353 *((int *)arg) = auinfo.play.buffer_size;
354 return 0;
355
356 #if defined(__NetBSD__) || defined(__OpenBSD__)
357 case PM_REQ_GETFRAGSIZ:
358 if(ioctl(audioctl_fd, AUDIO_GETINFO, &auinfo) < 0)
359 return -1;
360 *((int *)arg) = auinfo.blocksize;
361 return 0;
362 #endif
363
364 case PM_REQ_OUTPUT_FINISH:
365 return ioctl(audioctl_fd, AUDIO_DRAIN, NULL);
366
367 case PM_REQ_GETFILLED:
368 if(ioctl(audioctl_fd, AUDIO_GETINFO, &auinfo) < 0)
369 return -1;
370 #if defined(__NetBSD__) || defined(__OpenBSD__)
371 *((int *)arg) = auinfo.play.seek;
372 #else
373 if(auinfo.play.samples == play_samples_offset)
374 return -1; /* auinfo.play.samples is not active */
375 i = output_counter;
376 if(!(dpm.encoding & PE_MONO)) i >>= 1;
377 if(dpm.encoding & PE_16BIT) i >>= 1;
378 *((int *)arg) = i - (auinfo.play.samples - play_samples_offset);
379 #endif
380 return 0;
381
382 case PM_REQ_GETSAMPLES:
383 if(ioctl(audioctl_fd, AUDIO_GETINFO, &auinfo) < 0)
384 return -1;
385 if(auinfo.play.samples == play_samples_offset)
386 return -1; /* auinfo.play.samples is not active */
387 *((int *)arg) = auinfo.play.samples - play_samples_offset;
388 return 0;
389
390 case PM_REQ_DISCARD:
391 if(ioctl(audioctl_fd, AUDIO_GETINFO, &auinfo) != -1)
392 play_samples_offset = auinfo.play.samples;
393 output_counter = 0;
394 return sun_discard_playing();
395
396 case PM_REQ_FLUSH:
397 if(ioctl(audioctl_fd, AUDIO_GETINFO, &auinfo) != -1)
398 play_samples_offset = auinfo.play.samples;
399 output_counter = 0;
400 return 0;
401
402 case PM_REQ_RATE: {
403 audio_info_t auinfo;
404 int rate;
405
406 rate = *(int *)arg;
407 AUDIO_INITINFO(&auinfo);
408 auinfo.play.sample_rate = rate;
409 if(sun_audio_setinfo(&auinfo) == -1)
410 return -1;
411 play_mode->rate = rate;
412 return 0;
413 }
414
415 case PM_REQ_PLAY_START: /* Called just before playing */
416 case PM_REQ_PLAY_END: /* Called just after playing */
417 return 0;
418 }
419 return -1;
420 }
421