1 /* 2ooM: The Master of Orion II Reverse Engineering Project
2 * Copyright (C) 2006 Nick Bowler
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
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. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #define SDL_MODULE "SDL"
25 #define SDL_MODULE_VERSION "0.1.0"
31 #include "../audio/avfile.h"
32 #include "../plugin/plugin.h"
33 #include "../sys/log.h"
35 #define init sdl_LTX_init
37 static struct log_context logger
;
38 static struct av_accessor av
;
40 static struct SDL_AudioSpec sdlfmt
;
41 static struct audio_format fmt
;
52 static struct channel
{
54 struct audio_data
*data
;
59 static void data_delete(struct audio_data
*data
)
65 static int setchannels(int numchannels
)
71 if (!(new = realloc(channels
, numchannels
* sizeof *channels
))) {
78 for (i
= nchannels
; i
< numchannels
; i
++)
79 channels
[i
].data
= NULL
;
81 nchannels
= numchannels
;
87 /* Read a block of channel data */
88 static void stream_read(int c
, int len
)
90 Uint8
*stream
= channels
[c
].data
->data
;
94 rc
= av
.read_audio(channels
[c
].data
->aid
, stream
, len
);
98 if (rc
< len
&& channels
[c
].nloops
!= 0) {
99 if (av
.rewind(channels
[c
].data
->aid
) == -1)
101 if (channels
[c
].nloops
> 0)
102 channels
[c
].nloops
--;
111 memset(stream
, 0, len
);
115 static void render_audio(void *userdata
, Uint8
*stream
, int len
)
120 /* Read data for all channels */
121 for (i
= 0; i
< nchannels
; i
++)
122 if (channels
[i
].data
&& channels
[i
].data
->flags
& AUD_OPEN_STREAM
)
125 /* TO DO: Not explode with sample formats other than S16_LE */
126 for (samp
= (Sint16
*) stream
; (Uint8
*) samp
- stream
< len
; samp
++) {
128 ptrdiff_t off
= samp
- (Sint16
*) stream
;
130 for (i
= 0; i
< nchannels
; i
++) {
131 if (!channels
[i
].data
)
134 buf
= (Sint16
*) channels
[i
].data
->data
;
137 /* Mix sample into output */
138 if (m
>= 0 && c
>= 0)
139 m
= (m
+ c
) - (m
* c
) / 32768;
140 else if (m
<= 0 && c
<= 0)
141 m
= (m
+ c
) + (m
* c
) / 32768;
150 /* Clean up finished channels */
151 for (i
= 0; i
< nchannels
; i
++)
152 if (!channels
[i
].nloops
)
153 channels
[i
].data
= NULL
;
156 static struct audio_data
*sdl_ao_open(const char *resource
, int flags
)
158 struct audio_data
*new = calloc(1, sizeof *new);
163 if ((new->aid
= av
.open(resource
, AV_INPUT_AUDIO
, &fmt
, NULL
)) == -1)
167 if (flags
& AUD_OPEN_STREAM
) {
168 new->datasz
= sdlfmt
.samples
* 4;
170 if (!(new->data
= malloc(new->datasz
)))
172 } else if (flags
& AUD_OPEN_SAMPLE
) {
182 static void sdl_ao_close(struct audio_data
*data
)
190 static int sdl_ao_play(struct audio_data
*data
, int nloops
)
195 if (nloops
< -1 || nloops
== 0)
200 for (i
= 0; i
< nchannels
; i
++)
201 if (!channels
[i
].data
)
204 /* Cancelling a currently playing channel may be better */
209 channels
[i
].data
= data
;
210 channels
[i
].chanid
= chanid
;
211 channels
[i
].nloops
= nloops
;
215 chanid
= (chanid
+ 1) % INT_MAX
+1;
220 /* Open the SDL audio device. TO DO: This is ugly, fix it. */
221 static int sdl_ao_init(int flags
)
223 struct SDL_AudioSpec desired
;
225 if (!SDL_WasInit(SDL_INIT_AUDIO
))
226 if (SDL_InitSubSystem(SDL_INIT_AUDIO
) == -1) {
227 logger
.write(LOG_ERROR
, SDL_MODULE
,
228 "Failed to init audio: %s", SDL_GetError());
232 /* TO DO: This should be configurable */
233 desired
.freq
= 22050;
234 desired
.format
= AUDIO_S16LSB
;
235 desired
.channels
= 2;
236 desired
.userdata
= NULL
;
237 desired
.callback
= render_audio
;
238 desired
.samples
= 2048;
240 if (SDL_OpenAudio(&desired
, &sdlfmt
) == -1) {
241 logger
.write(LOG_ERROR
, SDL_MODULE
,
242 "Failed to open audio device: %s", SDL_GetError());
246 fmt
.frequency
= sdlfmt
.freq
;
247 fmt
.channels
= sdlfmt
.channels
;
249 switch (sdlfmt
.format
) {
251 fmt
.format
= AFMT_U8
;
254 fmt
.format
= AFMT_S8
;
257 fmt
.format
= AFMT_U16_LE
;
260 fmt
.format
= AFMT_S16_LE
;
263 fmt
.format
= AFMT_U16_BE
;
266 fmt
.format
= AFMT_S16_BE
;
272 if (setchannels(8) == -1)
283 int init(plugin_useservice_t useservice
)
285 static struct audio_output ao_sdl
= {
288 // .fini = sdl_ao_fini,
290 .close
= sdl_ao_close
,
292 // .stop = sdl_ao_stop,
295 if (useservice(LOG_SERVICE
, SDL_MODULE
, &logger
))
297 if (useservice(AV_ACCESS_SERVICE
, SDL_MODULE
, &av
))
299 if (useservice(AO_SERVICE
, SDL_MODULE
, &ao_sdl
))
302 if (SDL_Init(SDL_INIT_NOPARACHUTE
) == -1) {
303 logger
.write(LOG_WARNING
, SDL_MODULE
,
304 "Failed to initialize SDL: %s", SDL_GetError());
308 logger
.write(LOG_INFO
, SDL_MODULE
,
309 "SDL Audio/Video output driver " SDL_MODULE_VERSION
);