1
0
Fork 0
forked from len0rd/rockbox
foxbox/apps/plugins/xrick/system/syssnd_rockbox.c
Sebastian Leonhardt 102c374248 added xrick game
original xrick code by 'BigOrno' at:
http://www.bigorno.net/xrick/

Rockbox port, plus bugfixes at:
https://github.com/pierluigi-vicinanza/xrick

Further changes:

 * Additonal fixes from g#3026
 * Port to modern plugin API
 * Add Pluginlib keymap fallback
 * Support all >1bpp screens
 * Fix build warnings in miniz
 * Better error message when resources are missing

Change-Id: Id83928bc2539901b0221692f65cbca41389c58e7
2024-06-30 17:24:16 -04:00

483 lines
11 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Port of xrick, a Rick Dangerous clone, to Rockbox.
* See http://www.bigorno.net/xrick/
*
* Copyright (C) 2008-2014 Pierluigi Vicinanza
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "xrick/config.h"
#ifdef ENABLE_SOUND
#include "xrick/system/system.h"
#include "xrick/game.h"
#include "xrick/debug.h"
#include "xrick/system/syssnd_rockbox.h"
#include "plugin.h"
/*
* Global variables
*/
const U8 syssnd_period = 20;
/*
* Local variables
*/
enum
{
SYSSND_MIX_CHANNELS = 5,
SYSSND_MIX_SAMPLES = 1024, /* try changing this value if sound mixing is too slow or choppy */
SYSSND_SOURCE_SAMPLES = SYSSND_MIX_SAMPLES / 2
};
/* channels to be mixed */
static channel_t channels[SYSSND_MIX_CHANNELS];
/* buffer used to mix sounds sent to pcm playback, stores 16b stereo 44Khz audio samples */
enum { AUDIO_BUFFER_COUNT = 4 };
typedef struct
{
U32 data[SYSSND_MIX_SAMPLES];
size_t length; /* in 8 bit mono samples */
} mix_buffer_t;
static mix_buffer_t mixBuffers[AUDIO_BUFFER_COUNT];
static size_t writeIndex;
static size_t readIndex;
static size_t fillCount;
static bool isAudioPlaying;
static bool isAudioInitialised = false;
/*
* Prototypes
*/
static void endChannel(size_t c);
static void get_more(const void **start, size_t *size);
/*
* Deactivate channel
*/
static void endChannel(size_t c)
{
channels[c].loop = 0;
channels[c].sound = NULL;
}
/*
* Audio callback
*/
static void get_more(const void **start, size_t *size)
{
if (fillCount > 0)
{
/* Store output data address and size. */
*start = mixBuffers[readIndex].data;
*size = mixBuffers[readIndex].length * 8;
/* Free this part of output buffer. */
mixBuffers[readIndex].length = 0;
/* Advance to the next part of output buffer. */
readIndex = (readIndex + 1) & (AUDIO_BUFFER_COUNT - 1);
fillCount--;
}
else
{
/* Nothing to play. */
isAudioPlaying = false;
}
}
/*
* Mix audio samples and fill playback buffer
*/
void syssnd_update(void)
{
if (!isAudioInitialised)
{
return;
}
for (;;)
{
size_t c;
size_t sampleOffset;
size_t maxSampleCount;
bool isFirstSound;
U8 *sourceBuf, *sourceBufEnd;
U32 *destBuf;
/* Cancel if whole buffer filled. */
if (fillCount >= (AUDIO_BUFFER_COUNT - 1))
{
return;
}
maxSampleCount = 0;
sampleOffset = mixBuffers[writeIndex].length;
destBuf = mixBuffers[writeIndex].data + sampleOffset * 2;
isFirstSound = true;
for (c = 0; c < SYSSND_MIX_CHANNELS ; ++c)
{
U32 * mixBuffer;
size_t sampleCount;
channel_t * channel = &channels[c];
if (!channel->sound /* no sound to play on this channel */
|| (channel->loop == 0)) /* channel is inactive */
{
continue;
}
if (isFirstSound)
{
/* clear mixing buffer */
rb->memset(destBuf, 0, (SYSSND_MIX_SAMPLES - (sampleOffset * 2)) * sizeof(U32));
isFirstSound = false;
}
sampleCount = MIN(SYSSND_SOURCE_SAMPLES - sampleOffset, channel->len);
if (maxSampleCount < sampleCount)
{
maxSampleCount = sampleCount;
}
/* mix sound samples */
mixBuffer = destBuf;
sourceBuf = channel->buf;
sourceBufEnd = channel->buf + sampleCount;
while (sourceBuf < sourceBufEnd)
{
/* Convert from unsigned 8 bit mono 22khz to signed 16 bit stereo 44khz */
const int sourceSample = *sourceBuf++;
int monoSample = (sourceSample - 0x80) << 8;
U32 stereoSample = *mixBuffer;
monoSample += (S32)(stereoSample) >> 16;
if (monoSample >= 0x8000)
{
monoSample = 0x7FFF;
}
else if (monoSample < -0x8000)
{
monoSample = -0x8000;
}
stereoSample = (U16)monoSample | ((U16)monoSample << 16);
*mixBuffer++ = stereoSample;
*mixBuffer++ = stereoSample;
}
channel->buf = sourceBufEnd;
channel->len -= sampleCount;
if (channel->len == 0) /* ending ? */
{
if (channel->loop > 0)
{
channel->loop--;
}
if (channel->loop)
{
/* just loop */
IFDEBUG_AUDIO2(sys_printf("xrick/audio: channel %d - loop\n", c););
channel->buf = channel->sound->buf;
channel->len = channel->sound->len;
}
else
{
/* end for real */
IFDEBUG_AUDIO2(sys_printf("xrick/audio: channel %d - end\n", c););
endChannel(c);
}
}
}
if (maxSampleCount == 0)
{
return;
}
mixBuffers[writeIndex].length += maxSampleCount;
/* Advance one part of audio buffer. */
writeIndex = (writeIndex + 1) & (AUDIO_BUFFER_COUNT - 1);
fillCount++;
if (!isAudioPlaying && fillCount > 0)
{
rb->pcm_play_data(&get_more, NULL, NULL, 0);
isAudioPlaying = true;
}
}
}
/*
* Initialise audio
*/
bool syssnd_init(void)
{
if (isAudioInitialised)
{
return true;
}
IFDEBUG_AUDIO(sys_printf("xrick/audio: start\n"););
rb->talk_disable(true);
/* Stop playback to reconfigure audio settings and acquire audio buffer */
rb->mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK);
#if INPUT_SRC_CAPS != 0
/* Select playback */
rb->audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK);
rb->audio_set_output_source(AUDIO_SRC_PLAYBACK);
#endif
rb->pcm_set_frequency(HW_FREQ_44);
rb->pcm_apply_settings();
rb->memset(channels, 0, sizeof(channels));
rb->memset(mixBuffers, 0, sizeof(mixBuffers));
writeIndex = 0;
readIndex = 0;
fillCount = 0;
isAudioPlaying = false;
isAudioInitialised = true;
IFDEBUG_AUDIO(sys_printf("xrick/audio: ready\n"););
return true;
}
/*
* Shutdown
*/
void syssnd_shutdown(void)
{
if (!isAudioInitialised)
{
return;
}
/* Stop playback. */
rb->pcm_play_stop();
/* Reset playing status. */
isAudioPlaying = false;
/* Restore default sampling rate. */
rb->pcm_set_frequency(HW_SAMPR_DEFAULT);
rb->pcm_apply_settings();
rb->talk_disable(false);
isAudioInitialised = false;
IFDEBUG_AUDIO(sys_printf("xrick/audio: stop\n"););
}
/*
* Play a sound
*
* loop: number of times the sound should be played, -1 to loop forever
*
* NOTE if sound is already playing, simply reset it (i.e. can not have
* twice the same sound playing -- tends to become noisy when too many
* bad guys die at the same time).
*/
void syssnd_play(sound_t *sound, S8 loop)
{
size_t c;
if (!isAudioInitialised || !sound)
{
return;
}
c = 0;
while (channels[c].sound != sound &&
channels[c].loop != 0 &&
c < SYSSND_MIX_CHANNELS)
{
c++;
}
if (c >= SYSSND_MIX_CHANNELS)
{
return;
}
if (!sound->buf)
{
syssnd_load(sound);
if (!sound->buf)
{
sys_error("(audio) can not load %s", sound->name);
return;
}
}
IFDEBUG_AUDIO(
if (channels[c].sound == sound)
{
sys_printf("xrick/audio: already playing %s on channel %d - resetting\n",
sound->name, c);
}
else
{
sys_printf("xrick/audio: playing %s on channel %d\n", sound->name, c);
}
);
channels[c].loop = loop;
channels[c].sound = sound;
channels[c].buf = sound->buf;
channels[c].len = sound->len;
}
/*
* Pause all sounds
*/
void syssnd_pauseAll(bool pause)
{
if (!isAudioInitialised)
{
return;
}
rb->pcm_play_lock();
rb->mixer_channel_play_pause(PCM_MIXER_CHAN_PLAYBACK, !pause);
rb->pcm_play_unlock();
}
/*
* Stop a sound
*/
void syssnd_stop(sound_t *sound)
{
size_t c;
if (!isAudioInitialised || !sound)
{
return;
}
for (c = 0; c < SYSSND_MIX_CHANNELS; c++)
{
if (channels[c].sound == sound)
{
endChannel(c);
}
}
}
/*
* Stops all channels.
*/
void syssnd_stopAll(void)
{
size_t c;
if (!isAudioInitialised)
{
return;
}
for (c = 0; c < SYSSND_MIX_CHANNELS; c++)
{
if (channels[c].sound)
{
endChannel(c);
}
}
}
/*
* Load a sound.
*/
void syssnd_load(sound_t *sound)
{
int bytesRead;
file_t fp;
bool success;
if (!isAudioInitialised || !sound)
{
return;
}
success = false;
do
{
sound->buf = sysmem_push(sound->len);
if (!sound->buf)
{
sys_error("(audio) not enough memory for \"%s\", %d bytes needed", sound->name, sound->len);
break;
}
fp = sysfile_open(sound->name);
if (!fp)
{
sys_error("(audio) unable to open \"%s\"", sound->name);
break;
}
sysfile_seek(fp, sizeof(wave_header_t), SEEK_SET); /* skip WAVE header */
bytesRead = sysfile_read(fp, sound->buf, sound->len, 1);
sysfile_close(fp);
if (bytesRead != 1)
{
sys_error("(audio) unable to read from \"%s\"", sound->name);
break;
}
success = true;
} while (false);
if (!success)
{
sysmem_pop(sound->buf);
sound->buf = NULL;
sound->len = 0;
return;
}
IFDEBUG_AUDIO(sys_printf("xrick/audio: successfully loaded \"%s\"\n", sound->name););
}
/*
* Unload a sound.
*/
void syssnd_unload(sound_t *sound)
{
if (!isAudioInitialised || !sound || !sound->buf)
{
return;
}
sysmem_pop(sound->buf);
sound->buf = NULL;
sound->len = 0;
}
#endif /* ENABLE_SOUND */
/* eof */