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_empeg.c,v 1.16 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 <unistd.h>
29 # include <fcntl.h>
30 # include <sys/ioctl.h>
31 # include <sys/soundcard.h>
32 # include <errno.h>
33 # include <string.h>
34 # include <mad.h>
35
36 # include "audio.h"
37
38 # define AUDIO_DEVICE "/dev/dsp"
39 # define AUDIO_FILLSZ 4608
40
41 static int sfd;
42
43 static
init(struct audio_init * init)44 int init(struct audio_init *init)
45 {
46 if (init->path == 0)
47 init->path = AUDIO_DEVICE;
48
49 sfd = open(init->path, O_WRONLY);
50 if (sfd == -1) {
51 audio_error = ":";
52 return -1;
53 }
54
55 return 0;
56 }
57
58 static
config(struct audio_config * config)59 int config(struct audio_config *config)
60 {
61 /*
62 * The empeg-car's audio device is locked at 44100 Hz stereo 16-bit
63 * signed little-endian; no configuration is necessary or possible,
64 * but we may need to resample the output and/or convert to stereo.
65 */
66
67 config->channels = 2;
68 config->speed = 44100;
69 config->precision = 16;
70
71 return 0;
72 }
73
74 static
output(unsigned char const * ptr,unsigned int len)75 int output(unsigned char const *ptr, unsigned int len)
76 {
77 while (len) {
78 int wrote;
79
80 wrote = write(sfd, ptr, len);
81 if (wrote == -1) {
82 if (errno == EINTR)
83 continue;
84 else {
85 audio_error = ":write";
86 return -1;
87 }
88 }
89
90 ptr += wrote;
91 len -= wrote;
92 }
93
94 return 0;
95 }
96
97 static
buffer(unsigned char const * ptr,unsigned int len)98 int buffer(unsigned char const *ptr, unsigned int len)
99 {
100 static unsigned char hold[AUDIO_FILLSZ];
101 static unsigned int held;
102 unsigned int left, grab;
103
104 if (len == 0) {
105 if (held) {
106 memset(&hold[held], 0, sizeof(hold) - held);
107 held = 0;
108
109 return output(hold, sizeof(hold));
110 }
111
112 return 0;
113 }
114
115 if (held == 0 && len == sizeof(hold))
116 return output(ptr, len);
117
118 left = sizeof(hold) - held;
119
120 while (len) {
121 grab = len < left ? len : left;
122
123 memcpy(&hold[held], ptr, grab);
124 held += grab;
125 left -= grab;
126
127 ptr += grab;
128 len -= grab;
129
130 if (left == 0) {
131 if (output(hold, sizeof(hold)) == -1)
132 return -1;
133
134 held = 0;
135 left = sizeof(hold);
136 }
137 }
138
139 return 0;
140 }
141
142 # define drain() buffer(0, 0)
143
144 static
play(struct audio_play * play)145 int play(struct audio_play *play)
146 {
147 unsigned char data[MAX_NSAMPLES * 2 * 2];
148 unsigned int len;
149
150 len = audio_pcm_s16le(data, play->nsamples,
151 play->samples[0], play->samples[1],
152 play->mode, play->stats);
153
154 return buffer(data, len);
155 }
156
157 static
stop(struct audio_stop * stop)158 int stop(struct audio_stop *stop)
159 {
160 return 0;
161 }
162
163 static
finish(struct audio_finish * finish)164 int finish(struct audio_finish *finish)
165 {
166 int result = 0;
167
168 if (drain() == -1)
169 result = -1;
170
171 if (close(sfd) == -1 && result == 0) {
172 audio_error = ":close";
173 result = -1;
174 }
175
176 return result;
177 }
178
audio_empeg(union audio_control * control)179 int audio_empeg(union audio_control *control)
180 {
181 audio_error = 0;
182
183 switch (control->command) {
184 case AUDIO_COMMAND_INIT:
185 return init(&control->init);
186
187 case AUDIO_COMMAND_CONFIG:
188 return config(&control->config);
189
190 case AUDIO_COMMAND_PLAY:
191 return play(&control->play);
192
193 case AUDIO_COMMAND_STOP:
194 return stop(&control->stop);
195
196 case AUDIO_COMMAND_FINISH:
197 return finish(&control->finish);
198 }
199
200 return 0;
201 }
202