Added sound support to the NetBSD port. Most of the code comes from the original...
[divverent/darkplaces.git] / snd_bsd.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
12
13 See the 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 */
20
21 #include <sys/param.h>
22 #include <sys/audioio.h>
23 #include <sys/endian.h>
24 #include <sys/ioctl.h>
25
26 #include <fcntl.h>
27 #include <paths.h>
28 #include <unistd.h>
29
30 #include "quakedef.h"
31
32
33 static const int tryrates[] = {44100, 22051, 11025, 8000};
34
35 static int audio_fd = -1;
36 static qboolean snd_inited = false;
37
38 // TODO: allocate them in SNDDMA_Init, with a size depending on
39 // the sound format (enough for 0.5 sec of sound for instance)
40 #define SND_BUFF_SIZE 65536
41 static qbyte dma_buffer [SND_BUFF_SIZE];
42 static qbyte writebuf [SND_BUFF_SIZE];
43
44
45 qboolean SNDDMA_Init (void)
46 {
47         unsigned int i;
48         const char *snddev = _PATH_SOUND;
49         audio_info_t info;
50
51         memset ((void*)shm, 0, sizeof (*shm));
52
53         // Open the audio device
54         audio_fd = open (snddev, O_WRONLY | O_NDELAY | O_NONBLOCK);
55         if (audio_fd < 0)
56         {
57                 Con_Printf ("Can't open the sound device (%s)\n", snddev);
58                 return false;
59         }
60
61         // Look for an appropriate sound format
62         // TODO: we should also test mono/stereo and bits
63         // TODO: support "-sndspeed", "-sndbits", "-sndmono" and "-sndstereo"
64         shm->channels = 2;
65         shm->samplebits = 16;
66         for (i = 0; i < sizeof (tryrates) / sizeof (tryrates[0]); i++)
67         {
68                 shm->speed = tryrates[i];
69
70                 AUDIO_INITINFO (&info);
71                 info.play.sample_rate = shm->speed;
72                 info.play.channels = shm->channels;
73                 info.play.precision = shm->samplebits;
74 // We only handle sound cards of the same endianess than the CPU
75 #if BYTE_ORDER == BIG_ENDIAN
76                 info.play.encoding = AUDIO_ENCODING_SLINEAR_BE;
77 #else
78                 info.play.encoding = AUDIO_ENCODING_SLINEAR_LE;
79 #endif
80                 if (ioctl (audio_fd, AUDIO_SETINFO, &info) == 0)
81                         break;
82         }
83         if (i == sizeof (tryrates) / sizeof (tryrates[0]))
84         {
85                 Con_Printf ("Can't select an appropriate sound output format\n");
86                 close (audio_fd);
87                 return false;
88         }
89
90         // Print some information
91         Con_Printf ("%d bit %s sound initialized (rate: %dHz)\n",
92                                 info.play.precision,
93                                 (info.play.channels == 2) ? "stereo" : "mono",
94                                 info.play.sample_rate);
95
96         shm->samples = sizeof (dma_buffer) / (shm->samplebits / 8);
97         shm->samplepos = 0;
98         shm->buffer = dma_buffer;
99
100         snd_inited = true;
101         return true;
102 }
103
104 int SNDDMA_GetDMAPos (void)
105 {
106         audio_info_t info;
107
108         if (!snd_inited)
109                 return 0;
110
111         if (ioctl (audio_fd, AUDIO_GETINFO, &info) < 0)
112         {
113                 Con_Printf ("Error: can't get audio info\n");
114                 SNDDMA_Shutdown ();
115                 return 0;
116         }
117
118         return ((info.play.samples * shm->channels) % shm->samples);
119 }
120
121 void SNDDMA_Shutdown (void)
122 {
123         if (snd_inited)
124         {
125                 close (audio_fd);
126                 audio_fd = -1;
127                 snd_inited = false;
128         }
129 }
130
131 /*
132 ==============
133 SNDDMA_Submit
134
135 Send sound to device if buffer isn't really the dma buffer
136 ===============
137 */
138 void SNDDMA_Submit (void)
139 {
140         int bsize;
141         int bytes, b;
142         static int wbufp = 0;
143         unsigned char *p;
144         int idx;
145         int stop = paintedtime;
146
147         if (!snd_inited)
148                 return;
149
150         if (paintedtime < wbufp)
151                 wbufp = 0; // reset
152
153         bsize = shm->channels * (shm->samplebits / 8);
154         bytes = (paintedtime - wbufp) * bsize;
155
156         if (!bytes)
157                 return;
158
159         if (bytes > sizeof (writebuf))
160         {
161                 bytes = sizeof (writebuf);
162                 stop = wbufp + bytes / bsize;
163         }
164
165         // Transfert the sound data from the circular dma_buffer to writebuf
166         // TODO: using 2 memcpys instead of this loop should be faster
167         p = writebuf;
168         idx = (wbufp*bsize) & (sizeof (dma_buffer) - 1);
169         for (b = bytes; b; b--)
170         {
171                 *p++ = dma_buffer[idx];
172                 idx = (idx + 1) & (sizeof (dma_buffer) - 1);
173         }
174
175         if (write (audio_fd, writebuf, bytes) < bytes)
176                 Con_Printf ("audio can't keep up!\n");
177
178         wbufp = stop;
179 }
180
181 void *S_LockBuffer (void)
182 {
183         return shm->buffer;
184 }
185
186 void S_UnlockBuffer (void)
187 {
188 }