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