eliminated qbyte type, now uses unsigned char throughout the engine for this purpose
[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 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 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_Print( "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 // COMMANDLINEOPTION: SDL Sound: -sndspeed <hz> chooses 44100 hz, 22100 hz, or 11025 hz sound output rate
100         i = COM_CheckParm( "-sndspeed" );
101         if( i && i != ( com_argc - 1 ) )
102                 shm->format.speed = atoi( com_argv[ i+1 ] );
103         else
104                 shm->format.speed = 44100;
105
106         shm->samplepos = 0;
107         shm->samples = AUDIO_SDL_SAMPLES * AUDIO_LOCALFACTOR;
108         shm->bufferlength = shm->samples * shm->format.width;
109         shm->buffer = (unsigned char *)Mem_Alloc( snd_mempool, shm->bufferlength );
110
111         // Init the as structure
112         as.buffer = shm->buffer;
113         as.width = shm->format.width;
114         as.pos = 0;
115         as.size = shm->bufferlength;
116
117         // Init the SDL Audio subsystem
118         spec.callback = Buffer_Callback;
119         spec.channels = shm->format.channels;
120         spec.format = AUDIO_S16SYS;
121         spec.freq = shm->format.speed;
122         spec.userdata = NULL;
123         spec.samples = AUDIO_SDL_SAMPLES;
124
125         if( SDL_OpenAudio( &spec, NULL ) ) {
126                 Con_Print( "Failed to open the audio device!\n" );
127                 Con_DPrintf(
128                         "Audio Specification:\n"
129                         "\tChannels  : %i\n"
130                         "\tFormat    : %x\n"
131                         "\tFrequency : %i\n"
132                         "\tBuffersize: %i Bytes(%i Samples)\n",
133                         spec.channels, spec.format, spec.freq, shm->bufferlength , spec.samples );
134                 Mem_Free( shm->buffer );
135                 return false;
136         }
137
138         SDL_PauseAudio( false );
139
140         return true;
141 }
142
143 /*
144 ==============
145 SNDDMA_GetDMAPos
146
147 return the current sample position (in mono samples read)
148 inside the recirculating dma buffer, so the mixing code will know
149 how many sample are required to fill it up.
150 ===============
151 */
152 int SNDDMA_GetDMAPos(void)
153 {
154         shm->samplepos = (as.pos / as.width) % shm->samples;
155         return shm->samplepos;
156 }
157
158 /*
159 ==============
160 SNDDMA_Submit
161
162 Send sound to device if buffer isn't really the dma buffer
163 ===============
164 */
165 void SNDDMA_Submit(void)
166 {
167
168 }
169
170 /*
171 ==============
172 SNDDMA_Shutdown
173
174 Reset the sound device for exiting
175 ===============
176 */
177 void SNDDMA_Shutdown(void)
178 {
179         SDL_CloseAudio();
180         Mem_Free( as.buffer );
181 }
182
183 void *S_LockBuffer(void)
184 {
185         SDL_LockAudio();
186         return shm->buffer;
187 }
188
189 void S_UnlockBuffer(void)
190 {
191         SDL_UnlockAudio();
192 }
193
194 static void Buffer_Callback(void *userdata, Uint8 *stream, int len)
195 {
196         if( len > as.size )
197                 len = as.size;
198         if( len > as.size - as.pos ) {
199                 memcpy( stream, (Uint8*) as.buffer + as.pos, as.size - as.pos );
200                 len -= as.size - as.pos;
201                 as.pos = 0;
202         }
203         memcpy( stream, (Uint8*) as.buffer + as.pos, len );
204         as.pos = (as.pos + len) % as.size;
205 }
206