implemented DP_TRACE_HITCONTENTSMASK_SURFACEINFO extension, this allows QC to find...
[divverent/darkplaces.git] / snd_sdl.c
1 /*
2 Copyright (C) 2004 Andreas Kirsch
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 #include "quakedef.h"
20 #include "snd_main.h"
21 #include <SDL.h>
22
23 /*
24 Info:
25 SDL samples are really frames (full set of samples for all speakers)
26 */
27
28 #define AUDIO_SDL_SAMPLEFRAMES          4096
29 #define AUDIO_LOCALFACTOR               4
30
31 typedef struct AudioState_s
32 {
33         int             width;
34     int         size;
35         int             pos;
36         void    *buffer;
37 } AudioState;
38
39
40 static AudioState        as;
41
42 static void Buffer_Callback(void *userdata, Uint8 *stream, int len);
43
44 /*
45 ==================
46 S_BlockSound
47 ==================
48 */
49 void S_BlockSound( void )
50 {
51         snd_blocked++;
52
53         if( snd_blocked == 1 )
54                 SDL_PauseAudio( true );
55 }
56
57
58 /*
59 ==================
60 S_UnblockSound
61 ==================
62 */
63 void S_UnblockSound( void )
64 {
65         snd_blocked--;
66         if( snd_blocked == 0 )
67                 SDL_PauseAudio( false );
68 }
69
70
71 /*
72 ==================
73 SNDDMA_Init
74
75 Try to find a sound device to mix for.
76 Returns false if nothing is found.
77 ==================
78 */
79
80 qboolean SNDDMA_Init(void)
81 {
82         SDL_AudioSpec wantspec;
83         int i;
84         int channels;
85
86         // Init the SDL Audio subsystem
87         if( SDL_InitSubSystem( SDL_INIT_AUDIO ) ) {
88                 Con_Print( "Initializing the SDL Audio subsystem failed!\n" );
89                 return false;
90         }
91
92         for (channels = 8;channels >= 1;channels--)
93         {
94                 if ((channels & 1) && channels != 1)
95                         continue;
96 // COMMANDLINEOPTION: SDL Sound: -sndmono sets sound output to mono
97                 if ((i=COM_CheckParm("-sndmono")) != 0)
98                         if (channels != 1)
99                                 continue;
100 // COMMANDLINEOPTION: SDL Sound: -sndstereo sets sound output to stereo
101                 if ((i=COM_CheckParm("-sndstereo")) != 0)
102                         if (channels != 2)
103                                 continue;
104 // COMMANDLINEOPTION: SDL Sound: -sndquad sets sound output to 4 channel surround
105                 if ((i=COM_CheckParm("-sndquad")) != 0)
106                         if (channels != 4)
107                                 continue;
108                 // Init the SDL Audio subsystem
109                 wantspec.callback = Buffer_Callback;
110                 wantspec.userdata = NULL;
111                 wantspec.freq = 44100;
112                 // COMMANDLINEOPTION: SDL Sound: -sndspeed <hz> chooses sound output rate (try values such as 44100, 48000, 22050, 11025 (quake), 24000, 32000, 96000, 192000, etc)
113                 i = COM_CheckParm( "-sndspeed" );
114                 if( i && i != ( com_argc - 1 ) )
115                         wantspec.freq = atoi( com_argv[ i+1 ] );
116                 wantspec.format = AUDIO_S16SYS;
117                 wantspec.channels = channels;
118                 wantspec.samples = AUDIO_SDL_SAMPLEFRAMES;
119
120                 if( SDL_OpenAudio( &wantspec, NULL ) )
121                 {
122                         Con_Printf("%s\n", SDL_GetError());
123                         continue;
124                 }
125
126                 // Init the shm structure
127                 memset( (void*) shm, 0, sizeof(*shm) );
128                 shm->format.channels = wantspec.channels;
129                 shm->format.width = 2;
130                 shm->format.speed = wantspec.freq;
131
132                 shm->samplepos = 0;
133                 shm->sampleframes = wantspec.samples * AUDIO_LOCALFACTOR;
134                 shm->samples = shm->sampleframes * shm->format.channels;
135                 shm->bufferlength = shm->samples * shm->format.width;
136                 shm->buffer = (unsigned char *)Mem_Alloc( snd_mempool, shm->bufferlength );
137
138                 // Init the as structure
139                 as.buffer = shm->buffer;
140                 as.width = shm->format.width;
141                 as.pos = 0;
142                 as.size = shm->bufferlength;
143                 break;
144         }
145         if (channels < 1)
146         {
147                 Con_Print( "Failed to open the audio device!\n" );
148                 Con_DPrintf(
149                         "Audio Specification:\n"
150                         "\tChannels  : %i\n"
151                         "\tFormat    : %x\n"
152                         "\tFrequency : %i\n"
153                         "\tSamples   : %i\n",
154                         wantspec.channels, wantspec.format, wantspec.freq, wantspec.samples );
155                 return false;
156         }
157
158         SDL_PauseAudio( false );
159
160         return true;
161 }
162
163 /*
164 ==============
165 SNDDMA_GetDMAPos
166
167 return the current sample position (in mono samples read)
168 inside the recirculating dma buffer, so the mixing code will know
169 how many sample are required to fill it up.
170 ===============
171 */
172 int SNDDMA_GetDMAPos(void)
173 {
174         shm->samplepos = (as.pos / as.width) % shm->samples;
175         return shm->samplepos;
176 }
177
178 /*
179 ==============
180 SNDDMA_Submit
181
182 Send sound to device if buffer isn't really the dma buffer
183 ===============
184 */
185 void SNDDMA_Submit(void)
186 {
187
188 }
189
190 /*
191 ==============
192 SNDDMA_Shutdown
193
194 Reset the sound device for exiting
195 ===============
196 */
197 void SNDDMA_Shutdown(void)
198 {
199         SDL_CloseAudio();
200         Mem_Free( as.buffer );
201 }
202
203 void *S_LockBuffer(void)
204 {
205         SDL_LockAudio();
206         return shm->buffer;
207 }
208
209 void S_UnlockBuffer(void)
210 {
211         SDL_UnlockAudio();
212 }
213
214 static void Buffer_Callback(void *userdata, Uint8 *stream, int len)
215 {
216         if( len > as.size )
217                 len = as.size;
218         if( len > as.size - as.pos ) {
219                 memcpy( stream, (Uint8*) as.buffer + as.pos, as.size - as.pos );
220                 len -= as.size - as.pos;
221                 as.pos = 0;
222         }
223         memcpy( stream, (Uint8*) as.buffer + as.pos, len );
224         as.pos = (as.pos + len) % as.size;
225 }
226