patch from div0 to add -sndmono and -sndstereo options to sdl sound code
[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                 // Init the SDL Audio subsystem
105                 wantspec.callback = Buffer_Callback;
106                 wantspec.userdata = NULL;
107                 wantspec.freq = 44100;
108                 // COMMANDLINEOPTION: SDL Sound: -sndspeed <hz> chooses sound output rate (try values such as 44100, 48000, 22050, 11025 (quake), 24000, 32000, 96000, 192000, etc)
109                 i = COM_CheckParm( "-sndspeed" );
110                 if( i && i != ( com_argc - 1 ) )
111                         wantspec.freq = atoi( com_argv[ i+1 ] );
112                 wantspec.format = AUDIO_S16SYS;
113                 wantspec.channels = channels;
114                 wantspec.samples = AUDIO_SDL_SAMPLEFRAMES;
115
116                 if( SDL_OpenAudio( &wantspec, NULL ) )
117                 {
118                         Con_Printf("%s\n", SDL_GetError());
119                         continue;
120                 }
121
122                 // Init the shm structure
123                 memset( (void*) shm, 0, sizeof(*shm) );
124                 shm->format.channels = wantspec.channels;
125                 shm->format.width = 2;
126                 shm->format.speed = wantspec.freq;
127
128                 shm->samplepos = 0;
129                 shm->sampleframes = wantspec.samples * AUDIO_LOCALFACTOR;
130                 shm->samples = shm->sampleframes * shm->format.channels;
131                 shm->bufferlength = shm->samples * shm->format.width;
132                 shm->buffer = (unsigned char *)Mem_Alloc( snd_mempool, shm->bufferlength );
133
134                 // Init the as structure
135                 as.buffer = shm->buffer;
136                 as.width = shm->format.width;
137                 as.pos = 0;
138                 as.size = shm->bufferlength;
139                 break;
140         }
141         if (channels < 1)
142         {
143                 Con_Print( "Failed to open the audio device!\n" );
144                 Con_DPrintf(
145                         "Audio Specification:\n"
146                         "\tChannels  : %i\n"
147                         "\tFormat    : %x\n"
148                         "\tFrequency : %i\n"
149                         "\tSamples   : %i\n",
150                         wantspec.channels, wantspec.format, wantspec.freq, wantspec.samples );
151                 return false;
152         }
153
154         SDL_PauseAudio( false );
155
156         return true;
157 }
158
159 /*
160 ==============
161 SNDDMA_GetDMAPos
162
163 return the current sample position (in mono samples read)
164 inside the recirculating dma buffer, so the mixing code will know
165 how many sample are required to fill it up.
166 ===============
167 */
168 int SNDDMA_GetDMAPos(void)
169 {
170         shm->samplepos = (as.pos / as.width) % shm->samples;
171         return shm->samplepos;
172 }
173
174 /*
175 ==============
176 SNDDMA_Submit
177
178 Send sound to device if buffer isn't really the dma buffer
179 ===============
180 */
181 void SNDDMA_Submit(void)
182 {
183
184 }
185
186 /*
187 ==============
188 SNDDMA_Shutdown
189
190 Reset the sound device for exiting
191 ===============
192 */
193 void SNDDMA_Shutdown(void)
194 {
195         SDL_CloseAudio();
196         Mem_Free( as.buffer );
197 }
198
199 void *S_LockBuffer(void)
200 {
201         SDL_LockAudio();
202         return shm->buffer;
203 }
204
205 void S_UnlockBuffer(void)
206 {
207         SDL_UnlockAudio();
208 }
209
210 static void Buffer_Callback(void *userdata, Uint8 *stream, int len)
211 {
212         if( len > as.size )
213                 len = as.size;
214         if( len > as.size - as.pos ) {
215                 memcpy( stream, (Uint8*) as.buffer + as.pos, as.size - as.pos );
216                 len -= as.size - as.pos;
217                 as.pos = 0;
218         }
219         memcpy( stream, (Uint8*) as.buffer + as.pos, len );
220         as.pos = (as.pos + len) % as.size;
221 }
222