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