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 aRts_a.c by Peter L Jones <peter@drealm.org.uk>
21 based on esd_a.c
22
23 Functions to play sound through aRts
24 */
25
26 /* 2003.06.05 mput <root@mput.dip.jp>
27 * I and Masanao Izumo had different codes. Mine was implemented
28 * by Peter L Jones <peter@drealm.org.uk>, which was posted to
29 * linux-audio-develpers ML. Izumo's was written by Bernhard
30 * "Bero" Rosenkraenzer <bero@redhat.com>. Both worked correctly,
31 * but differed a bit. I have mergerd Bero's code into Peter's.
32 */
33
34 #ifdef HAVE_CONFIG_H
35 #include "config.h"
36 #endif /* HAVE_CONFIG_H */
37 #define _GNU_SOURCE
38 #include <stdio.h>
39 #include <unistd.h>
40 #include <fcntl.h>
41
42 #ifndef NO_STRING_H
43 #include <string.h>
44 #else
45 #include <strings.h>
46 #endif
47
48 #include <artsc.h>
49
50 #include "timidity.h"
51 #include "common.h"
52 #include "output.h"
53 #include "controls.h"
54 #include "timer.h"
55 #include "instrum.h"
56 #include "playmidi.h"
57 #include "miditrace.h"
58
59 static int arts_init_state = 0; /* 0=no init, 1=arts_init, 2=arts_free */
60 static arts_stream_t stream = 0;
61 static int server_buffer = 0;
62 static int output_count = 0;
63
64 static int open_output(void); /* 0=success, 1=warning, -1=fatal error */
65 static void close_output(void);
66 static int output_data(char *buf, int32 nbytes);
67 static int acntl(int request, void *arg);
68
69 /* export the playback mode. aRts cannot support auto-detection properly
70 * see TiMidity bug report #35 on Kagemai. Do not add any functionality
71 * that would require TiMidity to call arts_init() again after an
72 * arts_free(), it will blow up */
73
74 #define dpm arts_play_mode
75
76 PlayMode dpm = {
77 /*rate*/ DEFAULT_RATE,
78 /*encoding*/ PE_16BIT|PE_SIGNED,
79 /*flag*/ PF_PCM_STREAM/*|PF_BUFF_FRAGM_OPT*//**/,
80 /*fd*/ -1,
81 /*extra_param*/ {0}, /* default: get all the buffer fragments you can */
82 /*id*/ "aRts",
83 /*id char*/ 'R',
84 /*name*/ "arts",
85 open_output,
86 close_output,
87 output_data,
88 acntl,
89 };
90
91 /*************************************************************************/
92 /* We currently only honor the PE_MONO bit, and the sample rate. */
93
open_output(void)94 static int open_output(void)
95 {
96 int i, include_enc, exclude_enc;
97 int sample_width, channels;
98
99 include_enc = 0;
100 exclude_enc = PE_ULAW|PE_ALAW|PE_BYTESWAP; /* They can't mean these */
101 if(dpm.encoding & PE_16BIT)
102 include_enc |= PE_SIGNED;
103 else
104 exclude_enc |= PE_SIGNED;
105 dpm.encoding = validate_encoding(dpm.encoding, include_enc, exclude_enc);
106 sample_width = (dpm.encoding & PE_16BIT) ? 16 : 8;
107 channels = (dpm.encoding & PE_MONO) ? 1 : 2;
108
109 /* Open the audio device */
110 switch (arts_init_state) {
111 case 0:
112 if((i = arts_init()) != 0)
113 {
114 ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: %s",
115 dpm.name, arts_error_text(i));
116 return -1;
117 }
118 arts_init_state = 1;
119 break;
120 case 2:
121 ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
122 "TiMidity aRts bug: open_output() after close_output() not supported");
123 return -1;
124 }
125 stream = arts_play_stream(dpm.rate,
126 LE_LONG(sample_width),
127 channels,
128 "timidity");
129 if(stream == NULL)
130 {
131 ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: %s",
132 dpm.name, strerror(errno));
133 return -1;
134 }
135 arts_stream_set(stream, ARTS_P_BLOCKING, 1);
136
137 server_buffer = arts_stream_get(stream, ARTS_P_SERVER_LATENCY) * dpm.rate * (sample_width/8) * channels / 1000;
138 output_count = 0;
139 return 0;
140 /* "this aRts function isnot yet implemented"
141 *
142 if (dpm.extra_param[0]) {
143 i = arts_stream_set(stream,
144 ARTS_P_PACKET_COUNT,
145 dpm.extra_param[0]);
146 if (i < 0) {
147 ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: %s",
148 dpm.name, arts_error_text(i));
149 return 1;
150 }
151 }
152 return 0;
153 *
154 */
155 }
156
output_data(char * buf,int32 nbytes)157 static int output_data(char *buf, int32 nbytes)
158 {
159 int n;
160
161 if (stream == 0) return -1;
162
163 while(nbytes > 0)
164 {
165 if((n = arts_write(stream, buf, nbytes)) < 0)
166 {
167 ctl->cmsg(CMSG_WARNING, VERB_VERBOSE,
168 "%s: %s", dpm.name, arts_error_text(n));
169 if(errno == EWOULDBLOCK)
170 {
171 /* It is possible to come here because of bug of the
172 * sound driver.
173 */
174 continue;
175 }
176 return -1;
177 }
178 output_count += n;
179 buf += n;
180 nbytes -= n;
181 }
182
183 return 0;
184 }
185
close_output(void)186 static void close_output(void)
187 {
188 if(stream == 0)
189 return;
190 arts_close_stream(stream);
191 arts_free();
192 arts_init_state = 2;
193 stream = 0;
194 }
195
acntl(int request,void * arg)196 static int acntl(int request, void *arg)
197 {
198 int tmp, tmp1, samples;
199 switch(request)
200 {
201 case PM_REQ_DISCARD: /* Discard stream */
202 arts_close_stream(stream);
203 stream=NULL;
204 return 0;
205 case PM_REQ_RATE: /* Change sample rate */
206 arts_close_stream(stream);
207 tmp = (dpm.encoding & PE_16BIT) ? 16 : 8;
208 tmp1 = (dpm.encoding & PE_MONO) ? 1 : 2;
209 stream = arts_play_stream(*(int*)arg,
210 LE_LONG(tmp),
211 tmp1,
212 "timidity");
213 server_buffer = arts_stream_get(stream, ARTS_P_SERVER_LATENCY) * dpm.rate * (tmp/8) * tmp1 / 1000;
214 return 0;
215 case PM_REQ_GETQSIZ: /* Get maximum queue size */
216 *(int*)arg = arts_stream_get(stream, ARTS_P_BUFFER_SIZE);
217 return 0;
218 case PM_REQ_SETQSIZ: /* Set queue size */
219 *(int*)arg = arts_stream_set(stream, ARTS_P_BUFFER_SIZE, *(int*)arg);
220 return 0;
221 case PM_REQ_GETFRAGSIZ: /* Get device fragment size */
222 *(int*)arg = arts_stream_get(stream, ARTS_P_PACKET_SIZE);
223 return 0;
224 case PM_REQ_GETSAMPLES: /* Get current play sample */
225 tmp = arts_stream_get(stream, ARTS_P_BUFFER_SIZE) -
226 arts_stream_get(stream, ARTS_P_BUFFER_SPACE) +
227 server_buffer;
228 samples = output_count - tmp;
229 if(samples < 0)
230 samples = 0;
231 if(!(dpm.encoding & PE_MONO)) samples >>= 1;
232 if(dpm.encoding & PE_16BIT) samples >>= 1;
233 *(int*)arg = samples;
234 return 0;
235 case PM_REQ_GETFILLABLE: /* Get fillable device queue size */
236 *(int*)arg = arts_stream_get(stream, ARTS_P_BUFFER_SPACE);
237 return 0;
238 case PM_REQ_GETFILLED: /* Get filled device queue size */
239 *(int*)arg = arts_stream_get(stream, ARTS_P_BUFFER_SIZE) - arts_stream_get(stream, ARTS_P_BUFFER_SPACE);
240 return 0;
241 /* The following are not (yet) implemented: */
242 case PM_REQ_FLUSH: /* Wait until playback is complete */
243 case PM_REQ_MIDI: /* Send MIDI event */
244 case PM_REQ_INST_NAME: /* Get instrument name */
245 return -1;
246 case PM_REQ_OUTPUT_FINISH: /* Sent after last output_data */
247 case PM_REQ_PLAY_START: /* Called just before playing */
248 case PM_REQ_PLAY_END: /* Called just after playing */
249 return 0;
250 }
251 return -1;
252 }
253