1 /*
2 * madplay - MPEG audio decoder and player
3 * Copyright (C) 2000-2004 Robert Leslie
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 * $Id: audio_sun.c,v 1.32 2004/01/23 09:41:31 rob Exp $
20 */
21
22 # ifdef HAVE_CONFIG_H
23 # include "config.h"
24 # endif
25
26 # include "global.h"
27
28 # include <stdlib.h>
29 # include <unistd.h>
30 # include <fcntl.h>
31 # include <sys/ioctl.h>
32 # include <sys/audioio.h>
33 # include <string.h>
34 # include <errno.h>
35 # include <sys/types.h>
36
37 # ifdef HAVE_STROPTS_H
38 # include <stropts.h>
39 # endif
40
41 # include <sys/conf.h>
42 # include <mad.h>
43
44 # include "gettext.h"
45
46 # include "audio.h"
47
48 # if defined(WORDS_BIGENDIAN)
49 # define audio_pcm_s32 audio_pcm_s32be
50 # define audio_pcm_s24 audio_pcm_s24be
51 # define audio_pcm_s16 audio_pcm_s16be
52 # else
53 # define audio_pcm_s32 audio_pcm_s32le
54 # define audio_pcm_s24 audio_pcm_s24le
55 # define audio_pcm_s16 audio_pcm_s16le
56 # endif
57
58 # define AUDIO_DEVICE "/dev/audio"
59
60 static int sfd;
61 static audio_pcmfunc_t *audio_pcm;
62
63 static
init(struct audio_init * init)64 int init(struct audio_init *init)
65 {
66 if (init->path == 0)
67 init->path = getenv("AUDIODEV");
68
69 if (init->path == 0)
70 init->path = AUDIO_DEVICE;
71
72 sfd = open(init->path, O_WRONLY);
73 if (sfd == -1) {
74 audio_error = ":";
75 return -1;
76 }
77
78 return 0;
79 }
80
81 static
set_pause(int flag)82 int set_pause(int flag)
83 {
84 static int paused;
85 audio_info_t info;
86
87 if (flag != paused) {
88 paused = 0;
89
90 AUDIO_INITINFO(&info);
91 info.play.pause = flag;
92
93 if (ioctl(sfd, AUDIO_SETINFO, &info) == -1) {
94 audio_error = ":ioctl(AUDIO_SETINFO)";
95 return -1;
96 }
97
98 paused = flag;
99 }
100
101 return 0;
102 }
103
104 static
config(struct audio_config * config)105 int config(struct audio_config *config)
106 {
107 unsigned int bitdepth;
108 audio_info_t info;
109
110 bitdepth = config->precision & ~7;
111 if (bitdepth == 0)
112 bitdepth = 16;
113 else if (bitdepth > 32)
114 bitdepth = 32;
115
116 set_pause(0);
117
118 if (ioctl(sfd, AUDIO_DRAIN, 0) == -1) {
119 audio_error = ":ioctl(AUDIO_DRAIN)";
120 return -1;
121 }
122
123 AUDIO_INITINFO(&info);
124
125 info.play.sample_rate = config->speed;
126 info.play.channels = config->channels;
127 info.play.precision = bitdepth;
128 info.play.encoding = AUDIO_ENCODING_LINEAR;
129
130 if (ioctl(sfd, AUDIO_SETINFO, &info) == -1) {
131 audio_error = ":ioctl(AUDIO_SETINFO)";
132 return -1;
133 }
134
135 /* validate settings */
136
137 if (ioctl(sfd, AUDIO_GETINFO, &info) == -1) {
138 audio_error = ":ioctl(AUDIO_GETINFO)";
139 return -1;
140 }
141
142 config->channels = info.play.channels;
143 config->speed = info.play.sample_rate;
144 bitdepth = info.play.precision;
145
146 switch (bitdepth) {
147 case 8:
148 audio_pcm = audio_pcm_u8;
149 break;
150
151 case 16:
152 audio_pcm = audio_pcm_s16;
153 break;
154
155 case 24:
156 audio_pcm = audio_pcm_s24;
157 break;
158
159 case 32:
160 audio_pcm = audio_pcm_s32;
161 break;
162
163 default:
164 audio_error = _("unsupported bit depth");
165 return -1;
166 }
167
168 config->precision = bitdepth;
169
170 return 0;
171 }
172
173 static
output(unsigned char const * ptr,unsigned int len)174 int output(unsigned char const *ptr, unsigned int len)
175 {
176 while (len) {
177 int wrote;
178
179 wrote = write(sfd, ptr, len);
180 if (wrote == -1) {
181 if (errno == EINTR)
182 continue;
183 else {
184 audio_error = ":write";
185 return -1;
186 }
187 }
188
189 ptr += wrote;
190 len -= wrote;
191 }
192
193 return 0;
194 }
195
196 static
play(struct audio_play * play)197 int play(struct audio_play *play)
198 {
199 unsigned char data[MAX_NSAMPLES * 4 * 2];
200 unsigned int len;
201
202 set_pause(0);
203
204 len = audio_pcm(data, play->nsamples, play->samples[0], play->samples[1],
205 play->mode, play->stats);
206
207 return output(data, len);
208 }
209
210 static
flush(void)211 int flush(void)
212 {
213 # if defined(I_FLUSH)
214 if (ioctl(sfd, I_FLUSH, FLUSHW) == -1) {
215 audio_error = ":ioctl(I_FLUSH)";
216 return -1;
217 }
218 # elif defined(AUDIO_FLUSH)
219 if (ioctl(sfd, AUDIO_FLUSH) == -1) {
220 audio_error = ":ioctl(AUDIO_FLUSH)";
221 return -1;
222 }
223 # endif
224
225 return 0;
226 }
227
228 static
stop(struct audio_stop * stop)229 int stop(struct audio_stop *stop)
230 {
231 int result;
232
233 result = set_pause(1);
234
235 if (result == 0 && stop->flush && flush() == -1)
236 result = -1;
237
238 return result;
239 }
240
241 static
finish(struct audio_finish * finish)242 int finish(struct audio_finish *finish)
243 {
244 int result;
245
246 result = set_pause(0);
247
248 if (close(sfd) == -1 && result == 0) {
249 audio_error = ":close";
250 result = -1;
251 }
252
253 return result;
254 }
255
audio_sun(union audio_control * control)256 int audio_sun(union audio_control *control)
257 {
258 audio_error = 0;
259
260 switch (control->command) {
261 case AUDIO_COMMAND_INIT:
262 return init(&control->init);
263
264 case AUDIO_COMMAND_CONFIG:
265 return config(&control->config);
266
267 case AUDIO_COMMAND_PLAY:
268 return play(&control->play);
269
270 case AUDIO_COMMAND_STOP:
271 return stop(&control->stop);
272
273 case AUDIO_COMMAND_FINISH:
274 return finish(&control->finish);
275 }
276
277 return 0;
278 }
279