~ubuntu-branches/ubuntu/precise/openarena/precise

« back to all changes in this revision

Viewing changes to code/unix/sdl_snd.c

  • Committer: Bazaar Package Importer
  • Author(s): Bruno "Fuddl" Kleinert
  • Date: 2007-01-20 12:28:09 UTC
  • Revision ID: james.westby@ubuntu.com-20070120122809-2yza5ojt7nqiyiam
Tags: upstream-0.6.0
ImportĀ upstreamĀ versionĀ 0.6.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#if USE_SDL_SOUND
 
2
 
 
3
/*
 
4
 * SDL implementation for Quake 3: Arena's GPL source release.
 
5
 *
 
6
 * This is a replacement of the Linux/OpenSoundSystem code with
 
7
 *  an SDL backend, since it allows us to trivially point just about any
 
8
 *  existing 2D audio backend known to man on any platform at the code,
 
9
 *  plus it benefits from all of SDL's tapdancing to support buggy drivers,
 
10
 *  etc, and gets us free ALSA support, too.
 
11
 *
 
12
 * This is the best idea for a direct modernization of the Linux sound code
 
13
 *  in Quake 3. However, it would be nice to replace this with true 3D
 
14
 *  positional audio, compliments of OpenAL...
 
15
 *
 
16
 * Written by Ryan C. Gordon (icculus@icculus.org). Please refer to
 
17
 *    http://icculus.org/quake3/ for the latest version of this code.
 
18
 *
 
19
 *  Patches and comments are welcome at the above address.
 
20
 *
 
21
 * I cut-and-pasted this from linux_snd.c, and moved it to SDL line-by-line.
 
22
 *  There is probably some cruft that could be removed here.
 
23
 *
 
24
 * You should define USE_SDL=1 and then add this to the makefile.
 
25
 *  USE_SDL will disable the Open Sound System target.
 
26
 */
 
27
 
 
28
/*
 
29
Original copyright on Q3A sources:
 
30
===========================================================================
 
31
Copyright (C) 1999-2005 Id Software, Inc.
 
32
 
 
33
This file is part of Quake III Arena source code.
 
34
 
 
35
Quake III Arena source code is free software; you can redistribute it
 
36
and/or modify it under the terms of the GNU General Public License as
 
37
published by the Free Software Foundation; either version 2 of the License,
 
38
or (at your option) any later version.
 
39
 
 
40
Quake III Arena source code is distributed in the hope that it will be
 
41
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 
42
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
43
GNU General Public License for more details.
 
44
 
 
45
You should have received a copy of the GNU General Public License
 
46
along with Quake III Arena source code; if not, write to the Free Software
 
47
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
48
===========================================================================
 
49
*/
 
50
 
 
51
#include <stdlib.h>
 
52
#include <stdio.h>
 
53
 
 
54
#include "SDL.h"
 
55
 
 
56
#include "../qcommon/q_shared.h"
 
57
#include "../client/snd_local.h"
 
58
 
 
59
qboolean snd_inited = qfalse;
 
60
 
 
61
cvar_t *s_sdlBits;
 
62
cvar_t *s_sdlSpeed;
 
63
cvar_t *s_sdlChannels;
 
64
cvar_t *s_sdlDevSamps;
 
65
cvar_t *s_sdlMixSamps;
 
66
 
 
67
static qboolean use_custom_memset = qfalse;
 
68
 
 
69
/*
 
70
===============
 
71
Snd_Memset
 
72
 
 
73
https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=371
 
74
 
 
75
<TTimo> some shitty mess with DMA buffers
 
76
<TTimo> the mmap'ing permissions were write only
 
77
<TTimo> and glibc optimized for mmx would do memcpy with a prefetch and a read
 
78
<TTimo> causing segfaults
 
79
<TTimo> some other systems would not let you mmap the DMA with read permissions
 
80
<TTimo> so I think I ended up attempting opening with read/write, then try write only
 
81
<TTimo> and use my own copy instead of the glibc crap
 
82
===============
 
83
*/
 
84
 
 
85
#ifdef Snd_Memset
 
86
#undef Snd_Memset
 
87
#endif
 
88
void Snd_Memset (void* dest, const int val, const size_t count)
 
89
{
 
90
        int *pDest;
 
91
        int i, iterate;
 
92
 
 
93
        if (!use_custom_memset)
 
94
        {
 
95
                Com_Memset(dest,val,count);
 
96
                return;
 
97
        }
 
98
        iterate = count / sizeof(int);
 
99
        pDest = (int*)dest;
 
100
        for(i=0; i<iterate; i++)
 
101
        {
 
102
                pDest[i] = val;
 
103
        }
 
104
}
 
105
 
 
106
/* The audio callback. All the magic happens here. */
 
107
static int dmapos = 0;
 
108
static int dmasize = 0;
 
109
 
 
110
/*
 
111
===============
 
112
sdl_audio_callback
 
113
===============
 
114
*/
 
115
static void sdl_audio_callback(void *userdata, Uint8 *stream, int len)
 
116
{
 
117
        int pos = (dmapos * (dma.samplebits/8));
 
118
        if (pos >= dmasize)
 
119
                dmapos = pos = 0;
 
120
 
 
121
        if (!snd_inited)  /* shouldn't happen, but just in case... */
 
122
        {
 
123
                memset(stream, '\0', len);
 
124
                return;
 
125
        }
 
126
        else
 
127
        {
 
128
                int tobufend = dmasize - pos;  /* bytes to buffer's end. */
 
129
                int len1 = len;
 
130
                int len2 = 0;
 
131
 
 
132
                if (len1 > tobufend)
 
133
                {
 
134
                        len1 = tobufend;
 
135
                        len2 = len - len1;
 
136
                }
 
137
                memcpy(stream, dma.buffer + pos, len1);
 
138
                if (len2 <= 0)
 
139
                        dmapos += (len1 / (dma.samplebits/8));
 
140
                else  /* wraparound? */
 
141
                {
 
142
                        memcpy(stream+len1, dma.buffer, len2);
 
143
                        dmapos = (len2 / (dma.samplebits/8));
 
144
                }
 
145
        }
 
146
 
 
147
        if (dmapos >= dmasize)
 
148
                dmapos = 0;
 
149
}
 
150
 
 
151
static struct
 
152
{
 
153
        Uint16  enumFormat;
 
154
        char            *stringFormat;
 
155
} formatToStringTable[ ] =
 
156
{
 
157
        { AUDIO_U8,     "AUDIO_U8" },
 
158
        { AUDIO_S8,     "AUDIO_S8" },
 
159
        { AUDIO_U16LSB, "AUDIO_U16LSB" },
 
160
        { AUDIO_S16LSB, "AUDIO_S16LSB" },
 
161
        { AUDIO_U16MSB, "AUDIO_U16MSB" },
 
162
        { AUDIO_S16MSB, "AUDIO_S16MSB" }
 
163
};
 
164
 
 
165
static int formatToStringTableSize =
 
166
  sizeof( formatToStringTable ) / sizeof( formatToStringTable[ 0 ] );
 
167
 
 
168
/*
 
169
===============
 
170
print_audiospec
 
171
===============
 
172
*/
 
173
static void print_audiospec(const char *str, const SDL_AudioSpec *spec)
 
174
{
 
175
        int             i;
 
176
        char    *fmt = NULL;
 
177
 
 
178
        Com_Printf("%s:\n", str);
 
179
 
 
180
        for( i = 0; i < formatToStringTableSize; i++ ) {
 
181
                if( spec->format == formatToStringTable[ i ].enumFormat ) {
 
182
                        fmt = formatToStringTable[ i ].stringFormat;
 
183
                }
 
184
        }
 
185
 
 
186
        if( fmt ) {
 
187
                Com_Printf( "  Format:   %s\n", fmt );
 
188
        } else {
 
189
                Com_Printf( "  Format:   " S_COLOR_RED "UNKNOWN\n", fmt );
 
190
        }
 
191
 
 
192
        Com_Printf( "  Freq:     %d\n", (int) spec->freq );
 
193
        Com_Printf( "  Samples:  %d\n", (int) spec->samples );
 
194
        Com_Printf( "  Channels: %d\n", (int) spec->channels );
 
195
}
 
196
 
 
197
/*
 
198
===============
 
199
SNDDMA_Init
 
200
===============
 
201
*/
 
202
qboolean SNDDMA_Init(void)
 
203
{
 
204
        char drivername[128];
 
205
        SDL_AudioSpec desired;
 
206
        SDL_AudioSpec obtained;
 
207
        int tmp;
 
208
 
 
209
        if (snd_inited)
 
210
                return qtrue;
 
211
 
 
212
        Com_Printf("Initializing SDL audio driver...\n");
 
213
 
 
214
        if (!s_sdlBits) {
 
215
                s_sdlBits = Cvar_Get("s_sdlBits", "16", CVAR_ARCHIVE);
 
216
                s_sdlSpeed = Cvar_Get("s_sdlSpeed", "0", CVAR_ARCHIVE);
 
217
                s_sdlChannels = Cvar_Get("s_sdlChannels", "2", CVAR_ARCHIVE);
 
218
                s_sdlDevSamps = Cvar_Get("s_sdlDevSamps", "0", CVAR_ARCHIVE);
 
219
                s_sdlMixSamps = Cvar_Get("s_sdlMixSamps", "0", CVAR_ARCHIVE);
 
220
        }
 
221
 
 
222
        if (!SDL_WasInit(SDL_INIT_AUDIO))
 
223
        {
 
224
                if (SDL_Init(SDL_INIT_AUDIO) == -1)
 
225
                {
 
226
                        Com_Printf("SDL_Init(SDL_INIT_AUDIO) failed: %s\n", SDL_GetError());
 
227
                        return qfalse;
 
228
                }
 
229
        }
 
230
 
 
231
        if (SDL_AudioDriverName(drivername, sizeof (drivername)) == NULL)
 
232
                strcpy(drivername, "(UNKNOWN)");
 
233
        Com_Printf("SDL audio driver is \"%s\".\n", drivername);
 
234
 
 
235
        memset(&desired, '\0', sizeof (desired));
 
236
        memset(&obtained, '\0', sizeof (obtained));
 
237
 
 
238
        tmp = ((int) s_sdlBits->value);
 
239
        if ((tmp != 16) && (tmp != 8))
 
240
                tmp = 16;
 
241
 
 
242
        desired.freq = (int) s_sdlSpeed->value;
 
243
        if(!desired.freq) desired.freq = 22050;
 
244
        desired.format = ((tmp == 16) ? AUDIO_S16SYS : AUDIO_U8);
 
245
 
 
246
        // I dunno if this is the best idea, but I'll give it a try...
 
247
        //  should probably check a cvar for this...
 
248
        if (s_sdlDevSamps->value)
 
249
                desired.samples = s_sdlDevSamps->value;
 
250
        else
 
251
        {
 
252
                // just pick a sane default.
 
253
                if (desired.freq <= 11025)
 
254
                        desired.samples = 256;
 
255
                else if (desired.freq <= 22050)
 
256
                        desired.samples = 512;
 
257
                else if (desired.freq <= 44100)
 
258
                        desired.samples = 1024;
 
259
                else
 
260
                        desired.samples = 2048;  // (*shrug*)
 
261
        }
 
262
 
 
263
        desired.channels = (int) s_sdlChannels->value;
 
264
        desired.callback = sdl_audio_callback;
 
265
 
 
266
        if (SDL_OpenAudio(&desired, &obtained) == -1)
 
267
        {
 
268
                Com_Printf("SDL_OpenAudio() failed: %s\n", SDL_GetError());
 
269
                SDL_QuitSubSystem(SDL_INIT_AUDIO);
 
270
                return qfalse;
 
271
        } // if
 
272
 
 
273
        print_audiospec("SDL_AudioSpec", &obtained);
 
274
 
 
275
        // dma.samples needs to be big, or id's mixer will just refuse to
 
276
        //  work at all; we need to keep it significantly bigger than the
 
277
        //  amount of SDL callback samples, and just copy a little each time
 
278
        //  the callback runs.
 
279
        // 32768 is what the OSS driver filled in here on my system. I don't
 
280
        //  know if it's a good value overall, but at least we know it's
 
281
        //  reasonable...this is why I let the user override.
 
282
        tmp = s_sdlMixSamps->value;
 
283
        if (!tmp)
 
284
                tmp = (obtained.samples * obtained.channels) * 10;
 
285
 
 
286
        if (tmp & (tmp - 1))  // not a power of two? Seems to confuse something.
 
287
        {
 
288
                int val = 1;
 
289
                while (val < tmp)
 
290
                        val <<= 1;
 
291
 
 
292
                tmp = val;
 
293
        }
 
294
 
 
295
        dmapos = 0;
 
296
        dma.samplebits = obtained.format & 0xFF;  // first byte of format is bits.
 
297
        dma.channels = obtained.channels;
 
298
        dma.samples = tmp;
 
299
        dma.submission_chunk = 1;
 
300
        dma.speed = obtained.freq;
 
301
        dmasize = (dma.samples * (dma.samplebits/8));
 
302
        dma.buffer = calloc(1, dmasize);
 
303
 
 
304
        Com_Printf("Starting SDL audio callback...\n");
 
305
        SDL_PauseAudio(0);  // start callback.
 
306
 
 
307
        Com_Printf("SDL audio initialized.\n");
 
308
        snd_inited = qtrue;
 
309
        return qtrue;
 
310
}
 
311
 
 
312
/*
 
313
===============
 
314
SNDDMA_GetDMAPos
 
315
===============
 
316
*/
 
317
int SNDDMA_GetDMAPos(void)
 
318
{
 
319
        return dmapos;
 
320
}
 
321
 
 
322
/*
 
323
===============
 
324
SNDDMA_Shutdown
 
325
===============
 
326
*/
 
327
void SNDDMA_Shutdown(void)
 
328
{
 
329
        Com_Printf("Closing SDL audio device...\n");
 
330
        SDL_PauseAudio(1);
 
331
        SDL_CloseAudio();
 
332
        SDL_QuitSubSystem(SDL_INIT_AUDIO);
 
333
        free(dma.buffer);
 
334
        dma.buffer = NULL;
 
335
        dmapos = dmasize = 0;
 
336
        snd_inited = qfalse;
 
337
        Com_Printf("SDL audio device shut down.\n");
 
338
}
 
339
 
 
340
/*
 
341
===============
 
342
SNDDMA_Submit
 
343
 
 
344
Send sound to device if buffer isn't really the dma buffer
 
345
===============
 
346
*/
 
347
void SNDDMA_Submit(void)
 
348
{
 
349
        SDL_UnlockAudio();
 
350
}
 
351
 
 
352
/*
 
353
===============
 
354
SNDDMA_BeginPainting
 
355
===============
 
356
*/
 
357
void SNDDMA_BeginPainting (void)
 
358
{
 
359
        SDL_LockAudio();
 
360
}
 
361
 
 
362
#endif  // USE_SDL_SOUND