1 /*
2 * snd_sun.c -- SUN Audio driver for BSD and SunOS
3 * Based on the code from Quake1 and the DarkPlaces project, with small
4 * adaptations to make it work with Hexen II: Hammer of Thyrion (uHexen2)
5 * $Id: snd_sun.c 4980 2012-10-03 07:55:28Z sezero $
6 *
7 * Copyright (C) 1996-1997 Id Software, Inc.
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or (at
12 * your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 *
18 * See the GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
25 #include "quakedef.h"
26 #include "snd_sys.h"
27
28 #if HAVE_SUN_SOUND
29
30 #include "snd_sun.h"
31
32 #undef _SUNAUDIO_BSD
33 #undef _SUNAUDIO_SUNOS
34 #if defined(__sun) || defined(sun)
35 #define _SUNAUDIO_BSD 0
36 #define _SUNAUDIO_SUNOS 1
37 #else /* NetBSD and OpenBSD */
38 #define _SUNAUDIO_BSD 1
39 #define _SUNAUDIO_SUNOS 0
40 #endif
41
42 #include <sys/param.h>
43 #include <sys/ioctl.h>
44 #include <sys/audioio.h>
45 #include <fcntl.h>
46 #include <unistd.h>
47
48 #if _SUNAUDIO_SUNOS
49
50 #define FORMAT_U8 AUDIO_ENCODING_LINEAR8
51 #define FORMAT_S16 AUDIO_ENCODING_LINEAR
52
53 #elif _SUNAUDIO_BSD
54
55 #include <paths.h>
56
57 #define FORMAT_U8 AUDIO_ENCODING_LINEAR8
58 #if ENDIAN_RUNTIME_DETECT
59 static int FORMAT_S16;
60 #elif (BYTE_ORDER == BIG_ENDIAN)
61 #define FORMAT_S16 AUDIO_ENCODING_SLINEAR_BE
62 #elif (BYTE_ORDER == LITTLE_ENDIAN)
63 #define FORMAT_S16 AUDIO_ENCODING_SLINEAR_LE
64 #else
65 #error "Unsupported endianness."
66 #endif /* BYTE_ORDER */
67
68 #endif /* _SUNAUDIO_BSD */
69
70 static char s_sun_driver[] = "SunAudio";
71
72 static int audio_fd = -1;
73
74 #define SND_BUFF_SIZE 8192
75 static unsigned char dma_buffer [SND_BUFF_SIZE];
76 static unsigned char writebuf [1024];
77 static int wbufp;
78
79
S_SUN_Init(dma_t * dma)80 static qboolean S_SUN_Init (dma_t *dma)
81 {
82 const char *snddev;
83 audio_info_t info;
84
85 #if ENDIAN_RUNTIME_DETECT && _SUNAUDIO_BSD
86 switch (host_byteorder)
87 {
88 case BIG_ENDIAN:
89 FORMAT_S16 = AUDIO_ENCODING_SLINEAR_BE;
90 break;
91 case LITTLE_ENDIAN:
92 FORMAT_S16 = AUDIO_ENCODING_SLINEAR_LE;
93 break;
94 default:
95 Sys_Error("%s: Unsupported byte order.", __thisfunc__);
96 break;
97 }
98 #endif /* ENDIAN_RUNTIME_DETECT */
99
100 /* Open the audio device */
101 #if defined(_PATH_SOUND)
102 snddev = _PATH_SOUND;
103 #elif _SUNAUDIO_SUNOS
104 snddev = "/dev/audio";
105 #else /* bsd */
106 snddev = "/dev/sound";
107 #endif
108 audio_fd = open (snddev, O_WRONLY | O_NDELAY | O_NONBLOCK);
109 if (audio_fd == -1)
110 {
111 Con_Printf("Can't open the sound device (%s)\n", snddev);
112 return false;
113 }
114
115 memset ((void *) dma, 0, sizeof(dma_t));
116 shm = dma;
117
118 AUDIO_INITINFO (&info);
119
120 /* these desired values are decided in snd_dma.c according
121 * to the defaults and the user's command line parameters. */
122 info.play.sample_rate = desired_speed;
123 info.play.channels = desired_channels;
124 info.play.precision = desired_bits;
125 info.play.encoding = (desired_bits == 8) ? FORMAT_U8 : FORMAT_S16;
126 if (ioctl(audio_fd, AUDIO_SETINFO, &info) != 0)
127 {
128 /* TODO: also try other options of sampling
129 * rate and format upon failure??? */
130 Con_Printf("Couldn't set desired sound output format (%d bit, %s, %d Hz)\n",
131 desired_bits, (desired_channels == 2) ? "stereo" : "mono", desired_speed);
132 close (audio_fd);
133 shm = NULL;
134 return false;
135 }
136
137 shm->channels = info.play.channels;
138 shm->samplebits = info.play.precision;
139 shm->speed = info.play.sample_rate;
140
141 if (shm->speed != desired_speed)
142 Con_Printf ("Warning: Rate set (%d) didn't match requested rate (%d)!\n", shm->speed, desired_speed);
143
144 shm->samples = sizeof(dma_buffer) / (shm->samplebits / 8);
145 shm->submission_chunk = 1;
146 shm->samplepos = 0;
147 shm->buffer = dma_buffer;
148
149 return true;
150 }
151
S_SUN_Shutdown(void)152 static void S_SUN_Shutdown (void)
153 {
154 if (shm)
155 {
156 shm = NULL;
157 close (audio_fd);
158 audio_fd = -1;
159 }
160 }
161
S_SUN_GetDMAPos(void)162 static int S_SUN_GetDMAPos (void)
163 {
164 audio_info_t info;
165
166 if (!shm)
167 return 0;
168
169 if (ioctl(audio_fd, AUDIO_GETINFO, &info) == -1)
170 {
171 Con_Printf("Error: can't get audio info\n");
172 S_SUN_Shutdown ();
173 return 0;
174 }
175
176 return ((info.play.samples * shm->channels) % shm->samples);
177 }
178
179 /*
180 ==============
181 SNDDMA_LockBuffer
182
183 Makes sure dma buffer is valid
184 ==============
185 */
S_SUN_LockBuffer(void)186 static void S_SUN_LockBuffer (void)
187 {
188 /* nothing to do here */
189 }
190
191 /*
192 ==============
193 SNDDMA_Submit
194
195 Unlock the dma buffer /
196 Send sound to the device
197 ===============
198 */
S_SUN_Submit(void)199 static void S_SUN_Submit (void)
200 {
201 int bsize;
202 int bytes, b;
203 unsigned char *p;
204 int idx;
205 int stop = paintedtime;
206
207 if (!shm)
208 return;
209
210 if (paintedtime < wbufp)
211 wbufp = 0; /* reset */
212
213 bsize = shm->channels * shm->samplebits / 8;
214 bytes = (paintedtime - wbufp) * bsize;
215
216 if (!bytes)
217 return;
218
219 if (bytes > sizeof(writebuf))
220 {
221 bytes = sizeof(writebuf);
222 stop = wbufp + bytes / bsize;
223 }
224
225 /* transfer the sound data from the circular dma_buffer to writebuf */
226 /* TODO: using 2 memcpys instead of this loop should be faster */
227 p = writebuf;
228 idx = (wbufp * bsize) & (sizeof(dma_buffer) - 1);
229 for (b = bytes; b; b--)
230 {
231 *p++ = dma_buffer[idx];
232 idx = (idx + 1) & (sizeof(dma_buffer) - 1);
233 }
234
235 if (write(audio_fd, writebuf, bytes) < bytes)
236 Con_Printf("audio can't keep up!\n");
237
238 wbufp = stop;
239 }
240
S_SUN_BlockSound(void)241 static void S_SUN_BlockSound (void)
242 {
243 }
244
S_SUN_UnblockSound(void)245 static void S_SUN_UnblockSound (void)
246 {
247 }
248
249 snd_driver_t snddrv_sunaudio =
250 {
251 S_SUN_Init,
252 S_SUN_Shutdown,
253 S_SUN_GetDMAPos,
254 S_SUN_LockBuffer,
255 S_SUN_Submit,
256 S_SUN_BlockSound,
257 S_SUN_UnblockSound,
258 s_sun_driver,
259 SNDDRV_ID_SUN,
260 false,
261 NULL
262 };
263
264 #endif /* HAVE_SUN_SOUND */
265
266