DarkPlaces
Game engine based on the Quake 1 engine by id Software, developed by LadyHavoc
 
snd_sdl.c
Go to the documentation of this file.
1/*
2Copyright (C) 2004 Andreas Kirsch
3
4This program is free software; you can redistribute it and/or
5modify it under the terms of the GNU General Public License
6as published by the Free Software Foundation; either version 2
7of the License, or (at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18*/
19#include <math.h>
20#include <SDL.h>
21
22#include "darkplaces.h"
23#include "vid.h"
24
25#include "snd_main.h"
26
27
28static unsigned int sdlaudiotime = 0;
29static int audio_device = 0;
30
31
32// This function is called when the audio device needs more data.
33// Note: SDL obtains the device lock before calling this function, no need to lock it here.
34static void Buffer_Callback (void *userdata, Uint8 *stream, int len)
35{
36 unsigned int factor, RequestedFrames, MaxFrames, FrameCount;
37 unsigned int StartOffset, EndOffset;
38
40 if ((unsigned int)len % factor != 0)
41 Sys_Error("SDL sound: invalid buffer length passed to Buffer_Callback (%d bytes)\n", len);
42
43 RequestedFrames = (unsigned int)len / factor;
44
46 {
47 S_MixToBuffer(stream, RequestedFrames);
48 if (snd_blocked)
49 memset(stream, snd_renderbuffer->format.width == 1 ? 0x80 : 0, len);
50 return;
51 }
52
53 // Transfert up to a chunk of samples from snd_renderbuffer to stream
55 if (MaxFrames > RequestedFrames)
56 FrameCount = RequestedFrames;
57 else
58 FrameCount = MaxFrames;
60 EndOffset = (snd_renderbuffer->startframe + FrameCount) % snd_renderbuffer->maxframes;
61 if (snd_blocked)
62 memset(stream, snd_renderbuffer->format.width == 1 ? 0x80 : 0, len);
63 else if (StartOffset > EndOffset) // if the buffer wraps
64 {
65 unsigned int PartialLength1, PartialLength2;
66
67 PartialLength1 = (snd_renderbuffer->maxframes - StartOffset) * factor;
68 memcpy(stream, &snd_renderbuffer->ring[StartOffset * factor], PartialLength1);
69
70 PartialLength2 = FrameCount * factor - PartialLength1;
71 memcpy(&stream[PartialLength1], &snd_renderbuffer->ring[0], PartialLength2);
72
73 // As of SDL 2.0 buffer needs to be fully initialized, so fill leftover part with silence
74 // FIXME this is another place that assumes 8bit is always unsigned and others always signed
75 memset(&stream[PartialLength1 + PartialLength2], snd_renderbuffer->format.width == 1 ? 0x80 : 0, len - (PartialLength1 + PartialLength2));
76 }
77 else
78 {
79 memcpy(stream, &snd_renderbuffer->ring[StartOffset * factor], FrameCount * factor);
80
81 // As of SDL 2.0 buffer needs to be fully initialized, so fill leftover part with silence
82 // FIXME this is another place that assumes 8bit is always unsigned and others always signed
83 memset(&stream[FrameCount * factor], snd_renderbuffer->format.width == 1 ? 0x80 : 0, len - (FrameCount * factor));
84 }
85
86 snd_renderbuffer->startframe += FrameCount;
87
88 if (FrameCount < RequestedFrames && developer_insane.integer && vid_activewindow)
89 Con_DPrintf("SDL sound: %u sample frames missing\n", RequestedFrames - FrameCount);
90
91 sdlaudiotime += RequestedFrames;
92}
93
94
95/*
96====================
97SndSys_Init
98
99Create "snd_renderbuffer" with the proper sound format if the call is successful
100May return a suggested format if the requested format isn't available
101====================
102*/
104{
105 unsigned int buffersize;
106 SDL_AudioSpec wantspec;
107 SDL_AudioSpec obtainspec;
108
109 snd_threaded = false;
110
111 Con_DPrint ("SndSys_Init: using the SDL module\n");
112
113 // Init the SDL Audio subsystem
114 if( SDL_InitSubSystem( SDL_INIT_AUDIO ) ) {
115 Con_Print( "Initializing the SDL Audio subsystem failed!\n" );
116 return false;
117 }
118
119 // SDL2 wiki recommends this range
120 buffersize = bound(512, ceil((double)fmt->speed * snd_bufferlength.value / 1000.0), 8192);
121
122 // Init the SDL Audio subsystem
123 memset(&wantspec, 0, sizeof(wantspec));
124 wantspec.callback = Buffer_Callback;
125 wantspec.userdata = NULL;
126 wantspec.freq = fmt->speed;
127 wantspec.format = fmt->width == 1 ? AUDIO_U8 : (fmt->width == 2 ? AUDIO_S16SYS : AUDIO_F32);
128 wantspec.channels = fmt->channels;
129 wantspec.samples = CeilPowerOf2(buffersize); // needs to be a power of 2 on some platforms.
130
131 Con_Printf("Wanted audio Specification:\n"
132 " Channels : %i\n"
133 " Format : 0x%X\n"
134 " Frequency : %i\n"
135 " Samples : %i\n",
136 wantspec.channels, wantspec.format, wantspec.freq, wantspec.samples);
137
138 if ((audio_device = SDL_OpenAudioDevice(NULL, 0, &wantspec, &obtainspec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | SDL_AUDIO_ALLOW_CHANNELS_CHANGE)) == 0)
139 {
140 Con_Printf(CON_ERROR "Failed to open the audio device! (%s)\n", SDL_GetError() );
141 return false;
142 }
143
144 Con_Printf("Obtained audio specification:\n"
145 " Channels : %i\n"
146 " Format : 0x%X\n"
147 " Frequency : %i\n"
148 " Samples : %i\n",
149 obtainspec.channels, obtainspec.format, obtainspec.freq, obtainspec.samples);
150
151 fmt->speed = obtainspec.freq;
152 fmt->channels = obtainspec.channels;
153
154 snd_threaded = true;
155
159
160 sdlaudiotime = 0;
161 SDL_PauseAudioDevice(audio_device, 0);
162
163 return true;
164}
165
166
167/*
168====================
169SndSys_Shutdown
170
171Stop the sound card, delete "snd_renderbuffer" and free its other resources
172====================
173*/
175{
176 if (audio_device > 0) {
177 SDL_CloseAudioDevice(audio_device);
178 audio_device = 0;
179 }
180 if (snd_renderbuffer != NULL)
181 {
185 }
186}
187
188
189/*
190====================
191SndSys_Submit
192
193Submit the contents of "snd_renderbuffer" to the sound card
194====================
195*/
196void SndSys_Submit (void)
197{
198 // Nothing to do here (this sound module is callback-based)
199}
200
201
202/*
203====================
204SndSys_GetSoundTime
205
206Returns the number of sample frames consumed since the sound started
207====================
208*/
209unsigned int SndSys_GetSoundTime (void)
210{
211 return sdlaudiotime;
212}
213
214
215/*
216====================
217SndSys_LockRenderBuffer
218
219Get the exclusive lock on "snd_renderbuffer"
220====================
221*/
223{
224 SDL_LockAudioDevice(audio_device);
225 return true;
226}
227
228
229/*
230====================
231SndSys_UnlockRenderBuffer
232
233Release the exclusive lock on "snd_renderbuffer"
234====================
235*/
237{
238 SDL_UnlockAudioDevice(audio_device);
239}
240
241/*
242====================
243SndSys_SendKeyEvents
244
245Send keyboard events originating from the sound system (e.g. MIDI)
246====================
247*/
249{
250 // not supported
251}
void Con_Print(const char *msg)
Prints to all appropriate console targets, and adds timestamps.
Definition console.c:1504
void Con_DPrintf(const char *fmt,...)
A Con_Printf that only shows up if the "developer" cvar is set.
Definition console.c:1544
void Con_Printf(const char *fmt,...)
Prints to all appropriate console targets.
Definition console.c:1514
void Con_DPrint(const char *msg)
A Con_Print that only shows up if the "developer" cvar is set.
Definition console.c:1531
#define CON_ERROR
Definition console.h:102
void Cvar_SetValueQuick(cvar_t *var, float value)
Definition cvar.c:473
static int(ZEXPORT *qz_inflate)(z_stream *strm
cvar_t developer_insane
Definition host.c:50
unsigned int CeilPowerOf2(unsigned int value)
returns the smallest integer greater than or equal to "value", or 0 if "value" is too big
Definition mathlib.c:292
#define bound(min, num, max)
Definition mathlib.h:34
float ceil(float f)
#define NULL
Definition qtypes.h:12
bool qbool
Definition qtypes.h:9
qbool snd_threaded
enables use of snd_usethreadedmixing, provided that no sound hacks are in effect (like timedemo)
Definition snd_main.c:136
cvar_t snd_channellayout
Definition snd_main.c:170
cvar_t snd_bufferlength
Definition snd_main.c:248
bool snd_blocked
When true, we submit silence to the audio device.
Definition snd_main.c:155
snd_ringbuffer_t * snd_renderbuffer
Definition snd_main.c:131
qbool snd_usethreadedmixing
if true, the main thread does not mix sound, soundtime does not advance, and neither does snd_renderb...
Definition snd_main.c:137
#define SND_CHANNELLAYOUT_STANDARD
Definition snd_main.h:135
snd_ringbuffer_t * Snd_CreateRingBuffer(const snd_format_t *format, unsigned int sampleframes, void *buffer)
If "buffer" is NULL, the function allocates one buffer of "sampleframes" sample frames itself (if "sa...
Definition snd_mem.c:43
void S_MixToBuffer(void *stream, unsigned int frames)
Definition snd_mix.c:369
#define SND_CHANNELLAYOUT_AUTO
Definition snd_main.h:134
void SndSys_Submit(void)
Submit the contents of "snd_renderbuffer" to the sound card.
Definition snd_sdl.c:196
void SndSys_SendKeyEvents(void)
if the sound system can generate events, send them
Definition snd_sdl.c:248
static int audio_device
Definition snd_sdl.c:29
qbool SndSys_Init(snd_format_t *fmt)
Create "snd_renderbuffer", attempting to use the chosen sound format, but accepting if the driver wan...
Definition snd_sdl.c:103
static unsigned int sdlaudiotime
Definition snd_sdl.c:28
static void Buffer_Callback(void *userdata, Uint8 *stream, int len)
Definition snd_sdl.c:34
unsigned int SndSys_GetSoundTime(void)
Returns the number of sample frames consumed since the sound started.
Definition snd_sdl.c:209
void SndSys_Shutdown(void)
Stop the sound card, delete "snd_renderbuffer" and free its other resources.
Definition snd_sdl.c:174
qbool SndSys_LockRenderBuffer(void)
Get the exclusive lock on "snd_renderbuffer".
Definition snd_sdl.c:222
void SndSys_UnlockRenderBuffer(void)
Release the exclusive lock on "snd_renderbuffer".
Definition snd_sdl.c:236
float value
Definition cvar.h:74
int integer
Definition cvar.h:73
unsigned int speed
Definition snd_main.h:32
unsigned short channels
Definition snd_main.h:34
unsigned short width
Definition snd_main.h:33
snd_format_t format
Definition snd_main.h:47
unsigned char * ring
Definition snd_main.h:48
unsigned int startframe
index of the first frame in the buffer if startframe == endframe, the bufffer is empty
Definition snd_main.h:52
unsigned int maxframes
max size (buffer size), in sample frames
Definition snd_main.h:49
unsigned int endframe
index of the first EMPTY frame in the "ring" buffer may be smaller than startframe if the "ring" buff...
Definition snd_main.h:55
void Sys_Error(const char *error,...) DP_FUNC_PRINTF(1) DP_FUNC_NORETURN
Causes the entire program to exit ASAP.
Definition sys_shared.c:724
qbool vid_activewindow
Definition vid_shared.c:77
#define Mem_Free(mem)
Definition zone.h:96