upgraded both QuakeC VMs to use a table of negative string indices for all dynamic...
[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 #include "snd_main.h"
32
33
34 static const int tryrates[] = {44100, 22050, 11025, 8000};
35
36 static int audio_fd = -1;
37 static qboolean snd_inited = false;
38
39 // TODO: allocate them in SNDDMA_Init, with a size depending on
40 // the sound format (enough for 0.5 sec of sound for instance)
41 #define SND_BUFF_SIZE 65536
42 static qbyte dma_buffer [SND_BUFF_SIZE];
43 static qbyte writebuf [SND_BUFF_SIZE];
44
45
46 qboolean SNDDMA_Init (void)
47 {
48         unsigned int i;
49         const char *snddev;
50         audio_info_t info;
51
52         memset ((void*)shm, 0, sizeof (*shm));
53
54         // Open the audio device
55 #ifdef _PATH_SOUND
56         snddev = _PATH_SOUND;
57 #else
58         snddev = "/dev/sound";
59 #endif
60         audio_fd = open (snddev, O_WRONLY | O_NDELAY | O_NONBLOCK);
61         if (audio_fd < 0)
62         {
63                 Con_Printf("Can't open the sound device (%s)\n", snddev);
64                 return false;
65         }
66
67         // Look for an appropriate sound format
68         // TODO: we should also test mono/stereo and bits
69         // TODO: support "-sndspeed", "-sndbits", "-sndmono" and "-sndstereo"
70         shm->format.channels = 2;
71         shm->format.width = 2;
72         for (i = 0; i < sizeof (tryrates) / sizeof (tryrates[0]); i++)
73         {
74                 shm->format.speed = tryrates[i];
75
76                 AUDIO_INITINFO (&info);
77                 info.play.sample_rate = shm->format.speed;
78                 info.play.channels = shm->format.channels;
79                 info.play.precision = shm->format.width * 8;
80 // We only handle sound cards of the same endianess than the CPU
81 #if BYTE_ORDER == BIG_ENDIAN
82                 info.play.encoding = AUDIO_ENCODING_SLINEAR_BE;
83 #else
84                 info.play.encoding = AUDIO_ENCODING_SLINEAR_LE;
85 #endif
86                 if (ioctl (audio_fd, AUDIO_SETINFO, &info) == 0)
87                         break;
88         }
89         if (i == sizeof (tryrates) / sizeof (tryrates[0]))
90         {
91                 Con_Print("Can't select an appropriate sound output format\n");
92                 close (audio_fd);
93                 return false;
94         }
95
96         // Print some information
97         Con_Printf("%d bit %s sound initialized (rate: %dHz)\n",
98                                 info.play.precision,
99                                 (info.play.channels == 2) ? "stereo" : "mono",
100                                 info.play.sample_rate);
101
102         shm->samples = sizeof (dma_buffer) / shm->format.width;
103         shm->samplepos = 0;
104         shm->buffer = dma_buffer;
105
106         snd_inited = true;
107         return true;
108 }
109
110 int SNDDMA_GetDMAPos (void)
111 {
112         audio_info_t info;
113
114         if (!snd_inited)
115                 return 0;
116
117         if (ioctl (audio_fd, AUDIO_GETINFO, &info) < 0)
118         {
119                 Con_Print("Error: can't get audio info\n");
120                 SNDDMA_Shutdown ();
121                 return 0;
122         }
123
124         return ((info.play.samples * shm->format.channels) % shm->samples);
125 }
126
127 void SNDDMA_Shutdown (void)
128 {
129         if (snd_inited)
130         {
131                 close (audio_fd);
132                 audio_fd = -1;
133                 snd_inited = false;
134         }
135 }
136
137 /*
138 ==============
139 SNDDMA_Submit
140
141 Send sound to device if buffer isn't really the dma buffer
142 ===============
143 */
144 void SNDDMA_Submit (void)
145 {
146         int bsize;
147         int bytes, b;
148         static int wbufp = 0;
149         unsigned char *p;
150         int idx;
151         int stop = paintedtime;
152
153         if (!snd_inited)
154                 return;
155
156         if (paintedtime < wbufp)
157                 wbufp = 0; // reset
158
159         bsize = shm->format.channels * shm->format.width;
160         bytes = (paintedtime - wbufp) * bsize;
161
162         if (!bytes)
163                 return;
164
165         if (bytes > sizeof (writebuf))
166         {
167                 bytes = sizeof (writebuf);
168                 stop = wbufp + bytes / bsize;
169         }
170
171         // Transfert the sound data from the circular dma_buffer to writebuf
172         // TODO: using 2 memcpys instead of this loop should be faster
173         p = writebuf;
174         idx = (wbufp*bsize) & (sizeof (dma_buffer) - 1);
175         for (b = bytes; b; b--)
176         {
177                 *p++ = dma_buffer[idx];
178                 idx = (idx + 1) & (sizeof (dma_buffer) - 1);
179         }
180
181         if (write (audio_fd, writebuf, bytes) < bytes)
182                 Con_Print("audio can't keep up!\n");
183
184         wbufp = stop;
185 }
186
187 void *S_LockBuffer (void)
188 {
189         return shm->buffer;
190 }
191
192 void S_UnlockBuffer (void)
193 {
194 }