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