summaryrefslogtreecommitdiff
path: root/apps/plugins/xrick/system/syssnd_rockbox.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/xrick/system/syssnd_rockbox.c')
-rw-r--r--apps/plugins/xrick/system/syssnd_rockbox.c483
1 files changed, 483 insertions, 0 deletions
diff --git a/apps/plugins/xrick/system/syssnd_rockbox.c b/apps/plugins/xrick/system/syssnd_rockbox.c
new file mode 100644
index 0000000000..97ed5474f1
--- /dev/null
+++ b/apps/plugins/xrick/system/syssnd_rockbox.c
@@ -0,0 +1,483 @@
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Port of xrick, a Rick Dangerous clone, to Rockbox.
11 * See http://www.bigorno.net/xrick/
12 *
13 * Copyright (C) 2008-2014 Pierluigi Vicinanza
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
19 *
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
22 *
23 ****************************************************************************/
24
25#include "xrick/config.h"
26
27#ifdef ENABLE_SOUND
28
29#include "xrick/system/system.h"
30
31#include "xrick/game.h"
32#include "xrick/debug.h"
33#include "xrick/system/syssnd_rockbox.h"
34
35#include "plugin.h"
36
37/*
38 * Global variables
39 */
40const U8 syssnd_period = 20;
41
42/*
43 * Local variables
44 */
45enum
46{
47 SYSSND_MIX_CHANNELS = 5,
48 SYSSND_MIX_SAMPLES = 1024, /* try changing this value if sound mixing is too slow or choppy */
49 SYSSND_SOURCE_SAMPLES = SYSSND_MIX_SAMPLES / 2
50};
51
52/* channels to be mixed */
53static channel_t channels[SYSSND_MIX_CHANNELS];
54/* buffer used to mix sounds sent to pcm playback, stores 16b stereo 44Khz audio samples */
55enum { AUDIO_BUFFER_COUNT = 4 };
56typedef struct
57{
58 U32 data[SYSSND_MIX_SAMPLES];
59 size_t length; /* in 8 bit mono samples */
60} mix_buffer_t;
61static mix_buffer_t mixBuffers[AUDIO_BUFFER_COUNT];
62static size_t writeIndex;
63static size_t readIndex;
64static size_t fillCount;
65static bool isAudioPlaying;
66static bool isAudioInitialised = false;
67
68/*
69 * Prototypes
70 */
71static void endChannel(size_t c);
72static void get_more(const void **start, size_t *size);
73
74/*
75 * Deactivate channel
76 */
77static void endChannel(size_t c)
78{
79 channels[c].loop = 0;
80 channels[c].sound = NULL;
81}
82
83/*
84 * Audio callback
85 */
86static void get_more(const void **start, size_t *size)
87{
88 if (fillCount > 0)
89 {
90 /* Store output data address and size. */
91 *start = mixBuffers[readIndex].data;
92 *size = mixBuffers[readIndex].length * 8;
93
94 /* Free this part of output buffer. */
95 mixBuffers[readIndex].length = 0;
96
97 /* Advance to the next part of output buffer. */
98 readIndex = (readIndex + 1) & (AUDIO_BUFFER_COUNT - 1);
99 fillCount--;
100 }
101 else
102 {
103 /* Nothing to play. */
104 isAudioPlaying = false;
105 }
106}
107
108/*
109 * Mix audio samples and fill playback buffer
110 */
111void syssnd_update(void)
112{
113 if (!isAudioInitialised)
114 {
115 return;
116 }
117
118 for (;;)
119 {
120 size_t c;
121 size_t sampleOffset;
122 size_t maxSampleCount;
123 bool isFirstSound;
124 U8 *sourceBuf, *sourceBufEnd;
125 U32 *destBuf;
126
127 /* Cancel if whole buffer filled. */
128 if (fillCount >= (AUDIO_BUFFER_COUNT - 1))
129 {
130 return;
131 }
132
133 maxSampleCount = 0;
134
135 sampleOffset = mixBuffers[writeIndex].length;
136 destBuf = mixBuffers[writeIndex].data + sampleOffset * 2;
137
138 isFirstSound = true;
139 for (c = 0; c < SYSSND_MIX_CHANNELS ; ++c)
140 {
141 U32 * mixBuffer;
142 size_t sampleCount;
143 channel_t * channel = &channels[c];
144
145 if (!channel->sound /* no sound to play on this channel */
146 || (channel->loop == 0)) /* channel is inactive */
147 {
148 continue;
149 }
150
151 if (isFirstSound)
152 {
153 /* clear mixing buffer */
154 rb->memset(destBuf, 0, (SYSSND_MIX_SAMPLES - (sampleOffset * 2)) * sizeof(U32));
155 isFirstSound = false;
156 }
157
158 sampleCount = MIN(SYSSND_SOURCE_SAMPLES - sampleOffset, channel->len);
159 if (maxSampleCount < sampleCount)
160 {
161 maxSampleCount = sampleCount;
162 }
163
164 /* mix sound samples */
165 mixBuffer = destBuf;
166 sourceBuf = channel->buf;
167 sourceBufEnd = channel->buf + sampleCount;
168 while (sourceBuf < sourceBufEnd)
169 {
170 /* Convert from unsigned 8 bit mono 22khz to signed 16 bit stereo 44khz */
171 const int sourceSample = *sourceBuf++;
172 int monoSample = (sourceSample - 0x80) << 8;
173 U32 stereoSample = *mixBuffer;
174 monoSample += (S32)(stereoSample) >> 16;
175 if (monoSample >= 0x8000)
176 {
177 monoSample = 0x7FFF;
178 }
179 else if (monoSample < -0x8000)
180 {
181 monoSample = -0x8000;
182 }
183 stereoSample = (U16)monoSample | ((U16)monoSample << 16);
184 *mixBuffer++ = stereoSample;
185 *mixBuffer++ = stereoSample;
186 }
187 channel->buf = sourceBufEnd;
188
189 channel->len -= sampleCount;
190 if (channel->len == 0) /* ending ? */
191 {
192 if (channel->loop > 0)
193 {
194 channel->loop--;
195 }
196 if (channel->loop)
197 {
198 /* just loop */
199 IFDEBUG_AUDIO2(sys_printf("xrick/audio: channel %d - loop\n", c););
200 channel->buf = channel->sound->buf;
201 channel->len = channel->sound->len;
202 }
203 else
204 {
205 /* end for real */
206 IFDEBUG_AUDIO2(sys_printf("xrick/audio: channel %d - end\n", c););
207 endChannel(c);
208 }
209 }
210 }
211
212 if (maxSampleCount == 0)
213 {
214 return;
215 }
216
217 mixBuffers[writeIndex].length += maxSampleCount;
218
219 /* Advance one part of audio buffer. */
220 writeIndex = (writeIndex + 1) & (AUDIO_BUFFER_COUNT - 1);
221 fillCount++;
222
223 if (!isAudioPlaying && fillCount > 0)
224 {
225 rb->pcm_play_data(&get_more, NULL, NULL, 0);
226 isAudioPlaying = true;
227 }
228 }
229}
230
231/*
232 * Initialise audio
233 */
234bool syssnd_init(void)
235{
236 if (isAudioInitialised)
237 {
238 return true;
239 }
240
241 IFDEBUG_AUDIO(sys_printf("xrick/audio: start\n"););
242
243 rb->talk_disable(true);
244
245 /* Stop playback to reconfigure audio settings and acquire audio buffer */
246 rb->mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK);
247
248#if INPUT_SRC_CAPS != 0
249 /* Select playback */
250 rb->audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK);
251 rb->audio_set_output_source(AUDIO_SRC_PLAYBACK);
252#endif
253
254 rb->pcm_set_frequency(HW_FREQ_44);
255 rb->pcm_apply_settings();
256
257 rb->memset(channels, 0, sizeof(channels));
258 rb->memset(mixBuffers, 0, sizeof(mixBuffers));
259
260 writeIndex = 0;
261 readIndex = 0;
262 fillCount = 0;
263 isAudioPlaying = false;
264
265 isAudioInitialised = true;
266 IFDEBUG_AUDIO(sys_printf("xrick/audio: ready\n"););
267 return true;
268}
269
270/*
271 * Shutdown
272 */
273void syssnd_shutdown(void)
274{
275 if (!isAudioInitialised)
276 {
277 return;
278 }
279
280 /* Stop playback. */
281 rb->pcm_play_stop();
282
283 /* Reset playing status. */
284 isAudioPlaying = false;
285
286 /* Restore default sampling rate. */
287 rb->pcm_set_frequency(HW_SAMPR_DEFAULT);
288 rb->pcm_apply_settings();
289
290 rb->talk_disable(false);
291
292 isAudioInitialised = false;
293 IFDEBUG_AUDIO(sys_printf("xrick/audio: stop\n"););
294}
295
296/*
297 * Play a sound
298 *
299 * loop: number of times the sound should be played, -1 to loop forever
300 *
301 * NOTE if sound is already playing, simply reset it (i.e. can not have
302 * twice the same sound playing -- tends to become noisy when too many
303 * bad guys die at the same time).
304 */
305void syssnd_play(sound_t *sound, S8 loop)
306{
307 size_t c;
308
309 if (!isAudioInitialised || !sound)
310 {
311 return;
312 }
313
314 c = 0;
315 while (channels[c].sound != sound &&
316 channels[c].loop != 0 &&
317 c < SYSSND_MIX_CHANNELS)
318 {
319 c++;
320 }
321 if (c >= SYSSND_MIX_CHANNELS)
322 {
323 return;
324 }
325
326 if (!sound->buf)
327 {
328 syssnd_load(sound);
329 if (!sound->buf)
330 {
331 sys_error("(audio) can not load %s", sound->name);
332 return;
333 }
334 }
335
336 IFDEBUG_AUDIO(
337 if (channels[c].sound == sound)
338 {
339 sys_printf("xrick/audio: already playing %s on channel %d - resetting\n",
340 sound->name, c);
341 }
342 else
343 {
344 sys_printf("xrick/audio: playing %s on channel %d\n", sound->name, c);
345 }
346 );
347
348 channels[c].loop = loop;
349 channels[c].sound = sound;
350 channels[c].buf = sound->buf;
351 channels[c].len = sound->len;
352}
353
354/*
355 * Pause all sounds
356 */
357void syssnd_pauseAll(bool pause)
358{
359 if (!isAudioInitialised)
360 {
361 return;
362 }
363
364 rb->pcm_play_lock();
365 rb->mixer_channel_play_pause(PCM_MIXER_CHAN_PLAYBACK, !pause);
366 rb->pcm_play_unlock();
367}
368
369/*
370 * Stop a sound
371 */
372void syssnd_stop(sound_t *sound)
373{
374 size_t c;
375
376 if (!isAudioInitialised || !sound)
377 {
378 return;
379 }
380
381 for (c = 0; c < SYSSND_MIX_CHANNELS; c++)
382 {
383 if (channels[c].sound == sound)
384 {
385 endChannel(c);
386 }
387 }
388}
389
390/*
391 * Stops all channels.
392 */
393void syssnd_stopAll(void)
394{
395 size_t c;
396
397 if (!isAudioInitialised)
398 {
399 return;
400 }
401
402 for (c = 0; c < SYSSND_MIX_CHANNELS; c++)
403 {
404 if (channels[c].sound)
405 {
406 endChannel(c);
407 }
408 }
409}
410
411/*
412 * Load a sound.
413 */
414void syssnd_load(sound_t *sound)
415{
416 int bytesRead;
417 file_t fp;
418 bool success;
419
420 if (!isAudioInitialised || !sound)
421 {
422 return;
423 }
424
425 success = false;
426 do
427 {
428 sound->buf = sysmem_push(sound->len);
429 if (!sound->buf)
430 {
431 sys_error("(audio) not enough memory for \"%s\", %d bytes needed", sound->name, sound->len);
432 break;
433 }
434
435 fp = sysfile_open(sound->name);
436 if (!fp)
437 {
438 sys_error("(audio) unable to open \"%s\"", sound->name);
439 break;
440 }
441
442 sysfile_seek(fp, sizeof(wave_header_t), SEEK_SET); /* skip WAVE header */
443
444 bytesRead = sysfile_read(fp, sound->buf, sound->len, 1);
445 sysfile_close(fp);
446 if (bytesRead != 1)
447 {
448 sys_error("(audio) unable to read from \"%s\"", sound->name);
449 break;
450 }
451
452 success = true;
453 } while (false);
454
455 if (!success)
456 {
457 sysmem_pop(sound->buf);
458 sound->buf = NULL;
459 sound->len = 0;
460 return;
461 }
462
463 IFDEBUG_AUDIO(sys_printf("xrick/audio: successfully loaded \"%s\"\n", sound->name););
464}
465
466/*
467 * Unload a sound.
468 */
469void syssnd_unload(sound_t *sound)
470{
471 if (!isAudioInitialised || !sound || !sound->buf)
472 {
473 return;
474 }
475
476 sysmem_pop(sound->buf);
477 sound->buf = NULL;
478 sound->len = 0;
479}
480
481#endif /* ENABLE_SOUND */
482
483/* eof */