diff options
Diffstat (limited to 'apps/plugins/xrick/system/syssnd_rockbox.c')
-rw-r--r-- | apps/plugins/xrick/system/syssnd_rockbox.c | 483 |
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 | */ | ||
40 | const U8 syssnd_period = 20; | ||
41 | |||
42 | /* | ||
43 | * Local variables | ||
44 | */ | ||
45 | enum | ||
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 */ | ||
53 | static channel_t channels[SYSSND_MIX_CHANNELS]; | ||
54 | /* buffer used to mix sounds sent to pcm playback, stores 16b stereo 44Khz audio samples */ | ||
55 | enum { AUDIO_BUFFER_COUNT = 4 }; | ||
56 | typedef struct | ||
57 | { | ||
58 | U32 data[SYSSND_MIX_SAMPLES]; | ||
59 | size_t length; /* in 8 bit mono samples */ | ||
60 | } mix_buffer_t; | ||
61 | static mix_buffer_t mixBuffers[AUDIO_BUFFER_COUNT]; | ||
62 | static size_t writeIndex; | ||
63 | static size_t readIndex; | ||
64 | static size_t fillCount; | ||
65 | static bool isAudioPlaying; | ||
66 | static bool isAudioInitialised = false; | ||
67 | |||
68 | /* | ||
69 | * Prototypes | ||
70 | */ | ||
71 | static void endChannel(size_t c); | ||
72 | static void get_more(const void **start, size_t *size); | ||
73 | |||
74 | /* | ||
75 | * Deactivate channel | ||
76 | */ | ||
77 | static void endChannel(size_t c) | ||
78 | { | ||
79 | channels[c].loop = 0; | ||
80 | channels[c].sound = NULL; | ||
81 | } | ||
82 | |||
83 | /* | ||
84 | * Audio callback | ||
85 | */ | ||
86 | static 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 | */ | ||
111 | void 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 | */ | ||
234 | bool 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 | */ | ||
273 | void 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 | */ | ||
305 | void 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 | */ | ||
357 | void 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 | */ | ||
372 | void 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 | */ | ||
393 | void 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 | */ | ||
414 | void 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 | */ | ||
469 | void 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 */ | ||