1 /*
2 * soundsun.c - Implementation of the Sun/Solaris/NetBSD sound device
3 *
4 * Written by
5 * Teemu Rantanen <tvr@cs.hut.fi>
6 *
7 * NetBSD patch by
8 * Krister Walfridsson <cato@df.lth.se>
9 *
10 * This file is part of VICE, the Versatile Commodore Emulator.
11 * See README for copyright notice.
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
26 * 02111-1307 USA.
27 *
28 */
29
30 #include "vice.h"
31
32 #if defined(HAVE_SYS_AUDIOIO_H) && !defined(__OpenBSD__)
33
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <sys/uio.h>
37
38 #ifdef HAVE_UNISTD_H
39 #include <unistd.h>
40 #endif
41
42 #ifdef HAVE_FCNTL_H
43 #include <fcntl.h>
44 #endif
45 #ifdef HAVE_SYS_STAT_H
46 #include <sys/stat.h>
47 #endif
48 #ifdef HAVE_SYS_TYPES_H
49 #include <sys/types.h>
50 #endif
51
52 #include <sys/audioio.h>
53
54 #if defined(__NetBSD__)
55 #include <sys/ioctl.h> /* For ioctl and _IOWR */
56 #include <string.h> /* For memset */
57 #endif
58
59 #include "log.h"
60 #include "sound.h"
61
62 static int sun_bufferspace(void);
63
64 static int sun_fd = -1;
65 static int sun_8bit = 0;
66 static int sun_channels = 1;
67 static int sun_bufsize = 0;
68 #if !defined(__NetBSD__)
69 static int sun_written = 0;
70 #endif
71
toulaw8(int16_t data)72 static int toulaw8(int16_t data)
73 {
74 int v, s, a;
75
76 a = data / 8;
77
78 v = (a < 0 ? -a : a);
79 s = (a < 0 ? 0 : 0x80);
80
81 if (v >= 4080) {
82 a = 0;
83 } else if (v >= 2032) {
84 a = 0x0f - (v - 2032) / 128;
85 } else if (v >= 1008) {
86 a = 0x1f - (v - 1008) / 64;
87 } else if (v >= 496) {
88 a = 0x2f - (v - 496) / 32;
89 } else if (v >= 240) {
90 a = 0x3f - (v - 240) / 16;
91 } else if (v >= 112) {
92 a = 0x4f - (v - 112) / 8;
93 } else if (v >= 48) {
94 a = 0x5f - (v - 48) / 4;
95 } else if (v >= 16) {
96 a = 0x6f - (v - 16) / 2;
97 } else {
98 a = 0x7f - v;
99 }
100
101 a |= s;
102
103 return a;
104 }
105
106
sun_init(const char * param,int * speed,int * fragsize,int * fragnr,int * channels)107 static int sun_init(const char *param, int *speed, int *fragsize, int *fragnr, int *channels)
108 {
109 int st;
110 struct audio_info info;
111
112 #if !defined(__NetBSD__)
113 /* No stereo capability. */
114 *channels = 1;
115 #endif
116
117 if (!param) {
118 if (getenv("AUDIODEV")) {
119 param = (const char *)getenv("AUDIODEV");
120 } else {
121 param = "/dev/audio";
122 }
123 }
124 sun_fd = open(param, O_WRONLY, 0777);
125 if (sun_fd < 0) {
126 return 1;
127 }
128 AUDIO_INITINFO(&info);
129 info.play.sample_rate = *speed;
130 info.play.channels = *channels;
131 info.play.precision = 16;
132 info.play.encoding = AUDIO_ENCODING_LINEAR;
133 st = ioctl(sun_fd, AUDIO_SETINFO, &info);
134 if (st < 0) {
135 AUDIO_INITINFO(&info);
136 info.play.sample_rate = 8000;
137 info.play.channels = 1;
138 info.play.precision = 8;
139 info.play.encoding = AUDIO_ENCODING_ULAW;
140 st = ioctl(sun_fd, AUDIO_SETINFO, &info);
141 if (st < 0) {
142 goto fail;
143 }
144 sun_8bit = 1;
145 *speed = 8000;
146 *channels = 1;
147 log_message(LOG_DEFAULT, "Playing 8 bit ulaw at 8000Hz");
148 }
149 sun_bufsize = (*fragsize) * (*fragnr);
150 #if !defined(__NetBSD__)
151 sun_written = 0;
152 #endif
153 sun_channels = *channels;
154 return 0;
155 fail:
156 close(sun_fd);
157 sun_fd = -1;
158 return 1;
159 }
160
sun_write(int16_t * pbuf,size_t nr)161 static int sun_write(int16_t *pbuf, size_t nr)
162 {
163 unsigned int total, i, now;
164 if (sun_8bit) {
165 /* XXX: ugly to change contents of the buffer */
166 for (i = 0; i < nr; i++) {
167 ((char *)pbuf)[i] = toulaw8(pbuf[i]);
168 }
169 total = nr;
170 } else {
171 total = nr * sizeof(int16_t) * sun_channels;
172 }
173 for (i = 0; i < total; i += now)
174 {
175 now = write(sun_fd, (char *)pbuf + i, total - i);
176 if (now <= 0) {
177 return 1;
178 }
179 }
180 #if !defined(__NetBSD__)
181 sun_written += nr;
182 #endif
183
184 while (sun_bufferspace() < 0) {
185 usleep(5000);
186 }
187
188 return 0;
189 }
190
sun_bufferspace(void)191 static int sun_bufferspace(void)
192 {
193 int st;
194 #if defined(__NetBSD__)
195 int size;
196 #endif
197 struct audio_info info;
198 /* ioctl(fd, AUDIO_GET_STATUS, &info) yields number of played samples
199 in info.play.samples. */
200 st = ioctl(sun_fd, AUDIO_GETINFO, &info);
201 if (st < 0) {
202 return -1;
203 }
204 #if defined(__NetBSD__)
205 size = (sun_8bit ? 1 : 2) * sun_channels;
206 return sun_bufsize - info.play.seek / size;
207 #else
208 return sun_bufsize - (sun_written - info.play.samples);
209 #endif
210 }
211
sun_close(void)212 static void sun_close(void)
213 {
214 close(sun_fd);
215 sun_fd = -1;
216 sun_8bit = 0;
217 sun_bufsize = 0;
218 #if !defined(__NetBSD__)
219 sun_written = 0;
220 #endif
221 sun_channels = 1;
222 }
223
224
225 static sound_device_t sun_device =
226 {
227 #if defined(__NetBSD__)
228 "netbsd",
229 #else
230 "sun",
231 #endif
232 sun_init,
233 sun_write,
234 NULL,
235 NULL,
236 sun_bufferspace,
237 sun_close,
238 NULL,
239 NULL,
240 1,
241 #if !defined(__NetBSD__)
242 1,
243 #else
244 2,
245 #endif
246 false
247 };
248
sound_init_sun_device(void)249 int sound_init_sun_device(void)
250 {
251 return sound_register_device(&sun_device);
252 }
253 #endif
254