-Added SDL sound support (still needs a bit debugging, though).
[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 <SDL.h>
21
22 /*
23 Info:
24 One SDL sample consists of x channel samples
25 The mixer supposes that the driver has one channel entry/sample though it has x channels/sample
26 like the SDL
27 */
28
29 #define AUDIO_SDL_SAMPLES               4096
30 #define AUDIO_LOCALFACTOR               4
31
32 typedef struct AudioState_s
33 {
34         int             width;
35     int         size;
36         int             pos;
37         void    *buffer;
38 } AudioState;
39
40
41 extern mempool_t *snd_mempool;
42 static AudioState        as;
43
44 static void Buffer_Callback(void *userdata, Uint8 *stream, int len);
45
46 /*
47 ==================
48 S_BlockSound
49 ==================
50 */
51 void S_BlockSound( void )
52 {
53         snd_blocked++;
54
55         if( snd_blocked == 1 )
56                 SDL_PauseAudio( true );
57 }
58
59
60 /*
61 ==================
62 S_UnblockSound
63 ==================
64 */
65 void S_UnblockSound( void )
66 {
67         snd_blocked--;
68         if( snd_blocked == 0 )
69                 SDL_PauseAudio( false );
70 }
71
72
73 /*
74 ==================
75 SNDDMA_Init
76
77 Try to find a sound device to mix for.
78 Returns false if nothing is found.
79 ==================
80 */
81
82 qboolean SNDDMA_Init(void)
83 {
84         SDL_AudioSpec spec;
85         int i;
86
87         // Init the SDL Audio subsystem
88         if( SDL_InitSubSystem( SDL_INIT_AUDIO ) ) {
89                 Con_SafePrint( "Initializing the SDL Audio subsystem failed!\n" );
90                 return false;
91         }
92
93         // Init the shm structure
94         memset( (void*) shm, 0, sizeof(*shm) );
95         
96         shm->format.channels = 2; //stereo
97         shm->format.width = 2;
98
99         i = COM_CheckParm( "-sndspeed" );
100         if( i && i != ( com_argc - 1 ) )
101                 shm->format.speed = atoi( com_argv[ i+1 ] );
102         else
103                 shm->format.speed = 44100;
104
105         shm->samplepos = 0;
106         shm->samples = AUDIO_SDL_SAMPLES * AUDIO_LOCALFACTOR;
107         shm->bufferlength = shm->samples * shm->format.width;
108         shm->buffer = Mem_Alloc( snd_mempool, shm->bufferlength );
109
110         // Init the as structure
111         as.buffer = shm->buffer;
112         as.width = shm->format.width;
113         as.pos = 0;
114         as.size = shm->bufferlength;
115
116         // Init the SDL Audio subsystem
117         spec.callback = Buffer_Callback;
118         spec.channels = shm->format.channels;
119         spec.format = AUDIO_S16LSB;
120         spec.freq = shm->format.speed;
121         spec.userdata = NULL;
122         spec.samples = AUDIO_SDL_SAMPLES; 
123         
124         if( SDL_OpenAudio( &spec, NULL ) ) {
125                 Con_SafePrint( "Failed to open the audio device!\n" );
126                 Con_DPrintf( 
127                         "Audio Specification:\n"
128                         "\tChannels  : %i\n"
129                         "\tFormat    : %x\n"
130                         "\tFrequency : %i\n"
131                         "\tBuffersize: %i Bytes(%i Samples)\n", 
132                         spec.channels, spec.format, spec.freq, shm->bufferlength , spec.samples );
133                 Mem_Free( shm->buffer ); 
134                 return false;
135         }
136
137         SDL_PauseAudio( false );
138
139         return true;
140 }
141
142 /*
143 ==============
144 SNDDMA_GetDMAPos
145
146 return the current sample position (in mono samples read)
147 inside the recirculating dma buffer, so the mixing code will know
148 how many sample are required to fill it up.
149 ===============
150 */
151 int SNDDMA_GetDMAPos(void)
152 {
153         shm->samplepos = (as.pos / as.width) % shm->samples;
154         return shm->samplepos;
155 }
156
157 /*
158 ==============
159 SNDDMA_Submit
160
161 Send sound to device if buffer isn't really the dma buffer
162 ===============
163 */
164 void SNDDMA_Submit(void)
165 {
166
167 }
168
169 /*
170 ==============
171 SNDDMA_Shutdown
172
173 Reset the sound device for exiting
174 ===============
175 */
176 void SNDDMA_Shutdown(void)
177 {
178         SDL_CloseAudio();
179         Mem_Free( as.buffer );
180 }
181
182 void *S_LockBuffer(void)
183 {
184         SDL_LockAudio();
185         return shm->buffer;
186 }
187
188 void S_UnlockBuffer(void)
189 {
190         SDL_UnlockAudio();
191 }
192
193 static void Buffer_Callback(void *userdata, Uint8 *stream, int len)
194 {
195         if( len > as.size )
196                 len = as.size;
197         if( len > as.size - as.pos ) {
198                 memcpy( stream, (Uint8*) as.buffer + as.pos, as.size - as.pos );
199                 len -= as.size - as.pos;
200                 as.pos = 0;
201         }
202         memcpy( stream, (Uint8*) as.buffer + as.pos, len );
203         as.pos = (as.pos + len) % as.size;
204 }
205