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 };
247 
sound_init_sun_device(void)248 int sound_init_sun_device(void)
249 {
250     return sound_register_device(&sun_device);
251 }
252 #endif
253