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