diff options
-rw-r--r-- | backends/platform/psp/Makefile | 4 | ||||
-rw-r--r-- | backends/platform/psp/audio.cpp | 228 | ||||
-rw-r--r-- | backends/platform/psp/audio.h | 71 | ||||
-rw-r--r-- | backends/platform/psp/module.mk | 3 | ||||
-rw-r--r-- | backends/platform/psp/osys_psp.cpp | 31 | ||||
-rw-r--r-- | backends/platform/psp/osys_psp.h | 2 |
6 files changed, 332 insertions, 7 deletions
diff --git a/backends/platform/psp/Makefile b/backends/platform/psp/Makefile index 8e83563d10..6967973da7 100644 --- a/backends/platform/psp/Makefile +++ b/backends/platform/psp/Makefile @@ -147,8 +147,8 @@ OBJS := powerman.o \ cursor.o \ trace.o \ psploader.o \ - pspkeyboard.o - + pspkeyboard.o \ + audio.o # Include common Scummvm makefile include $(srcdir)/Makefile.common diff --git a/backends/platform/psp/audio.cpp b/backends/platform/psp/audio.cpp new file mode 100644 index 0000000000..c19cbb52a2 --- /dev/null +++ b/backends/platform/psp/audio.cpp @@ -0,0 +1,228 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/backends/platform/psp/osys_psp.cpp $ + * $Id: osys_psp.cpp 46126 2009-11-24 14:18:46Z fingolfin $ + * + */ + +#include <SDL.h> +#include <pspthreadman.h> +#include <pspaudio.h> + +#include "common/scummsys.h" +#include "backends/platform/psp/audio.h" + +//#define __PSP_DEBUG_FUNCS__ /* For debugging function calls */ +//#define __PSP_DEBUG_PRINT__ /* For debug printouts */ + +#include "backends/platform/psp/trace.h" + +bool PspAudio::open(uint32 freq, uint32 numOfChannels, uint32 numOfSamples, callbackFunc callback, void *userData) { + DEBUG_ENTER_FUNC(); + if (_init) { + PSP_ERROR("audio device already initialized\n"); + return true; + } + + PSP_DEBUG_PRINT("freq[%d], numOfChannels[%d], numOfSamples[%d], callback[%p], userData[%x]\n", + freq, numOfChannels, numOfSamples, callback, (uint32)userData); + + numOfSamples = PSP_AUDIO_SAMPLE_ALIGN(numOfSamples); + uint32 bufLen = numOfSamples * numOfChannels * NUM_BUFFERS * sizeof(uint16); + + PSP_DEBUG_PRINT("total buffer size[%d]\n", bufLen); + + _buffers[0] = (byte *)memalign(64, bufLen); + if (!_buffers[0]) { + PSP_ERROR("failed to allocate memory for audio buffers\n"); + return false; + } + memset(_buffers[0], 0, bufLen); // clean the buffer + + // Fill in the rest of the buffer pointers + byte *pBuffer = _buffers[0]; + for (int i = 1; i < NUM_BUFFERS; i++) { + pBuffer += numOfSamples * numOfChannels * sizeof(uint16); + _buffers[i] = pBuffer; + } + + // Reserve a HW channel for our audio + _pspChannel = sceAudioChReserve(PSP_AUDIO_NEXT_CHANNEL, numOfSamples, numOfChannels == 2 ? PSP_AUDIO_FORMAT_STEREO : PSP_AUDIO_FORMAT_MONO); + if (_pspChannel < 0) { + PSP_ERROR("failed to reserve audio channel\n"); + return false; + } + + PSP_DEBUG_PRINT("reserved channel[%d] for audio\n", _pspChannel); + + // Save our data + _numOfChannels = numOfChannels; + _numOfSamples = numOfSamples; + _bufferSize = numOfSamples * numOfChannels * sizeof(uint16); // should be the right size to send the app + _callback = callback; + _userData = userData; + _emptyBuffers = NUM_BUFFERS; + _bufferToFill = 0; + _bufferToPlay = 0; + + _init = true; + _paused = true; // start in paused mode + + createThread(); + + return true; +} + +bool PspAudio::createThread() { + DEBUG_ENTER_FUNC(); + int threadId = sceKernelCreateThread("audioThread", thread, 30, 16*1024, THREAD_ATTR_USER, 0); + + if (threadId < 0) { // error + PSP_ERROR("failed to create audio thread. Error code %d\n", threadId); + return false; + } + + PspAudio *_this = this; // trick to get into context when the thread starts + + if (sceKernelStartThread(threadId, sizeof(uint32 *), &_this) < 0) { + PSP_ERROR("failed to start thread %d\n", threadId); + return false; + } + + PSP_DEBUG_PRINT("created audio thread[%x]\n", threadId); + + return true; +} + +// Static function to be called upon thread startup. Will call a non-static function +int PspAudio::thread(SceSize, void *__this) { + DEBUG_ENTER_FUNC(); + PspAudio *_this = *(PspAudio **)__this; // get our this for the context + + _this->audioThread(); + return 0; +}; + +// The real thread function +void PspAudio::audioThread() { + bool isPlaying = false; + int remainingSamples = 0; + + assert(_callback); + PSP_DEBUG_PRINT_FUNC("audio thread started\n"); + + while (_init) { // Keep looping so long as we haven't been told to stop + if (_paused) + PSP_DEBUG_PRINT("audio thread paused\n"); + while (_paused) { // delay until we stop pausing + SDL_Delay(100); + } + if (!_paused) + PSP_DEBUG_PRINT("audio thread unpaused\n"); + + // check if the audio is playing + remainingSamples = sceAudioGetChannelRestLen(_pspChannel); + if (remainingSamples < 0) { + PSP_ERROR("failed to get remaining samples\n"); + return; + } + isPlaying = remainingSamples ? true : false; + + PSP_DEBUG_PRINT("remaining samples[%d]\n", remainingSamples); + + while (true) { // really only execute once. this just helps write the logic + if (isPlaying) { + _stoppedPlayingOnceFlag = false; + + // check if a buffer is empty + if (_emptyBuffers) { // we have some empty buffers + PSP_DEBUG_PRINT("sound playing & an empty buffer. filling buffer[%d]. empty buffers[%d]\n", _bufferToFill, _emptyBuffers); + _callback(_userData, _buffers[_bufferToFill], _bufferSize); // ask mixer to fill in + nextBuffer(_bufferToFill); + _emptyBuffers--; + break; + } else { // we have no empty buffers + // calculate how long we need to sleep. time(us) = samples * 1000000 / freq + // since frequency is always 44100, we can do a shortcut: + // time(us) = samples * (10000 / 441) + uint32 sleepTime = (remainingSamples * 10000) / 441; + if (!sleepTime) + break; + PSP_DEBUG_PRINT("sound playing & no empty buffers. sleeping for %d samples for %dus\n", remainingSamples, sleepTime); + sceKernelDelayThread(sleepTime); + break; + } + } else { // we're not playing right now + if (_stoppedPlayingOnceFlag == false) { // we only want to do this when we finish playing + nextBuffer(_bufferToPlay); + _emptyBuffers++; + _stoppedPlayingOnceFlag = true; + } + + if (_emptyBuffers == NUM_BUFFERS) { // problem: we have only empty buffers! + PSP_DEBUG_PRINT("no sound playing & no full buffer. filling buffer[%d]. empty buffers[%d]\n", _bufferToFill, _emptyBuffers); + _callback(_userData, _buffers[_bufferToFill], _bufferSize); + nextBuffer(_bufferToFill); + _emptyBuffers--; + break; + } else { // we have at least one non-empty buffer + PSP_DEBUG_PRINT("no sound playing & a full buffer. playing buffer[%d]. empty buffers[%d]\n", _bufferToPlay, _emptyBuffers); + playBuffer(); + break; + } + } + } // while true + } // while _init + + // destroy everything + free(_buffers[0]); + sceAudioChRelease(_pspChannel); + PSP_DEBUG_PRINT("audio thread exiting. ****************************\n"); +} + +// Much faster than using % +inline void PspAudio::nextBuffer(int &bufferIdx) { + DEBUG_ENTER_FUNC(); + bufferIdx++; + if (bufferIdx >= NUM_BUFFERS) + bufferIdx = 0; +} + +// Don't do it with blocking +inline bool PspAudio::playBuffer() { + DEBUG_ENTER_FUNC(); + int ret; + if (_numOfChannels == 1) + ret = sceAudioOutput(_pspChannel, PSP_AUDIO_VOLUME_MAX, _buffers[_bufferToPlay]); + else + ret = sceAudioOutputPanned(_pspChannel, PSP_AUDIO_VOLUME_MAX, PSP_AUDIO_VOLUME_MAX, _buffers[_bufferToPlay]); + + if (ret < 0) { + PSP_ERROR("failed to output audio. Error[%d]\n", ret); + return false; + } + return true; +} + +void PspAudio::close() { + PSP_DEBUG_PRINT("close had been called ***************\n"); + _init = false; +} diff --git a/backends/platform/psp/audio.h b/backends/platform/psp/audio.h new file mode 100644 index 0000000000..97e2391319 --- /dev/null +++ b/backends/platform/psp/audio.h @@ -0,0 +1,71 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/backends/platform/psp/osys_psp.cpp $ + * $Id: osys_psp.cpp 46126 2009-11-24 14:18:46Z fingolfin $ + * + */ + +#ifndef PSP_AUDIO_H +#define PSP_AUDIO_H + +class PspAudio { +public: + enum { + NUM_BUFFERS = 2, + FREQUENCY = 44100 /* only frequency we allow */ + }; + typedef void (* callbackFunc)(void *userData, byte *samples, int len); + PspAudio() : _pspChannel(0), + _numOfChannels(0), _numOfSamples(0), _callback(0), + _bufferToPlay(0), _bufferToFill(0), _emptyBuffers(NUM_BUFFERS), + _init(false), _paused(true), _stoppedPlayingOnceFlag(true) { + for (int i=0; i<NUM_BUFFERS; i++) + _buffers[i] = 0; + } + ~PspAudio() { close(); } + bool playBuffer(); + void nextBuffer(int &bufferIdx); + static int thread(SceSize, void *); + void audioThread(); + bool open(uint32 freq, uint32 numOfChannels, uint32 numOfSamples, callbackFunc callback, void *userData); + bool createThread(); + void close(); + uint32 getFrequency() { return FREQUENCY; } + void pause() { _paused = true; } + void unpause() { _paused = false; } + +private: + int _pspChannel; // chosen hardware output channel + uint32 _numOfChannels; // 1 for mono; 2 for stereo + uint32 _numOfSamples; + callbackFunc _callback; // the callback to call between outputting audio + void *_userData; // userData to send with callback + byte *_buffers[NUM_BUFFERS]; + int _bufferToPlay; // the next buffer to output + int _bufferToFill; + int _bufferSize; + int _emptyBuffers; + bool _init; // flag for initialization + bool _paused; + bool _stoppedPlayingOnceFlag; // used to make sure we know when the playing stopped +}; + +#endif /* PSP_AUDIO_H */ diff --git a/backends/platform/psp/module.mk b/backends/platform/psp/module.mk index f7191fe14f..0e5bd8737d 100644 --- a/backends/platform/psp/module.mk +++ b/backends/platform/psp/module.mk @@ -12,7 +12,8 @@ MODULE_OBJS := powerman.o \ cursor.o \ trace.o \ psploader.o \ - pspkeyboard.o + pspkeyboard.o \ + audio.o MODULE_DIRS += \ backends/platform/psp/ diff --git a/backends/platform/psp/osys_psp.cpp b/backends/platform/psp/osys_psp.cpp index 58d98bc219..5f51135a9a 100644 --- a/backends/platform/psp/osys_psp.cpp +++ b/backends/platform/psp/osys_psp.cpp @@ -48,6 +48,7 @@ #include "backends/platform/psp/trace.h" +#define USE_PSP_AUDIO #define SAMPLES_PER_SEC 44100 @@ -58,7 +59,11 @@ static int timer_handler(int t) { } void OSystem_PSP::initSDL() { +#ifdef USE_PSP_AUDIO + SDL_Init(SDL_INIT_TIMER); +#else SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER); +#endif } OSystem_PSP::~OSystem_PSP() {} @@ -331,8 +336,6 @@ void OSystem_PSP::mixCallback(void *sys, byte *samples, int len) { } void OSystem_PSP::setupMixer(void) { - SDL_AudioSpec desired; - SDL_AudioSpec obtained; // Determine the desired output sampling frequency. uint32 samplesPerSec = 0; @@ -349,6 +352,22 @@ void OSystem_PSP::setupMixer(void) { while (samples * 16 > samplesPerSec * 2) samples >>= 1; + assert(!_mixer); + +#ifdef USE_PSP_AUDIO + if (!_audio.open(samplesPerSec, 2, samples, mixCallback, this)) { + PSP_ERROR("failed to open audio\n"); + return; + } + samplesPerSec = _audio.getFrequency(); // may have been changed by audio system + _mixer = new Audio::MixerImpl(this, samplesPerSec); + assert(_mixer); + _mixer->setReady(true); + _audio.unpause(); +#else + SDL_AudioSpec obtained; + SDL_AudioSpec desired; + memset(&desired, 0, sizeof(desired)); desired.freq = samplesPerSec; desired.format = AUDIO_S16SYS; @@ -356,8 +375,7 @@ void OSystem_PSP::setupMixer(void) { desired.samples = samples; desired.callback = mixCallback; desired.userdata = this; - - assert(!_mixer); + if (SDL_OpenAudio(&desired, &obtained) != 0) { warning("Could not open audio: %s", SDL_GetError()); _mixer = new Audio::MixerImpl(this, samplesPerSec); @@ -376,10 +394,15 @@ void OSystem_PSP::setupMixer(void) { SDL_PauseAudio(0); } +#endif /* USE_PSP_AUDIO */ } void OSystem_PSP::quit() { +#ifdef USE_PSP_AUDIO + _audio.close(); +#else SDL_CloseAudio(); +#endif SDL_Quit(); sceKernelExitGame(); } diff --git a/backends/platform/psp/osys_psp.h b/backends/platform/psp/osys_psp.h index 8c5b40dcdf..4d9cf31b18 100644 --- a/backends/platform/psp/osys_psp.h +++ b/backends/platform/psp/osys_psp.h @@ -38,6 +38,7 @@ #include "backends/platform/psp/pspkeyboard.h" #include "backends/platform/psp/display_manager.h" #include "backends/platform/psp/input.h" +#include "backends/platform/psp/audio.h" #include <SDL.h> @@ -55,6 +56,7 @@ private: DisplayManager _displayManager; PSPKeyboard _keyboard; InputHandler _inputHandler; + PspAudio _audio; void initSDL(); |