synced with scummvm svn rev 47951
|
@ -45,6 +45,14 @@ Common::EventManager *BaseBackend::getEventManager() {
|
||||||
return s_eventManager;
|
return s_eventManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
FIXME: Maybe we should push the default config file loading/saving code below
|
||||||
|
out to all the backends?
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
#if defined(UNIX)
|
#if defined(UNIX)
|
||||||
#if defined(SAMSUNGTV)
|
#if defined(SAMSUNGTV)
|
||||||
#define DEFAULT_CONFIG_FILE "/dtv/usb/sda1/.scummvmrc"
|
#define DEFAULT_CONFIG_FILE "/dtv/usb/sda1/.scummvmrc"
|
||||||
|
|
|
@ -1,49 +0,0 @@
|
||||||
/* Residual - A 3D game interpreter
|
|
||||||
*
|
|
||||||
* Residual is the legal property of its developers, whose names
|
|
||||||
* are too numerous to list here. Please refer to the AUTHORS
|
|
||||||
* 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$
|
|
||||||
* $Id$
|
|
||||||
*/
|
|
||||||
|
|
||||||
#if defined(__PSP__)
|
|
||||||
#include "backends/fs/psp/psp-fs-factory.h"
|
|
||||||
#include "backends/fs/psp/psp-fs.cpp"
|
|
||||||
|
|
||||||
DECLARE_SINGLETON(PSPFilesystemFactory);
|
|
||||||
|
|
||||||
AbstractFSNode *PSPFilesystemFactory::makeRootFileNode() const {
|
|
||||||
return new PSPFilesystemNode();
|
|
||||||
}
|
|
||||||
|
|
||||||
AbstractFSNode *PSPFilesystemFactory::makeCurrentDirectoryFileNode() const {
|
|
||||||
char buf[MAXPATHLEN];
|
|
||||||
char * ret = 0;
|
|
||||||
|
|
||||||
PowerMan.beginCriticalSection();
|
|
||||||
ret = getcwd(buf, MAXPATHLEN);
|
|
||||||
PowerMan.endCriticalSection();
|
|
||||||
|
|
||||||
return (ret ? new PSPFilesystemNode(buf) : NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
AbstractFSNode *PSPFilesystemFactory::makeFileNodePath(const Common::String &path) const {
|
|
||||||
return new PSPFilesystemNode(path, true);
|
|
||||||
}
|
|
||||||
#endif
|
|
|
@ -1,226 +0,0 @@
|
||||||
/* Residual - A 3D game interpreter
|
|
||||||
*
|
|
||||||
* Residual is the legal property of its developers, whose names
|
|
||||||
* are too numerous to list here. Please refer to the AUTHORS
|
|
||||||
* 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$
|
|
||||||
* $Id$
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef __PSP__
|
|
||||||
|
|
||||||
#include "engines/engine.h"
|
|
||||||
#include "backends/fs/abstract-fs.h"
|
|
||||||
#include "backends/fs/psp/psp-stream.h"
|
|
||||||
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <pspkernel.h>
|
|
||||||
|
|
||||||
#define ROOT_PATH "ms0:/"
|
|
||||||
|
|
||||||
#include "backends/platform/psp/trace.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implementation of the ScummVM file system API based on PSPSDK API.
|
|
||||||
*
|
|
||||||
* Parts of this class are documented in the base interface class, AbstractFSNode.
|
|
||||||
*/
|
|
||||||
class PSPFilesystemNode : public AbstractFSNode {
|
|
||||||
protected:
|
|
||||||
Common::String _displayName;
|
|
||||||
Common::String _path;
|
|
||||||
bool _isDirectory;
|
|
||||||
bool _isValid;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* Creates a PSPFilesystemNode with the root node as path.
|
|
||||||
*/
|
|
||||||
PSPFilesystemNode();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a PSPFilesystemNode for a given path.
|
|
||||||
*
|
|
||||||
* @param path Common::String with the path the new node should point to.
|
|
||||||
* @param verify true if the isValid and isDirectory flags should be verified during the construction.
|
|
||||||
*/
|
|
||||||
PSPFilesystemNode(const Common::String &p, bool verify = true);
|
|
||||||
|
|
||||||
virtual bool exists() const;
|
|
||||||
virtual Common::String getDisplayName() const { return _displayName; }
|
|
||||||
virtual Common::String getName() const { return _displayName; }
|
|
||||||
virtual Common::String getPath() const { return _path; }
|
|
||||||
virtual bool isDirectory() const { return _isDirectory; }
|
|
||||||
virtual bool isReadable() const;
|
|
||||||
virtual bool isWritable() const;
|
|
||||||
|
|
||||||
virtual AbstractFSNode *getChild(const Common::String &n) const;
|
|
||||||
virtual bool getChildren(AbstractFSList &list, ListMode mode, bool hidden) const;
|
|
||||||
virtual AbstractFSNode *getParent() const;
|
|
||||||
|
|
||||||
virtual Common::SeekableReadStream *createReadStream();
|
|
||||||
virtual Common::WriteStream *createWriteStream();
|
|
||||||
};
|
|
||||||
|
|
||||||
PSPFilesystemNode::PSPFilesystemNode() {
|
|
||||||
_isDirectory = true;
|
|
||||||
_displayName = "Root";
|
|
||||||
_isValid = true;
|
|
||||||
_path = ROOT_PATH;
|
|
||||||
}
|
|
||||||
|
|
||||||
PSPFilesystemNode::PSPFilesystemNode(const Common::String &p, bool verify) {
|
|
||||||
assert(p.size() > 0);
|
|
||||||
|
|
||||||
_path = p;
|
|
||||||
_displayName = lastPathComponent(_path, '/');
|
|
||||||
_isValid = true;
|
|
||||||
_isDirectory = true;
|
|
||||||
|
|
||||||
if (verify) {
|
|
||||||
struct stat st;
|
|
||||||
if (PowerMan.beginCriticalSection()==PowerManager::Blocked)
|
|
||||||
PSPDebugSuspend("Suspended in PSPFilesystemNode::PSPFilesystemNode\n");
|
|
||||||
_isValid = (0 == stat(_path.c_str(), &st));
|
|
||||||
PowerMan.endCriticalSection();
|
|
||||||
_isDirectory = S_ISDIR(st.st_mode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PSPFilesystemNode::exists() const {
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
if (PowerMan.beginCriticalSection() == PowerManager::Blocked)
|
|
||||||
PSPDebugSuspend("Suspended in PSPFilesystemNode::exists()\n"); // Make sure to block in case of suspend
|
|
||||||
|
|
||||||
ret = access(_path.c_str(), F_OK);
|
|
||||||
PowerMan.endCriticalSection();
|
|
||||||
|
|
||||||
return ret == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PSPFilesystemNode::isReadable() const {
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
if (PowerMan.beginCriticalSection() == PowerManager::Blocked)
|
|
||||||
PSPDebugSuspend("Suspended in PSPFilesystemNode::isReadable()\n"); // Make sure to block in case of suspend
|
|
||||||
|
|
||||||
ret = access(_path.c_str(), R_OK);
|
|
||||||
PowerMan.endCriticalSection();
|
|
||||||
|
|
||||||
return ret == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PSPFilesystemNode::isWritable() const {
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
if (PowerMan.beginCriticalSection() == PowerManager::Blocked)
|
|
||||||
PSPDebugSuspend("Suspended in PSPFilesystemNode::isWritable()\n"); // Make sure to block in case of suspend
|
|
||||||
|
|
||||||
ret = access(_path.c_str(), W_OK);
|
|
||||||
PowerMan.endCriticalSection();
|
|
||||||
|
|
||||||
return ret == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
AbstractFSNode *PSPFilesystemNode::getChild(const Common::String &n) const {
|
|
||||||
// FIXME: Pretty lame implementation! We do no error checking to speak
|
|
||||||
// of, do not check if this is a special node, etc.
|
|
||||||
assert(_isDirectory);
|
|
||||||
|
|
||||||
Common::String newPath(_path);
|
|
||||||
if (_path.lastChar() != '/')
|
|
||||||
newPath += '/';
|
|
||||||
newPath += n;
|
|
||||||
|
|
||||||
return new PSPFilesystemNode(newPath, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PSPFilesystemNode::getChildren(AbstractFSList &myList, ListMode mode, bool hidden) const {
|
|
||||||
assert(_isDirectory);
|
|
||||||
|
|
||||||
//TODO: honor the hidden flag
|
|
||||||
|
|
||||||
bool ret = true;
|
|
||||||
|
|
||||||
if (PowerMan.beginCriticalSection() == PowerManager::Blocked)
|
|
||||||
PSPDebugSuspend("Suspended in PSPFilesystemNode::getChildren\n"); // Make sure to block in case of suspend
|
|
||||||
|
|
||||||
int dfd = sceIoDopen(_path.c_str());
|
|
||||||
if (dfd > 0) {
|
|
||||||
SceIoDirent dir;
|
|
||||||
memset(&dir, 0, sizeof(dir));
|
|
||||||
|
|
||||||
while (sceIoDread(dfd, &dir) > 0) {
|
|
||||||
// Skip 'invisible files
|
|
||||||
if (dir.d_name[0] == '.')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
PSPFilesystemNode entry;
|
|
||||||
|
|
||||||
entry._isValid = true;
|
|
||||||
entry._displayName = dir.d_name;
|
|
||||||
|
|
||||||
Common::String newPath(_path);
|
|
||||||
if (newPath.lastChar() != '/')
|
|
||||||
newPath += '/';
|
|
||||||
newPath += dir.d_name;
|
|
||||||
|
|
||||||
entry._path = newPath;
|
|
||||||
entry._isDirectory = dir.d_stat.st_attr & FIO_SO_IFDIR;
|
|
||||||
|
|
||||||
// Honor the chosen mode
|
|
||||||
if ((mode == Common::FSNode::kListFilesOnly && entry._isDirectory) ||
|
|
||||||
(mode == Common::FSNode::kListDirectoriesOnly && !entry._isDirectory))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
myList.push_back(new PSPFilesystemNode(entry));
|
|
||||||
}
|
|
||||||
|
|
||||||
sceIoDclose(dfd);
|
|
||||||
ret = true;
|
|
||||||
} else { // dfd <= 0
|
|
||||||
ret = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
PowerMan.endCriticalSection();
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
AbstractFSNode *PSPFilesystemNode::getParent() const {
|
|
||||||
if (_path == ROOT_PATH)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
const char *start = _path.c_str();
|
|
||||||
const char *end = lastPathComponent(_path, '/');
|
|
||||||
|
|
||||||
return new PSPFilesystemNode(Common::String(start, end - start), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
Common::SeekableReadStream *PSPFilesystemNode::createReadStream() {
|
|
||||||
return PSPIoStream::makeFromPath(getPath(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
Common::WriteStream *PSPFilesystemNode::createWriteStream() {
|
|
||||||
return PSPIoStream::makeFromPath(getPath(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif //#ifdef __PSP__
|
|
|
@ -1,303 +0,0 @@
|
||||||
/* Residual - A 3D game interpreter
|
|
||||||
*
|
|
||||||
* Residual is the legal property of its developers, whose names
|
|
||||||
* are too numerous to list here. Please refer to the AUTHORS
|
|
||||||
* 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$
|
|
||||||
* $Id$
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#ifdef __PSP__
|
|
||||||
|
|
||||||
#include <SDL/SDL_thread.h>
|
|
||||||
#include <SDL/SDL_mutex.h>
|
|
||||||
|
|
||||||
#include "backends/platform/psp/trace.h"
|
|
||||||
#include "backends/platform/psp/powerman.h"
|
|
||||||
#include "backends/fs/psp/psp-stream.h"
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
PSPIoStream::PSPIoStream(const Common::String &path, bool writeMode)
|
|
||||||
: StdioStream((void *)1), _path(path), _writeMode(writeMode) {
|
|
||||||
|
|
||||||
assert(!path.empty());
|
|
||||||
|
|
||||||
_handle = (void *)0; // Need to do this since base class asserts not 0.
|
|
||||||
_ferror = false;
|
|
||||||
_feof = false;
|
|
||||||
_pos = 0;
|
|
||||||
|
|
||||||
#ifdef __PSP_DEBUG_SUSPEND__
|
|
||||||
_errorSuspend = 0;
|
|
||||||
_errorSource = 0;
|
|
||||||
_errorPos = 0;
|
|
||||||
_errorHandle = 0;
|
|
||||||
_suspendCount = 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
PSPIoStream::~PSPIoStream() {
|
|
||||||
if (PowerMan.beginCriticalSection() == PowerManager::Blocked)
|
|
||||||
PSPDebugSuspend("Suspended in PSPIoStream::~PSPIoStream()\n");
|
|
||||||
|
|
||||||
PowerMan.unregisterSuspend(this); // Unregister with powermanager to be suspended
|
|
||||||
// Must do this before fclose() or resume() will reopen.
|
|
||||||
|
|
||||||
fclose((FILE *)_handle); // We don't need a critical section(?). Worst case, the handle gets closed on its own
|
|
||||||
|
|
||||||
PowerMan.endCriticalSection();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to open the file pointed to by the path.
|
|
||||||
//
|
|
||||||
//
|
|
||||||
void *PSPIoStream::open() {
|
|
||||||
if (PowerMan.beginCriticalSection() == PowerManager::Blocked) {
|
|
||||||
// No need to open. Just return the _handle resume() already opened.
|
|
||||||
PSPDebugSuspend("Suspended in PSPIoStream::open\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
_handle = fopen(_path.c_str(), _writeMode ? "wb" : "rb"); // open
|
|
||||||
|
|
||||||
PowerMan.registerSuspend(this); // Register with the powermanager to be suspended
|
|
||||||
|
|
||||||
PowerMan.endCriticalSection();
|
|
||||||
|
|
||||||
return _handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PSPIoStream::err() const {
|
|
||||||
if (_ferror)
|
|
||||||
PSPDebugSuspend("In PSPIoStream::err - mem_ferror=%d, source=%d, suspend error=%d, pos=%d, _errorPos=%d, _errorHandle=%p, suspendCount=%d _handle\n",
|
|
||||||
_ferror, _errorSource, _errorSuspend, _pos, _errorPos, _errorHandle, _suspendCount);
|
|
||||||
|
|
||||||
return _ferror;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PSPIoStream::clearErr() {
|
|
||||||
_ferror = false; // Remove regular error bit
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PSPIoStream::eos() const {
|
|
||||||
return _feof;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32 PSPIoStream::pos() const {
|
|
||||||
return _pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int32 PSPIoStream::size() const {
|
|
||||||
if (PowerMan.beginCriticalSection() == PowerManager::Blocked)
|
|
||||||
PSPDebugSuspend("Suspended in PSPIoStream::size()\n");
|
|
||||||
|
|
||||||
fseek((FILE *)_handle, 0, SEEK_END);
|
|
||||||
int32 length = ftell((FILE *)_handle);
|
|
||||||
fseek((FILE *)_handle, _pos, SEEK_SET);
|
|
||||||
|
|
||||||
if (_pos < 0 || length < 0) { // Check for errors
|
|
||||||
PSPDebugSuspend("In PSPIoStream::size(). encountered an error!\n");
|
|
||||||
_ferror = true;
|
|
||||||
length = -1; // If our oldPos is bad, we want length to be bad too to signal
|
|
||||||
clearerr((FILE *)_handle);
|
|
||||||
|
|
||||||
#ifdef __PSP_DEBUG_SUSPEND__
|
|
||||||
_errorSource = 2;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
PowerMan.endCriticalSection();
|
|
||||||
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PSPIoStream::seek(int32 offs, int whence) {
|
|
||||||
// Check if we can access the file
|
|
||||||
if (PowerMan.beginCriticalSection() == PowerManager::Blocked)
|
|
||||||
PSPDebugSuspend("Suspended in PSPIoStream::seek()\n");
|
|
||||||
|
|
||||||
int ret = fseek((FILE *)_handle, offs, whence);
|
|
||||||
|
|
||||||
if (ret != 0) {
|
|
||||||
_ferror = true;
|
|
||||||
PSPDebugSuspend("In PSPIoStream::seek(). encountered an error!\n");
|
|
||||||
clearerr((FILE *)_handle);
|
|
||||||
_feof = feof((FILE *)_handle);
|
|
||||||
|
|
||||||
#ifdef __PSP_DEBUG_SUSPEND__
|
|
||||||
_errorSource = 3;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else { // everything ok
|
|
||||||
_feof = false; // Reset eof flag since we know it was ok
|
|
||||||
}
|
|
||||||
|
|
||||||
_pos = ftell((FILE *)_handle); // update pos
|
|
||||||
|
|
||||||
PowerMan.endCriticalSection();
|
|
||||||
|
|
||||||
return ret == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32 PSPIoStream::read(void *ptr, uint32 len) {
|
|
||||||
// Check if we can access the file
|
|
||||||
if (PowerMan.beginCriticalSection() == PowerManager::Blocked)
|
|
||||||
PSPDebugSuspend("Suspended in PSPIoStream::read()\n");
|
|
||||||
|
|
||||||
size_t ret = fread((byte *)ptr, 1, len, (FILE *)_handle);
|
|
||||||
|
|
||||||
_pos += ret; // Update pos
|
|
||||||
|
|
||||||
if (ret != len) { // Check for eof
|
|
||||||
_feof = feof((FILE *)_handle);
|
|
||||||
if (!_feof) { // It wasn't an eof. Must be an error
|
|
||||||
_ferror = true;
|
|
||||||
clearerr((FILE *)_handle);
|
|
||||||
_pos = ftell((FILE *)_handle); // Update our position
|
|
||||||
PSPDebugSuspend("In PSPIoStream::read(). encountered an error!\n");
|
|
||||||
|
|
||||||
#ifdef __PSP_DEBUG_SUSPEND__
|
|
||||||
_errorSource = 4;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PowerMan.endCriticalSection();
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32 PSPIoStream::write(const void *ptr, uint32 len) {
|
|
||||||
// Check if we can access the file
|
|
||||||
if (PowerMan.beginCriticalSection() == PowerManager::Blocked)
|
|
||||||
PSPDebugSuspend("Suspended in PSPIoStream::read()\n");
|
|
||||||
|
|
||||||
size_t ret = fwrite(ptr, 1, len, (FILE *)_handle);
|
|
||||||
|
|
||||||
_pos += ret;
|
|
||||||
|
|
||||||
if (ret != len) { // Set error
|
|
||||||
_ferror = true;
|
|
||||||
clearerr((FILE *)_handle);
|
|
||||||
_pos = ftell((FILE *)_handle); // Update pos
|
|
||||||
PSPDebugTrace("In PSPIoStream::write(). encountered an error!\n");
|
|
||||||
|
|
||||||
#ifdef __PSP_DEBUG_SUSPEND__
|
|
||||||
_errorSource = 5;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
PowerMan.endCriticalSection();
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PSPIoStream::flush() {
|
|
||||||
// Enter critical section
|
|
||||||
if (PowerMan.beginCriticalSection() == PowerManager::Blocked)
|
|
||||||
PSPDebugSuspend("Suspended in PSPIoStream::read()\n");
|
|
||||||
|
|
||||||
int ret = fflush((FILE *)_handle);
|
|
||||||
|
|
||||||
if (ret != 0) {
|
|
||||||
_ferror = true;
|
|
||||||
clearerr((FILE *)_handle);
|
|
||||||
PSPDebugSuspend("In PSPIoStream::flush(). encountered an error!\n");
|
|
||||||
|
|
||||||
#ifdef __PSP_DEBUG_SUSPEND__
|
|
||||||
_errorSource = 6;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
PowerMan.endCriticalSection();
|
|
||||||
|
|
||||||
return ret == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For the PSP, since we're building in suspend support, we moved opening
|
|
||||||
// the actual file to an open function since we need an actual PSPIoStream object to suspend.
|
|
||||||
//
|
|
||||||
PSPIoStream *PSPIoStream::makeFromPath(const Common::String &path, bool writeMode) {
|
|
||||||
PSPIoStream *stream = new PSPIoStream(path, writeMode);
|
|
||||||
|
|
||||||
if (stream->open() > 0) {
|
|
||||||
return stream;
|
|
||||||
} else {
|
|
||||||
delete stream;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Function to suspend the IO stream (called by PowerManager)
|
|
||||||
*/
|
|
||||||
int PSPIoStream::suspend() {
|
|
||||||
#ifdef __PSP_DEBUG_SUSPEND__
|
|
||||||
_suspendCount++;
|
|
||||||
|
|
||||||
if (_handle > 0 && _pos < 0) {
|
|
||||||
_errorSuspend = SuspendError;
|
|
||||||
_errorPos = _pos;
|
|
||||||
_errorHandle = _handle;
|
|
||||||
}
|
|
||||||
#endif /* __PSP_DEBUG_SUSPEND__ */
|
|
||||||
|
|
||||||
if (_handle > 0) {
|
|
||||||
fclose((FILE *)_handle); // close our file descriptor
|
|
||||||
_handle = (void *)0xFFFFFFFF; // Set handle to non-null invalid value so makeFromPath doesn't return error
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Function to resume the IO stream (called by Power Manager)
|
|
||||||
*/
|
|
||||||
int PSPIoStream::resume() {
|
|
||||||
int ret = 0;
|
|
||||||
#ifdef __PSP_DEBUG_SUSPEND__
|
|
||||||
_suspendCount--;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// We reopen our file descriptor
|
|
||||||
_handle = fopen(_path.c_str(), _writeMode ? "wb" : "rb");
|
|
||||||
if (_handle <= 0) {
|
|
||||||
PSPDebugSuspend("PSPIoStream::resume(): Couldn't reopen file %s\n", _path.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resume our previous position
|
|
||||||
if (_handle > 0 && _pos > 0) {
|
|
||||||
ret = fseek((FILE *)_handle, _pos, SEEK_SET);
|
|
||||||
|
|
||||||
#ifdef __PSP_DEBUG_SUSPEND__
|
|
||||||
if (ret != 0) { // Check for problem
|
|
||||||
_errorSuspend = ResumeError;
|
|
||||||
_errorPos = _pos;
|
|
||||||
_errorHandle = _handle;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* __PSP__ */
|
|
|
@ -1,86 +0,0 @@
|
||||||
/* Residual - A 3D game interpreter
|
|
||||||
*
|
|
||||||
* Residual is the legal property of its developers, whose names
|
|
||||||
* are too numerous to list here. Please refer to the AUTHORS
|
|
||||||
* 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$
|
|
||||||
* $Id$
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef PSPSTREAM_H_
|
|
||||||
#define PSPSTREAM_H_
|
|
||||||
|
|
||||||
#include "backends/fs/stdiostream.h"
|
|
||||||
#include "backends/platform/psp/powerman.h"
|
|
||||||
#include "common/list.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Class to handle special suspend/resume needs of PSP IO Streams
|
|
||||||
*/
|
|
||||||
class PSPIoStream : public StdioStream, public Suspendable {
|
|
||||||
protected:
|
|
||||||
Common::String _path; /* Need to maintain for reopening after suspend */
|
|
||||||
bool _writeMode; /* "" */
|
|
||||||
int _pos; /* "" */
|
|
||||||
mutable int _ferror; /* Save file ferror */
|
|
||||||
mutable bool _feof; /* and eof */
|
|
||||||
|
|
||||||
enum {
|
|
||||||
SuspendError = 2,
|
|
||||||
ResumeError = 3
|
|
||||||
};
|
|
||||||
|
|
||||||
int _errorSuspend;
|
|
||||||
mutable int _errorSource;
|
|
||||||
|
|
||||||
#ifdef __PSP_DEBUG_SUSPEND__
|
|
||||||
int _errorPos;
|
|
||||||
void * _errorHandle;
|
|
||||||
int _suspendCount;
|
|
||||||
#endif /* __PSP_DEBUG_SUSPEND__ */
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* Given a path, invoke fopen on that path and wrap the result in a
|
|
||||||
* PSPIoStream instance.
|
|
||||||
*/
|
|
||||||
static PSPIoStream *makeFromPath(const Common::String &path, bool writeMode);
|
|
||||||
|
|
||||||
PSPIoStream(const Common::String &path, bool writeMode);
|
|
||||||
virtual ~PSPIoStream();
|
|
||||||
|
|
||||||
void * open(); // open the file pointed to by the file path
|
|
||||||
|
|
||||||
bool err() const;
|
|
||||||
void clearErr();
|
|
||||||
bool eos() const;
|
|
||||||
|
|
||||||
virtual uint32 write(const void *dataPtr, uint32 dataSize);
|
|
||||||
virtual bool flush();
|
|
||||||
|
|
||||||
virtual int32 pos() const;
|
|
||||||
virtual int32 size() const;
|
|
||||||
virtual bool seek(int32 offs, int whence = SEEK_SET);
|
|
||||||
virtual uint32 read(void *dataPtr, uint32 dataSize);
|
|
||||||
|
|
||||||
int suspend(); /* Suspendable interface (power manager) */
|
|
||||||
int resume(); /* " " */
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* PSPSTREAM_H_ */
|
|
|
@ -142,7 +142,7 @@ int MidiDriver_CORE::open() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err != noErr)
|
if (err != noErr)
|
||||||
warning("Failed loading custom sound font '%s' (error %d)\n", soundfont, err);
|
warning("Failed loading custom sound font '%s' (error %ld)\n", soundfont, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef COREAUDIO_DISABLE_REVERB
|
#ifdef COREAUDIO_DISABLE_REVERB
|
||||||
|
|
|
@ -109,8 +109,8 @@ void MidiDriver_CoreMIDI::close() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MidiDriver_CoreMIDI::send(uint32 b) {
|
void MidiDriver_CoreMIDI::send(uint32 b) {
|
||||||
assert(mOutPort != 0);
|
assert(mOutPort != NULL);
|
||||||
assert(mDest != 0);
|
assert(mDest != NULL);
|
||||||
|
|
||||||
// Extract the MIDI data
|
// Extract the MIDI data
|
||||||
byte status_byte = (b & 0x000000FF);
|
byte status_byte = (b & 0x000000FF);
|
||||||
|
@ -153,8 +153,8 @@ void MidiDriver_CoreMIDI::send(uint32 b) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MidiDriver_CoreMIDI::sysEx(const byte *msg, uint16 length) {
|
void MidiDriver_CoreMIDI::sysEx(const byte *msg, uint16 length) {
|
||||||
assert(mOutPort != 0);
|
assert(mOutPort != NULL);
|
||||||
assert(mDest != 0);
|
assert(mDest != NULL);
|
||||||
|
|
||||||
byte buf[384];
|
byte buf[384];
|
||||||
MIDIPacketList *packetList = (MIDIPacketList *)buf;
|
MIDIPacketList *packetList = (MIDIPacketList *)buf;
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
/* Residual - A 3D game interpreter
|
/* Residual - A 3D game interpreter
|
||||||
*
|
*
|
||||||
* Copyright (C) 2001 Ludvig Strigeus
|
* Residual is the legal property of its developers, whose names
|
||||||
* Copyright (C) 2001-2006 The ScummVM project
|
* are too numerous to list here. Please refer to the AUTHORS
|
||||||
|
* file distributed with this source distribution.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
@ -16,6 +17,9 @@
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program; if not, write to the Free Software
|
* along with this program; if not, write to the Free Software
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -7,7 +7,6 @@ MODULE_OBJS := \
|
||||||
fs/stdiostream.o \
|
fs/stdiostream.o \
|
||||||
fs/amigaos4/amigaos4-fs-factory.o \
|
fs/amigaos4/amigaos4-fs-factory.o \
|
||||||
fs/posix/posix-fs-factory.o \
|
fs/posix/posix-fs-factory.o \
|
||||||
fs/psp/psp-fs-factory.o \
|
|
||||||
fs/windows/windows-fs-factory.o \
|
fs/windows/windows-fs-factory.o \
|
||||||
keymapper/action.o \
|
keymapper/action.o \
|
||||||
keymapper/keymap.o \
|
keymapper/keymap.o \
|
||||||
|
|
|
@ -212,6 +212,7 @@ bool OSystem_SDL::dispatchSDLEvent(SDL_Event &ev, Common::Event &event) {
|
||||||
return handleJoyAxisMotion(ev, event);
|
return handleJoyAxisMotion(ev, event);
|
||||||
|
|
||||||
case SDL_VIDEOEXPOSE:
|
case SDL_VIDEOEXPOSE:
|
||||||
|
/* Residual doesn't support this */
|
||||||
//_forceFull = true;
|
//_forceFull = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -220,7 +221,7 @@ bool OSystem_SDL::dispatchSDLEvent(SDL_Event &ev, Common::Event &event) {
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,6 +336,7 @@ bool OSystem_SDL::handleMouseMotion(SDL_Event &ev, Common::Event &event) {
|
||||||
event.type = Common::EVENT_MOUSEMOVE;
|
event.type = Common::EVENT_MOUSEMOVE;
|
||||||
fillMouseEvent(event, ev.motion.x, ev.motion.y);
|
fillMouseEvent(event, ev.motion.x, ev.motion.y);
|
||||||
|
|
||||||
|
/* Residual doesn't support this */
|
||||||
//setMousePos(event.mouse.x, event.mouse.y);
|
//setMousePos(event.mouse.x, event.mouse.y);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -446,8 +446,6 @@ void OSystem_SDL::setMouseCursor(const byte *buf, uint w, uint h, int hotspot_x,
|
||||||
if (w == 0 || h == 0)
|
if (w == 0 || h == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Residual doesn't support this
|
/* Residual doesn't support this
|
||||||
_mouseCurState.hotX = hotspot_x;
|
_mouseCurState.hotX = hotspot_x;
|
||||||
_mouseCurState.hotY = hotspot_y;
|
_mouseCurState.hotY = hotspot_y;
|
||||||
|
|
|
@ -386,16 +386,36 @@ bool OSystem_SDL::hasFeature(Feature f) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void OSystem_SDL::setFeatureState(Feature f, bool enable) {
|
void OSystem_SDL::setFeatureState(Feature f, bool enable) {
|
||||||
/* switch (f) {
|
switch (f) {
|
||||||
|
case kFeatureFullscreenMode:
|
||||||
|
//setFullscreenMode(enable);
|
||||||
|
break;
|
||||||
|
/* case kFeatureAspectRatioCorrection:
|
||||||
|
//setAspectRatioCorrection(enable);
|
||||||
|
break;
|
||||||
|
case kFeatureAutoComputeDirtyRects:
|
||||||
|
if (enable)
|
||||||
|
_modeFlags |= DF_WANT_RECT_OPTIM;
|
||||||
|
else
|
||||||
|
_modeFlags &= ~DF_WANT_RECT_OPTIM;
|
||||||
|
break;
|
||||||
|
case kFeatureIconifyWindow:
|
||||||
|
if (enable)
|
||||||
|
SDL_WM_IconifyWindow();*/
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}*/
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OSystem_SDL::getFeatureState(Feature f) {
|
bool OSystem_SDL::getFeatureState(Feature f) {
|
||||||
switch (f) {
|
switch (f) {
|
||||||
case kFeatureFullscreenMode:
|
case kFeatureFullscreenMode:
|
||||||
return _fullscreen;
|
return _fullscreen;
|
||||||
|
/* case kFeatureAspectRatioCorrection:
|
||||||
|
return _videoMode.aspectRatioCorrection;
|
||||||
|
case kFeatureAutoComputeDirtyRects:
|
||||||
|
return _modeFlags & DF_WANT_RECT_OPTIM;*/
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -616,12 +636,12 @@ void OSystem_SDL::setupMixer() {
|
||||||
_samplesPerSec = SAMPLES_PER_SEC;
|
_samplesPerSec = SAMPLES_PER_SEC;
|
||||||
|
|
||||||
// Determine the sample buffer size. We want it to store enough data for
|
// Determine the sample buffer size. We want it to store enough data for
|
||||||
// about 1/16th of a second. Note that it must be a power of two.
|
// at least 1/16th of a second (though at maximum 8192 samples). Note
|
||||||
// So e.g. at 22050 Hz, we request a sample buffer size of 2048.
|
// that it must be a power of two. So e.g. at 22050 Hz, we request a
|
||||||
|
// sample buffer size of 2048.
|
||||||
int samples = 8192;
|
int samples = 8192;
|
||||||
while (16 * samples >= _samplesPerSec) {
|
while (samples * 16 > _samplesPerSec * 2)
|
||||||
samples >>= 1;
|
samples >>= 1;
|
||||||
}
|
|
||||||
|
|
||||||
memset(&desired, 0, sizeof(desired));
|
memset(&desired, 0, sizeof(desired));
|
||||||
desired.freq = _samplesPerSec;
|
desired.freq = _samplesPerSec;
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
#include "common/fs.h"
|
#include "common/fs.h"
|
||||||
#include "common/archive.h"
|
#include "common/archive.h"
|
||||||
#include "common/config-manager.h"
|
#include "common/config-manager.h"
|
||||||
#include "common/libz.h"
|
#include "common/zlib.h"
|
||||||
|
|
||||||
#include <errno.h> // for removeSavefile()
|
#include <errno.h> // for removeSavefile()
|
||||||
|
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
/* Residual - A 3D game interpreter
|
|
||||||
*
|
|
||||||
* Residual is the legal property of its developers, whose names
|
|
||||||
* are too numerous to list here. Please refer to the AUTHORS
|
|
||||||
* 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$
|
|
||||||
* $Id$
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef __PSP__
|
|
||||||
|
|
||||||
#include "backends/saves/psp/psp-saves.h"
|
|
||||||
#include "backends/platform/psp/powerman.h"
|
|
||||||
|
|
||||||
#include "common/config-manager.h"
|
|
||||||
#include "common/savefile.h"
|
|
||||||
|
|
||||||
#include <pspkernel.h>
|
|
||||||
|
|
||||||
#define PSP_DEFAULT_SAVE_PATH "ms0:/residual_savegames"
|
|
||||||
|
|
||||||
|
|
||||||
PSPSaveFileManager::PSPSaveFileManager() {
|
|
||||||
// Register default savepath
|
|
||||||
ConfMan.registerDefault("savepath", PSP_DEFAULT_SAVE_PATH);
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
PSPSaveFileManager::PSPSaveFileManager(const Common::String &defaultSavepath)
|
|
||||||
: DefaultSaveFileManager(defaultSavepath) {
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
void PSPSaveFileManager::checkPath(const Common::FSNode &dir) {
|
|
||||||
const char *savePath = dir.getPath().c_str();
|
|
||||||
clearError();
|
|
||||||
|
|
||||||
PowerMan.beginCriticalSection();
|
|
||||||
|
|
||||||
//check if the save directory exists
|
|
||||||
SceUID fd = sceIoDopen(savePath);
|
|
||||||
if (fd < 0) {
|
|
||||||
//No? then let's create it.
|
|
||||||
sceIoMkdir(savePath, 0777);
|
|
||||||
} else {
|
|
||||||
//it exists, so close it again.
|
|
||||||
sceIoDclose(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
PowerMan.endCriticalSection();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
Before Width: | Height: | Size: 150 KiB After Width: | Height: | Size: 150 KiB |
Before Width: | Height: | Size: 598 KiB After Width: | Height: | Size: 598 KiB |
Before Width: | Height: | Size: 150 KiB After Width: | Height: | Size: 150 KiB |
Before Width: | Height: | Size: 598 KiB After Width: | Height: | Size: 598 KiB |
Before Width: | Height: | Size: 150 KiB After Width: | Height: | Size: 150 KiB |
Before Width: | Height: | Size: 598 KiB After Width: | Height: | Size: 598 KiB |
Before Width: | Height: | Size: 150 KiB After Width: | Height: | Size: 150 KiB |
Before Width: | Height: | Size: 598 KiB After Width: | Height: | Size: 598 KiB |
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||||
<keyboard initial_mode="lowercase" v_align="bottom" h_align="centre">
|
<keyboard initial_mode="lowercase" v_align="bottom" h_align="centre">
|
||||||
|
<!-- coords key = "start x, start y, end x, end y" -->
|
||||||
<!-- Lowercase -->
|
<!-- Lowercase -->
|
||||||
<mode name="lowercase" resolutions="320x240,640x480">
|
<mode name="lowercase" resolutions="320x240,640x480">
|
||||||
<layout resolution="320x240" bitmap="lowercase320x240.bmp" transparent_color="255,0,255">
|
<layout resolution="320x240" bitmap="lowercase320x240.bmp" transparent_color="255,0,255">
|
||||||
|
@ -20,7 +20,8 @@
|
||||||
<area shape="rect" coords="235,26,253,43" target="f11" />
|
<area shape="rect" coords="235,26,253,43" target="f11" />
|
||||||
<area shape="rect" coords="255,26,272,45" target="f12" />
|
<area shape="rect" coords="255,26,272,45" target="f12" />
|
||||||
<area shape="rect" coords="276,27,310,43" target="del" />
|
<area shape="rect" coords="276,27,310,43" target="del" />
|
||||||
<area shape="rect" coords="276,47,308,64" target="backspace" />
|
<area shape="rect" coords="276,46,299,65" target="delete" />
|
||||||
|
<area shape="rect" coords="300,46,311,65" target="backspace" />
|
||||||
<area shape="rect" coords="8,68,32,85" target="tab" />
|
<area shape="rect" coords="8,68,32,85" target="tab" />
|
||||||
<area shape="rect" coords="36,68,53,85" target="q" />
|
<area shape="rect" coords="36,68,53,85" target="q" />
|
||||||
<area shape="rect" coords="57,68,75,86" target="w" />
|
<area shape="rect" coords="57,68,75,86" target="w" />
|
||||||
|
@ -76,85 +77,86 @@
|
||||||
<area shape="rect" coords="202,110,219,128" target="," />
|
<area shape="rect" coords="202,110,219,128" target="," />
|
||||||
<area shape="rect" coords="223,110,241,128" target="." />
|
<area shape="rect" coords="223,110,241,128" target="." />
|
||||||
<area shape="rect" coords="243,110,261,128" target="/" />
|
<area shape="rect" coords="243,110,261,128" target="/" />
|
||||||
<area shape="rect" coords="277,133,292,148" target="ok" />
|
<area shape="rect" coords="269,131,288,150" target="ok" />
|
||||||
<area shape="rect" coords="292,133,309,148" target="cancel" />
|
<area shape="rect" coords="292,131,311,150" target="cancel" />
|
||||||
</map>
|
</map>
|
||||||
</layout>
|
</layout>
|
||||||
<layout resolution="640x480" bitmap="lowercase640x480.bmp" transparent_color="255,0,255">
|
<layout resolution="640x480" bitmap="lowercase640x480.bmp" transparent_color="255,0,255">
|
||||||
<map>
|
<map>
|
||||||
<area shape="rect" coords="26,23,613,37" target="display_area" />
|
<area shape="rect" coords="26,23,613,37" target="display_area" />
|
||||||
<area shape="rect" coords="18,52,51,85" target="esc" />
|
<area shape="rect" coords="18,52,51,85" target="esc" />
|
||||||
<area shape="rect" coords="100,51,135,88" target="f2" />
|
<area shape="rect" coords="100,51,135,88" target="f2" />
|
||||||
<area shape="rect" coords="59,49,94,88" target="f1" />
|
<area shape="rect" coords="59,49,94,88" target="f1" />
|
||||||
<area shape="rect" coords="142,53,176,89" target="f3" />
|
<area shape="rect" coords="142,53,176,89" target="f3" />
|
||||||
<area shape="rect" coords="181,51,216,87" target="f4" />
|
<area shape="rect" coords="181,51,216,87" target="f4" />
|
||||||
<area shape="rect" coords="223,52,258,88" target="f5" />
|
<area shape="rect" coords="223,52,258,88" target="f5" />
|
||||||
<area shape="rect" coords="265,52,299,89" target="f6" />
|
<area shape="rect" coords="265,52,299,89" target="f6" />
|
||||||
<area shape="rect" coords="306,51,341,90" target="f7" />
|
<area shape="rect" coords="306,51,341,90" target="f7" />
|
||||||
<area shape="rect" coords="347,53,382,89" target="f8" />
|
<area shape="rect" coords="347,53,382,89" target="f8" />
|
||||||
<area shape="rect" coords="389,49,424,88" target="f9" />
|
<area shape="rect" coords="389,49,424,88" target="f9" />
|
||||||
<area shape="rect" coords="431,52,464,89" target="f10" />
|
<area shape="rect" coords="431,52,464,89" target="f10" />
|
||||||
<area shape="rect" coords="470,53,506,88" target="f11" />
|
<area shape="rect" coords="470,53,506,88" target="f11" />
|
||||||
<area shape="rect" coords="511,52,546,90" target="f12" />
|
<area shape="rect" coords="511,52,546,90" target="f12" />
|
||||||
<area shape="rect" coords="552,55,620,87" target="del" />
|
<area shape="rect" coords="552,55,620,87" target="del" />
|
||||||
<area shape="rect" coords="553,94,618,129" target="backspace" />
|
<area shape="rect" coords="552,93,598,131" target="delete" />
|
||||||
<area shape="rect" coords="17,136,66,171" target="tab" />
|
<area shape="rect" coords="601,93,623,131" target="backspace" />
|
||||||
<area shape="rect" coords="73,137,108,171" target="q" />
|
<area shape="rect" coords="17,136,66,171" target="tab" />
|
||||||
<area shape="rect" coords="114,136,150,172" target="w" />
|
<area shape="rect" coords="73,137,108,171" target="q" />
|
||||||
<area shape="rect" coords="157,134,189,171" target="e" />
|
<area shape="rect" coords="114,136,150,172" target="w" />
|
||||||
<area shape="rect" coords="197,135,232,171" target="r" />
|
<area shape="rect" coords="157,134,189,171" target="e" />
|
||||||
<area shape="rect" coords="239,135,274,172" target="t" />
|
<area shape="rect" coords="197,135,232,171" target="r" />
|
||||||
<area shape="rect" coords="282,136,317,173" target="y" />
|
<area shape="rect" coords="239,135,274,172" target="t" />
|
||||||
<area shape="rect" coords="322,135,359,173" target="u" />
|
<area shape="rect" coords="282,136,317,173" target="y" />
|
||||||
<area shape="rect" coords="364,134,399,172" target="i" />
|
<area shape="rect" coords="322,135,359,173" target="u" />
|
||||||
<area shape="rect" coords="404,135,440,171" target="o" />
|
<area shape="rect" coords="364,134,399,172" target="i" />
|
||||||
<area shape="rect" coords="446,136,480,172" target="p" />
|
<area shape="rect" coords="404,135,440,171" target="o" />
|
||||||
<area shape="rect" coords="89,177,127,216" target="a" />
|
<area shape="rect" coords="446,136,480,172" target="p" />
|
||||||
<area shape="rect" coords="131,177,169,214" target="s" />
|
<area shape="rect" coords="89,177,127,216" target="a"/>
|
||||||
<area shape="rect" coords="173,178,209,215" target="d" />
|
<area shape="rect" coords="131,177,169,214" target="s" />
|
||||||
<area shape="rect" coords="215,178,250,214" target="f" />
|
<area shape="rect" coords="173,178,209,215" target="d" />
|
||||||
<area shape="rect" coords="256,178,291,214" target="g" />
|
<area shape="rect" coords="215,178,250,214" target="f" />
|
||||||
<area shape="rect" coords="299,176,332,214" target="h" />
|
<area shape="rect" coords="256,178,291,214" target="g" />
|
||||||
<area shape="rect" coords="339,178,374,215" target="j" />
|
<area shape="rect" coords="299,176,332,214" target="h" />
|
||||||
<area shape="rect" coords="379,178,415,215" target="k" />
|
<area shape="rect" coords="339,178,374,215" target="j" />
|
||||||
<area shape="rect" coords="421,178,457,215" target="l" />
|
<area shape="rect" coords="379,178,415,215" target="k" />
|
||||||
<area shape="rect" coords="546,179,622,214" target="enter" />
|
<area shape="rect" coords="421,178,457,215" target="l" />
|
||||||
<area shape="rect" coords="19,221,101,256" target="shift" />
|
<area shape="rect" coords="546,179,622,214" target="enter" />
|
||||||
<area shape="rect" coords="17,179,84,213" target="caps" />
|
<area shape="rect" coords="19,221,101,256" target="shift" />
|
||||||
<area shape="rect" coords="117,220,152,256" target="z" />
|
<area shape="rect" coords="17,179,84,213" target="caps" />
|
||||||
<area shape="rect" coords="158,220,192,256" target="x" />
|
<area shape="rect" coords="117,220,152,256" target="z" />
|
||||||
<area shape="rect" coords="198,219,233,256" target="c" />
|
<area shape="rect" coords="158,220,192,256" target="x" />
|
||||||
<area shape="rect" coords="240,220,276,257" target="v" />
|
<area shape="rect" coords="198,219,233,256" target="c" />
|
||||||
<area shape="rect" coords="283,219,316,255" target="b" />
|
<area shape="rect" coords="240,220,276,257" target="v" />
|
||||||
<area shape="rect" coords="324,220,359,256" target="n" />
|
<area shape="rect" coords="283,219,316,255" target="b" />
|
||||||
<area shape="rect" coords="365,220,399,257" target="m" />
|
<area shape="rect" coords="324,220,359,256" target="n" />
|
||||||
<area shape="rect" coords="542,219,623,257" target="symbols" />
|
<area shape="rect" coords="365,220,399,257" target="m" />
|
||||||
<area shape="rect" coords="19,260,68,296" target="ctrl" />
|
<area shape="rect" coords="542,219,623,257" target="symbols" />
|
||||||
<area shape="rect" coords="76,261,123,295" target="alt" />
|
<area shape="rect" coords="19,260,68,296" target="ctrl" />
|
||||||
<area shape="rect" coords="135,261,525,297" target="space" />
|
<area shape="rect" coords="76,261,123,295" target="alt" />
|
||||||
<area shape="rect" coords="16,95,53,129" target="|" />
|
<area shape="rect" coords="135,261,525,297" target="space" />
|
||||||
<area shape="rect" coords="57,95,92,129" target="1" />
|
<area shape="rect" coords="16,95,53,129" target="|" />
|
||||||
<area shape="rect" coords="100,94,134,130" target="2" />
|
<area shape="rect" coords="57,95,92,129" target="1" />
|
||||||
<area shape="rect" coords="140,93,174,131" target="3" />
|
<area shape="rect" coords="100,94,134,130" target="2" />
|
||||||
<area shape="rect" coords="182,93,217,130" target="4" />
|
<area shape="rect" coords="140,93,174,131" target="3" />
|
||||||
<area shape="rect" coords="222,93,258,132" target="5" />
|
<area shape="rect" coords="182,93,217,130" target="4" />
|
||||||
<area shape="rect" coords="265,94,299,131" target="6" />
|
<area shape="rect" coords="222,93,258,132" target="5" />
|
||||||
<area shape="rect" coords="305,94,341,129" target="7" />
|
<area shape="rect" coords="265,94,299,131" target="6" />
|
||||||
<area shape="rect" coords="348,93,382,128" target="8" />
|
<area shape="rect" coords="305,94,341,129" target="7" />
|
||||||
<area shape="rect" coords="389,94,423,130" target="9" />
|
<area shape="rect" coords="348,93,382,128" target="8" />
|
||||||
<area shape="rect" coords="431,93,465,130" target="0" />
|
<area shape="rect" coords="389,94,423,130" target="9" />
|
||||||
<area shape="rect" coords="471,94,505,131" target="-" />
|
<area shape="rect" coords="431,93,465,130" target="0" />
|
||||||
<area shape="rect" coords="511,93,546,131" target="=" />
|
<area shape="rect" coords="471,94,505,131" target="-" />
|
||||||
<area shape="rect" coords="488,136,522,172" target="[" />
|
<area shape="rect" coords="511,93,546,131" target="=" />
|
||||||
<area shape="rect" coords="529,135,565,173" target="]" />
|
<area shape="rect" coords="488,136,522,172" target="[" />
|
||||||
<area shape="rect" coords="569,136,620,172" target="#" />
|
<area shape="rect" coords="529,135,565,173" target="]" />
|
||||||
<area shape="rect" coords="462,177,498,214" target=";" />
|
<area shape="rect" coords="569,136,620,172" target="#" />
|
||||||
<area shape="rect" coords="503,178,539,214" target="’" />
|
<area shape="rect" coords="462,177,498,214" target=";" />
|
||||||
<area shape="rect" coords="405,220,440,256" target="," />
|
<area shape="rect" coords="503,178,539,214" target="’" />
|
||||||
<area shape="rect" coords="447,220,483,256" target="." />
|
<area shape="rect" coords="405,220,440,256" target="," />
|
||||||
<area shape="rect" coords="487,220,524,256" target="/" />
|
<area shape="rect" coords="447,220,483,256" target="." />
|
||||||
<area shape="rect" coords="555,266,585,298" target="ok" />
|
<area shape="rect" coords="487,220,524,256" target="/" />
|
||||||
<area shape="rect" coords="585,266,619,298" target="cancel" />
|
<area shape="rect" coords="538,266,576,298" target="ok" />
|
||||||
|
<area shape="rect" coords="585,266,622,298" target="cancel" />
|
||||||
</map>
|
</map>
|
||||||
</layout>
|
</layout>
|
||||||
<event name="esc" type="key" code="27" ascii="27" modifiers="" />
|
<event name="esc" type="key" code="27" ascii="27" modifiers="" />
|
||||||
|
@ -230,6 +232,7 @@
|
||||||
<event name="ok" type="submit" />
|
<event name="ok" type="submit" />
|
||||||
<event name="cancel" type="cancel" />
|
<event name="cancel" type="cancel" />
|
||||||
<event name="quit" type="submit" />
|
<event name="quit" type="submit" />
|
||||||
|
<event name="delete" type="delete" />
|
||||||
</mode>
|
</mode>
|
||||||
|
|
||||||
<!-- Uppercase -->
|
<!-- Uppercase -->
|
||||||
|
@ -251,7 +254,8 @@
|
||||||
<area shape="rect" coords="235,26,253,43" target="f11" />
|
<area shape="rect" coords="235,26,253,43" target="f11" />
|
||||||
<area shape="rect" coords="255,26,272,45" target="f12" />
|
<area shape="rect" coords="255,26,272,45" target="f12" />
|
||||||
<area shape="rect" coords="276,27,310,43" target="del" />
|
<area shape="rect" coords="276,27,310,43" target="del" />
|
||||||
<area shape="rect" coords="276,47,308,64" target="backspace" />
|
<area shape="rect" coords="276,46,299,65" target="delete" />
|
||||||
|
<area shape="rect" coords="300,46,311,65" target="backspace" />
|
||||||
<area shape="rect" coords="8,68,32,85" target="tab" />
|
<area shape="rect" coords="8,68,32,85" target="tab" />
|
||||||
<area shape="rect" coords="36,68,53,85" target="Q" />
|
<area shape="rect" coords="36,68,53,85" target="Q" />
|
||||||
<area shape="rect" coords="57,68,75,86" target="W" />
|
<area shape="rect" coords="57,68,75,86" target="W" />
|
||||||
|
@ -307,81 +311,86 @@
|
||||||
<area shape="rect" coords="202,110,219,128" target="," />
|
<area shape="rect" coords="202,110,219,128" target="," />
|
||||||
<area shape="rect" coords="223,110,241,128" target="." />
|
<area shape="rect" coords="223,110,241,128" target="." />
|
||||||
<area shape="rect" coords="243,110,261,128" target="/" />
|
<area shape="rect" coords="243,110,261,128" target="/" />
|
||||||
|
<area shape="rect" coords="269,131,288,150" target="ok" />
|
||||||
|
<area shape="rect" coords="292,131,311,150" target="cancel" />
|
||||||
</map>
|
</map>
|
||||||
</layout>
|
</layout>
|
||||||
<layout resolution="640x480" bitmap="uppercase640x480.bmp" transparent_color="255,0,255">
|
<layout resolution="640x480" bitmap="uppercase640x480.bmp" transparent_color="255,0,255">
|
||||||
<map>
|
<map>
|
||||||
<area shape="rect" coords="26,23,613,37" target="display_area" />
|
<area shape="rect" coords="26,23,613,37" target="display_area" />
|
||||||
<area shape="rect" coords="18,52,51,85" target="esc" />
|
<area shape="rect" coords="18,52,51,85" target="esc" />
|
||||||
<area shape="rect" coords="100,51,135,88" target="f2" />
|
<area shape="rect" coords="100,51,135,88" target="f2" />
|
||||||
<area shape="rect" coords="59,49,94,88" target="f1" />
|
<area shape="rect" coords="59,49,94,88" target="f1" />
|
||||||
<area shape="rect" coords="142,53,176,89" target="f3" />
|
<area shape="rect" coords="142,53,176,89" target="f3" />
|
||||||
<area shape="rect" coords="181,51,216,87" target="f4" />
|
<area shape="rect" coords="181,51,216,87" target="f4" />
|
||||||
<area shape="rect" coords="223,52,258,88" target="f5" />
|
<area shape="rect" coords="223,52,258,88" target="f5" />
|
||||||
<area shape="rect" coords="265,52,299,89" target="f6" />
|
<area shape="rect" coords="265,52,299,89" target="f6" />
|
||||||
<area shape="rect" coords="306,51,341,90" target="f7" />
|
<area shape="rect" coords="306,51,341,90" target="f7" />
|
||||||
<area shape="rect" coords="347,53,382,89" target="f8" />
|
<area shape="rect" coords="347,53,382,89" target="f8" />
|
||||||
<area shape="rect" coords="389,49,424,88" target="f9" />
|
<area shape="rect" coords="389,49,424,88" target="f9" />
|
||||||
<area shape="rect" coords="431,52,464,89" target="f10" />
|
<area shape="rect" coords="431,52,464,89" target="f10" />
|
||||||
<area shape="rect" coords="470,53,506,88" target="f11" />
|
<area shape="rect" coords="470,53,506,88" target="f11" />
|
||||||
<area shape="rect" coords="511,52,546,90" target="f12" />
|
<area shape="rect" coords="511,52,546,90" target="f12" />
|
||||||
<area shape="rect" coords="552,55,620,87" target="del" />
|
<area shape="rect" coords="552,55,620,87" target="del" />
|
||||||
<area shape="rect" coords="553,94,618,129" target="backspace" />
|
<area shape="rect" coords="552,93,598,131" target="delete" />
|
||||||
<area shape="rect" coords="17,136,66,171" target="tab" />
|
<area shape="rect" coords="601,93,623,131" target="backspace" />
|
||||||
<area shape="rect" coords="73,137,108,171" target="Q" />
|
<area shape="rect" coords="17,136,66,171" target="tab" />
|
||||||
<area shape="rect" coords="114,136,150,172" target="W" />
|
<area shape="rect" coords="73,137,108,171" target="Q" />
|
||||||
<area shape="rect" coords="157,134,189,171" target="E" />
|
<area shape="rect" coords="114,136,150,172" target="W" />
|
||||||
<area shape="rect" coords="197,135,232,171" target="R" />
|
<area shape="rect" coords="157,134,189,171" target="E" />
|
||||||
<area shape="rect" coords="239,135,274,172" target="T" />
|
<area shape="rect" coords="197,135,232,171" target="R" />
|
||||||
<area shape="rect" coords="282,136,317,173" target="Y" />
|
<area shape="rect" coords="239,135,274,172" target="T" />
|
||||||
<area shape="rect" coords="322,135,359,173" target="U" />
|
<area shape="rect" coords="282,136,317,173" target="Y" />
|
||||||
<area shape="rect" coords="364,134,399,172" target="I" />
|
<area shape="rect" coords="322,135,359,173" target="U" />
|
||||||
<area shape="rect" coords="404,135,440,171" target="O" />
|
<area shape="rect" coords="364,134,399,172" target="I" />
|
||||||
<area shape="rect" coords="446,136,480,172" target="P" />
|
<area shape="rect" coords="404,135,440,171" target="O" />
|
||||||
<area shape="rect" coords="89,177,127,216" target="A" />
|
<area shape="rect" coords="446,136,480,172" target="P" />
|
||||||
<area shape="rect" coords="131,177,169,214" target="S" />
|
<area shape="rect" coords="89,177,127,216" target="A" />
|
||||||
<area shape="rect" coords="173,178,209,215" target="D" />
|
<area shape="rect" coords="131,177,169,214" target="S" />
|
||||||
<area shape="rect" coords="215,178,250,214" target="F" />
|
<area shape="rect" coords="173,178,209,215" target="D" />
|
||||||
<area shape="rect" coords="256,178,291,214" target="G" />
|
<area shape="rect" coords="215,178,250,214" target="F" />
|
||||||
<area shape="rect" coords="299,176,332,214" target="H" />
|
<area shape="rect" coords="256,178,291,214" target="G" />
|
||||||
<area shape="rect" coords="339,178,374,215" target="J" />
|
<area shape="rect" coords="299,176,332,214" target="H" />
|
||||||
<area shape="rect" coords="379,178,415,215" target="K" />
|
<area shape="rect" coords="339,178,374,215" target="J" />
|
||||||
<area shape="rect" coords="421,178,457,215" target="L" />
|
<area shape="rect" coords="379,178,415,215" target="K" />
|
||||||
<area shape="rect" coords="546,179,622,214" target="enter" />
|
<area shape="rect" coords="421,178,457,215" target="L" />
|
||||||
<area shape="rect" coords="19,221,101,256" target="shift" />
|
<area shape="rect" coords="546,179,622,214" target="enter" />
|
||||||
<area shape="rect" coords="17,179,84,213" target="caps" />
|
<area shape="rect" coords="19,221,101,256" target="shift" />
|
||||||
<area shape="rect" coords="117,220,152,256" target="Z" />
|
<area shape="rect" coords="17,179,84,213" target="caps" />
|
||||||
<area shape="rect" coords="158,220,192,256" target="X" />
|
<area shape="rect" coords="117,220,152,256" target="Z" />
|
||||||
<area shape="rect" coords="198,219,233,256" target="C" />
|
<area shape="rect" coords="158,220,192,256" target="X" />
|
||||||
<area shape="rect" coords="240,220,276,257" target="V" />
|
<area shape="rect" coords="198,219,233,256" target="C" />
|
||||||
<area shape="rect" coords="283,219,316,255" target="B" />
|
<area shape="rect" coords="240,220,276,257" target="V" />
|
||||||
<area shape="rect" coords="324,220,359,256" target="N" />
|
<area shape="rect" coords="283,219,316,255" target="B" />
|
||||||
<area shape="rect" coords="365,220,399,257" target="M" />
|
<area shape="rect" coords="324,220,359,256" target="N" />
|
||||||
<area shape="rect" coords="542,219,623,257" target="symbols" />
|
<area shape="rect" coords="365,220,399,257" target="M" />
|
||||||
<area shape="rect" coords="19,260,68,296" target="ctrl" />
|
<area shape="rect" coords="542,219,623,257" target="symbols" />
|
||||||
<area shape="rect" coords="76,261,123,295" target="alt" />
|
<area shape="rect" coords="19,260,68,296" target="ctrl" />
|
||||||
<area shape="rect" coords="135,261,525,297" target="space" />
|
<area shape="rect" coords="76,261,123,295" target="alt" />
|
||||||
<area shape="rect" coords="16,95,53,129" target="|" />
|
<area shape="rect" coords="135,261,525,297" target="space" />
|
||||||
<area shape="rect" coords="57,95,92,129" target="1" />
|
<area shape="rect" coords="16,95,53,129" target="|" />
|
||||||
<area shape="rect" coords="100,94,134,130" target="2" />
|
<area shape="rect" coords="57,95,92,129" target="1" />
|
||||||
<area shape="rect" coords="140,93,174,131" target="3" />
|
<area shape="rect" coords="100,94,134,130" target="2" />
|
||||||
<area shape="rect" coords="182,93,217,130" target="4" />
|
<area shape="rect" coords="140,93,174,131" target="3" />
|
||||||
<area shape="rect" coords="222,93,258,132" target="5" />
|
<area shape="rect" coords="182,93,217,130" target="4" />
|
||||||
<area shape="rect" coords="265,94,299,131" target="6" />
|
<area shape="rect" coords="222,93,258,132" target="5" />
|
||||||
<area shape="rect" coords="305,94,341,129" target="7" />
|
<area shape="rect" coords="265,94,299,131" target="6" />
|
||||||
<area shape="rect" coords="348,93,382,128" target="8" />
|
<area shape="rect" coords="305,94,341,129" target="7" />
|
||||||
<area shape="rect" coords="389,94,423,130" target="9" />
|
<area shape="rect" coords="348,93,382,128" target="8" />
|
||||||
<area shape="rect" coords="431,93,465,130" target="0" />
|
<area shape="rect" coords="389,94,423,130" target="9" />
|
||||||
<area shape="rect" coords="471,94,505,131" target="-" />
|
<area shape="rect" coords="431,93,465,130" target="0" />
|
||||||
<area shape="rect" coords="511,93,546,131" target="=" />
|
<area shape="rect" coords="471,94,505,131" target="-" />
|
||||||
<area shape="rect" coords="488,136,522,172" target="[" />
|
<area shape="rect" coords="511,93,546,131" target="=" />
|
||||||
<area shape="rect" coords="529,135,565,173" target="]" />
|
<area shape="rect" coords="488,136,522,172" target="[" />
|
||||||
<area shape="rect" coords="569,136,620,172" target="#" />
|
<area shape="rect" coords="529,135,565,173" target="]" />
|
||||||
<area shape="rect" coords="462,177,498,214" target=";" />
|
<area shape="rect" coords="569,136,620,172" target="#" />
|
||||||
<area shape="rect" coords="503,178,539,214" target="’" />
|
<area shape="rect" coords="462,177,498,214" target=";" />
|
||||||
<area shape="rect" coords="405,220,440,256" target="," />
|
<area shape="rect" coords="503,178,539,214" target="’" />
|
||||||
<area shape="rect" coords="447,220,483,256" target="." />
|
<area shape="rect" coords="405,220,440,256" target="," />
|
||||||
<area shape="rect" coords="487,220,524,256" target="/" />
|
<area shape="rect" coords="447,220,483,256" target="." />
|
||||||
|
<area shape="rect" coords="487,220,524,256" target="/" />
|
||||||
|
<area shape="rect" coords="538,266,576,298" target="ok" />
|
||||||
|
<area shape="rect" coords="585,266,622,298" target="cancel" />
|
||||||
</map>
|
</map>
|
||||||
</layout>
|
</layout>
|
||||||
<event name="esc" type="key" code="27" ascii="27" modifiers="" />
|
<event name="esc" type="key" code="27" ascii="27" modifiers="" />
|
||||||
|
@ -454,7 +463,10 @@
|
||||||
<event name="7" type="key" code="55" ascii="55" modifiers="" />
|
<event name="7" type="key" code="55" ascii="55" modifiers="" />
|
||||||
<event name="8" type="key" code="56" ascii="56" modifiers="" />
|
<event name="8" type="key" code="56" ascii="56" modifiers="" />
|
||||||
<event name="9" type="key" code="57" ascii="57" modifiers="" />
|
<event name="9" type="key" code="57" ascii="57" modifiers="" />
|
||||||
|
<event name="ok" type="submit" />
|
||||||
|
<event name="cancel" type="cancel" />
|
||||||
<event name="quit" type="submit" />
|
<event name="quit" type="submit" />
|
||||||
|
<event name="delete" type="delete" />
|
||||||
</mode>
|
</mode>
|
||||||
|
|
||||||
<!-- Lowercase Symbols -->
|
<!-- Lowercase Symbols -->
|
||||||
|
@ -489,7 +501,8 @@
|
||||||
<area shape="rect" coords="215,46,232,64" target=")" />
|
<area shape="rect" coords="215,46,232,64" target=")" />
|
||||||
<area shape="rect" coords="235,47,252,65" target="_" />
|
<area shape="rect" coords="235,47,252,65" target="_" />
|
||||||
<area shape="rect" coords="255,46,272,65" target="+" />
|
<area shape="rect" coords="255,46,272,65" target="+" />
|
||||||
<area shape="rect" coords="276,47,308,64" target="backspace" />
|
<area shape="rect" coords="276,46,299,65" target="delete" />
|
||||||
|
<area shape="rect" coords="300,46,311,65" target="backspace" />
|
||||||
<area shape="rect" coords="8,68,32,85" target="tab" />
|
<area shape="rect" coords="8,68,32,85" target="tab" />
|
||||||
<area shape="rect" coords="36,68,53,85" target="q" />
|
<area shape="rect" coords="36,68,53,85" target="q" />
|
||||||
<area shape="rect" coords="57,68,75,86" target="w" />
|
<area shape="rect" coords="57,68,75,86" target="w" />
|
||||||
|
@ -532,81 +545,86 @@
|
||||||
<area shape="rect" coords="9,130,33,148" target="ctrl" />
|
<area shape="rect" coords="9,130,33,148" target="ctrl" />
|
||||||
<area shape="rect" coords="38,130,61,147" target="alt" />
|
<area shape="rect" coords="38,130,61,147" target="alt" />
|
||||||
<area shape="rect" coords="67,130,262,148" target="space" />
|
<area shape="rect" coords="67,130,262,148" target="space" />
|
||||||
|
<area shape="rect" coords="269,131,288,150" target="ok" />
|
||||||
|
<area shape="rect" coords="292,131,311,150" target="cancel" />
|
||||||
</map>
|
</map>
|
||||||
</layout>
|
</layout>
|
||||||
<layout resolution="640x480" bitmap="lowercase-symbols640x480.bmp" transparent_color="255,0,255">
|
<layout resolution="640x480" bitmap="lowercase-symbols640x480.bmp" transparent_color="255,0,255">
|
||||||
<map>
|
<map>
|
||||||
<area shape="rect" coords="26,23,613,37" target="display_area" />
|
<area shape="rect" coords="26,23,613,37" target="display_area" />
|
||||||
<area shape="rect" coords="18,52,51,85" target="esc" />
|
<area shape="rect" coords="18,52,51,85" target="esc" />
|
||||||
<area shape="rect" coords="100,51,135,88" target="f2" />
|
<area shape="rect" coords="100,51,135,88" target="f2" />
|
||||||
<area shape="rect" coords="59,49,94,88" target="f1" />
|
<area shape="rect" coords="59,49,94,88" target="f1" />
|
||||||
<area shape="rect" coords="142,53,176,89" target="f3" />
|
<area shape="rect" coords="142,53,176,89" target="f3" />
|
||||||
<area shape="rect" coords="181,51,216,87" target="f4" />
|
<area shape="rect" coords="181,51,216,87" target="f4" />
|
||||||
<area shape="rect" coords="223,52,258,88" target="f5" />
|
<area shape="rect" coords="223,52,258,88" target="f5" />
|
||||||
<area shape="rect" coords="265,52,299,89" target="f6" />
|
<area shape="rect" coords="265,52,299,89" target="f6" />
|
||||||
<area shape="rect" coords="306,51,341,90" target="f7" />
|
<area shape="rect" coords="306,51,341,90" target="f7" />
|
||||||
<area shape="rect" coords="347,53,382,89" target="f8" />
|
<area shape="rect" coords="347,53,382,89" target="f8" />
|
||||||
<area shape="rect" coords="389,49,424,88" target="f9" />
|
<area shape="rect" coords="389,49,424,88" target="f9" />
|
||||||
<area shape="rect" coords="431,52,464,89" target="f10" />
|
<area shape="rect" coords="431,52,464,89" target="f10" />
|
||||||
<area shape="rect" coords="470,53,506,88" target="f11" />
|
<area shape="rect" coords="470,53,506,88" target="f11" />
|
||||||
<area shape="rect" coords="511,52,546,90" target="f12" />
|
<area shape="rect" coords="511,52,546,90" target="f12" />
|
||||||
<area shape="rect" coords="552,55,620,87" target="del" />
|
<area shape="rect" coords="552,55,620,87" target="del" />
|
||||||
<area shape="rect" coords="16,95,53,129" target="¬" />
|
<area shape="rect" coords="16,95,53,129" target="¬" />
|
||||||
<area shape="rect" coords="57,95,92,129" target="!" />
|
<area shape="rect" coords="57,95,92,129" target="!" />
|
||||||
<area shape="rect" coords="100,94,134,130" target="quote" />
|
<area shape="rect" coords="100,94,134,130" target="quote" />
|
||||||
<area shape="rect" coords="140,93,174,131" target="£" />
|
<area shape="rect" coords="140,93,174,131" target="£" />
|
||||||
<area shape="rect" coords="182,93,217,130" target="$" />
|
<area shape="rect" coords="182,93,217,130" target="$" />
|
||||||
<area shape="rect" coords="222,93,258,132" target="%" />
|
<area shape="rect" coords="222,93,258,132" target="%" />
|
||||||
<area shape="rect" coords="265,94,299,131" target="^" />
|
<area shape="rect" coords="265,94,299,131" target="^" />
|
||||||
<area shape="rect" coords="305,94,341,129" target="&" />
|
<area shape="rect" coords="305,94,341,129" target="&" />
|
||||||
<area shape="rect" coords="348,93,382,128" target="*" />
|
<area shape="rect" coords="348,93,382,128" target="*" />
|
||||||
<area shape="rect" coords="389,94,423,130" target="(" />
|
<area shape="rect" coords="389,94,423,130" target="(" />
|
||||||
<area shape="rect" coords="431,93,465,130" target=")" />
|
<area shape="rect" coords="431,93,465,130" target=")" />
|
||||||
<area shape="rect" coords="471,94,505,131" target="_" />
|
<area shape="rect" coords="471,94,505,131" target="_" />
|
||||||
<area shape="rect" coords="511,93,546,131" target="+" />
|
<area shape="rect" coords="511,93,546,131" target="+" />
|
||||||
<area shape="rect" coords="553,94,618,129" target="backspace" />
|
<area shape="rect" coords="552,93,598,131" target="delete" />
|
||||||
<area shape="rect" coords="17,136,66,171" target="tab" />
|
<area shape="rect" coords="601,93,623,131" target="backspace" />
|
||||||
<area shape="rect" coords="73,137,108,171" target="q" />
|
<area shape="rect" coords="17,136,66,171" target="tab" />
|
||||||
<area shape="rect" coords="114,136,150,172" target="w" />
|
<area shape="rect" coords="73,137,108,171" target="q" />
|
||||||
<area shape="rect" coords="157,134,189,171" target="e" />
|
<area shape="rect" coords="114,136,150,172" target="w" />
|
||||||
<area shape="rect" coords="197,135,232,171" target="r" />
|
<area shape="rect" coords="157,134,189,171" target="e" />
|
||||||
<area shape="rect" coords="239,135,274,172" target="t" />
|
<area shape="rect" coords="197,135,232,171" target="r" />
|
||||||
<area shape="rect" coords="282,136,317,173" target="y" />
|
<area shape="rect" coords="239,135,274,172" target="t" />
|
||||||
<area shape="rect" coords="322,135,359,173" target="u" />
|
<area shape="rect" coords="282,136,317,173" target="y" />
|
||||||
<area shape="rect" coords="364,134,399,172" target="i" />
|
<area shape="rect" coords="322,135,359,173" target="u" />
|
||||||
<area shape="rect" coords="404,135,440,171" target="o" />
|
<area shape="rect" coords="364,134,399,172" target="i" />
|
||||||
<area shape="rect" coords="446,136,480,172" target="p" />
|
<area shape="rect" coords="404,135,440,171" target="o" />
|
||||||
<area shape="rect" coords="488,136,522,172" target="{" />
|
<area shape="rect" coords="446,136,480,172" target="p" />
|
||||||
<area shape="rect" coords="529,135,565,173" target="}" />
|
<area shape="rect" coords="488,136,522,172" target="{" />
|
||||||
<area shape="rect" coords="569,136,620,172" target="~" />
|
<area shape="rect" coords="529,135,565,173" target="}" />
|
||||||
<area shape="rect" coords="17,179,84,213" target="caps" />
|
<area shape="rect" coords="569,136,620,172" target="~" />
|
||||||
<area shape="rect" coords="89,177,127,216" target="a" />
|
<area shape="rect" coords="17,179,84,213" target="caps" />
|
||||||
<area shape="rect" coords="131,177,169,214" target="s" />
|
<area shape="rect" coords="89,177,127,216" target="a" />
|
||||||
<area shape="rect" coords="173,178,209,215" target="d" />
|
<area shape="rect" coords="131,177,169,214" target="s" />
|
||||||
<area shape="rect" coords="215,178,250,214" target="f" />
|
<area shape="rect" coords="173,178,209,215" target="d" />
|
||||||
<area shape="rect" coords="256,178,291,214" target="g" />
|
<area shape="rect" coords="215,178,250,214" target="f" />
|
||||||
<area shape="rect" coords="299,176,332,214" target="h" />
|
<area shape="rect" coords="256,178,291,214" target="g" />
|
||||||
<area shape="rect" coords="339,178,374,215" target="j" />
|
<area shape="rect" coords="299,176,332,214" target="h" />
|
||||||
<area shape="rect" coords="379,178,415,215" target="k" />
|
<area shape="rect" coords="339,178,374,215" target="j" />
|
||||||
<area shape="rect" coords="421,178,457,215" target="l" />
|
<area shape="rect" coords="379,178,415,215" target="k" />
|
||||||
<area shape="rect" coords="462,177,498,214" target=":" />
|
<area shape="rect" coords="421,178,457,215" target="l" />
|
||||||
<area shape="rect" coords="503,178,539,214" target="@" />
|
<area shape="rect" coords="462,177,498,214" target=":" />
|
||||||
<area shape="rect" coords="546,179,622,214" target="enter" />
|
<area shape="rect" coords="503,178,539,214" target="@" />
|
||||||
<area shape="rect" coords="19,221,101,256" target="shift" />
|
<area shape="rect" coords="546,179,622,214" target="enter" />
|
||||||
<area shape="rect" coords="117,220,152,256" target="z" />
|
<area shape="rect" coords="19,221,101,256" target="shift" />
|
||||||
<area shape="rect" coords="158,220,192,256" target="x" />
|
<area shape="rect" coords="117,220,152,256" target="z" />
|
||||||
<area shape="rect" coords="198,219,233,256" target="c" />
|
<area shape="rect" coords="158,220,192,256" target="x" />
|
||||||
<area shape="rect" coords="240,220,276,257" target="v" />
|
<area shape="rect" coords="198,219,233,256" target="c" />
|
||||||
<area shape="rect" coords="283,219,316,255" target="b" />
|
<area shape="rect" coords="240,220,276,257" target="v" />
|
||||||
<area shape="rect" coords="324,220,359,256" target="n" />
|
<area shape="rect" coords="283,219,316,255" target="b" />
|
||||||
<area shape="rect" coords="365,220,399,257" target="m" />
|
<area shape="rect" coords="324,220,359,256" target="n" />
|
||||||
<area shape="rect" coords="405,220,440,256" target="<" />
|
<area shape="rect" coords="365,220,399,257" target="m" />
|
||||||
<area shape="rect" coords="447,220,483,256" target=">" />
|
<area shape="rect" coords="405,220,440,256" target="<" />
|
||||||
<area shape="rect" coords="487,220,524,256" target="?" />
|
<area shape="rect" coords="447,220,483,256" target=">" />
|
||||||
<area shape="rect" coords="542,219,623,257" target="symbols" />
|
<area shape="rect" coords="487,220,524,256" target="?" />
|
||||||
<area shape="rect" coords="19,260,68,296" target="ctrl" />
|
<area shape="rect" coords="542,219,623,257" target="symbols" />
|
||||||
<area shape="rect" coords="76,261,123,295" target="alt" />
|
<area shape="rect" coords="19,260,68,296" target="ctrl" />
|
||||||
<area shape="rect" coords="135,261,525,297" target="space" />
|
<area shape="rect" coords="76,261,123,295" target="alt" />
|
||||||
|
<area shape="rect" coords="135,261,525,297" target="space" />
|
||||||
|
<area shape="rect" coords="538,266,576,298" target="ok" />
|
||||||
|
<area shape="rect" coords="585,266,622,298" target="cancel" />
|
||||||
</map>
|
</map>
|
||||||
</layout>
|
</layout>
|
||||||
<event name="esc" type="key" code="27" ascii="27" modifiers="" />
|
<event name="esc" type="key" code="27" ascii="27" modifiers="" />
|
||||||
|
@ -679,7 +697,10 @@
|
||||||
<event name="x" type="key" code="120" ascii="120" modifiers="" />
|
<event name="x" type="key" code="120" ascii="120" modifiers="" />
|
||||||
<event name="y" type="key" code="121" ascii="121" modifiers="" />
|
<event name="y" type="key" code="121" ascii="121" modifiers="" />
|
||||||
<event name="z" type="key" code="122" ascii="122" modifiers="" />
|
<event name="z" type="key" code="122" ascii="122" modifiers="" />
|
||||||
|
<event name="ok" type="submit" />
|
||||||
|
<event name="cancel" type="cancel" />
|
||||||
<event name="quit" type="submit" />
|
<event name="quit" type="submit" />
|
||||||
|
<event name="delete" type="delete" />
|
||||||
</mode>
|
</mode>
|
||||||
|
|
||||||
<!-- Uppercase Symbols -->
|
<!-- Uppercase Symbols -->
|
||||||
|
@ -714,7 +735,8 @@
|
||||||
<area shape="rect" coords="215,46,232,64" target=")" />
|
<area shape="rect" coords="215,46,232,64" target=")" />
|
||||||
<area shape="rect" coords="235,47,252,65" target="_" />
|
<area shape="rect" coords="235,47,252,65" target="_" />
|
||||||
<area shape="rect" coords="255,46,272,65" target="+" />
|
<area shape="rect" coords="255,46,272,65" target="+" />
|
||||||
<area shape="rect" coords="276,47,308,64" target="backspace" />
|
<area shape="rect" coords="276,46,299,65" target="delete" />
|
||||||
|
<area shape="rect" coords="300,46,311,65" target="backspace" />
|
||||||
<area shape="rect" coords="8,68,32,85" target="tab" />
|
<area shape="rect" coords="8,68,32,85" target="tab" />
|
||||||
<area shape="rect" coords="36,68,53,85" target="Q" />
|
<area shape="rect" coords="36,68,53,85" target="Q" />
|
||||||
<area shape="rect" coords="57,68,75,86" target="W" />
|
<area shape="rect" coords="57,68,75,86" target="W" />
|
||||||
|
@ -757,81 +779,86 @@
|
||||||
<area shape="rect" coords="9,130,33,148" target="ctrl" />
|
<area shape="rect" coords="9,130,33,148" target="ctrl" />
|
||||||
<area shape="rect" coords="38,130,61,147" target="alt" />
|
<area shape="rect" coords="38,130,61,147" target="alt" />
|
||||||
<area shape="rect" coords="67,130,262,148" target="space" />
|
<area shape="rect" coords="67,130,262,148" target="space" />
|
||||||
|
<area shape="rect" coords="269,131,288,150" target="ok" />
|
||||||
|
<area shape="rect" coords="292,131,311,150" target="cancel" />
|
||||||
</map>
|
</map>
|
||||||
</layout>
|
</layout>
|
||||||
<layout resolution="640x480" bitmap="uppercase-symbols640x480.bmp" transparent_color="255,0,255">
|
<layout resolution="640x480" bitmap="uppercase-symbols640x480.bmp" transparent_color="255,0,255">
|
||||||
<map>
|
<map>
|
||||||
<area shape="rect" coords="26,23,613,37" target="display_area" />
|
<area shape="rect" coords="26,23,613,37" target="display_area" />
|
||||||
<area shape="rect" coords="18,52,51,85" target="esc" />
|
<area shape="rect" coords="18,52,51,85" target="esc" />
|
||||||
<area shape="rect" coords="100,51,135,88" target="f2" />
|
<area shape="rect" coords="100,51,135,88" target="f2" />
|
||||||
<area shape="rect" coords="59,49,94,88" target="f1" />
|
<area shape="rect" coords="59,49,94,88" target="f1" />
|
||||||
<area shape="rect" coords="142,53,176,89" target="f3" />
|
<area shape="rect" coords="142,53,176,89" target="f3" />
|
||||||
<area shape="rect" coords="181,51,216,87" target="f4" />
|
<area shape="rect" coords="181,51,216,87" target="f4" />
|
||||||
<area shape="rect" coords="223,52,258,88" target="f5" />
|
<area shape="rect" coords="223,52,258,88" target="f5" />
|
||||||
<area shape="rect" coords="265,52,299,89" target="f6" />
|
<area shape="rect" coords="265,52,299,89" target="f6" />
|
||||||
<area shape="rect" coords="306,51,341,90" target="f7" />
|
<area shape="rect" coords="306,51,341,90" target="f7" />
|
||||||
<area shape="rect" coords="347,53,382,89" target="f8" />
|
<area shape="rect" coords="347,53,382,89" target="f8" />
|
||||||
<area shape="rect" coords="389,49,424,88" target="f9" />
|
<area shape="rect" coords="389,49,424,88" target="f9" />
|
||||||
<area shape="rect" coords="431,52,464,89" target="f10" />
|
<area shape="rect" coords="431,52,464,89" target="f10" />
|
||||||
<area shape="rect" coords="470,53,506,88" target="f11" />
|
<area shape="rect" coords="470,53,506,88" target="f11" />
|
||||||
<area shape="rect" coords="511,52,546,90" target="f12" />
|
<area shape="rect" coords="511,52,546,90" target="f12" />
|
||||||
<area shape="rect" coords="552,55,620,87" target="del" />
|
<area shape="rect" coords="552,55,620,87" target="del" />
|
||||||
<area shape="rect" coords="16,95,53,129" target="¬" />
|
<area shape="rect" coords="16,95,53,129" target="¬" />
|
||||||
<area shape="rect" coords="57,95,92,129" target="!" />
|
<area shape="rect" coords="57,95,92,129" target="!" />
|
||||||
<area shape="rect" coords="100,94,134,130" target="quote" />
|
<area shape="rect" coords="100,94,134,130" target="quote" />
|
||||||
<area shape="rect" coords="140,93,174,131" target="£" />
|
<area shape="rect" coords="140,93,174,131" target="£" />
|
||||||
<area shape="rect" coords="182,93,217,130" target="$" />
|
<area shape="rect" coords="182,93,217,130" target="$" />
|
||||||
<area shape="rect" coords="222,93,258,132" target="%" />
|
<area shape="rect" coords="222,93,258,132" target="%" />
|
||||||
<area shape="rect" coords="265,94,299,131" target="^" />
|
<area shape="rect" coords="265,94,299,131" target="^" />
|
||||||
<area shape="rect" coords="305,94,341,129" target="&" />
|
<area shape="rect" coords="305,94,341,129" target="&" />
|
||||||
<area shape="rect" coords="348,93,382,128" target="*" />
|
<area shape="rect" coords="348,93,382,128" target="*" />
|
||||||
<area shape="rect" coords="389,94,423,130" target="(" />
|
<area shape="rect" coords="389,94,423,130" target="(" />
|
||||||
<area shape="rect" coords="431,93,465,130" target=")" />
|
<area shape="rect" coords="431,93,465,130" target=")" />
|
||||||
<area shape="rect" coords="471,94,505,131" target="_" />
|
<area shape="rect" coords="471,94,505,131" target="_" />
|
||||||
<area shape="rect" coords="511,93,546,131" target="+" />
|
<area shape="rect" coords="511,93,546,131" target="+" />
|
||||||
<area shape="rect" coords="553,94,618,129" target="backspace" />
|
<area shape="rect" coords="552,93,598,131" target="delete" />
|
||||||
<area shape="rect" coords="17,136,66,171" target="tab" />
|
<area shape="rect" coords="601,93,623,131" target="backspace" />
|
||||||
<area shape="rect" coords="73,137,108,171" target="Q" />
|
<area shape="rect" coords="17,136,66,171" target="tab" />
|
||||||
<area shape="rect" coords="114,136,150,172" target="W" />
|
<area shape="rect" coords="73,137,108,171" target="Q" />
|
||||||
<area shape="rect" coords="157,134,189,171" target="E" />
|
<area shape="rect" coords="114,136,150,172" target="W" />
|
||||||
<area shape="rect" coords="197,135,232,171" target="R" />
|
<area shape="rect" coords="157,134,189,171" target="E" />
|
||||||
<area shape="rect" coords="239,135,274,172" target="T" />
|
<area shape="rect" coords="197,135,232,171" target="R" />
|
||||||
<area shape="rect" coords="282,136,317,173" target="Y" />
|
<area shape="rect" coords="239,135,274,172" target="T" />
|
||||||
<area shape="rect" coords="322,135,359,173" target="U" />
|
<area shape="rect" coords="282,136,317,173" target="Y" />
|
||||||
<area shape="rect" coords="364,134,399,172" target="I" />
|
<area shape="rect" coords="322,135,359,173" target="U" />
|
||||||
<area shape="rect" coords="404,135,440,171" target="O" />
|
<area shape="rect" coords="364,134,399,172" target="I" />
|
||||||
<area shape="rect" coords="446,136,480,172" target="P" />
|
<area shape="rect" coords="404,135,440,171" target="O" />
|
||||||
<area shape="rect" coords="488,136,522,172" target="{" />
|
<area shape="rect" coords="446,136,480,172" target="P" />
|
||||||
<area shape="rect" coords="529,135,565,173" target="}" />
|
<area shape="rect" coords="488,136,522,172" target="{" />
|
||||||
<area shape="rect" coords="569,136,620,172" target="~" />
|
<area shape="rect" coords="529,135,565,173" target="}" />
|
||||||
<area shape="rect" coords="17,179,84,213" target="caps" />
|
<area shape="rect" coords="569,136,620,172" target="~" />
|
||||||
<area shape="rect" coords="89,177,127,216" target="A" />
|
<area shape="rect" coords="17,179,84,213" target="caps" />
|
||||||
<area shape="rect" coords="131,177,169,214" target="S" />
|
<area shape="rect" coords="89,177,127,216" target="A" />
|
||||||
<area shape="rect" coords="173,178,209,215" target="D" />
|
<area shape="rect" coords="131,177,169,214" target="S" />
|
||||||
<area shape="rect" coords="215,178,250,214" target="F" />
|
<area shape="rect" coords="173,178,209,215" target="D" />
|
||||||
<area shape="rect" coords="256,178,291,214" target="G" />
|
<area shape="rect" coords="215,178,250,214" target="F" />
|
||||||
<area shape="rect" coords="299,176,332,214" target="H" />
|
<area shape="rect" coords="256,178,291,214" target="G" />
|
||||||
<area shape="rect" coords="339,178,374,215" target="J" />
|
<area shape="rect" coords="299,176,332,214" target="H" />
|
||||||
<area shape="rect" coords="379,178,415,215" target="K" />
|
<area shape="rect" coords="339,178,374,215" target="J" />
|
||||||
<area shape="rect" coords="421,178,457,215" target="L" />
|
<area shape="rect" coords="379,178,415,215" target="K" />
|
||||||
<area shape="rect" coords="462,177,498,214" target=":" />
|
<area shape="rect" coords="421,178,457,215" target="L" />
|
||||||
<area shape="rect" coords="503,178,539,214" target="@" />
|
<area shape="rect" coords="462,177,498,214" target=":" />
|
||||||
<area shape="rect" coords="546,179,622,214" target="enter" />
|
<area shape="rect" coords="503,178,539,214" target="@" />
|
||||||
<area shape="rect" coords="19,221,101,256" target="shift" />
|
<area shape="rect" coords="546,179,622,214" target="enter" />
|
||||||
<area shape="rect" coords="117,220,152,256" target="z" />
|
<area shape="rect" coords="19,221,101,256" target="shift" />
|
||||||
<area shape="rect" coords="158,220,192,256" target="x" />
|
<area shape="rect" coords="117,220,152,256" target="z" />
|
||||||
<area shape="rect" coords="198,219,233,256" target="c" />
|
<area shape="rect" coords="158,220,192,256" target="x" />
|
||||||
<area shape="rect" coords="240,220,276,257" target="v" />
|
<area shape="rect" coords="198,219,233,256" target="c" />
|
||||||
<area shape="rect" coords="283,219,316,255" target="b" />
|
<area shape="rect" coords="240,220,276,257" target="v" />
|
||||||
<area shape="rect" coords="324,220,359,256" target="n" />
|
<area shape="rect" coords="283,219,316,255" target="b" />
|
||||||
<area shape="rect" coords="365,220,399,257" target="m" />
|
<area shape="rect" coords="324,220,359,256" target="n" />
|
||||||
<area shape="rect" coords="405,220,440,256" target="<" />
|
<area shape="rect" coords="365,220,399,257" target="m" />
|
||||||
<area shape="rect" coords="447,220,483,256" target=">" />
|
<area shape="rect" coords="405,220,440,256" target="<" />
|
||||||
<area shape="rect" coords="487,220,524,256" target="?" />
|
<area shape="rect" coords="447,220,483,256" target=">" />
|
||||||
<area shape="rect" coords="542,219,623,257" target="symbols" />
|
<area shape="rect" coords="487,220,524,256" target="?" />
|
||||||
<area shape="rect" coords="19,260,68,296" target="ctrl" />
|
<area shape="rect" coords="542,219,623,257" target="symbols" />
|
||||||
<area shape="rect" coords="76,261,123,295" target="alt" />
|
<area shape="rect" coords="19,260,68,296" target="ctrl" />
|
||||||
<area shape="rect" coords="135,261,525,297" target="space" />
|
<area shape="rect" coords="76,261,123,295" target="alt" />
|
||||||
|
<area shape="rect" coords="135,261,525,297" target="space" />
|
||||||
|
<area shape="rect" coords="538,266,576,298" target="ok" />
|
||||||
|
<area shape="rect" coords="585,266,622,298" target="cancel" />
|
||||||
</map>
|
</map>
|
||||||
</layout>
|
</layout>
|
||||||
<event name="esc" type="key" code="27" ascii="27" modifiers="" />
|
<event name="esc" type="key" code="27" ascii="27" modifiers="" />
|
||||||
|
@ -914,7 +941,9 @@
|
||||||
<event name="7" type="key" code="55" ascii="55" modifiers="" />
|
<event name="7" type="key" code="55" ascii="55" modifiers="" />
|
||||||
<event name="8" type="key" code="56" ascii="56" modifiers="" />
|
<event name="8" type="key" code="56" ascii="56" modifiers="" />
|
||||||
<event name="9" type="key" code="57" ascii="57" modifiers="" />
|
<event name="9" type="key" code="57" ascii="57" modifiers="" />
|
||||||
|
<event name="ok" type="submit" />
|
||||||
|
<event name="cancel" type="cancel" />
|
||||||
<event name="quit" type="submit" />
|
<event name="quit" type="submit" />
|
||||||
|
<event name="delete" type="delete" />
|
||||||
</mode>
|
</mode>
|
||||||
|
|
||||||
</keyboard>
|
</keyboard>
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
#include "backends/vkeybd/virtual-keyboard-gui.h"
|
#include "backends/vkeybd/virtual-keyboard-gui.h"
|
||||||
|
|
||||||
#include "graphics/cursorman.h"
|
#include "graphics/cursorman.h"
|
||||||
|
#include "graphics/fontman.h"
|
||||||
#include "gui/GuiManager.h"
|
#include "gui/GuiManager.h"
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
#include "common/scummsys.h"
|
#include "common/scummsys.h"
|
||||||
#include "common/rect.h"
|
#include "common/rect.h"
|
||||||
#include "common/system.h"
|
#include "common/system.h"
|
||||||
#include "graphics/fontman.h"
|
#include "graphics/font.h"
|
||||||
#include "graphics/surface.h"
|
#include "graphics/surface.h"
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
|
@ -207,7 +207,7 @@ void VirtualKeyboard::switchMode(Mode *newMode) {
|
||||||
_currentMode = newMode;
|
_currentMode = newMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VirtualKeyboard::switchMode(const String& newMode) {
|
void VirtualKeyboard::switchMode(const String &newMode) {
|
||||||
if (!_modes.contains(newMode)) {
|
if (!_modes.contains(newMode)) {
|
||||||
warning("Keyboard mode '%s' unknown", newMode.c_str());
|
warning("Keyboard mode '%s' unknown", newMode.c_str());
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -121,7 +121,7 @@ protected:
|
||||||
OverlayColor displayFontColor;
|
OverlayColor displayFontColor;
|
||||||
|
|
||||||
Mode() : image(0) {}
|
Mode() : image(0) {}
|
||||||
~Mode() {
|
~Mode() {
|
||||||
if (image) {
|
if (image) {
|
||||||
image->free();
|
image->free();
|
||||||
delete image;
|
delete image;
|
||||||
|
|
334
common/zlib.cpp
Normal file
|
@ -0,0 +1,334 @@
|
||||||
|
/* Residual - A 3D game interpreter
|
||||||
|
*
|
||||||
|
* Residual is the legal property of its developers, whose names
|
||||||
|
* are too numerous to list here. Please refer to the AUTHORS
|
||||||
|
* 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$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "common/zlib.h"
|
||||||
|
#include "common/util.h"
|
||||||
|
|
||||||
|
#if defined(USE_ZLIB)
|
||||||
|
#ifdef __SYMBIAN32__
|
||||||
|
#include <zlib\zlib.h>
|
||||||
|
#else
|
||||||
|
#include <zlib.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ZLIB_VERNUM < 0x1204
|
||||||
|
#error Version 1.2.0.4 or newer of zlib is required for this code
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
#if defined(USE_ZLIB)
|
||||||
|
|
||||||
|
bool uncompress(byte *dst, unsigned long *dstLen, const byte *src, unsigned long srcLen) {
|
||||||
|
return Z_OK == ::uncompress(dst, dstLen, src, srcLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple wrapper class which can be used to wrap around an arbitrary
|
||||||
|
* other SeekableReadStream and will then provide on-the-fly decompression support.
|
||||||
|
* Assumes the compressed data to be in gzip format.
|
||||||
|
*/
|
||||||
|
class GZipReadStream : public Common::SeekableReadStream {
|
||||||
|
protected:
|
||||||
|
enum {
|
||||||
|
BUFSIZE = 16384 // 1 << MAX_WBITS
|
||||||
|
};
|
||||||
|
|
||||||
|
byte _buf[BUFSIZE];
|
||||||
|
|
||||||
|
Common::SeekableReadStream *_wrapped;
|
||||||
|
z_stream _stream;
|
||||||
|
int _zlibErr;
|
||||||
|
uint32 _pos;
|
||||||
|
uint32 _origSize;
|
||||||
|
bool _eos;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
GZipReadStream(Common::SeekableReadStream *w) : _wrapped(w) {
|
||||||
|
assert(w != 0);
|
||||||
|
|
||||||
|
_stream.zalloc = Z_NULL;
|
||||||
|
_stream.zfree = Z_NULL;
|
||||||
|
_stream.opaque = Z_NULL;
|
||||||
|
|
||||||
|
// Verify file header is correct
|
||||||
|
w->seek(0, SEEK_SET);
|
||||||
|
uint16 header = w->readUint16BE();
|
||||||
|
assert(header == 0x1F8B ||
|
||||||
|
((header & 0x0F00) == 0x0800 && header % 31 == 0));
|
||||||
|
|
||||||
|
if (header == 0x1F8B) {
|
||||||
|
// Retrieve the original file size
|
||||||
|
w->seek(-4, SEEK_END);
|
||||||
|
_origSize = w->readUint32LE();
|
||||||
|
} else {
|
||||||
|
// Original size not available in zlib format
|
||||||
|
_origSize = 0;
|
||||||
|
}
|
||||||
|
_pos = 0;
|
||||||
|
w->seek(0, SEEK_SET);
|
||||||
|
_eos = false;
|
||||||
|
|
||||||
|
// Adding 32 to windowBits indicates to zlib that it is supposed to
|
||||||
|
// automatically detect whether gzip or zlib headers are used for
|
||||||
|
// the compressed file. This feature was added in zlib 1.2.0.4,
|
||||||
|
// released 10 August 2003.
|
||||||
|
// Note: This is *crucial* for savegame compatibility, do *not* remove!
|
||||||
|
_zlibErr = inflateInit2(&_stream, MAX_WBITS + 32);
|
||||||
|
if (_zlibErr != Z_OK)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Setup input buffer
|
||||||
|
_stream.next_in = _buf;
|
||||||
|
_stream.avail_in = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
~GZipReadStream() {
|
||||||
|
inflateEnd(&_stream);
|
||||||
|
delete _wrapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool err() const { return (_zlibErr != Z_OK) && (_zlibErr != Z_STREAM_END); }
|
||||||
|
void clearErr() {
|
||||||
|
// only reset _eos; I/O errors are not recoverable
|
||||||
|
_eos = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 read(void *dataPtr, uint32 dataSize) {
|
||||||
|
_stream.next_out = (byte *)dataPtr;
|
||||||
|
_stream.avail_out = dataSize;
|
||||||
|
|
||||||
|
// Keep going while we get no error
|
||||||
|
while (_zlibErr == Z_OK && _stream.avail_out) {
|
||||||
|
if (_stream.avail_in == 0 && !_wrapped->eos()) {
|
||||||
|
// If we are out of input data: Read more data, if available.
|
||||||
|
_stream.next_in = _buf;
|
||||||
|
_stream.avail_in = _wrapped->read(_buf, BUFSIZE);
|
||||||
|
}
|
||||||
|
_zlibErr = inflate(&_stream, Z_NO_FLUSH);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the position counter
|
||||||
|
_pos += dataSize - _stream.avail_out;
|
||||||
|
|
||||||
|
if (_zlibErr == Z_STREAM_END && _stream.avail_out > 0)
|
||||||
|
_eos = true;
|
||||||
|
|
||||||
|
return dataSize - _stream.avail_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool eos() const {
|
||||||
|
return _eos;
|
||||||
|
}
|
||||||
|
int32 pos() const {
|
||||||
|
return _pos;
|
||||||
|
}
|
||||||
|
int32 size() const {
|
||||||
|
return _origSize;
|
||||||
|
}
|
||||||
|
bool seek(int32 offset, int whence = SEEK_SET) {
|
||||||
|
int32 newPos = 0;
|
||||||
|
assert(whence != SEEK_END); // SEEK_END not supported
|
||||||
|
switch (whence) {
|
||||||
|
case SEEK_SET:
|
||||||
|
newPos = offset;
|
||||||
|
break;
|
||||||
|
case SEEK_CUR:
|
||||||
|
newPos = _pos + offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(newPos >= 0);
|
||||||
|
|
||||||
|
if ((uint32)newPos < _pos) {
|
||||||
|
// To search backward, we have to restart the whole decompression
|
||||||
|
// from the start of the file. A rather wasteful operation, best
|
||||||
|
// to avoid it. :/
|
||||||
|
#if DEBUG
|
||||||
|
warning("Backward seeking in GZipReadStream detected");
|
||||||
|
#endif
|
||||||
|
_pos = 0;
|
||||||
|
_wrapped->seek(0, SEEK_SET);
|
||||||
|
_zlibErr = inflateReset(&_stream);
|
||||||
|
if (_zlibErr != Z_OK)
|
||||||
|
return false; // FIXME: STREAM REWRITE
|
||||||
|
_stream.next_in = _buf;
|
||||||
|
_stream.avail_in = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset = newPos - _pos;
|
||||||
|
|
||||||
|
// Skip the given amount of data (very inefficient if one tries to skip
|
||||||
|
// huge amounts of data, but usually client code will only skip a few
|
||||||
|
// bytes, so this should be fine.
|
||||||
|
byte tmpBuf[1024];
|
||||||
|
while (!err() && offset > 0) {
|
||||||
|
offset -= read(tmpBuf, MIN((int32)sizeof(tmpBuf), offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
_eos = false;
|
||||||
|
return true; // FIXME: STREAM REWRITE
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple wrapper class which can be used to wrap around an arbitrary
|
||||||
|
* other WriteStream and will then provide on-the-fly compression support.
|
||||||
|
* The compressed data is written in the gzip format.
|
||||||
|
*/
|
||||||
|
class GZipWriteStream : public Common::WriteStream {
|
||||||
|
protected:
|
||||||
|
enum {
|
||||||
|
BUFSIZE = 16384 // 1 << MAX_WBITS
|
||||||
|
};
|
||||||
|
|
||||||
|
byte _buf[BUFSIZE];
|
||||||
|
Common::WriteStream *_wrapped;
|
||||||
|
z_stream _stream;
|
||||||
|
int _zlibErr;
|
||||||
|
|
||||||
|
void processData(int flushType) {
|
||||||
|
// This function is called by both write() and finalize().
|
||||||
|
while (_zlibErr == Z_OK && (_stream.avail_in || flushType == Z_FINISH)) {
|
||||||
|
if (_stream.avail_out == 0) {
|
||||||
|
if (_wrapped->write(_buf, BUFSIZE) != BUFSIZE) {
|
||||||
|
_zlibErr = Z_ERRNO;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_stream.next_out = _buf;
|
||||||
|
_stream.avail_out = BUFSIZE;
|
||||||
|
}
|
||||||
|
_zlibErr = deflate(&_stream, flushType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
GZipWriteStream(Common::WriteStream *w) : _wrapped(w) {
|
||||||
|
assert(w != 0);
|
||||||
|
_stream.zalloc = Z_NULL;
|
||||||
|
_stream.zfree = Z_NULL;
|
||||||
|
_stream.opaque = Z_NULL;
|
||||||
|
|
||||||
|
// Adding 16 to windowBits indicates to zlib that it is supposed to
|
||||||
|
// write gzip headers. This feature was added in zlib 1.2.0.4,
|
||||||
|
// released 10 August 2003.
|
||||||
|
// Note: This is *crucial* for savegame compatibility, do *not* remove!
|
||||||
|
_zlibErr = deflateInit2(&_stream,
|
||||||
|
Z_DEFAULT_COMPRESSION,
|
||||||
|
Z_DEFLATED,
|
||||||
|
MAX_WBITS + 16,
|
||||||
|
8,
|
||||||
|
Z_DEFAULT_STRATEGY);
|
||||||
|
assert(_zlibErr == Z_OK);
|
||||||
|
|
||||||
|
_stream.next_out = _buf;
|
||||||
|
_stream.avail_out = BUFSIZE;
|
||||||
|
_stream.avail_in = 0;
|
||||||
|
_stream.next_in = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
~GZipWriteStream() {
|
||||||
|
finalize();
|
||||||
|
deflateEnd(&_stream);
|
||||||
|
delete _wrapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool err() const {
|
||||||
|
// CHECKME: does Z_STREAM_END make sense here?
|
||||||
|
return (_zlibErr != Z_OK && _zlibErr != Z_STREAM_END) || _wrapped->err();
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearErr() {
|
||||||
|
// Note: we don't reset the _zlibErr here, as it is not
|
||||||
|
// clear in general how
|
||||||
|
_wrapped->clearErr();
|
||||||
|
}
|
||||||
|
|
||||||
|
void finalize() {
|
||||||
|
if (_zlibErr != Z_OK)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Process whatever remaining data there is.
|
||||||
|
processData(Z_FINISH);
|
||||||
|
|
||||||
|
// Since processData only writes out blocks of size BUFSIZE,
|
||||||
|
// we may have to flush some stragglers.
|
||||||
|
uint remainder = BUFSIZE - _stream.avail_out;
|
||||||
|
if (remainder > 0) {
|
||||||
|
if (_wrapped->write(_buf, remainder) != remainder) {
|
||||||
|
_zlibErr = Z_ERRNO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finalize the wrapped savefile, too
|
||||||
|
_wrapped->finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 write(const void *dataPtr, uint32 dataSize) {
|
||||||
|
if (err())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Hook in the new data ...
|
||||||
|
// Note: We need to make a const_cast here, as zlib is not aware
|
||||||
|
// of the const keyword.
|
||||||
|
_stream.next_in = const_cast<byte *>((const byte *)dataPtr);
|
||||||
|
_stream.avail_in = dataSize;
|
||||||
|
|
||||||
|
// ... and flush it to disk
|
||||||
|
processData(Z_NO_FLUSH);
|
||||||
|
|
||||||
|
return dataSize - _stream.avail_in;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // USE_ZLIB
|
||||||
|
|
||||||
|
Common::SeekableReadStream *wrapCompressedReadStream(Common::SeekableReadStream *toBeWrapped) {
|
||||||
|
#if defined(USE_ZLIB)
|
||||||
|
if (toBeWrapped) {
|
||||||
|
uint16 header = toBeWrapped->readUint16BE();
|
||||||
|
bool isCompressed = (header == 0x1F8B ||
|
||||||
|
((header & 0x0F00) == 0x0800 &&
|
||||||
|
header % 31 == 0));
|
||||||
|
toBeWrapped->seek(-2, SEEK_CUR);
|
||||||
|
if (isCompressed)
|
||||||
|
return new GZipReadStream(toBeWrapped);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return toBeWrapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
Common::WriteStream *wrapCompressedWriteStream(Common::WriteStream *toBeWrapped) {
|
||||||
|
#if defined(USE_ZLIB)
|
||||||
|
if (toBeWrapped)
|
||||||
|
return new GZipWriteStream(toBeWrapped);
|
||||||
|
#endif
|
||||||
|
return toBeWrapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // End of namespace Common
|
72
common/zlib.h
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
/* Residual - A 3D game interpreter
|
||||||
|
*
|
||||||
|
* Residual is the legal property of its developers, whose names
|
||||||
|
* are too numerous to list here. Please refer to the AUTHORS
|
||||||
|
* 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$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef COMMON_ZLIB_H
|
||||||
|
#define COMMON_ZLIB_H
|
||||||
|
|
||||||
|
#include "common/sys.h"
|
||||||
|
#include "common/stream.h"
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
#if defined(USE_ZLIB)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thin wrapper around zlib's uncompress() function. This wrapper makes
|
||||||
|
* it possible to uncompress data in engines without being forced to link
|
||||||
|
* them against zlib, thus simplifying the build system.
|
||||||
|
*
|
||||||
|
* @return true on success (i.e. Z_OK), false otherwise
|
||||||
|
*/
|
||||||
|
bool uncompress(byte *dst, unsigned long *dstLen, const byte *src, unsigned long srcLen);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Take an arbitrary SeekableReadStream and wrap it in a custom stream which
|
||||||
|
* provides transparent on-the-fly decompression. Assumes the data it
|
||||||
|
* retrieves from the wrapped stream to be either uncompressed or in gzip
|
||||||
|
* format. In the former case, the original stream is returned unmodified
|
||||||
|
* (and in particular, not wrapped).
|
||||||
|
*
|
||||||
|
* It is safe to call this with a NULL parameter (in this case, NULL is
|
||||||
|
* returned).
|
||||||
|
*/
|
||||||
|
Common::SeekableReadStream *wrapCompressedReadStream(Common::SeekableReadStream *toBeWrapped);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Take an arbitrary WriteStream and wrap it in a custom stream which provides
|
||||||
|
* transparent on-the-fly compression. The compressed data is written in the
|
||||||
|
* gzip format, unless ZLIB support has been disabled, in which case the given
|
||||||
|
* stream is returned unmodified (and in particular, not wrapped).
|
||||||
|
*
|
||||||
|
* It is safe to call this with a NULL parameter (in this case, NULL is
|
||||||
|
* returned).
|
||||||
|
*/
|
||||||
|
Common::WriteStream *wrapCompressedWriteStream(Common::WriteStream *toBeWrapped);
|
||||||
|
|
||||||
|
} // End of namespace Common
|
||||||
|
|
||||||
|
#endif
|
743
sound/decoders/flac.cpp
Normal file
|
@ -0,0 +1,743 @@
|
||||||
|
/* Residual - A 3D game interpreter
|
||||||
|
*
|
||||||
|
* Residual is the legal property of its developers, whose names
|
||||||
|
* are too numerous to list here. Please refer to the AUTHORS
|
||||||
|
* 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$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "sound/decoders/flac.h"
|
||||||
|
|
||||||
|
#ifdef USE_FLAC
|
||||||
|
|
||||||
|
#include "common/debug.h"
|
||||||
|
#include "common/stream.h"
|
||||||
|
#include "common/util.h"
|
||||||
|
|
||||||
|
#include "sound/audiostream.h"
|
||||||
|
#include "sound/audiocd.h"
|
||||||
|
|
||||||
|
#define FLAC__NO_DLL // that MS-magic gave me headaches - just link the library you like
|
||||||
|
#include <FLAC/export.h>
|
||||||
|
|
||||||
|
|
||||||
|
// check if we have FLAC >= 1.1.3; LEGACY_FLAC code can be removed once FLAC-1.1.3 propagates everywhere
|
||||||
|
#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT < 8
|
||||||
|
#define LEGACY_FLAC
|
||||||
|
#else
|
||||||
|
#undef LEGACY_FLAC
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef LEGACY_FLAC
|
||||||
|
|
||||||
|
// Before FLAC 1.1.3, we needed to use the stream decoder API.
|
||||||
|
#include <FLAC/seekable_stream_decoder.h>
|
||||||
|
typedef uint FLAC_size_t;
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
// With FLAC 1.1.3, the stream decoder API was merged into the regular
|
||||||
|
// stream API. In order to stay compatible with older FLAC versions, we
|
||||||
|
// simply add some typedefs and #ifdefs to map between the old and new API.
|
||||||
|
// We use the typedefs (instead of only #defines) in order to somewhat
|
||||||
|
// improve the readability of the code.
|
||||||
|
|
||||||
|
#include <FLAC/stream_decoder.h>
|
||||||
|
typedef size_t FLAC_size_t;
|
||||||
|
// Add aliases for the old names
|
||||||
|
typedef FLAC__StreamDecoderState FLAC__SeekableStreamDecoderState;
|
||||||
|
typedef FLAC__StreamDecoderReadStatus FLAC__SeekableStreamDecoderReadStatus;
|
||||||
|
typedef FLAC__StreamDecoderSeekStatus FLAC__SeekableStreamDecoderSeekStatus;
|
||||||
|
typedef FLAC__StreamDecoderTellStatus FLAC__SeekableStreamDecoderTellStatus;
|
||||||
|
typedef FLAC__StreamDecoderLengthStatus FLAC__SeekableStreamDecoderLengthStatus;
|
||||||
|
typedef FLAC__StreamDecoder FLAC__SeekableStreamDecoder;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
namespace Audio {
|
||||||
|
|
||||||
|
#pragma mark -
|
||||||
|
#pragma mark --- FLAC stream ---
|
||||||
|
#pragma mark -
|
||||||
|
|
||||||
|
static const uint MAX_OUTPUT_CHANNELS = 2;
|
||||||
|
|
||||||
|
|
||||||
|
class FLACStream : public SeekableAudioStream {
|
||||||
|
protected:
|
||||||
|
Common::SeekableReadStream *_inStream;
|
||||||
|
bool _disposeAfterUse;
|
||||||
|
|
||||||
|
::FLAC__SeekableStreamDecoder *_decoder;
|
||||||
|
|
||||||
|
/** Header of the stream */
|
||||||
|
FLAC__StreamMetadata_StreamInfo _streaminfo;
|
||||||
|
|
||||||
|
/** index + 1(!) of the last sample to be played */
|
||||||
|
FLAC__uint64 _lastSample;
|
||||||
|
|
||||||
|
/** total play time */
|
||||||
|
Timestamp _length;
|
||||||
|
|
||||||
|
/** true if the last sample was decoded from the FLAC-API - there might still be data in the buffer */
|
||||||
|
bool _lastSampleWritten;
|
||||||
|
|
||||||
|
typedef int16 SampleType;
|
||||||
|
enum { BUFTYPE_BITS = 16 };
|
||||||
|
|
||||||
|
enum {
|
||||||
|
// Maximal buffer size. According to the FLAC format specification, the block size is
|
||||||
|
// a 16 bit value (in fact it seems the maximal block size is 32768, but we play it safe).
|
||||||
|
BUFFER_SIZE = 65536
|
||||||
|
};
|
||||||
|
|
||||||
|
struct {
|
||||||
|
SampleType bufData[BUFFER_SIZE];
|
||||||
|
SampleType *bufReadPos;
|
||||||
|
uint bufFill;
|
||||||
|
} _sampleCache;
|
||||||
|
|
||||||
|
SampleType *_outBuffer;
|
||||||
|
uint _requestedSamples;
|
||||||
|
|
||||||
|
typedef void (*PFCONVERTBUFFERS)(SampleType*, const FLAC__int32*[], uint, const uint, const uint8);
|
||||||
|
PFCONVERTBUFFERS _methodConvertBuffers;
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
FLACStream(Common::SeekableReadStream *inStream, bool dispose);
|
||||||
|
virtual ~FLACStream();
|
||||||
|
|
||||||
|
int readBuffer(int16 *buffer, const int numSamples);
|
||||||
|
|
||||||
|
bool isStereo() const { return _streaminfo.channels >= 2; }
|
||||||
|
int getRate() const { return _streaminfo.sample_rate; }
|
||||||
|
bool endOfData() const {
|
||||||
|
// End of data is reached if there either is no valid stream data available,
|
||||||
|
// or if we reached the last sample and completely emptied the sample cache.
|
||||||
|
return _streaminfo.channels == 0 || (_lastSampleWritten && _sampleCache.bufFill == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool seek(const Timestamp &where);
|
||||||
|
Timestamp getLength() const { return _length; }
|
||||||
|
|
||||||
|
bool isStreamDecoderReady() const { return getStreamDecoderState() == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC ; }
|
||||||
|
protected:
|
||||||
|
uint getChannels() const { return MIN<uint>(_streaminfo.channels, MAX_OUTPUT_CHANNELS); }
|
||||||
|
|
||||||
|
bool allocateBuffer(uint minSamples);
|
||||||
|
|
||||||
|
inline FLAC__StreamDecoderState getStreamDecoderState() const;
|
||||||
|
|
||||||
|
inline bool processSingleBlock();
|
||||||
|
inline bool processUntilEndOfMetadata();
|
||||||
|
bool seekAbsolute(FLAC__uint64 sample);
|
||||||
|
|
||||||
|
inline ::FLAC__SeekableStreamDecoderReadStatus callbackRead(FLAC__byte buffer[], FLAC_size_t *bytes);
|
||||||
|
inline ::FLAC__SeekableStreamDecoderSeekStatus callbackSeek(FLAC__uint64 absoluteByteOffset);
|
||||||
|
inline ::FLAC__SeekableStreamDecoderTellStatus callbackTell(FLAC__uint64 *absoluteByteOffset);
|
||||||
|
inline ::FLAC__SeekableStreamDecoderLengthStatus callbackLength(FLAC__uint64 *streamLength);
|
||||||
|
inline bool callbackEOF();
|
||||||
|
inline ::FLAC__StreamDecoderWriteStatus callbackWrite(const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[]);
|
||||||
|
inline void callbackMetadata(const ::FLAC__StreamMetadata *metadata);
|
||||||
|
inline void callbackError(::FLAC__StreamDecoderErrorStatus status);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static ::FLAC__SeekableStreamDecoderReadStatus callWrapRead(const ::FLAC__SeekableStreamDecoder *decoder, FLAC__byte buffer[], FLAC_size_t *bytes, void *clientData);
|
||||||
|
static ::FLAC__SeekableStreamDecoderSeekStatus callWrapSeek(const ::FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 absoluteByteOffset, void *clientData);
|
||||||
|
static ::FLAC__SeekableStreamDecoderTellStatus callWrapTell(const ::FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *absoluteByteOffset, void *clientData);
|
||||||
|
static ::FLAC__SeekableStreamDecoderLengthStatus callWrapLength(const ::FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *streamLength, void *clientData);
|
||||||
|
static FLAC__bool callWrapEOF(const ::FLAC__SeekableStreamDecoder *decoder, void *clientData);
|
||||||
|
static ::FLAC__StreamDecoderWriteStatus callWrapWrite(const ::FLAC__SeekableStreamDecoder *decoder, const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *clientData);
|
||||||
|
static void callWrapMetadata(const ::FLAC__SeekableStreamDecoder *decoder, const ::FLAC__StreamMetadata *metadata, void *clientData);
|
||||||
|
static void callWrapError(const ::FLAC__SeekableStreamDecoder *decoder, ::FLAC__StreamDecoderErrorStatus status, void *clientData);
|
||||||
|
|
||||||
|
void setBestConvertBufferMethod();
|
||||||
|
static void convertBuffersGeneric(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits);
|
||||||
|
static void convertBuffersStereoNS(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits);
|
||||||
|
static void convertBuffersStereo8Bit(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits);
|
||||||
|
static void convertBuffersMonoNS(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits);
|
||||||
|
static void convertBuffersMono8Bit(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits);
|
||||||
|
};
|
||||||
|
|
||||||
|
FLACStream::FLACStream(Common::SeekableReadStream *inStream, bool dispose)
|
||||||
|
#ifdef LEGACY_FLAC
|
||||||
|
: _decoder(::FLAC__seekable_stream_decoder_new()),
|
||||||
|
#else
|
||||||
|
: _decoder(::FLAC__stream_decoder_new()),
|
||||||
|
#endif
|
||||||
|
_inStream(inStream),
|
||||||
|
_disposeAfterUse(dispose),
|
||||||
|
_length(0, 1000), _lastSample(0),
|
||||||
|
_outBuffer(NULL), _requestedSamples(0), _lastSampleWritten(false),
|
||||||
|
_methodConvertBuffers(&FLACStream::convertBuffersGeneric)
|
||||||
|
{
|
||||||
|
assert(_inStream);
|
||||||
|
memset(&_streaminfo, 0, sizeof(_streaminfo));
|
||||||
|
|
||||||
|
_sampleCache.bufReadPos = NULL;
|
||||||
|
_sampleCache.bufFill = 0;
|
||||||
|
|
||||||
|
_methodConvertBuffers = &FLACStream::convertBuffersGeneric;
|
||||||
|
|
||||||
|
bool success;
|
||||||
|
#ifdef LEGACY_FLAC
|
||||||
|
::FLAC__seekable_stream_decoder_set_read_callback(_decoder, &FLACStream::callWrapRead);
|
||||||
|
::FLAC__seekable_stream_decoder_set_seek_callback(_decoder, &FLACStream::callWrapSeek);
|
||||||
|
::FLAC__seekable_stream_decoder_set_tell_callback(_decoder, &FLACStream::callWrapTell);
|
||||||
|
::FLAC__seekable_stream_decoder_set_length_callback(_decoder, &FLACStream::callWrapLength);
|
||||||
|
::FLAC__seekable_stream_decoder_set_eof_callback(_decoder, &FLACStream::callWrapEOF);
|
||||||
|
::FLAC__seekable_stream_decoder_set_write_callback(_decoder, &FLACStream::callWrapWrite);
|
||||||
|
::FLAC__seekable_stream_decoder_set_metadata_callback(_decoder, &FLACStream::callWrapMetadata);
|
||||||
|
::FLAC__seekable_stream_decoder_set_error_callback(_decoder, &FLACStream::callWrapError);
|
||||||
|
::FLAC__seekable_stream_decoder_set_client_data(_decoder, (void*)this);
|
||||||
|
|
||||||
|
success = (::FLAC__seekable_stream_decoder_init(_decoder) == FLAC__SEEKABLE_STREAM_DECODER_OK);
|
||||||
|
#else
|
||||||
|
success = (::FLAC__stream_decoder_init_stream(
|
||||||
|
_decoder,
|
||||||
|
&FLACStream::callWrapRead,
|
||||||
|
&FLACStream::callWrapSeek,
|
||||||
|
&FLACStream::callWrapTell,
|
||||||
|
&FLACStream::callWrapLength,
|
||||||
|
&FLACStream::callWrapEOF,
|
||||||
|
&FLACStream::callWrapWrite,
|
||||||
|
&FLACStream::callWrapMetadata,
|
||||||
|
&FLACStream::callWrapError,
|
||||||
|
(void*)this
|
||||||
|
) == FLAC__STREAM_DECODER_INIT_STATUS_OK);
|
||||||
|
#endif
|
||||||
|
if (success) {
|
||||||
|
if (processUntilEndOfMetadata() && _streaminfo.channels > 0) {
|
||||||
|
_lastSample = _streaminfo.total_samples + 1;
|
||||||
|
_length = Timestamp(0, _lastSample - 1, getRate());
|
||||||
|
return; // no error occured
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
warning("FLACStream: could not create audio stream");
|
||||||
|
}
|
||||||
|
|
||||||
|
FLACStream::~FLACStream() {
|
||||||
|
if (_decoder != NULL) {
|
||||||
|
#ifdef LEGACY_FLAC
|
||||||
|
(void) ::FLAC__seekable_stream_decoder_finish(_decoder);
|
||||||
|
::FLAC__seekable_stream_decoder_delete(_decoder);
|
||||||
|
#else
|
||||||
|
(void) ::FLAC__stream_decoder_finish(_decoder);
|
||||||
|
::FLAC__stream_decoder_delete(_decoder);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
if (_disposeAfterUse)
|
||||||
|
delete _inStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline FLAC__StreamDecoderState FLACStream::getStreamDecoderState() const {
|
||||||
|
assert(_decoder != NULL);
|
||||||
|
#ifdef LEGACY_FLAC
|
||||||
|
return ::FLAC__seekable_stream_decoder_get_stream_decoder_state(_decoder);
|
||||||
|
#else
|
||||||
|
return ::FLAC__stream_decoder_get_state(_decoder);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool FLACStream::processSingleBlock() {
|
||||||
|
assert(_decoder != NULL);
|
||||||
|
#ifdef LEGACY_FLAC
|
||||||
|
return 0 != ::FLAC__seekable_stream_decoder_process_single(_decoder);
|
||||||
|
#else
|
||||||
|
return 0 != ::FLAC__stream_decoder_process_single(_decoder);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool FLACStream::processUntilEndOfMetadata() {
|
||||||
|
assert(_decoder != NULL);
|
||||||
|
#ifdef LEGACY_FLAC
|
||||||
|
return 0 != ::FLAC__seekable_stream_decoder_process_until_end_of_metadata(_decoder);
|
||||||
|
#else
|
||||||
|
return 0 != ::FLAC__stream_decoder_process_until_end_of_metadata(_decoder);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FLACStream::seekAbsolute(FLAC__uint64 sample) {
|
||||||
|
assert(_decoder != NULL);
|
||||||
|
#ifdef LEGACY_FLAC
|
||||||
|
const bool result = (0 != ::FLAC__seekable_stream_decoder_seek_absolute(_decoder, sample));
|
||||||
|
#else
|
||||||
|
const bool result = (0 != ::FLAC__stream_decoder_seek_absolute(_decoder, sample));
|
||||||
|
#endif
|
||||||
|
if (result) {
|
||||||
|
_lastSampleWritten = (_lastSample != 0 && sample >= _lastSample); // only set if we are SURE
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FLACStream::seek(const Timestamp &where) {
|
||||||
|
_sampleCache.bufFill = 0;
|
||||||
|
_sampleCache.bufReadPos = NULL;
|
||||||
|
// FLAC uses the sample pair number, thus we always use "false" for the isStereo parameter
|
||||||
|
// of the convertTimeToStreamPos helper.
|
||||||
|
return seekAbsolute((FLAC__uint64)convertTimeToStreamPos(where, getRate(), false).totalNumberOfFrames());
|
||||||
|
}
|
||||||
|
|
||||||
|
int FLACStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||||
|
const uint numChannels = getChannels();
|
||||||
|
|
||||||
|
if (numChannels == 0) {
|
||||||
|
warning("FLACStream: Stream not sucessfully initialised, cant playback");
|
||||||
|
return -1; // streaminfo wasnt read!
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(numSamples % numChannels == 0); // must be multiple of channels!
|
||||||
|
assert(buffer != NULL);
|
||||||
|
assert(_outBuffer == NULL);
|
||||||
|
assert(_requestedSamples == 0);
|
||||||
|
|
||||||
|
_outBuffer = buffer;
|
||||||
|
_requestedSamples = numSamples;
|
||||||
|
|
||||||
|
// If there is still data in our buffer from the last time around,
|
||||||
|
// copy that first.
|
||||||
|
if (_sampleCache.bufFill > 0) {
|
||||||
|
assert(_sampleCache.bufReadPos >= _sampleCache.bufData);
|
||||||
|
assert(_sampleCache.bufFill % numChannels == 0);
|
||||||
|
|
||||||
|
const uint copySamples = MIN((uint)numSamples, _sampleCache.bufFill);
|
||||||
|
memcpy(buffer, _sampleCache.bufReadPos, copySamples*sizeof(buffer[0]));
|
||||||
|
|
||||||
|
_outBuffer = buffer + copySamples;
|
||||||
|
_requestedSamples = numSamples - copySamples;
|
||||||
|
_sampleCache.bufReadPos += copySamples;
|
||||||
|
_sampleCache.bufFill -= copySamples;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool decoderOk = true;
|
||||||
|
|
||||||
|
FLAC__StreamDecoderState state = getStreamDecoderState();
|
||||||
|
|
||||||
|
// Keep poking FLAC to process more samples until we completely satisfied the request
|
||||||
|
// respectively until we run out of data.
|
||||||
|
while (!_lastSampleWritten && _requestedSamples > 0 && state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) {
|
||||||
|
assert(_sampleCache.bufFill == 0);
|
||||||
|
assert(_requestedSamples % numChannels == 0);
|
||||||
|
processSingleBlock();
|
||||||
|
state = getStreamDecoderState();
|
||||||
|
|
||||||
|
if (state == FLAC__STREAM_DECODER_END_OF_STREAM)
|
||||||
|
_lastSampleWritten = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error handling
|
||||||
|
switch (state) {
|
||||||
|
case FLAC__STREAM_DECODER_END_OF_STREAM:
|
||||||
|
_lastSampleWritten = true;
|
||||||
|
break;
|
||||||
|
case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
decoderOk = false;
|
||||||
|
warning("FLACStream: An error occured while decoding. DecoderState is: %s",
|
||||||
|
FLAC__StreamDecoderStateString[getStreamDecoderState()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute how many samples we actually produced
|
||||||
|
const int samples = (int)(_outBuffer - buffer);
|
||||||
|
assert(samples % numChannels == 0);
|
||||||
|
|
||||||
|
_outBuffer = NULL; // basically unnecessary, only for the purpose of the asserts
|
||||||
|
_requestedSamples = 0; // basically unnecessary, only for the purpose of the asserts
|
||||||
|
|
||||||
|
return decoderOk ? samples : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ::FLAC__SeekableStreamDecoderReadStatus FLACStream::callbackRead(FLAC__byte buffer[], FLAC_size_t *bytes) {
|
||||||
|
if (*bytes == 0) {
|
||||||
|
#ifdef LEGACY_FLAC
|
||||||
|
return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_ERROR; /* abort to avoid a deadlock */
|
||||||
|
#else
|
||||||
|
return FLAC__STREAM_DECODER_READ_STATUS_ABORT; /* abort to avoid a deadlock */
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint32 bytesRead = _inStream->read(buffer, *bytes);
|
||||||
|
|
||||||
|
if (bytesRead == 0) {
|
||||||
|
#ifdef LEGACY_FLAC
|
||||||
|
return _inStream->eos() ? FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK : FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_ERROR;
|
||||||
|
#else
|
||||||
|
return _inStream->eos() ? FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM : FLAC__STREAM_DECODER_READ_STATUS_ABORT;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
*bytes = static_cast<uint>(bytesRead);
|
||||||
|
#ifdef LEGACY_FLAC
|
||||||
|
return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK;
|
||||||
|
#else
|
||||||
|
return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void FLACStream::setBestConvertBufferMethod() {
|
||||||
|
PFCONVERTBUFFERS tempMethod = &FLACStream::convertBuffersGeneric;
|
||||||
|
|
||||||
|
const uint numChannels = getChannels();
|
||||||
|
const uint8 numBits = (uint8)_streaminfo.bits_per_sample;
|
||||||
|
|
||||||
|
assert(numChannels >= 1);
|
||||||
|
assert(numBits >= 4 && numBits <=32);
|
||||||
|
|
||||||
|
if (numChannels == 1) {
|
||||||
|
if (numBits == 8)
|
||||||
|
tempMethod = &FLACStream::convertBuffersMono8Bit;
|
||||||
|
if (numBits == BUFTYPE_BITS)
|
||||||
|
tempMethod = &FLACStream::convertBuffersMonoNS;
|
||||||
|
} else if (numChannels == 2) {
|
||||||
|
if (numBits == 8)
|
||||||
|
tempMethod = &FLACStream::convertBuffersStereo8Bit;
|
||||||
|
if (numBits == BUFTYPE_BITS)
|
||||||
|
tempMethod = &FLACStream::convertBuffersStereoNS;
|
||||||
|
} /* else ... */
|
||||||
|
|
||||||
|
_methodConvertBuffers = tempMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1 channel, no scaling
|
||||||
|
void FLACStream::convertBuffersMonoNS(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits) {
|
||||||
|
assert(numChannels == 1);
|
||||||
|
assert(numBits == BUFTYPE_BITS);
|
||||||
|
|
||||||
|
FLAC__int32 const* inChannel1 = inChannels[0];
|
||||||
|
|
||||||
|
while (numSamples >= 4) {
|
||||||
|
bufDestination[0] = static_cast<SampleType>(inChannel1[0]);
|
||||||
|
bufDestination[1] = static_cast<SampleType>(inChannel1[1]);
|
||||||
|
bufDestination[2] = static_cast<SampleType>(inChannel1[2]);
|
||||||
|
bufDestination[3] = static_cast<SampleType>(inChannel1[3]);
|
||||||
|
bufDestination += 4;
|
||||||
|
inChannel1 += 4;
|
||||||
|
numSamples -= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; numSamples > 0; --numSamples) {
|
||||||
|
*bufDestination++ = static_cast<SampleType>(*inChannel1++);
|
||||||
|
}
|
||||||
|
|
||||||
|
inChannels[0] = inChannel1;
|
||||||
|
assert(numSamples == 0); // dint copy too many samples
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1 channel, scaling from 8Bit
|
||||||
|
void FLACStream::convertBuffersMono8Bit(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits) {
|
||||||
|
assert(numChannels == 1);
|
||||||
|
assert(numBits == 8);
|
||||||
|
assert(8 < BUFTYPE_BITS);
|
||||||
|
|
||||||
|
FLAC__int32 const* inChannel1 = inChannels[0];
|
||||||
|
|
||||||
|
while (numSamples >= 4) {
|
||||||
|
bufDestination[0] = static_cast<SampleType>(inChannel1[0]) << (BUFTYPE_BITS - 8);
|
||||||
|
bufDestination[1] = static_cast<SampleType>(inChannel1[1]) << (BUFTYPE_BITS - 8);
|
||||||
|
bufDestination[2] = static_cast<SampleType>(inChannel1[2]) << (BUFTYPE_BITS - 8);
|
||||||
|
bufDestination[3] = static_cast<SampleType>(inChannel1[3]) << (BUFTYPE_BITS - 8);
|
||||||
|
bufDestination += 4;
|
||||||
|
inChannel1 += 4;
|
||||||
|
numSamples -= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; numSamples > 0; --numSamples) {
|
||||||
|
*bufDestination++ = static_cast<SampleType>(*inChannel1++) << (BUFTYPE_BITS - 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
inChannels[0] = inChannel1;
|
||||||
|
assert(numSamples == 0); // dint copy too many samples
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2 channels, no scaling
|
||||||
|
void FLACStream::convertBuffersStereoNS(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits) {
|
||||||
|
assert(numChannels == 2);
|
||||||
|
assert(numBits == BUFTYPE_BITS);
|
||||||
|
assert(numSamples % 2 == 0); // must be integral multiply of channels
|
||||||
|
|
||||||
|
|
||||||
|
FLAC__int32 const* inChannel1 = inChannels[0]; // Left Channel
|
||||||
|
FLAC__int32 const* inChannel2 = inChannels[1]; // Right Channel
|
||||||
|
|
||||||
|
while (numSamples >= 2*2) {
|
||||||
|
bufDestination[0] = static_cast<SampleType>(inChannel1[0]);
|
||||||
|
bufDestination[1] = static_cast<SampleType>(inChannel2[0]);
|
||||||
|
bufDestination[2] = static_cast<SampleType>(inChannel1[1]);
|
||||||
|
bufDestination[3] = static_cast<SampleType>(inChannel2[1]);
|
||||||
|
bufDestination += 2 * 2;
|
||||||
|
inChannel1 += 2;
|
||||||
|
inChannel2 += 2;
|
||||||
|
numSamples -= 2 * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (numSamples > 0) {
|
||||||
|
bufDestination[0] = static_cast<SampleType>(*inChannel1++);
|
||||||
|
bufDestination[1] = static_cast<SampleType>(*inChannel2++);
|
||||||
|
bufDestination += 2;
|
||||||
|
numSamples -= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
inChannels[0] = inChannel1;
|
||||||
|
inChannels[1] = inChannel2;
|
||||||
|
assert(numSamples == 0); // dint copy too many samples
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2 channels, scaling from 8Bit
|
||||||
|
void FLACStream::convertBuffersStereo8Bit(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits) {
|
||||||
|
assert(numChannels == 2);
|
||||||
|
assert(numBits == 8);
|
||||||
|
assert(numSamples % 2 == 0); // must be integral multiply of channels
|
||||||
|
assert(8 < BUFTYPE_BITS);
|
||||||
|
|
||||||
|
FLAC__int32 const* inChannel1 = inChannels[0]; // Left Channel
|
||||||
|
FLAC__int32 const* inChannel2 = inChannels[1]; // Right Channel
|
||||||
|
|
||||||
|
while (numSamples >= 2*2) {
|
||||||
|
bufDestination[0] = static_cast<SampleType>(inChannel1[0]) << (BUFTYPE_BITS - 8);
|
||||||
|
bufDestination[1] = static_cast<SampleType>(inChannel2[0]) << (BUFTYPE_BITS - 8);
|
||||||
|
bufDestination[2] = static_cast<SampleType>(inChannel1[1]) << (BUFTYPE_BITS - 8);
|
||||||
|
bufDestination[3] = static_cast<SampleType>(inChannel2[1]) << (BUFTYPE_BITS - 8);
|
||||||
|
bufDestination += 2 * 2;
|
||||||
|
inChannel1 += 2;
|
||||||
|
inChannel2 += 2;
|
||||||
|
numSamples -= 2 * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (numSamples > 0) {
|
||||||
|
bufDestination[0] = static_cast<SampleType>(*inChannel1++) << (BUFTYPE_BITS - 8);
|
||||||
|
bufDestination[1] = static_cast<SampleType>(*inChannel2++) << (BUFTYPE_BITS - 8);
|
||||||
|
bufDestination += 2;
|
||||||
|
numSamples -= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
inChannels[0] = inChannel1;
|
||||||
|
inChannels[1] = inChannel2;
|
||||||
|
assert(numSamples == 0); // dint copy too many samples
|
||||||
|
}
|
||||||
|
|
||||||
|
// all Purpose-conversion - slowest of em all
|
||||||
|
void FLACStream::convertBuffersGeneric(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits) {
|
||||||
|
assert(numSamples % numChannels == 0); // must be integral multiply of channels
|
||||||
|
|
||||||
|
if (numBits < BUFTYPE_BITS) {
|
||||||
|
const uint8 kPower = (uint8)(BUFTYPE_BITS - numBits);
|
||||||
|
|
||||||
|
for (; numSamples > 0; numSamples -= numChannels) {
|
||||||
|
for (uint i = 0; i < numChannels; ++i)
|
||||||
|
*bufDestination++ = static_cast<SampleType>(*(inChannels[i]++)) << kPower;
|
||||||
|
}
|
||||||
|
} else if (numBits > BUFTYPE_BITS) {
|
||||||
|
const uint8 kPower = (uint8)(numBits - BUFTYPE_BITS);
|
||||||
|
|
||||||
|
for (; numSamples > 0; numSamples -= numChannels) {
|
||||||
|
for (uint i = 0; i < numChannels; ++i)
|
||||||
|
*bufDestination++ = static_cast<SampleType>(*(inChannels[i]++) >> kPower) ;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (; numSamples > 0; numSamples -= numChannels) {
|
||||||
|
for (uint i = 0; i < numChannels; ++i)
|
||||||
|
*bufDestination++ = static_cast<SampleType>(*(inChannels[i]++));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(numSamples == 0); // dint copy too many samples
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ::FLAC__StreamDecoderWriteStatus FLACStream::callbackWrite(const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[]) {
|
||||||
|
assert(frame->header.channels == _streaminfo.channels);
|
||||||
|
assert(frame->header.sample_rate == _streaminfo.sample_rate);
|
||||||
|
assert(frame->header.bits_per_sample == _streaminfo.bits_per_sample);
|
||||||
|
assert(frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER || _streaminfo.min_blocksize == _streaminfo.max_blocksize);
|
||||||
|
|
||||||
|
// We require that either the sample cache is empty, or that no samples were requested
|
||||||
|
assert(_sampleCache.bufFill == 0 || _requestedSamples == 0);
|
||||||
|
|
||||||
|
uint numSamples = frame->header.blocksize;
|
||||||
|
const uint numChannels = getChannels();
|
||||||
|
const uint8 numBits = (uint8)_streaminfo.bits_per_sample;
|
||||||
|
|
||||||
|
assert(_requestedSamples % numChannels == 0); // must be integral multiply of channels
|
||||||
|
|
||||||
|
const FLAC__uint64 firstSampleNumber = (frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER) ?
|
||||||
|
frame->header.number.sample_number : (static_cast<FLAC__uint64>(frame->header.number.frame_number)) * _streaminfo.max_blocksize;
|
||||||
|
|
||||||
|
// Check whether we are about to reach beyond the last sample we are supposed to play.
|
||||||
|
if (_lastSample != 0 && firstSampleNumber + numSamples >= _lastSample) {
|
||||||
|
numSamples = (uint)(firstSampleNumber >= _lastSample ? 0 : _lastSample - firstSampleNumber);
|
||||||
|
_lastSampleWritten = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The value in _requestedSamples counts raw samples, so if there are more than one
|
||||||
|
// channel, we have to multiply the number of available sample "pairs" by numChannels
|
||||||
|
numSamples *= numChannels;
|
||||||
|
|
||||||
|
const FLAC__int32 *inChannels[MAX_OUTPUT_CHANNELS];
|
||||||
|
for (uint i = 0; i < numChannels; ++i)
|
||||||
|
inChannels[i] = buffer[i];
|
||||||
|
|
||||||
|
// write the incoming samples directly into the buffer provided to us by the mixer
|
||||||
|
if (_requestedSamples > 0) {
|
||||||
|
assert(_requestedSamples % numChannels == 0);
|
||||||
|
assert(_outBuffer != NULL);
|
||||||
|
|
||||||
|
// Copy & convert the available samples (limited both by how many we have available, and
|
||||||
|
// by how many are actually needed).
|
||||||
|
const uint copySamples = MIN(_requestedSamples, numSamples);
|
||||||
|
(*_methodConvertBuffers)(_outBuffer, inChannels, copySamples, numChannels, numBits);
|
||||||
|
|
||||||
|
_requestedSamples -= copySamples;
|
||||||
|
numSamples -= copySamples;
|
||||||
|
_outBuffer += copySamples;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write all remaining samples (i.e. those which didn't fit into the mixer buffer)
|
||||||
|
// into the sample cache.
|
||||||
|
if (_sampleCache.bufFill == 0)
|
||||||
|
_sampleCache.bufReadPos = _sampleCache.bufData;
|
||||||
|
const uint cacheSpace = (_sampleCache.bufData + BUFFER_SIZE) - (_sampleCache.bufReadPos + _sampleCache.bufFill);
|
||||||
|
assert(numSamples <= cacheSpace);
|
||||||
|
(*_methodConvertBuffers)(_sampleCache.bufReadPos + _sampleCache.bufFill, inChannels, numSamples, numChannels, numBits);
|
||||||
|
|
||||||
|
_sampleCache.bufFill += numSamples;
|
||||||
|
|
||||||
|
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ::FLAC__SeekableStreamDecoderSeekStatus FLACStream::callbackSeek(FLAC__uint64 absoluteByteOffset) {
|
||||||
|
_inStream->seek(absoluteByteOffset, SEEK_SET);
|
||||||
|
const bool result = (absoluteByteOffset == (FLAC__uint64)_inStream->pos());
|
||||||
|
|
||||||
|
#ifdef LEGACY_FLAC
|
||||||
|
return result ? FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK : FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR;
|
||||||
|
#else
|
||||||
|
return result ? FLAC__STREAM_DECODER_SEEK_STATUS_OK : FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ::FLAC__SeekableStreamDecoderTellStatus FLACStream::callbackTell(FLAC__uint64 *absoluteByteOffset) {
|
||||||
|
*absoluteByteOffset = static_cast<FLAC__uint64>(_inStream->pos());
|
||||||
|
#ifdef LEGACY_FLAC
|
||||||
|
return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK;
|
||||||
|
#else
|
||||||
|
return FLAC__STREAM_DECODER_TELL_STATUS_OK;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ::FLAC__SeekableStreamDecoderLengthStatus FLACStream::callbackLength(FLAC__uint64 *streamLength) {
|
||||||
|
*streamLength = static_cast<FLAC__uint64>(_inStream->size());
|
||||||
|
#ifdef LEGACY_FLAC
|
||||||
|
return FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK;
|
||||||
|
#else
|
||||||
|
return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool FLACStream::callbackEOF() {
|
||||||
|
return _inStream->eos();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline void FLACStream::callbackMetadata(const ::FLAC__StreamMetadata *metadata) {
|
||||||
|
assert(_decoder != NULL);
|
||||||
|
assert(metadata->type == FLAC__METADATA_TYPE_STREAMINFO); // others arent really interesting
|
||||||
|
|
||||||
|
_streaminfo = metadata->data.stream_info;
|
||||||
|
setBestConvertBufferMethod(); // should be set after getting stream-information. FLAC always parses the info first
|
||||||
|
}
|
||||||
|
inline void FLACStream::callbackError(::FLAC__StreamDecoderErrorStatus status) {
|
||||||
|
// some of these are non-critical-Errors
|
||||||
|
debug(1, "FLACStream: An error occured while decoding. DecoderState is: %s",
|
||||||
|
FLAC__StreamDecoderErrorStatusString[status]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Static Callback Wrappers */
|
||||||
|
::FLAC__SeekableStreamDecoderReadStatus FLACStream::callWrapRead(const ::FLAC__SeekableStreamDecoder *decoder, FLAC__byte buffer[], FLAC_size_t *bytes, void *clientData) {
|
||||||
|
FLACStream *instance = (FLACStream *)clientData;
|
||||||
|
assert(0 != instance);
|
||||||
|
return instance->callbackRead(buffer, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
::FLAC__SeekableStreamDecoderSeekStatus FLACStream::callWrapSeek(const ::FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 absoluteByteOffset, void *clientData) {
|
||||||
|
FLACStream *instance = (FLACStream *)clientData;
|
||||||
|
assert(0 != instance);
|
||||||
|
return instance->callbackSeek(absoluteByteOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
::FLAC__SeekableStreamDecoderTellStatus FLACStream::callWrapTell(const ::FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *absoluteByteOffset, void *clientData) {
|
||||||
|
FLACStream *instance = (FLACStream *)clientData;
|
||||||
|
assert(0 != instance);
|
||||||
|
return instance->callbackTell(absoluteByteOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
::FLAC__SeekableStreamDecoderLengthStatus FLACStream::callWrapLength(const ::FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *streamLength, void *clientData) {
|
||||||
|
FLACStream *instance = (FLACStream *)clientData;
|
||||||
|
assert(0 != instance);
|
||||||
|
return instance->callbackLength(streamLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
FLAC__bool FLACStream::callWrapEOF(const ::FLAC__SeekableStreamDecoder *decoder, void *clientData) {
|
||||||
|
FLACStream *instance = (FLACStream *)clientData;
|
||||||
|
assert(0 != instance);
|
||||||
|
return instance->callbackEOF();
|
||||||
|
}
|
||||||
|
|
||||||
|
::FLAC__StreamDecoderWriteStatus FLACStream::callWrapWrite(const ::FLAC__SeekableStreamDecoder *decoder, const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *clientData) {
|
||||||
|
FLACStream *instance = (FLACStream *)clientData;
|
||||||
|
assert(0 != instance);
|
||||||
|
return instance->callbackWrite(frame, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FLACStream::callWrapMetadata(const ::FLAC__SeekableStreamDecoder *decoder, const ::FLAC__StreamMetadata *metadata, void *clientData) {
|
||||||
|
FLACStream *instance = (FLACStream *)clientData;
|
||||||
|
assert(0 != instance);
|
||||||
|
instance->callbackMetadata(metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FLACStream::callWrapError(const ::FLAC__SeekableStreamDecoder *decoder, ::FLAC__StreamDecoderErrorStatus status, void *clientData) {
|
||||||
|
FLACStream *instance = (FLACStream *)clientData;
|
||||||
|
assert(0 != instance);
|
||||||
|
instance->callbackError(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#pragma mark -
|
||||||
|
#pragma mark --- FLAC factory functions ---
|
||||||
|
#pragma mark -
|
||||||
|
|
||||||
|
SeekableAudioStream *makeFLACStream(
|
||||||
|
Common::SeekableReadStream *stream,
|
||||||
|
DisposeAfterUse::Flag disposeAfterUse) {
|
||||||
|
SeekableAudioStream *s = new FLACStream(stream, disposeAfterUse);
|
||||||
|
if (s && s->endOfData()) {
|
||||||
|
delete s;
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End of namespace Audio
|
||||||
|
|
||||||
|
#endif // #ifdef USE_FLAC
|
|
@ -23,29 +23,51 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef BACKEND_PSP_SAVES_H
|
/**
|
||||||
#define BACKEND_PSP_SAVES_H
|
* @file
|
||||||
|
* Sound decoder used in engines:
|
||||||
|
* - agos
|
||||||
|
* - kyra
|
||||||
|
* - m4
|
||||||
|
* - queen
|
||||||
|
* - saga
|
||||||
|
* - scumm
|
||||||
|
* - sword1
|
||||||
|
* - sword2
|
||||||
|
* - touche
|
||||||
|
* - tucker
|
||||||
|
*/
|
||||||
|
|
||||||
#include "backends/saves/default/default-saves.h"
|
#ifndef SOUND_FLAC_H
|
||||||
|
#define SOUND_FLAC_H
|
||||||
|
|
||||||
|
#include "common/types.h"
|
||||||
|
#include "common/sys.h"
|
||||||
|
|
||||||
|
#ifdef USE_FLAC
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
class SeekableReadStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Audio {
|
||||||
|
|
||||||
|
class AudioStream;
|
||||||
|
class SeekableAudioStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Customization of the DefaultSaveFileManager for the PSP platform.
|
* Create a new SeekableAudioStream from the FLAC data in the given stream.
|
||||||
* The only two differences are that the default constructor sets
|
* Allows for seeking (which is why we require a SeekableReadStream).
|
||||||
* up a default savepath, and that checkPath tries to create the savedir,
|
*
|
||||||
* if missing, via the sceIoMkdir() call.
|
* @param stream the SeekableReadStream from which to read the FLAC data
|
||||||
|
* @param disposeAfterUse whether to delete the stream after use
|
||||||
|
* @return a new SeekableAudioStream, or NULL, if an error occured
|
||||||
*/
|
*/
|
||||||
class PSPSaveFileManager : public DefaultSaveFileManager {
|
SeekableAudioStream *makeFLACStream(
|
||||||
public:
|
Common::SeekableReadStream *stream,
|
||||||
PSPSaveFileManager();
|
DisposeAfterUse::Flag disposeAfterUse);
|
||||||
// PSPSaveFileManager(const Common::String &defaultSavepath);
|
|
||||||
|
|
||||||
protected:
|
} // End of namespace Audio
|
||||||
/**
|
|
||||||
* Checks the given path for read access, existence, etc.
|
|
||||||
* In addition, tries to create a missing savedir, if possible.
|
|
||||||
* Sets the internal error and error message accordingly.
|
|
||||||
*/
|
|
||||||
virtual void checkPath(const Common::FSNode &dir);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
#endif // #ifdef USE_FLAC
|
||||||
|
#endif // #ifndef SOUND_FLAC_H
|
352
sound/decoders/mp3.cpp
Normal file
|
@ -0,0 +1,352 @@
|
||||||
|
/* Residual - A 3D game interpreter
|
||||||
|
*
|
||||||
|
* Residual is the legal property of its developers, whose names
|
||||||
|
* are too numerous to list here. Please refer to the AUTHORS
|
||||||
|
* 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$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "sound/decoders/mp3.h"
|
||||||
|
|
||||||
|
#ifdef USE_MAD
|
||||||
|
|
||||||
|
#include "common/debug.h"
|
||||||
|
#include "common/stream.h"
|
||||||
|
#include "common/util.h"
|
||||||
|
|
||||||
|
#include "sound/audiocd.h"
|
||||||
|
#include "sound/audiostream.h"
|
||||||
|
|
||||||
|
#include <mad.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace Audio {
|
||||||
|
|
||||||
|
|
||||||
|
#pragma mark -
|
||||||
|
#pragma mark --- MP3 (MAD) stream ---
|
||||||
|
#pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
class MP3Stream : public SeekableAudioStream {
|
||||||
|
protected:
|
||||||
|
enum State {
|
||||||
|
MP3_STATE_INIT, // Need to init the decoder
|
||||||
|
MP3_STATE_READY, // ready for processing data
|
||||||
|
MP3_STATE_EOS // end of data reached (may need to loop)
|
||||||
|
};
|
||||||
|
|
||||||
|
Common::SeekableReadStream *_inStream;
|
||||||
|
DisposeAfterUse::Flag _disposeAfterUse;
|
||||||
|
|
||||||
|
uint _posInFrame;
|
||||||
|
State _state;
|
||||||
|
|
||||||
|
Timestamp _length;
|
||||||
|
mad_timer_t _totalTime;
|
||||||
|
|
||||||
|
mad_stream _stream;
|
||||||
|
mad_frame _frame;
|
||||||
|
mad_synth _synth;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
BUFFER_SIZE = 5 * 8192
|
||||||
|
};
|
||||||
|
|
||||||
|
// This buffer contains a slab of input data
|
||||||
|
byte _buf[BUFFER_SIZE + MAD_BUFFER_GUARD];
|
||||||
|
|
||||||
|
public:
|
||||||
|
MP3Stream(Common::SeekableReadStream *inStream,
|
||||||
|
DisposeAfterUse::Flag dispose);
|
||||||
|
~MP3Stream();
|
||||||
|
|
||||||
|
int readBuffer(int16 *buffer, const int numSamples);
|
||||||
|
|
||||||
|
bool endOfData() const { return _state == MP3_STATE_EOS; }
|
||||||
|
bool isStereo() const { return MAD_NCHANNELS(&_frame.header) == 2; }
|
||||||
|
int getRate() const { return _frame.header.samplerate; }
|
||||||
|
|
||||||
|
bool seek(const Timestamp &where);
|
||||||
|
Timestamp getLength() const { return _length; }
|
||||||
|
protected:
|
||||||
|
void decodeMP3Data();
|
||||||
|
void readMP3Data();
|
||||||
|
|
||||||
|
void initStream();
|
||||||
|
void readHeader();
|
||||||
|
void deinitStream();
|
||||||
|
};
|
||||||
|
|
||||||
|
MP3Stream::MP3Stream(Common::SeekableReadStream *inStream, DisposeAfterUse::Flag dispose) :
|
||||||
|
_inStream(inStream),
|
||||||
|
_disposeAfterUse(dispose),
|
||||||
|
_posInFrame(0),
|
||||||
|
_state(MP3_STATE_INIT),
|
||||||
|
_length(0, 1000),
|
||||||
|
_totalTime(mad_timer_zero) {
|
||||||
|
|
||||||
|
// The MAD_BUFFER_GUARD must always contain zeros (the reason
|
||||||
|
// for this is that the Layer III Huffman decoder of libMAD
|
||||||
|
// may read a few bytes beyond the end of the input buffer).
|
||||||
|
memset(_buf + BUFFER_SIZE, 0, MAD_BUFFER_GUARD);
|
||||||
|
|
||||||
|
// Calculate the length of the stream
|
||||||
|
initStream();
|
||||||
|
|
||||||
|
while (_state != MP3_STATE_EOS)
|
||||||
|
readHeader();
|
||||||
|
|
||||||
|
_length = Timestamp(mad_timer_count(_totalTime, MAD_UNITS_MILLISECONDS), getRate());
|
||||||
|
|
||||||
|
deinitStream();
|
||||||
|
|
||||||
|
// Reinit stream
|
||||||
|
_state = MP3_STATE_INIT;
|
||||||
|
|
||||||
|
// Decode the first chunk of data. This is necessary so that _frame
|
||||||
|
// is setup and isStereo() and getRate() return correct results.
|
||||||
|
decodeMP3Data();
|
||||||
|
}
|
||||||
|
|
||||||
|
MP3Stream::~MP3Stream() {
|
||||||
|
deinitStream();
|
||||||
|
|
||||||
|
if (_disposeAfterUse == DisposeAfterUse::YES)
|
||||||
|
delete _inStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MP3Stream::decodeMP3Data() {
|
||||||
|
do {
|
||||||
|
if (_state == MP3_STATE_INIT)
|
||||||
|
initStream();
|
||||||
|
|
||||||
|
if (_state == MP3_STATE_EOS)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// If necessary, load more data into the stream decoder
|
||||||
|
if (_stream.error == MAD_ERROR_BUFLEN)
|
||||||
|
readMP3Data();
|
||||||
|
|
||||||
|
while (_state == MP3_STATE_READY) {
|
||||||
|
// Decode the next frame
|
||||||
|
if (mad_frame_decode(&_frame, &_stream) == -1) {
|
||||||
|
if (_stream.error == MAD_ERROR_BUFLEN) {
|
||||||
|
break; // Read more data
|
||||||
|
} else if (MAD_RECOVERABLE(_stream.error)) {
|
||||||
|
// Note: we will occasionally see MAD_ERROR_BADDATAPTR errors here.
|
||||||
|
// These are normal and expected (caused by our frame skipping (i.e. "seeking")
|
||||||
|
// code above).
|
||||||
|
debug(6, "MP3Stream: Recoverable error in mad_frame_decode (%s)", mad_stream_errorstr(&_stream));
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
warning("MP3Stream: Unrecoverable error in mad_frame_decode (%s)", mad_stream_errorstr(&_stream));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Synthesize PCM data
|
||||||
|
mad_synth_frame(&_synth, &_frame);
|
||||||
|
_posInFrame = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (_state != MP3_STATE_EOS && _stream.error == MAD_ERROR_BUFLEN);
|
||||||
|
|
||||||
|
if (_stream.error != MAD_ERROR_NONE)
|
||||||
|
_state = MP3_STATE_EOS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MP3Stream::readMP3Data() {
|
||||||
|
uint32 remaining = 0;
|
||||||
|
|
||||||
|
// Give up immediately if we already used up all data in the stream
|
||||||
|
if (_inStream->eos()) {
|
||||||
|
_state = MP3_STATE_EOS;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_stream.next_frame) {
|
||||||
|
// If there is still data in the MAD stream, we need to preserve it.
|
||||||
|
// Note that we use memmove, as we are reusing the same buffer,
|
||||||
|
// and hence the data regions we copy from and to may overlap.
|
||||||
|
remaining = _stream.bufend - _stream.next_frame;
|
||||||
|
assert(remaining < BUFFER_SIZE); // Paranoia check
|
||||||
|
memmove(_buf, _stream.next_frame, remaining);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to read the next block
|
||||||
|
uint32 size = _inStream->read(_buf + remaining, BUFFER_SIZE - remaining);
|
||||||
|
if (size <= 0) {
|
||||||
|
_state = MP3_STATE_EOS;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Feed the data we just read into the stream decoder
|
||||||
|
_stream.error = MAD_ERROR_NONE;
|
||||||
|
mad_stream_buffer(&_stream, _buf, size + remaining);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MP3Stream::seek(const Timestamp &where) {
|
||||||
|
if (where == _length) {
|
||||||
|
_state = MP3_STATE_EOS;
|
||||||
|
return true;
|
||||||
|
} else if (where > _length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint32 time = where.msecs();
|
||||||
|
|
||||||
|
mad_timer_t destination;
|
||||||
|
mad_timer_set(&destination, time / 1000, time % 1000, 1000);
|
||||||
|
|
||||||
|
if (_state != MP3_STATE_READY || mad_timer_compare(destination, _totalTime) < 0)
|
||||||
|
initStream();
|
||||||
|
|
||||||
|
while (mad_timer_compare(destination, _totalTime) > 0 && _state != MP3_STATE_EOS)
|
||||||
|
readHeader();
|
||||||
|
|
||||||
|
return (_state != MP3_STATE_EOS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MP3Stream::initStream() {
|
||||||
|
if (_state != MP3_STATE_INIT)
|
||||||
|
deinitStream();
|
||||||
|
|
||||||
|
// Init MAD
|
||||||
|
mad_stream_init(&_stream);
|
||||||
|
mad_frame_init(&_frame);
|
||||||
|
mad_synth_init(&_synth);
|
||||||
|
|
||||||
|
// Reset the stream data
|
||||||
|
_inStream->seek(0, SEEK_SET);
|
||||||
|
_totalTime = mad_timer_zero;
|
||||||
|
_posInFrame = 0;
|
||||||
|
|
||||||
|
// Update state
|
||||||
|
_state = MP3_STATE_READY;
|
||||||
|
|
||||||
|
// Read the first few sample bytes
|
||||||
|
readMP3Data();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MP3Stream::readHeader() {
|
||||||
|
if (_state != MP3_STATE_READY)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// If necessary, load more data into the stream decoder
|
||||||
|
if (_stream.error == MAD_ERROR_BUFLEN)
|
||||||
|
readMP3Data();
|
||||||
|
|
||||||
|
while (_state != MP3_STATE_EOS) {
|
||||||
|
_stream.error = MAD_ERROR_NONE;
|
||||||
|
|
||||||
|
// Decode the next header. Note: mad_frame_decode would do this for us, too.
|
||||||
|
// However, for seeking we don't want to decode the full frame (else it would
|
||||||
|
// be far too slow). Hence we perform this explicitly in a separate step.
|
||||||
|
if (mad_header_decode(&_frame.header, &_stream) == -1) {
|
||||||
|
if (_stream.error == MAD_ERROR_BUFLEN) {
|
||||||
|
readMP3Data(); // Read more data
|
||||||
|
continue;
|
||||||
|
} else if (MAD_RECOVERABLE(_stream.error)) {
|
||||||
|
debug(6, "MP3Stream: Recoverable error in mad_header_decode (%s)", mad_stream_errorstr(&_stream));
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
warning("MP3Stream: Unrecoverable error in mad_header_decode (%s)", mad_stream_errorstr(&_stream));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sum up the total playback time so far
|
||||||
|
mad_timer_add(&_totalTime, _frame.header.duration);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_stream.error != MAD_ERROR_NONE)
|
||||||
|
_state = MP3_STATE_EOS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MP3Stream::deinitStream() {
|
||||||
|
if (_state == MP3_STATE_INIT)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Deinit MAD
|
||||||
|
mad_synth_finish(&_synth);
|
||||||
|
mad_frame_finish(&_frame);
|
||||||
|
mad_stream_finish(&_stream);
|
||||||
|
|
||||||
|
_state = MP3_STATE_EOS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int scale_sample(mad_fixed_t sample) {
|
||||||
|
// round
|
||||||
|
sample += (1L << (MAD_F_FRACBITS - 16));
|
||||||
|
|
||||||
|
// clip
|
||||||
|
if (sample > MAD_F_ONE - 1)
|
||||||
|
sample = MAD_F_ONE - 1;
|
||||||
|
else if (sample < -MAD_F_ONE)
|
||||||
|
sample = -MAD_F_ONE;
|
||||||
|
|
||||||
|
// quantize and scale to not saturate when mixing a lot of channels
|
||||||
|
return sample >> (MAD_F_FRACBITS + 1 - 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
int MP3Stream::readBuffer(int16 *buffer, const int numSamples) {
|
||||||
|
int samples = 0;
|
||||||
|
// Keep going as long as we have input available
|
||||||
|
while (samples < numSamples && _state != MP3_STATE_EOS) {
|
||||||
|
const int len = MIN(numSamples, samples + (int)(_synth.pcm.length - _posInFrame) * MAD_NCHANNELS(&_frame.header));
|
||||||
|
while (samples < len) {
|
||||||
|
*buffer++ = (int16)scale_sample(_synth.pcm.samples[0][_posInFrame]);
|
||||||
|
samples++;
|
||||||
|
if (MAD_NCHANNELS(&_frame.header) == 2) {
|
||||||
|
*buffer++ = (int16)scale_sample(_synth.pcm.samples[1][_posInFrame]);
|
||||||
|
samples++;
|
||||||
|
}
|
||||||
|
_posInFrame++;
|
||||||
|
}
|
||||||
|
if (_posInFrame >= _synth.pcm.length) {
|
||||||
|
// We used up all PCM data in the current frame -- read & decode more
|
||||||
|
decodeMP3Data();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return samples;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#pragma mark -
|
||||||
|
#pragma mark --- MP3 factory functions ---
|
||||||
|
#pragma mark -
|
||||||
|
|
||||||
|
SeekableAudioStream *makeMP3Stream(
|
||||||
|
Common::SeekableReadStream *stream,
|
||||||
|
DisposeAfterUse::Flag disposeAfterUse) {
|
||||||
|
SeekableAudioStream *s = new MP3Stream(stream, disposeAfterUse);
|
||||||
|
if (s && s->endOfData()) {
|
||||||
|
delete s;
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End of namespace Audio
|
||||||
|
|
||||||
|
#endif // #ifdef USE_MAD
|
|
@ -8,42 +8,66 @@
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
* as published by the Free Software Foundation; either version 2
|
* as published by the Free Software Foundation; either version 2
|
||||||
* of the License, or (at your option) any later version.
|
* of the License, or (at your option) any later version.
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
* This program is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program; if not, write to the Free Software
|
* along with this program; if not, write to the Free Software
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
*
|
*
|
||||||
* $URL$
|
* $URL$
|
||||||
* $Id$
|
* $Id$
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef PSP_FILESYSTEM_FACTORY_H
|
|
||||||
#define PSP_FILESYSTEM_FACTORY_H
|
|
||||||
|
|
||||||
#include "common/singleton.h"
|
|
||||||
#include "backends/fs/fs-factory.h"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates PSPFilesystemNode objects.
|
* @file
|
||||||
*
|
* Sound decoder used in engines:
|
||||||
* Parts of this class are documented in the base interface class, FilesystemFactory.
|
* - agos
|
||||||
|
* - kyra
|
||||||
|
* - m4
|
||||||
|
* - queen
|
||||||
|
* - saga
|
||||||
|
* - scumm
|
||||||
|
* - sword1
|
||||||
|
* - sword2
|
||||||
|
* - touche
|
||||||
|
* - tucker
|
||||||
*/
|
*/
|
||||||
class PSPFilesystemFactory : public FilesystemFactory, public Common::Singleton<PSPFilesystemFactory> {
|
|
||||||
public:
|
|
||||||
virtual AbstractFSNode *makeRootFileNode() const;
|
|
||||||
virtual AbstractFSNode *makeCurrentDirectoryFileNode() const;
|
|
||||||
virtual AbstractFSNode *makeFileNodePath(const Common::String &path) const;
|
|
||||||
|
|
||||||
protected:
|
#ifndef SOUND_MP3_H
|
||||||
PSPFilesystemFactory() {};
|
#define SOUND_MP3_H
|
||||||
|
|
||||||
private:
|
#include "common/types.h"
|
||||||
friend class Common::Singleton<SingletonBaseType>;
|
#include "common/sys.h"
|
||||||
};
|
|
||||||
|
|
||||||
#endif /*PSP_FILESYSTEM_FACTORY_H*/
|
#ifdef USE_MAD
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
class SeekableReadStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Audio {
|
||||||
|
|
||||||
|
class AudioStream;
|
||||||
|
class SeekableAudioStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new SeekableAudioStream from the MP3 data in the given stream.
|
||||||
|
* Allows for seeking (which is why we require a SeekableReadStream).
|
||||||
|
*
|
||||||
|
* @param stream the SeekableReadStream from which to read the MP3 data
|
||||||
|
* @param disposeAfterUse whether to delete the stream after use
|
||||||
|
* @return a new SeekableAudioStream, or NULL, if an error occured
|
||||||
|
*/
|
||||||
|
SeekableAudioStream *makeMP3Stream(
|
||||||
|
Common::SeekableReadStream *stream,
|
||||||
|
DisposeAfterUse::Flag disposeAfterUse);
|
||||||
|
|
||||||
|
} // End of namespace Audio
|
||||||
|
|
||||||
|
#endif // #ifdef USE_MAD
|
||||||
|
#endif // #ifndef SOUND_MP3_H
|
305
sound/decoders/raw.cpp
Normal file
|
@ -0,0 +1,305 @@
|
||||||
|
/* Residual - A 3D game interpreter
|
||||||
|
*
|
||||||
|
* Residual is the legal property of its developers, whose names
|
||||||
|
* are too numerous to list here. Please refer to the AUTHORS
|
||||||
|
* 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$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "common/endian.h"
|
||||||
|
#include "common/stream.h"
|
||||||
|
|
||||||
|
#include "sound/audiostream.h"
|
||||||
|
#include "sound/mixer.h"
|
||||||
|
#include "sound/decoders/raw.h"
|
||||||
|
|
||||||
|
namespace Audio {
|
||||||
|
|
||||||
|
// This used to be an inline template function, but
|
||||||
|
// buggy template function handling in MSVC6 forced
|
||||||
|
// us to go with the macro approach. So far this is
|
||||||
|
// the only template function that MSVC6 seemed to
|
||||||
|
// compile incorrectly. Knock on wood.
|
||||||
|
#define READ_ENDIAN_SAMPLE(is16Bit, isUnsigned, ptr, isLE) \
|
||||||
|
((is16Bit ? (isLE ? READ_LE_UINT16(ptr) : READ_BE_UINT16(ptr)) : (*ptr << 8)) ^ (isUnsigned ? 0x8000 : 0))
|
||||||
|
|
||||||
|
|
||||||
|
#pragma mark -
|
||||||
|
#pragma mark --- RawStream ---
|
||||||
|
#pragma mark -
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a stream, which allows for playing raw PCM data from a stream.
|
||||||
|
* It also features playback of multiple blocks from a given stream.
|
||||||
|
*/
|
||||||
|
template<bool stereo, bool is16Bit, bool isUnsigned, bool isLE>
|
||||||
|
class RawStream : public SeekableAudioStream {
|
||||||
|
|
||||||
|
// Allow backends to override buffer size
|
||||||
|
#ifdef CUSTOM_AUDIO_BUFFER_SIZE
|
||||||
|
static const int32 BUFFER_SIZE = CUSTOM_AUDIO_BUFFER_SIZE;
|
||||||
|
#else
|
||||||
|
static const int32 BUFFER_SIZE = 16384;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
protected:
|
||||||
|
byte *_buffer; ///< Streaming buffer
|
||||||
|
const byte *_ptr; ///< Pointer to current position in stream buffer
|
||||||
|
const int _rate; ///< Sample rate of stream
|
||||||
|
|
||||||
|
Timestamp _playtime; ///< Calculated total play time
|
||||||
|
Common::SeekableReadStream *_stream; ///< Stream to read data from
|
||||||
|
int32 _filePos; ///< Current position in stream
|
||||||
|
int32 _diskLeft; ///< Samples left in stream in current block not yet read to buffer
|
||||||
|
int32 _bufferLeft; ///< Samples left in buffer in current block
|
||||||
|
const DisposeAfterUse::Flag _disposeAfterUse; ///< Indicates whether the stream object should be deleted when this RawStream is destructed
|
||||||
|
|
||||||
|
const RawStreamBlockList _blocks; ///< Audio block list
|
||||||
|
RawStreamBlockList::const_iterator _curBlock; ///< Current audio block number
|
||||||
|
public:
|
||||||
|
RawStream(int rate, DisposeAfterUse::Flag disposeStream, Common::SeekableReadStream *stream, const RawStreamBlockList &blocks)
|
||||||
|
: _rate(rate), _playtime(0, rate), _stream(stream), _disposeAfterUse(disposeStream), _blocks(blocks), _curBlock(_blocks.begin()) {
|
||||||
|
|
||||||
|
assert(_blocks.size() > 0);
|
||||||
|
|
||||||
|
// Allocate streaming buffer
|
||||||
|
if (is16Bit)
|
||||||
|
_buffer = (byte *)malloc(BUFFER_SIZE * sizeof(int16));
|
||||||
|
else
|
||||||
|
_buffer = (byte *)malloc(BUFFER_SIZE * sizeof(byte));
|
||||||
|
|
||||||
|
_ptr = _buffer;
|
||||||
|
_bufferLeft = 0;
|
||||||
|
|
||||||
|
// Set current buffer state, playing first block
|
||||||
|
_filePos = _curBlock->pos;
|
||||||
|
_diskLeft = _curBlock->len;
|
||||||
|
|
||||||
|
// Add up length of all blocks in order to caluclate total play time
|
||||||
|
int32 len = 0;
|
||||||
|
for (RawStreamBlockList::const_iterator i = _blocks.begin(); i != _blocks.end(); ++i) {
|
||||||
|
assert(i->len % (stereo ? 2 : 1) == 0);
|
||||||
|
len += i->len;
|
||||||
|
}
|
||||||
|
|
||||||
|
_playtime = Timestamp(0, len / (stereo ? 2 : 1), rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
virtual ~RawStream() {
|
||||||
|
if (_disposeAfterUse == DisposeAfterUse::YES)
|
||||||
|
delete _stream;
|
||||||
|
|
||||||
|
free(_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
int readBuffer(int16 *buffer, const int numSamples);
|
||||||
|
|
||||||
|
bool isStereo() const { return stereo; }
|
||||||
|
bool endOfData() const { return (_curBlock == _blocks.end()) && (_diskLeft == 0) && (_bufferLeft == 0); }
|
||||||
|
|
||||||
|
int getRate() const { return _rate; }
|
||||||
|
Timestamp getLength() const { return _playtime; }
|
||||||
|
|
||||||
|
bool seek(const Timestamp &where);
|
||||||
|
};
|
||||||
|
|
||||||
|
template<bool stereo, bool is16Bit, bool isUnsigned, bool isLE>
|
||||||
|
int RawStream<stereo, is16Bit, isUnsigned, isLE>::readBuffer(int16 *buffer, const int numSamples) {
|
||||||
|
int oldPos = _stream->pos();
|
||||||
|
bool restoreFilePosition = false;
|
||||||
|
|
||||||
|
int samples = numSamples;
|
||||||
|
|
||||||
|
while (samples > 0 && ((_diskLeft > 0 || _bufferLeft > 0) || _curBlock != _blocks.end())) {
|
||||||
|
// Output samples in the buffer to the output
|
||||||
|
int len = MIN<int>(samples, _bufferLeft);
|
||||||
|
samples -= len;
|
||||||
|
_bufferLeft -= len;
|
||||||
|
|
||||||
|
while (len > 0) {
|
||||||
|
*buffer++ = READ_ENDIAN_SAMPLE(is16Bit, isUnsigned, _ptr, isLE);
|
||||||
|
_ptr += (is16Bit ? 2 : 1);
|
||||||
|
len--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Have we now finished this block? If so, read the next block
|
||||||
|
if ((_bufferLeft == 0) && (_diskLeft == 0) && _curBlock != _blocks.end()) {
|
||||||
|
// Next block
|
||||||
|
++_curBlock;
|
||||||
|
|
||||||
|
if (_curBlock != _blocks.end()) {
|
||||||
|
_filePos = _curBlock->pos;
|
||||||
|
_diskLeft = _curBlock->len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now read more data from disk if there is more to be read
|
||||||
|
if (_bufferLeft == 0 && _diskLeft > 0) {
|
||||||
|
int32 readAmount = MIN(_diskLeft, BUFFER_SIZE);
|
||||||
|
|
||||||
|
// TODO: We should check for both seek and read to success.
|
||||||
|
// If that is not the case, we should probably stop the
|
||||||
|
// stream playback.
|
||||||
|
_stream->seek(_filePos, SEEK_SET);
|
||||||
|
_stream->read(_buffer, readAmount * (is16Bit? 2: 1));
|
||||||
|
|
||||||
|
// Amount of data in buffer is now the amount read in, and
|
||||||
|
// the amount left to read on disk is decreased by the same amount
|
||||||
|
_bufferLeft = readAmount;
|
||||||
|
_diskLeft -= readAmount;
|
||||||
|
_ptr = (byte *)_buffer;
|
||||||
|
_filePos += readAmount * (is16Bit ? 2 : 1);
|
||||||
|
|
||||||
|
// Set this flag now we've used the file, it restores it's
|
||||||
|
// original position.
|
||||||
|
restoreFilePosition = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// In case calling code relies on the position of this stream staying
|
||||||
|
// constant, I restore the location if I've changed it. This is probably
|
||||||
|
// not necessary.
|
||||||
|
if (restoreFilePosition)
|
||||||
|
_stream->seek(oldPos, SEEK_SET);
|
||||||
|
|
||||||
|
return numSamples - samples;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool stereo, bool is16Bit, bool isUnsigned, bool isLE>
|
||||||
|
bool RawStream<stereo, is16Bit, isUnsigned, isLE>::seek(const Timestamp &where) {
|
||||||
|
const uint32 seekSample = convertTimeToStreamPos(where, getRate(), isStereo()).totalNumberOfFrames();
|
||||||
|
uint32 curSample = 0;
|
||||||
|
|
||||||
|
// Search for the disk block in which the specific sample is placed
|
||||||
|
for (_curBlock = _blocks.begin(); _curBlock != _blocks.end(); ++_curBlock) {
|
||||||
|
uint32 nextBlockSample = curSample + _curBlock->len;
|
||||||
|
|
||||||
|
if (nextBlockSample > seekSample)
|
||||||
|
break;
|
||||||
|
|
||||||
|
curSample = nextBlockSample;
|
||||||
|
}
|
||||||
|
|
||||||
|
_filePos = 0;
|
||||||
|
_diskLeft = 0;
|
||||||
|
_bufferLeft = 0;
|
||||||
|
|
||||||
|
if (_curBlock == _blocks.end()) {
|
||||||
|
return ((seekSample - curSample) == (uint32)_curBlock->len);
|
||||||
|
} else {
|
||||||
|
const uint32 offset = seekSample - curSample;
|
||||||
|
|
||||||
|
_filePos = _curBlock->pos + offset * (is16Bit ? 2 : 1);
|
||||||
|
_diskLeft = _curBlock->len - offset;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark -
|
||||||
|
#pragma mark --- Raw stream factories ---
|
||||||
|
#pragma mark -
|
||||||
|
|
||||||
|
/* In the following, we use preprocessor / macro tricks to simplify the code
|
||||||
|
* which instantiates the input streams. We used to use template functions for
|
||||||
|
* this, but MSVC6 / EVC 3-4 (used for WinCE builds) are extremely buggy when it
|
||||||
|
* comes to this feature of C++... so as a compromise we use macros to cut down
|
||||||
|
* on the (source) code duplication a bit.
|
||||||
|
* So while normally macro tricks are said to make maintenance harder, in this
|
||||||
|
* particular case it should actually help it :-)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define MAKE_LINEAR_DISK(STEREO, UNSIGNED) \
|
||||||
|
if (is16Bit) { \
|
||||||
|
if (isLE) \
|
||||||
|
return new RawStream<STEREO, true, UNSIGNED, true>(rate, disposeAfterUse, stream, blockList); \
|
||||||
|
else \
|
||||||
|
return new RawStream<STEREO, true, UNSIGNED, false>(rate, disposeAfterUse, stream, blockList); \
|
||||||
|
} else \
|
||||||
|
return new RawStream<STEREO, false, UNSIGNED, false>(rate, disposeAfterUse, stream, blockList)
|
||||||
|
|
||||||
|
SeekableAudioStream *makeRawStream(Common::SeekableReadStream *stream,
|
||||||
|
const RawStreamBlockList &blockList,
|
||||||
|
int rate,
|
||||||
|
byte flags,
|
||||||
|
DisposeAfterUse::Flag disposeAfterUse) {
|
||||||
|
const bool isStereo = (flags & Audio::FLAG_STEREO) != 0;
|
||||||
|
const bool is16Bit = (flags & Audio::FLAG_16BITS) != 0;
|
||||||
|
const bool isUnsigned = (flags & Audio::FLAG_UNSIGNED) != 0;
|
||||||
|
const bool isLE = (flags & Audio::FLAG_LITTLE_ENDIAN) != 0;
|
||||||
|
|
||||||
|
if (blockList.empty()) {
|
||||||
|
warning("Empty block list passed to makeRawStream");
|
||||||
|
if (disposeAfterUse == DisposeAfterUse::YES)
|
||||||
|
delete stream;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isStereo) {
|
||||||
|
if (isUnsigned) {
|
||||||
|
MAKE_LINEAR_DISK(true, true);
|
||||||
|
} else {
|
||||||
|
MAKE_LINEAR_DISK(true, false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isUnsigned) {
|
||||||
|
MAKE_LINEAR_DISK(false, true);
|
||||||
|
} else {
|
||||||
|
MAKE_LINEAR_DISK(false, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SeekableAudioStream *makeRawStream(Common::SeekableReadStream *stream,
|
||||||
|
int rate, byte flags,
|
||||||
|
DisposeAfterUse::Flag disposeAfterUse) {
|
||||||
|
RawStreamBlockList blocks;
|
||||||
|
RawStreamBlock block;
|
||||||
|
block.pos = 0;
|
||||||
|
|
||||||
|
const bool isStereo = (flags & Audio::FLAG_STEREO) != 0;
|
||||||
|
const bool is16Bit = (flags & Audio::FLAG_16BITS) != 0;
|
||||||
|
|
||||||
|
assert(stream->size() % ((is16Bit ? 2 : 1) * (isStereo ? 2 : 1)) == 0);
|
||||||
|
|
||||||
|
block.len = stream->size() / (is16Bit ? 2 : 1);
|
||||||
|
blocks.push_back(block);
|
||||||
|
|
||||||
|
return makeRawStream(stream, blocks, rate, flags, disposeAfterUse);
|
||||||
|
}
|
||||||
|
|
||||||
|
SeekableAudioStream *makeRawStream(const byte *buffer, uint32 size,
|
||||||
|
int rate, byte flags,
|
||||||
|
DisposeAfterUse::Flag disposeAfterUse) {
|
||||||
|
return makeRawStream(new Common::MemoryReadStream(buffer, size, disposeAfterUse), rate, flags, DisposeAfterUse::YES);
|
||||||
|
}
|
||||||
|
|
||||||
|
SeekableAudioStream *makeRawDiskStream_OLD(Common::SeekableReadStream *stream, RawStreamBlock *block, int numBlocks,
|
||||||
|
int rate, byte flags, DisposeAfterUse::Flag disposeStream) {
|
||||||
|
assert(numBlocks > 0);
|
||||||
|
RawStreamBlockList blocks;
|
||||||
|
for (int i = 0; i < numBlocks; ++i)
|
||||||
|
blocks.push_back(block[i]);
|
||||||
|
|
||||||
|
return makeRawStream(stream, blocks, rate, flags, disposeStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End of namespace Audio
|
153
sound/decoders/raw.h
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
/* Residual - A 3D game interpreter
|
||||||
|
*
|
||||||
|
* Residual is the legal property of its developers, whose names
|
||||||
|
* are too numerous to list here. Please refer to the AUTHORS
|
||||||
|
* 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$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SOUND_RAW_H
|
||||||
|
#define SOUND_RAW_H
|
||||||
|
|
||||||
|
#include "common/sys.h"
|
||||||
|
#include "common/types.h"
|
||||||
|
|
||||||
|
#include "common/list.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace Common { class SeekableReadStream; }
|
||||||
|
|
||||||
|
|
||||||
|
namespace Audio {
|
||||||
|
|
||||||
|
class AudioStream;
|
||||||
|
class SeekableAudioStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Various flags which can be bit-ORed and then passed to
|
||||||
|
* makeRawMemoryStream and some other AudioStream factories
|
||||||
|
* to control their behavior.
|
||||||
|
*
|
||||||
|
* Engine authors are advised not to rely on a certain value or
|
||||||
|
* order of these flags (in particular, do not store them verbatim
|
||||||
|
* in savestates).
|
||||||
|
*/
|
||||||
|
enum RawFlags {
|
||||||
|
/** unsigned samples (default: signed) */
|
||||||
|
FLAG_UNSIGNED = 1 << 0,
|
||||||
|
|
||||||
|
/** sound is 16 bits wide (default: 8bit) */
|
||||||
|
FLAG_16BITS = 1 << 1,
|
||||||
|
|
||||||
|
/** samples are little endian (default: big endian) */
|
||||||
|
FLAG_LITTLE_ENDIAN = 1 << 2,
|
||||||
|
|
||||||
|
/** sound is in stereo (default: mono) */
|
||||||
|
FLAG_STEREO = 1 << 3
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Struct used to define the audio data to be played by a RawStream.
|
||||||
|
*/
|
||||||
|
struct RawStreamBlock {
|
||||||
|
int32 pos; ///< Position in stream of the block (in bytes of course!)
|
||||||
|
int32 len; ///< Length of the block (in raw samples, not sample pairs!)
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List containing all blocks of a raw stream.
|
||||||
|
* @see RawStreamBlock
|
||||||
|
*/
|
||||||
|
typedef Common::List<RawStreamBlock> RawStreamBlockList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an audio stream, which plays from the given buffer.
|
||||||
|
*
|
||||||
|
* @param buffer Buffer to play from.
|
||||||
|
* @param size Size of the buffer in bytes.
|
||||||
|
* @param rate Rate of the sound data.
|
||||||
|
* @param flags Audio flags combination.
|
||||||
|
* @see RawFlags
|
||||||
|
* @param disposeAfterUse Whether to free the buffer after use (with free!).
|
||||||
|
* @return The new SeekableAudioStream (or 0 on failure).
|
||||||
|
*/
|
||||||
|
SeekableAudioStream *makeRawStream(const byte *buffer, uint32 size,
|
||||||
|
int rate, byte flags,
|
||||||
|
DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an audio stream, which plays from the given stream.
|
||||||
|
*
|
||||||
|
* @param stream Stream object to play from.
|
||||||
|
* @param rate Rate of the sound data.
|
||||||
|
* @param flags Audio flags combination.
|
||||||
|
* @see RawFlags
|
||||||
|
* @param disposeAfterUse Whether to delete the stream after use.
|
||||||
|
* @return The new SeekableAudioStream (or 0 on failure).
|
||||||
|
*/
|
||||||
|
SeekableAudioStream *makeRawStream(Common::SeekableReadStream *stream,
|
||||||
|
int rate, byte flags,
|
||||||
|
DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an audio stream, which plays from the given stream.
|
||||||
|
*
|
||||||
|
* @param stream Stream object to play from.
|
||||||
|
* @param blockList List of blocks to play.
|
||||||
|
* @see RawDiskStreamAudioBlock
|
||||||
|
* @see RawStreamBlockList
|
||||||
|
* @param rate Rate of the sound data.
|
||||||
|
* @param flags Audio flags combination.
|
||||||
|
* @see RawFlags
|
||||||
|
* @param disposeAfterUse Whether to delete the stream after use.
|
||||||
|
* @return The new SeekableAudioStream (or 0 on failure).
|
||||||
|
*/
|
||||||
|
SeekableAudioStream *makeRawStream(Common::SeekableReadStream *stream,
|
||||||
|
const RawStreamBlockList &blockList,
|
||||||
|
int rate,
|
||||||
|
byte flags,
|
||||||
|
DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NOTE:
|
||||||
|
* This API is considered deprecated.
|
||||||
|
*
|
||||||
|
* Creates a audio stream, which plays from given stream.
|
||||||
|
*
|
||||||
|
* @param stream Stream to play from
|
||||||
|
* @param block Pointer to an RawStreamBlock array
|
||||||
|
* @see RawStreamBlock
|
||||||
|
* @param numBlocks Number of blocks.
|
||||||
|
* @param rate The rate
|
||||||
|
* @param flags Flags combination.
|
||||||
|
* @see RawFlags
|
||||||
|
* @param disposeStream Whether the "stream" object should be destroyed after playback.
|
||||||
|
* @return The new SeekableAudioStream (or 0 on failure).
|
||||||
|
*/
|
||||||
|
SeekableAudioStream *makeRawDiskStream_OLD(Common::SeekableReadStream *stream,
|
||||||
|
RawStreamBlock *block, int numBlocks,
|
||||||
|
int rate, byte flags,
|
||||||
|
DisposeAfterUse::Flag disposeStream);
|
||||||
|
|
||||||
|
|
||||||
|
} // End of namespace Audio
|
||||||
|
|
||||||
|
#endif
|
258
sound/decoders/vorbis.cpp
Normal file
|
@ -0,0 +1,258 @@
|
||||||
|
/* Residual - A 3D game interpreter
|
||||||
|
*
|
||||||
|
* Residual is the legal property of its developers, whose names
|
||||||
|
* are too numerous to list here. Please refer to the AUTHORS
|
||||||
|
* 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$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "sound/decoders/vorbis.h"
|
||||||
|
|
||||||
|
#ifdef USE_VORBIS
|
||||||
|
|
||||||
|
#include "common/debug.h"
|
||||||
|
#include "common/stream.h"
|
||||||
|
#include "common/util.h"
|
||||||
|
|
||||||
|
#include "sound/audiostream.h"
|
||||||
|
#include "sound/audiocd.h"
|
||||||
|
|
||||||
|
#ifdef USE_TREMOR
|
||||||
|
#ifdef __GP32__ // GP32 uses custom libtremor
|
||||||
|
#include <ivorbisfile.h>
|
||||||
|
#else
|
||||||
|
#include <tremor/ivorbisfile.h>
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#include <vorbis/vorbisfile.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
namespace Audio {
|
||||||
|
|
||||||
|
// These are wrapper functions to allow using a SeekableReadStream object to
|
||||||
|
// provide data to the OggVorbis_File object.
|
||||||
|
|
||||||
|
static size_t read_stream_wrap(void *ptr, size_t size, size_t nmemb, void *datasource) {
|
||||||
|
Common::SeekableReadStream *stream = (Common::SeekableReadStream *)datasource;
|
||||||
|
|
||||||
|
uint32 result = stream->read(ptr, size * nmemb);
|
||||||
|
|
||||||
|
return result / size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int seek_stream_wrap(void *datasource, ogg_int64_t offset, int whence) {
|
||||||
|
Common::SeekableReadStream *stream = (Common::SeekableReadStream *)datasource;
|
||||||
|
stream->seek((int32)offset, whence);
|
||||||
|
return stream->pos();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int close_stream_wrap(void *datasource) {
|
||||||
|
// Do nothing -- we leave it up to the VorbisStream to free memory as appropriate.
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static long tell_stream_wrap(void *datasource) {
|
||||||
|
Common::SeekableReadStream *stream = (Common::SeekableReadStream *)datasource;
|
||||||
|
return stream->pos();
|
||||||
|
}
|
||||||
|
|
||||||
|
static ov_callbacks g_stream_wrap = {
|
||||||
|
read_stream_wrap, seek_stream_wrap, close_stream_wrap, tell_stream_wrap
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#pragma mark -
|
||||||
|
#pragma mark --- Ogg Vorbis stream ---
|
||||||
|
#pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
class VorbisStream : public SeekableAudioStream {
|
||||||
|
protected:
|
||||||
|
Common::SeekableReadStream *_inStream;
|
||||||
|
DisposeAfterUse::Flag _disposeAfterUse;
|
||||||
|
|
||||||
|
bool _isStereo;
|
||||||
|
int _rate;
|
||||||
|
|
||||||
|
Timestamp _length;
|
||||||
|
|
||||||
|
OggVorbis_File _ovFile;
|
||||||
|
|
||||||
|
int16 _buffer[4096];
|
||||||
|
const int16 *_bufferEnd;
|
||||||
|
const int16 *_pos;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// startTime / duration are in milliseconds
|
||||||
|
VorbisStream(Common::SeekableReadStream *inStream, DisposeAfterUse::Flag dispose);
|
||||||
|
~VorbisStream();
|
||||||
|
|
||||||
|
int readBuffer(int16 *buffer, const int numSamples);
|
||||||
|
|
||||||
|
bool endOfData() const { return _pos >= _bufferEnd; }
|
||||||
|
bool isStereo() const { return _isStereo; }
|
||||||
|
int getRate() const { return _rate; }
|
||||||
|
|
||||||
|
bool seek(const Timestamp &where);
|
||||||
|
Timestamp getLength() const { return _length; }
|
||||||
|
protected:
|
||||||
|
bool refill();
|
||||||
|
};
|
||||||
|
|
||||||
|
VorbisStream::VorbisStream(Common::SeekableReadStream *inStream, DisposeAfterUse::Flag dispose) :
|
||||||
|
_inStream(inStream),
|
||||||
|
_disposeAfterUse(dispose),
|
||||||
|
_length(0, 1000),
|
||||||
|
_bufferEnd(_buffer + ARRAYSIZE(_buffer)) {
|
||||||
|
|
||||||
|
int res = ov_open_callbacks(inStream, &_ovFile, NULL, 0, g_stream_wrap);
|
||||||
|
if (res < 0) {
|
||||||
|
warning("Could not create Vorbis stream (%d)", res);
|
||||||
|
_pos = _bufferEnd;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read in initial data
|
||||||
|
if (!refill())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Setup some header information
|
||||||
|
_isStereo = ov_info(&_ovFile, -1)->channels >= 2;
|
||||||
|
_rate = ov_info(&_ovFile, -1)->rate;
|
||||||
|
|
||||||
|
#ifdef USE_TREMOR
|
||||||
|
_length = Timestamp(ov_time_total(&_ovFile, -1), getRate());
|
||||||
|
#else
|
||||||
|
_length = Timestamp(uint32(ov_time_total(&_ovFile, -1) * 1000.0), getRate());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
VorbisStream::~VorbisStream() {
|
||||||
|
ov_clear(&_ovFile);
|
||||||
|
if (_disposeAfterUse == DisposeAfterUse::YES)
|
||||||
|
delete _inStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
int VorbisStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||||
|
int samples = 0;
|
||||||
|
while (samples < numSamples && _pos < _bufferEnd) {
|
||||||
|
const int len = MIN(numSamples - samples, (int)(_bufferEnd - _pos));
|
||||||
|
memcpy(buffer, _pos, len * 2);
|
||||||
|
buffer += len;
|
||||||
|
_pos += len;
|
||||||
|
samples += len;
|
||||||
|
if (_pos >= _bufferEnd) {
|
||||||
|
if (!refill())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return samples;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VorbisStream::seek(const Timestamp &where) {
|
||||||
|
// Vorbisfile uses the sample pair number, thus we always use "false" for the isStereo parameter
|
||||||
|
// of the convertTimeToStreamPos helper.
|
||||||
|
int res = ov_pcm_seek(&_ovFile, convertTimeToStreamPos(where, getRate(), false).totalNumberOfFrames());
|
||||||
|
if (res) {
|
||||||
|
warning("Error seeking in Vorbis stream (%d)", res);
|
||||||
|
_pos = _bufferEnd;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return refill();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VorbisStream::refill() {
|
||||||
|
// Read the samples
|
||||||
|
uint len_left = sizeof(_buffer);
|
||||||
|
char *read_pos = (char *)_buffer;
|
||||||
|
|
||||||
|
while (len_left > 0) {
|
||||||
|
long result;
|
||||||
|
|
||||||
|
#ifdef USE_TREMOR
|
||||||
|
// Tremor ov_read() always returns data as signed 16 bit interleaved PCM
|
||||||
|
// in host byte order. As such, it does not take arguments to request
|
||||||
|
// specific signedness, byte order or bit depth as in Vorbisfile.
|
||||||
|
result = ov_read(&_ovFile, read_pos, len_left,
|
||||||
|
NULL);
|
||||||
|
#else
|
||||||
|
#ifdef SCUMM_BIG_ENDIAN
|
||||||
|
result = ov_read(&_ovFile, read_pos, len_left,
|
||||||
|
1,
|
||||||
|
2, // 16 bit
|
||||||
|
1, // signed
|
||||||
|
NULL);
|
||||||
|
#else
|
||||||
|
result = ov_read(&_ovFile, read_pos, len_left,
|
||||||
|
0,
|
||||||
|
2, // 16 bit
|
||||||
|
1, // signed
|
||||||
|
NULL);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
if (result == OV_HOLE) {
|
||||||
|
// Possibly recoverable, just warn about it
|
||||||
|
warning("Corrupted data in Vorbis file");
|
||||||
|
} else if (result == 0) {
|
||||||
|
//warning("End of file while reading from Vorbis file");
|
||||||
|
//_pos = _bufferEnd;
|
||||||
|
//return false;
|
||||||
|
break;
|
||||||
|
} else if (result < 0) {
|
||||||
|
warning("Error reading from Vorbis stream (%d)", int(result));
|
||||||
|
_pos = _bufferEnd;
|
||||||
|
// Don't delete it yet, that causes problems in
|
||||||
|
// the CD player emulation code.
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
len_left -= result;
|
||||||
|
read_pos += result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_pos = _buffer;
|
||||||
|
_bufferEnd = (int16 *)read_pos;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#pragma mark -
|
||||||
|
#pragma mark --- Ogg Vorbis factory functions ---
|
||||||
|
#pragma mark -
|
||||||
|
|
||||||
|
SeekableAudioStream *makeVorbisStream(
|
||||||
|
Common::SeekableReadStream *stream,
|
||||||
|
DisposeAfterUse::Flag disposeAfterUse) {
|
||||||
|
SeekableAudioStream *s = new VorbisStream(stream, disposeAfterUse);
|
||||||
|
if (s && s->endOfData()) {
|
||||||
|
delete s;
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End of namespace Audio
|
||||||
|
|
||||||
|
#endif // #ifdef USE_VORBIS
|
73
sound/decoders/vorbis.h
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
/* Residual - A 3D game interpreter
|
||||||
|
*
|
||||||
|
* Residual is the legal property of its developers, whose names
|
||||||
|
* are too numerous to list here. Please refer to the AUTHORS
|
||||||
|
* 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$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Sound decoder used in engines:
|
||||||
|
* - agos
|
||||||
|
* - kyra
|
||||||
|
* - m4
|
||||||
|
* - queen
|
||||||
|
* - saga
|
||||||
|
* - scumm
|
||||||
|
* - sword1
|
||||||
|
* - sword2
|
||||||
|
* - touche
|
||||||
|
* - tucker
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SOUND_VORBIS_H
|
||||||
|
#define SOUND_VORBIS_H
|
||||||
|
|
||||||
|
#include "common/types.h"
|
||||||
|
#include "common/sys.h"
|
||||||
|
|
||||||
|
#ifdef USE_VORBIS
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
class SeekableReadStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Audio {
|
||||||
|
|
||||||
|
class AudioStream;
|
||||||
|
class SeekableAudioStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new SeekableAudioStream from the Ogg Vorbis data in the given stream.
|
||||||
|
* Allows for seeking (which is why we require a SeekableReadStream).
|
||||||
|
*
|
||||||
|
* @param stream the SeekableReadStream from which to read the Ogg Vorbis data
|
||||||
|
* @param disposeAfterUse whether to delete the stream after use
|
||||||
|
* @return a new SeekableAudioStream, or NULL, if an error occured
|
||||||
|
*/
|
||||||
|
SeekableAudioStream *makeVorbisStream(
|
||||||
|
Common::SeekableReadStream *stream,
|
||||||
|
DisposeAfterUse::Flag disposeAfterUse);
|
||||||
|
|
||||||
|
} // End of namespace Audio
|
||||||
|
|
||||||
|
#endif // #ifdef USE_VORBIS
|
||||||
|
#endif // #ifndef SOUND_VORBIS_H
|
469
sound/rate_arm.cpp
Normal file
|
@ -0,0 +1,469 @@
|
||||||
|
/* Residual - A 3D game interpreter
|
||||||
|
*
|
||||||
|
* Residual is the legal property of its developers, whose names
|
||||||
|
* are too numerous to list here. Please refer to the AUTHORS
|
||||||
|
* 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$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The code in this file, together with the rate_arm_asm.s file offers
|
||||||
|
* an ARM optimised version of the code in rate.cpp. The operation of this
|
||||||
|
* code should be identical to that of rate.cpp, but faster. The heavy
|
||||||
|
* lifting is done in the assembler file.
|
||||||
|
*
|
||||||
|
* To be as portable as possible we implement the core routines with C
|
||||||
|
* linkage in assembly, and implement the C++ routines that call into
|
||||||
|
* the C here. The C++ symbol mangling varies wildly between compilers,
|
||||||
|
* so this is the simplest way to ensure that the C/C++ combination should
|
||||||
|
* work on as many ARM based platforms as possible.
|
||||||
|
*
|
||||||
|
* Essentially the algorithm herein is the same as that in rate.cpp, so
|
||||||
|
* anyone seeking to understand this should attempt to understand that
|
||||||
|
* first. That code was based in turn on code with Copyright 1998 Fabrice
|
||||||
|
* Bellard - part of SoX (http://sox.sourceforge.net).
|
||||||
|
* Max Horn adapted that code to the needs of ScummVM and partially rewrote
|
||||||
|
* it, in the process removing any use of floating point arithmetic. Various
|
||||||
|
* other improvments over the original code were made.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "sound/audiostream.h"
|
||||||
|
#include "sound/rate.h"
|
||||||
|
#include "sound/mixer.h"
|
||||||
|
#include "common/util.h"
|
||||||
|
|
||||||
|
//#define DEBUG_RATECONV
|
||||||
|
|
||||||
|
namespace Audio {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The precision of the fractional computations used by the rate converter.
|
||||||
|
* Normally you should never have to modify this value.
|
||||||
|
* This stuff is defined in common/frac.h, but we redefine it here as the
|
||||||
|
* ARM routine we call doesn't respect those definitions.
|
||||||
|
*/
|
||||||
|
#define FRAC_BITS 16
|
||||||
|
#define FRAC_ONE (1<<FRAC_BITS)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The size of the intermediate input cache. Bigger values may increase
|
||||||
|
* performance, but only until some point (depends largely on cache size,
|
||||||
|
* target processor and various other factors), at which it will decrease
|
||||||
|
* again.
|
||||||
|
*/
|
||||||
|
#define INTERMEDIATE_BUFFER_SIZE 512
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Audio rate converter based on simple resampling. Used when no
|
||||||
|
* interpolation is required.
|
||||||
|
*
|
||||||
|
* Limited to sampling frequency <= 65535 Hz.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
const st_sample_t *inPtr;
|
||||||
|
int inLen;
|
||||||
|
|
||||||
|
/** position of how far output is ahead of input */
|
||||||
|
/** Holds what would have been opos-ipos */
|
||||||
|
long opos;
|
||||||
|
|
||||||
|
/** fractional position increment in the output stream */
|
||||||
|
long opos_inc;
|
||||||
|
|
||||||
|
st_sample_t inBuf[INTERMEDIATE_BUFFER_SIZE];
|
||||||
|
} SimpleRateDetails;
|
||||||
|
|
||||||
|
template<bool stereo, bool reverseStereo>
|
||||||
|
class SimpleRateConverter : public RateConverter {
|
||||||
|
protected:
|
||||||
|
SimpleRateDetails sr;
|
||||||
|
public:
|
||||||
|
SimpleRateConverter(st_rate_t inrate, st_rate_t outrate);
|
||||||
|
int flow(AudioStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r);
|
||||||
|
int drain(st_sample_t *obuf, st_size_t osamp, st_volume_t vol) {
|
||||||
|
return (ST_SUCCESS);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prepare processing.
|
||||||
|
*/
|
||||||
|
template<bool stereo, bool reverseStereo>
|
||||||
|
SimpleRateConverter<stereo, reverseStereo>::SimpleRateConverter(st_rate_t inrate, st_rate_t outrate) {
|
||||||
|
if (inrate == outrate) {
|
||||||
|
error("Input and Output rates must be different to use rate effect");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((inrate % outrate) != 0) {
|
||||||
|
error("Input rate must be a multiple of Output rate to use rate effect");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inrate >= 65536 || outrate >= 65536) {
|
||||||
|
error("rate effect can only handle rates < 65536");
|
||||||
|
}
|
||||||
|
|
||||||
|
sr.opos = 1;
|
||||||
|
|
||||||
|
/* increment */
|
||||||
|
sr.opos_inc = inrate / outrate;
|
||||||
|
|
||||||
|
sr.inLen = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#ifndef IPHONE
|
||||||
|
#define ARM_SimpleRate_M _ARM_SimpleRate_M
|
||||||
|
#define ARM_SimpleRate_S _ARM_SimpleRate_S
|
||||||
|
#define ARM_SimpleRate_R _ARM_SimpleRate_R
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" st_sample_t *ARM_SimpleRate_M(
|
||||||
|
AudioStream &input,
|
||||||
|
int (*fn)(Audio::AudioStream&,int16*,int),
|
||||||
|
SimpleRateDetails *sr,
|
||||||
|
st_sample_t *obuf,
|
||||||
|
st_size_t osamp,
|
||||||
|
st_volume_t vol_l,
|
||||||
|
st_volume_t vol_r);
|
||||||
|
|
||||||
|
extern "C" st_sample_t *ARM_SimpleRate_S(
|
||||||
|
AudioStream &input,
|
||||||
|
int (*fn)(Audio::AudioStream&,int16*,int),
|
||||||
|
SimpleRateDetails *sr,
|
||||||
|
st_sample_t *obuf,
|
||||||
|
st_size_t osamp,
|
||||||
|
st_volume_t vol_l,
|
||||||
|
st_volume_t vol_r);
|
||||||
|
|
||||||
|
extern "C" st_sample_t *ARM_SimpleRate_R(
|
||||||
|
AudioStream &input,
|
||||||
|
int (*fn)(Audio::AudioStream&,int16*,int),
|
||||||
|
SimpleRateDetails *sr,
|
||||||
|
st_sample_t *obuf,
|
||||||
|
st_size_t osamp,
|
||||||
|
st_volume_t vol_l,
|
||||||
|
st_volume_t vol_r);
|
||||||
|
|
||||||
|
extern "C" int SimpleRate_readFudge(Audio::AudioStream &input,
|
||||||
|
int16 *a, int b)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_RATECONV
|
||||||
|
fprintf(stderr, "Reading ptr=%x n%d\n", a, b);
|
||||||
|
fflush(stderr);
|
||||||
|
#endif
|
||||||
|
return input.readBuffer(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool stereo, bool reverseStereo>
|
||||||
|
int SimpleRateConverter<stereo, reverseStereo>::flow(AudioStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r) {
|
||||||
|
|
||||||
|
#ifdef DEBUG_RATECONV
|
||||||
|
fprintf(stderr, "Simple st=%d rev=%d\n", stereo, reverseStereo);
|
||||||
|
fflush(stderr);
|
||||||
|
#endif
|
||||||
|
st_sample_t *ostart = obuf;
|
||||||
|
|
||||||
|
if (!stereo) {
|
||||||
|
obuf = ARM_SimpleRate_M(input,
|
||||||
|
&SimpleRate_readFudge,
|
||||||
|
&sr,
|
||||||
|
obuf, osamp, vol_l, vol_r);
|
||||||
|
} else if (reverseStereo) {
|
||||||
|
obuf = ARM_SimpleRate_R(input,
|
||||||
|
&SimpleRate_readFudge,
|
||||||
|
&sr,
|
||||||
|
obuf, osamp, vol_l, vol_r);
|
||||||
|
} else {
|
||||||
|
obuf = ARM_SimpleRate_S(input,
|
||||||
|
&SimpleRate_readFudge,
|
||||||
|
&sr,
|
||||||
|
obuf, osamp, vol_l, vol_r);
|
||||||
|
}
|
||||||
|
return (obuf-ostart)/2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Audio rate converter based on simple linear Interpolation.
|
||||||
|
*
|
||||||
|
* The use of fractional increment allows us to use no buffer. It
|
||||||
|
* avoid the problems at the end of the buffer we had with the old
|
||||||
|
* method which stored a possibly big buffer of size
|
||||||
|
* lcm(in_rate,out_rate).
|
||||||
|
*
|
||||||
|
* Limited to sampling frequency <= 65535 Hz.
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const st_sample_t *inPtr;
|
||||||
|
int inLen;
|
||||||
|
|
||||||
|
/** position of how far output is ahead of input */
|
||||||
|
/** Holds what would have been opos-ipos<<16 + opos_frac */
|
||||||
|
long opos;
|
||||||
|
|
||||||
|
/** integer position increment in the output stream */
|
||||||
|
long opos_inc;
|
||||||
|
|
||||||
|
/** current sample(s) in the input stream (left/right channel) */
|
||||||
|
st_sample_t icur[2];
|
||||||
|
/** last sample(s) in the input stream (left/right channel) */
|
||||||
|
/** Note, these are deliberately ints, not st_sample_t's */
|
||||||
|
int32 ilast[2];
|
||||||
|
|
||||||
|
st_sample_t inBuf[INTERMEDIATE_BUFFER_SIZE];
|
||||||
|
} LinearRateDetails;
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#ifndef IPHONE
|
||||||
|
#define ARM_LinearRate_M _ARM_LinearRate_M
|
||||||
|
#define ARM_LinearRate_S _ARM_LinearRate_S
|
||||||
|
#define ARM_LinearRate_R _ARM_LinearRate_R
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" st_sample_t *ARM_LinearRate_M(
|
||||||
|
AudioStream &input,
|
||||||
|
int (*fn)(Audio::AudioStream&,int16*,int),
|
||||||
|
LinearRateDetails *lr,
|
||||||
|
st_sample_t *obuf,
|
||||||
|
st_size_t osamp,
|
||||||
|
st_volume_t vol_l,
|
||||||
|
st_volume_t vol_r);
|
||||||
|
|
||||||
|
extern "C" st_sample_t *ARM_LinearRate_S(
|
||||||
|
AudioStream &input,
|
||||||
|
int (*fn)(Audio::AudioStream&,int16*,int),
|
||||||
|
LinearRateDetails *lr,
|
||||||
|
st_sample_t *obuf,
|
||||||
|
st_size_t osamp,
|
||||||
|
st_volume_t vol_l,
|
||||||
|
st_volume_t vol_r);
|
||||||
|
|
||||||
|
extern "C" st_sample_t *ARM_LinearRate_R(
|
||||||
|
AudioStream &input,
|
||||||
|
int (*fn)(Audio::AudioStream&,int16*,int),
|
||||||
|
LinearRateDetails *lr,
|
||||||
|
st_sample_t *obuf,
|
||||||
|
st_size_t osamp,
|
||||||
|
st_volume_t vol_l,
|
||||||
|
st_volume_t vol_r);
|
||||||
|
|
||||||
|
template<bool stereo, bool reverseStereo>
|
||||||
|
class LinearRateConverter : public RateConverter {
|
||||||
|
protected:
|
||||||
|
LinearRateDetails lr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
LinearRateConverter(st_rate_t inrate, st_rate_t outrate);
|
||||||
|
int flow(AudioStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r);
|
||||||
|
int drain(st_sample_t *obuf, st_size_t osamp, st_volume_t vol) {
|
||||||
|
return (ST_SUCCESS);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prepare processing.
|
||||||
|
*/
|
||||||
|
template<bool stereo, bool reverseStereo>
|
||||||
|
LinearRateConverter<stereo, reverseStereo>::LinearRateConverter(st_rate_t inrate, st_rate_t outrate) {
|
||||||
|
unsigned long incr;
|
||||||
|
|
||||||
|
if (inrate == outrate) {
|
||||||
|
error("Input and Output rates must be different to use rate effect");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inrate >= 65536 || outrate >= 65536) {
|
||||||
|
error("rate effect can only handle rates < 65536");
|
||||||
|
}
|
||||||
|
|
||||||
|
lr.opos = FRAC_ONE;
|
||||||
|
|
||||||
|
/* increment */
|
||||||
|
incr = (inrate << FRAC_BITS) / outrate;
|
||||||
|
|
||||||
|
lr.opos_inc = incr;
|
||||||
|
|
||||||
|
lr.ilast[0] = lr.ilast[1] = 32768;
|
||||||
|
lr.icur[0] = lr.icur[1] = 0;
|
||||||
|
|
||||||
|
lr.inLen = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Processed signed long samples from ibuf to obuf.
|
||||||
|
* Return number of sample pairs processed.
|
||||||
|
*/
|
||||||
|
template<bool stereo, bool reverseStereo>
|
||||||
|
int LinearRateConverter<stereo, reverseStereo>::flow(AudioStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r) {
|
||||||
|
|
||||||
|
#ifdef DEBUG_RATECONV
|
||||||
|
fprintf(stderr, "Linear st=%d rev=%d\n", stereo, reverseStereo);
|
||||||
|
fflush(stderr);
|
||||||
|
#endif
|
||||||
|
st_sample_t *ostart = obuf;
|
||||||
|
|
||||||
|
if (!stereo) {
|
||||||
|
obuf = ARM_LinearRate_M(input,
|
||||||
|
&SimpleRate_readFudge,
|
||||||
|
&lr,
|
||||||
|
obuf, osamp, vol_l, vol_r);
|
||||||
|
} else if (reverseStereo) {
|
||||||
|
obuf = ARM_LinearRate_R(input,
|
||||||
|
&SimpleRate_readFudge,
|
||||||
|
&lr,
|
||||||
|
obuf, osamp, vol_l, vol_r);
|
||||||
|
} else {
|
||||||
|
obuf = ARM_LinearRate_S(input,
|
||||||
|
&SimpleRate_readFudge,
|
||||||
|
&lr,
|
||||||
|
obuf, osamp, vol_l, vol_r);
|
||||||
|
}
|
||||||
|
return (obuf-ostart)/2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple audio rate converter for the case that the inrate equals the outrate.
|
||||||
|
*/
|
||||||
|
extern "C" {
|
||||||
|
#ifndef IPHONE
|
||||||
|
#define ARM_CopyRate_M _ARM_CopyRate_M
|
||||||
|
#define ARM_CopyRate_S _ARM_CopyRate_S
|
||||||
|
#define ARM_CopyRate_R _ARM_CopyRate_R
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" st_sample_t *ARM_CopyRate_M(
|
||||||
|
st_size_t len,
|
||||||
|
st_sample_t *obuf,
|
||||||
|
st_volume_t vol_l,
|
||||||
|
st_volume_t vol_r,
|
||||||
|
st_sample_t *_buffer);
|
||||||
|
|
||||||
|
extern "C" st_sample_t *ARM_CopyRate_S(
|
||||||
|
st_size_t len,
|
||||||
|
st_sample_t *obuf,
|
||||||
|
st_volume_t vol_l,
|
||||||
|
st_volume_t vol_r,
|
||||||
|
st_sample_t *_buffer);
|
||||||
|
|
||||||
|
extern "C" st_sample_t *ARM_CopyRate_R(
|
||||||
|
st_size_t len,
|
||||||
|
st_sample_t *obuf,
|
||||||
|
st_volume_t vol_l,
|
||||||
|
st_volume_t vol_r,
|
||||||
|
st_sample_t *_buffer);
|
||||||
|
|
||||||
|
|
||||||
|
template<bool stereo, bool reverseStereo>
|
||||||
|
class CopyRateConverter : public RateConverter {
|
||||||
|
st_sample_t *_buffer;
|
||||||
|
st_size_t _bufferSize;
|
||||||
|
public:
|
||||||
|
CopyRateConverter() : _buffer(0), _bufferSize(0) {}
|
||||||
|
~CopyRateConverter() {
|
||||||
|
free(_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int flow(AudioStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r) {
|
||||||
|
assert(input.isStereo() == stereo);
|
||||||
|
|
||||||
|
#ifdef DEBUG_RATECONV
|
||||||
|
fprintf(stderr, "Copy st=%d rev=%d\n", stereo, reverseStereo);
|
||||||
|
fflush(stderr);
|
||||||
|
#endif
|
||||||
|
st_size_t len;
|
||||||
|
st_sample_t *ostart = obuf;
|
||||||
|
|
||||||
|
if (stereo)
|
||||||
|
osamp *= 2;
|
||||||
|
|
||||||
|
// Reallocate temp buffer, if necessary
|
||||||
|
if (osamp > _bufferSize) {
|
||||||
|
free(_buffer);
|
||||||
|
_buffer = (st_sample_t *)malloc(osamp * 2);
|
||||||
|
_bufferSize = osamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read up to 'osamp' samples into our temporary buffer
|
||||||
|
len = input.readBuffer(_buffer, osamp);
|
||||||
|
if (len <= 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Mix the data into the output buffer
|
||||||
|
if (stereo && reverseStereo)
|
||||||
|
obuf = ARM_CopyRate_R(len, obuf, vol_l, vol_r, _buffer);
|
||||||
|
else if (stereo)
|
||||||
|
obuf = ARM_CopyRate_S(len, obuf, vol_l, vol_r, _buffer);
|
||||||
|
else
|
||||||
|
obuf = ARM_CopyRate_M(len, obuf, vol_l, vol_r, _buffer);
|
||||||
|
|
||||||
|
return (obuf-ostart)/2;
|
||||||
|
}
|
||||||
|
virtual int drain(st_sample_t *obuf, st_size_t osamp, st_volume_t vol) {
|
||||||
|
return (ST_SUCCESS);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create and return a RateConverter object for the specified input and output rates.
|
||||||
|
*/
|
||||||
|
RateConverter *makeRateConverter(st_rate_t inrate, st_rate_t outrate, bool stereo, bool reverseStereo) {
|
||||||
|
if (inrate != outrate) {
|
||||||
|
if ((inrate % outrate) == 0) {
|
||||||
|
if (stereo) {
|
||||||
|
if (reverseStereo)
|
||||||
|
return new SimpleRateConverter<true, true>(inrate, outrate);
|
||||||
|
else
|
||||||
|
return new SimpleRateConverter<true, false>(inrate, outrate);
|
||||||
|
} else
|
||||||
|
return new SimpleRateConverter<false, false>(inrate, outrate);
|
||||||
|
} else {
|
||||||
|
if (stereo) {
|
||||||
|
if (reverseStereo)
|
||||||
|
return new LinearRateConverter<true, true>(inrate, outrate);
|
||||||
|
else
|
||||||
|
return new LinearRateConverter<true, false>(inrate, outrate);
|
||||||
|
} else
|
||||||
|
return new LinearRateConverter<false, false>(inrate, outrate);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (stereo) {
|
||||||
|
if (reverseStereo)
|
||||||
|
return new CopyRateConverter<true, true>();
|
||||||
|
else
|
||||||
|
return new CopyRateConverter<true, false>();
|
||||||
|
} else
|
||||||
|
return new CopyRateConverter<false, false>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End of namespace Audio
|
717
sound/rate_arm_asm.s
Normal file
|
@ -0,0 +1,717 @@
|
||||||
|
@ Residual - A 3D game interpreter
|
||||||
|
@
|
||||||
|
@ Residual is the legal property of its developers, whose names
|
||||||
|
@ are too numerous to list here. Please refer to the AUTHORS
|
||||||
|
@ 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$
|
||||||
|
@ $Id$
|
||||||
|
@
|
||||||
|
@ @author Robin Watts (robin@wss.co.uk)
|
||||||
|
@
|
||||||
|
@ This file, together with rate_arm.cpp, provides an ARM optimised version
|
||||||
|
@ of rate.cpp. The algorithm is essentially the same as that within rate.cpp
|
||||||
|
@ so to understand this file you should understand rate.cpp first.
|
||||||
|
|
||||||
|
.text
|
||||||
|
|
||||||
|
.global _ARM_CopyRate_M
|
||||||
|
.global _ARM_CopyRate_S
|
||||||
|
.global _ARM_CopyRate_R
|
||||||
|
.global _ARM_SimpleRate_M
|
||||||
|
.global _ARM_SimpleRate_S
|
||||||
|
.global _ARM_SimpleRate_R
|
||||||
|
.global _ARM_LinearRate_M
|
||||||
|
.global _ARM_LinearRate_S
|
||||||
|
.global _ARM_LinearRate_R
|
||||||
|
|
||||||
|
_ARM_CopyRate_M:
|
||||||
|
@ r0 = len
|
||||||
|
@ r1 = obuf
|
||||||
|
@ r2 = vol_l
|
||||||
|
@ r3 = vol_r
|
||||||
|
@ <> = ptr
|
||||||
|
LDR r12,[r13]
|
||||||
|
STMFD r13!,{r4-r7,r14}
|
||||||
|
|
||||||
|
MOV r14,#0 @ r14= 0
|
||||||
|
ORR r2, r2, r2, LSL #8 @ r2 = vol_l as 16 bits
|
||||||
|
ORR r3, r3, r3, LSL #8 @ r3 = vol_r as 16 bits
|
||||||
|
CopyRate_M_loop:
|
||||||
|
LDRSH r5, [r12], #2 @ r5 = tmp0 = tmp1 = *ptr++
|
||||||
|
LDRSH r6, [r1] @ r6 = obuf[0]
|
||||||
|
LDRSH r7, [r1, #2] @ r7 = obuf[1]
|
||||||
|
MUL r4, r2, r5 @ r4 = tmp0*vol_l
|
||||||
|
MUL r5, r3, r5 @ r5 = tmp1*vol_r
|
||||||
|
|
||||||
|
ADDS r6, r4, r6, LSL #16 @ r6 = obuf[0]<<16 + tmp0*vol_l
|
||||||
|
RSCVS r6, r14,#0x80000000 @ Clamp r6
|
||||||
|
ADDS r7, r5, r7, LSL #16 @ r7 = obuf[1]<<16 + tmp1*vol_r
|
||||||
|
RSCVS r7, r14,#0x80000000 @ Clamp r7
|
||||||
|
|
||||||
|
MOV r6, r6, LSR #16 @ Shift back to halfword
|
||||||
|
MOV r7, r7, LSR #16 @ Shift back to halfword
|
||||||
|
|
||||||
|
STRH r6, [r1], #2 @ Store output value
|
||||||
|
STRH r7, [r1], #2 @ Store output value
|
||||||
|
|
||||||
|
SUBS r0,r0,#1 @ len--
|
||||||
|
BGT CopyRate_M_loop @ and loop
|
||||||
|
|
||||||
|
MOV r0, r1 @ return obuf
|
||||||
|
|
||||||
|
LDMFD r13!,{r4-r7,PC}
|
||||||
|
|
||||||
|
_ARM_CopyRate_S:
|
||||||
|
@ r0 = len
|
||||||
|
@ r1 = obuf
|
||||||
|
@ r2 = vol_l
|
||||||
|
@ r3 = vol_r
|
||||||
|
@ <> = ptr
|
||||||
|
LDR r12,[r13]
|
||||||
|
STMFD r13!,{r4-r7,r14}
|
||||||
|
|
||||||
|
MOV r14,#0 @ r14= 0
|
||||||
|
ORR r2, r2, r2, LSL #8 @ r2 = vol_l as 16 bits
|
||||||
|
ORR r3, r3, r3, LSL #8 @ r3 = vol_r as 16 bits
|
||||||
|
CopyRate_S_loop:
|
||||||
|
LDRSH r4, [r12],#2 @ r4 = tmp0 = *ptr++
|
||||||
|
LDRSH r5, [r12],#2 @ r5 = tmp1 = *ptr++
|
||||||
|
LDRSH r6, [r1] @ r6 = obuf[0]
|
||||||
|
LDRSH r7, [r1,#2] @ r7 = obuf[1]
|
||||||
|
MUL r4, r2, r4 @ r5 = tmp0*vol_l
|
||||||
|
MUL r5, r3, r5 @ r6 = tmp1*vol_r
|
||||||
|
|
||||||
|
ADDS r6, r4, r6, LSL #16 @ r6 = obuf[0]<<16 + tmp0*vol_l
|
||||||
|
RSCVS r6, r14,#0x80000000 @ Clamp r6
|
||||||
|
ADDS r7, r5, r7, LSL #16 @ r7 = obuf[1]<<16 + tmp1*vol_r
|
||||||
|
RSCVS r7, r14,#0x80000000 @ Clamp r7
|
||||||
|
|
||||||
|
MOV r6, r6, LSR #16 @ Shift back to halfword
|
||||||
|
MOV r7, r7, LSR #16 @ Shift back to halfword
|
||||||
|
|
||||||
|
STRH r6, [r1],#2 @ Store output value
|
||||||
|
STRH r7, [r1],#2 @ Store output value
|
||||||
|
|
||||||
|
SUBS r0,r0,#2 @ len -= 2
|
||||||
|
BGT CopyRate_S_loop @ and loop
|
||||||
|
|
||||||
|
MOV r0, r1 @ return obuf
|
||||||
|
|
||||||
|
LDMFD r13!,{r4-r7,PC}
|
||||||
|
|
||||||
|
_ARM_CopyRate_R:
|
||||||
|
@ r0 = len
|
||||||
|
@ r1 = obuf
|
||||||
|
@ r2 = vol_l
|
||||||
|
@ r3 = vol_r
|
||||||
|
@ <> = ptr
|
||||||
|
LDR r12,[r13]
|
||||||
|
STMFD r13!,{r4-r7,r14}
|
||||||
|
|
||||||
|
MOV r14,#0 @ r14= 0
|
||||||
|
ORR r2, r2, r2, LSL #8 @ r2 = vol_l as 16 bits
|
||||||
|
ORR r3, r3, r3, LSL #8 @ r3 = vol_r as 16 bits
|
||||||
|
CopyRate_R_loop:
|
||||||
|
LDRSH r5, [r12],#2 @ r5 = tmp1 = *ptr++
|
||||||
|
LDRSH r4, [r12],#2 @ r4 = tmp0 = *ptr++
|
||||||
|
LDRSH r6, [r1] @ r6 = obuf[0]
|
||||||
|
LDRSH r7, [r1,#2] @ r7 = obuf[1]
|
||||||
|
MUL r4, r2, r4 @ r4 = tmp0*vol_l
|
||||||
|
MUL r5, r3, r5 @ r5 = tmp1*vol_r
|
||||||
|
|
||||||
|
ADDS r6, r4, r6, LSL #16 @ r6 = obuf[0]<<16 + tmp0*vol_l
|
||||||
|
RSCVS r6, r14,#0x80000000 @ Clamp r6
|
||||||
|
ADDS r7, r5, r7, LSL #16 @ r7 = obuf[1]<<16 + tmp1*vol_r
|
||||||
|
RSCVS r7, r14,#0x80000000 @ Clamp r7
|
||||||
|
|
||||||
|
MOV r6, r6, LSR #16 @ Shift back to halfword
|
||||||
|
MOV r7, r7, LSR #16 @ Shift back to halfword
|
||||||
|
|
||||||
|
STRH r6, [r1],#2 @ Store output value
|
||||||
|
STRH r7, [r1],#2 @ Store output value
|
||||||
|
|
||||||
|
SUBS r0,r0,#2 @ len -= 2
|
||||||
|
BGT CopyRate_R_loop @ and loop
|
||||||
|
|
||||||
|
MOV r0, r1 @ return obuf
|
||||||
|
|
||||||
|
LDMFD r13!,{r4-r7,PC}
|
||||||
|
|
||||||
|
_ARM_SimpleRate_M:
|
||||||
|
@ r0 = AudioStream &input
|
||||||
|
@ r1 = input.readBuffer
|
||||||
|
@ r2 = input->sr
|
||||||
|
@ r3 = obuf
|
||||||
|
@ <> = osamp
|
||||||
|
@ <> = vol_l
|
||||||
|
@ <> = vol_r
|
||||||
|
MOV r12,r13
|
||||||
|
STMFD r13!,{r0-r2,r4-r8,r10-r11,r14}
|
||||||
|
LDMFD r12,{r11,r12,r14} @ r11= osamp
|
||||||
|
@ r12= vol_l
|
||||||
|
@ r14= vol_r
|
||||||
|
LDMIA r2,{r0,r1,r2,r8} @ r0 = inPtr
|
||||||
|
@ r1 = inLen
|
||||||
|
@ r2 = opos
|
||||||
|
@ r8 = opos_inc
|
||||||
|
CMP r11,#0 @ if (osamp <= 0)
|
||||||
|
BLE SimpleRate_M_end @ bale
|
||||||
|
MOV r10,#0
|
||||||
|
ORR r12,r12,r12,LSL #8 @ r12= vol_l as 16 bits
|
||||||
|
ORR r14,r14,r14,LSL #8 @ r14= vol_r as 16 bits
|
||||||
|
SimpleRate_M_loop:
|
||||||
|
SUBS r1, r1, #1 @ r1 = inLen -= 1
|
||||||
|
BLT SimpleRate_M_read
|
||||||
|
SUBS r2, r2, #1 @ r2 = opos--
|
||||||
|
ADDGE r0, r0, #2 @ if (r2 >= 0) { sr.inPtr++
|
||||||
|
BGE SimpleRate_M_loop @ and loop }
|
||||||
|
SimpleRate_M_read_return:
|
||||||
|
LDRSH r5, [r0],#2 @ r5 = tmp1 = *inPtr++
|
||||||
|
LDRSH r6, [r3] @ r6 = obuf[0]
|
||||||
|
LDRSH r7, [r3,#2] @ r7 = obuf[1]
|
||||||
|
ADD r2, r2, r8 @ r2 = opos += opos_inc
|
||||||
|
MUL r4, r12,r5 @ r4 = tmp0*vol_l
|
||||||
|
MUL r5, r14,r5 @ r5 = tmp1*vol_r
|
||||||
|
|
||||||
|
ADDS r6, r4, r6, LSL #16 @ r6 = obuf[0]<<16 + tmp0*vol_l
|
||||||
|
RSCVS r6, r10,#0x80000000 @ Clamp r6
|
||||||
|
ADDS r7, r5, r7, LSL #16 @ r7 = obuf[1]<<16 + tmp1*vol_r
|
||||||
|
RSCVS r7, r10,#0x80000000 @ Clamp r7
|
||||||
|
|
||||||
|
MOV r6, r6, LSR #16 @ Shift back to halfword
|
||||||
|
MOV r7, r7, LSR #16 @ Shift back to halfword
|
||||||
|
|
||||||
|
STRH r6, [r3],#2 @ Store output value
|
||||||
|
STRH r7, [r3],#2 @ Store output value
|
||||||
|
|
||||||
|
SUBS r11,r11,#1 @ len--
|
||||||
|
BGT SimpleRate_M_loop @ and loop
|
||||||
|
SimpleRate_M_end:
|
||||||
|
LDR r14,[r13,#8] @ r14 = sr
|
||||||
|
ADD r13,r13,#12 @ Skip over r0-r2 on stack
|
||||||
|
STMIA r14,{r0,r1,r2} @ Store back updated values
|
||||||
|
|
||||||
|
MOV r0, r3 @ return obuf
|
||||||
|
|
||||||
|
LDMFD r13!,{r4-r8,r10-r11,PC}
|
||||||
|
SimpleRate_M_read:
|
||||||
|
LDR r0, [r13,#8] @ r0 = sr (8 = 4*2)
|
||||||
|
ADD r0, r0, #16 @ r0 = inPtr = inBuf
|
||||||
|
.ifdef PALMOS_MODE
|
||||||
|
LDR r10,[r13,#4*8] @ restore r10
|
||||||
|
.endif
|
||||||
|
STMFD r13!,{r0,r2-r3,r12,r14}
|
||||||
|
|
||||||
|
MOV r1, r0 @ r1 = inBuf
|
||||||
|
LDR r0, [r13,#20] @ r0 = AudioStream & input (20 = 4*5)
|
||||||
|
MOV r2, #512 @ r2 = ARRAYSIZE(inBuf)
|
||||||
|
|
||||||
|
@ Calling back into C++ here. WinCE is fairly easy about such things
|
||||||
|
@ but other OS are more awkward. r9 is preserved for Symbian, and
|
||||||
|
@ we have 3+8+5 = 16 things on the stack (an even number).
|
||||||
|
MOV r14,PC
|
||||||
|
LDR PC,[r13,#24] @ inLen = input.readBuffer(inBuf,512) (24 = 4*6)
|
||||||
|
SUBS r1, r0, #1 @ r1 = inLen-1
|
||||||
|
LDMFD r13!,{r0,r2-r3,r12,r14}
|
||||||
|
BLT SimpleRate_M_end
|
||||||
|
.ifdef PALMOS_MODE
|
||||||
|
MOV r10,#0
|
||||||
|
.endif
|
||||||
|
SUBS r2, r2, #1 @ r2 = opos--
|
||||||
|
ADDGE r0, r0, #2 @ if (r2 >= 0) { sr.inPtr++
|
||||||
|
BGE SimpleRate_M_loop @ and loop }
|
||||||
|
B SimpleRate_M_read_return
|
||||||
|
|
||||||
|
|
||||||
|
_ARM_SimpleRate_S:
|
||||||
|
@ r0 = AudioStream &input
|
||||||
|
@ r1 = input.readBuffer
|
||||||
|
@ r2 = input->sr
|
||||||
|
@ r3 = obuf
|
||||||
|
@ <> = osamp
|
||||||
|
@ <> = vol_l
|
||||||
|
@ <> = vol_r
|
||||||
|
MOV r12,r13
|
||||||
|
STMFD r13!,{r0-r2,r4-r8,r10-r11,r14}
|
||||||
|
LDMFD r12,{r11,r12,r14} @ r11= osamp
|
||||||
|
@ r12= vol_l
|
||||||
|
@ r14= vol_r
|
||||||
|
LDMIA r2,{r0,r1,r2,r8} @ r0 = inPtr
|
||||||
|
@ r1 = inLen
|
||||||
|
@ r2 = opos
|
||||||
|
@ r8 = opos_inc
|
||||||
|
CMP r11,#0 @ if (osamp <= 0)
|
||||||
|
BLE SimpleRate_S_end @ bale
|
||||||
|
MOV r10,#0
|
||||||
|
ORR r12,r12,r12,LSL #8 @ r12= vol_l as 16 bits
|
||||||
|
ORR r14,r14,r14,LSL #8 @ r14= vol_r as 16 bits
|
||||||
|
SimpleRate_S_loop:
|
||||||
|
SUBS r1, r1, #2 @ r1 = inLen -= 2
|
||||||
|
BLT SimpleRate_S_read
|
||||||
|
SUBS r2, r2, #1 @ r2 = opos--
|
||||||
|
ADDGE r0, r0, #4 @ if (r2 >= 0) { sr.inPtr += 2
|
||||||
|
BGE SimpleRate_S_loop @ and loop }
|
||||||
|
SimpleRate_S_read_return:
|
||||||
|
LDRSH r4, [r0],#2 @ r4 = tmp0 = *inPtr++
|
||||||
|
LDRSH r5, [r0],#2 @ r5 = tmp1 = *inPtr++
|
||||||
|
LDRSH r6, [r3] @ r6 = obuf[0]
|
||||||
|
LDRSH r7, [r3,#2] @ r7 = obuf[1]
|
||||||
|
ADD r2, r2, r8 @ r2 = opos += opos_inc
|
||||||
|
MUL r4, r12,r4 @ r5 = tmp0*vol_l
|
||||||
|
MUL r5, r14,r5 @ r6 = tmp1*vol_r
|
||||||
|
|
||||||
|
ADDS r6, r4, r6, LSL #16 @ r6 = obuf[0]<<16 + tmp0*vol_l
|
||||||
|
RSCVS r6, r10,#0x80000000 @ Clamp r6
|
||||||
|
ADDS r7, r5, r7, LSL #16 @ r7 = obuf[1]<<16 + tmp1*vol_r
|
||||||
|
RSCVS r7, r10,#0x80000000 @ Clamp r7
|
||||||
|
|
||||||
|
MOV r6, r6, LSR #16 @ Shift back to halfword
|
||||||
|
MOV r7, r7, LSR #16 @ Shift back to halfword
|
||||||
|
|
||||||
|
STRH r6, [r3],#2 @ Store output value
|
||||||
|
STRH r7, [r3],#2 @ Store output value
|
||||||
|
|
||||||
|
SUBS r11,r11,#1 @ osamp--
|
||||||
|
BGT SimpleRate_S_loop @ and loop
|
||||||
|
SimpleRate_S_end:
|
||||||
|
LDR r14,[r13,#8] @ r14 = sr
|
||||||
|
ADD r13,r13,#12 @ skip over r0-r2 on stack
|
||||||
|
STMIA r14,{r0,r1,r2} @ store back updated values
|
||||||
|
MOV r0, r3 @ return obuf
|
||||||
|
LDMFD r13!,{r4-r8,r10-r11,PC}
|
||||||
|
SimpleRate_S_read:
|
||||||
|
LDR r0, [r13,#8] @ r0 = sr (8 = 4*2)
|
||||||
|
ADD r0, r0, #16 @ r0 = inPtr = inBuf
|
||||||
|
.ifdef PALMOS_MODE
|
||||||
|
LDR r10,[r13,#4*8] @ restore r10
|
||||||
|
.endif
|
||||||
|
STMFD r13!,{r0,r2-r3,r12,r14}
|
||||||
|
MOV r1, r0 @ r1 = inBuf
|
||||||
|
LDR r0, [r13,#20] @ r0 = AudioStream & input (20 = 4*5)
|
||||||
|
MOV r2, #512 @ r2 = ARRAYSIZE(inBuf)
|
||||||
|
|
||||||
|
@ Calling back into C++ here. WinCE is fairly easy about such things
|
||||||
|
@ but other OS are more awkward. r9 is preserved for Symbian, and
|
||||||
|
@ we have 3+8+5 = 16 things on the stack (an even number).
|
||||||
|
MOV r14,PC
|
||||||
|
LDR PC,[r13,#24] @ inLen = input.readBuffer(inBuf,512) (24 = 4*6)
|
||||||
|
SUBS r1, r0, #2 @ r1 = inLen-2
|
||||||
|
LDMFD r13!,{r0,r2-r3,r12,r14}
|
||||||
|
BLT SimpleRate_S_end
|
||||||
|
.ifdef PALMOS_MODE
|
||||||
|
MOV r10,#0
|
||||||
|
.endif
|
||||||
|
SUBS r2, r2, #1 @ r2 = opos--
|
||||||
|
ADDGE r0, r0, #4 @ if (r2 >= 0) { sr.inPtr += 2
|
||||||
|
BGE SimpleRate_S_loop @ and loop }
|
||||||
|
B SimpleRate_S_read_return
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_ARM_SimpleRate_R:
|
||||||
|
@ r0 = AudioStream &input
|
||||||
|
@ r1 = input.readBuffer
|
||||||
|
@ r2 = input->sr
|
||||||
|
@ r3 = obuf
|
||||||
|
@ <> = osamp
|
||||||
|
@ <> = vol_l
|
||||||
|
@ <> = vol_r
|
||||||
|
MOV r12,r13
|
||||||
|
STMFD r13!,{r0-r2,r4-r8,r10-r11,r14}
|
||||||
|
LDMFD r12,{r11,r12,r14} @ r11= osamp
|
||||||
|
@ r12= vol_l
|
||||||
|
@ r14= vol_r
|
||||||
|
LDMIA r2,{r0,r1,r2,r8} @ r0 = inPtr
|
||||||
|
@ r1 = inLen
|
||||||
|
@ r2 = opos
|
||||||
|
@ r8 = opos_inc
|
||||||
|
CMP r11,#0 @ if (osamp <= 0)
|
||||||
|
BLE SimpleRate_R_end @ bale
|
||||||
|
MOV r10,#0
|
||||||
|
ORR r12,r12,r12,LSL #8 @ r12= vol_l as 16 bits
|
||||||
|
ORR r14,r14,r14,LSL #8 @ r14= vol_r as 16 bits
|
||||||
|
SimpleRate_R_loop:
|
||||||
|
SUBS r1, r1, #2 @ r1 = inLen -= 2
|
||||||
|
BLT SimpleRate_R_read
|
||||||
|
SUBS r2, r2, #1 @ r2 = opos--
|
||||||
|
ADDGE r0, r0, #4 @ if (r2 >= 0) { sr.inPtr += 2
|
||||||
|
BGE SimpleRate_R_loop @ and loop }
|
||||||
|
SimpleRate_R_read_return:
|
||||||
|
LDRSH r5, [r0],#2 @ r5 = tmp0 = *inPtr++
|
||||||
|
LDRSH r4, [r0],#2 @ r4 = tmp1 = *inPtr++
|
||||||
|
LDRSH r6, [r3] @ r6 = obuf[0]
|
||||||
|
LDRSH r7, [r3,#2] @ r7 = obuf[1]
|
||||||
|
ADD r2, r2, r8 @ r2 = opos += opos_inc
|
||||||
|
MUL r4, r12,r4 @ r5 = tmp0*vol_l
|
||||||
|
MUL r5, r14,r5 @ r6 = tmp1*vol_r
|
||||||
|
|
||||||
|
ADDS r6, r4, r6, LSL #16 @ r6 = obuf[0]<<16 + tmp0*vol_l
|
||||||
|
RSCVS r6, r10,#0x80000000 @ Clamp r6
|
||||||
|
ADDS r7, r5, r7, LSL #16 @ r7 = obuf[1]<<16 + tmp1*vol_r
|
||||||
|
RSCVS r7, r10,#0x80000000 @ Clamp r7
|
||||||
|
|
||||||
|
MOV r6, r6, LSR #16 @ Shift back to halfword
|
||||||
|
MOV r7, r7, LSR #16 @ Shift back to halfword
|
||||||
|
|
||||||
|
STRH r6, [r3],#2 @ Store output value
|
||||||
|
STRH r7, [r3],#2 @ Store output value
|
||||||
|
|
||||||
|
SUBS r11,r11,#1 @ osamp--
|
||||||
|
BGT SimpleRate_R_loop @ and loop
|
||||||
|
SimpleRate_R_end:
|
||||||
|
LDR r14,[r13,#8] @ r14 = sr
|
||||||
|
ADD r13,r13,#12 @ Skip over r0-r2 on stack
|
||||||
|
STMIA r14,{r0,r1,r2} @ Store back updated values
|
||||||
|
MOV r0, r3 @ return obuf
|
||||||
|
LDMFD r13!,{r4-r8,r10-r11,PC}
|
||||||
|
SimpleRate_R_read:
|
||||||
|
LDR r0, [r13,#8] @ r0 = sr (8 = 4*2)
|
||||||
|
ADD r0, r0, #16 @ r0 = inPtr = inBuf
|
||||||
|
.ifdef PALMOS_MODE
|
||||||
|
LDR r10,[r13,#4*8] @ restore r10
|
||||||
|
.endif
|
||||||
|
STMFD r13!,{r0,r2-r3,r12,r14}
|
||||||
|
|
||||||
|
MOV r1, r0 @ r1 = inBuf
|
||||||
|
LDR r0, [r13,#20] @ r0 = AudioStream & input (20 = 4*5)
|
||||||
|
MOV r2, #512 @ r2 = ARRAYSIZE(inBuf)
|
||||||
|
|
||||||
|
@ Calling back into C++ here. WinCE is fairly easy about such things
|
||||||
|
@ but other OS are more awkward. r9 is preserved for Symbian, and
|
||||||
|
@ we have 3+8+5 = 16 things on the stack (an even number).
|
||||||
|
MOV r14,PC
|
||||||
|
LDR PC,[r13,#24] @ inLen = input.readBuffer(inBuf,512) (24 = 4*6)
|
||||||
|
SUBS r1, r0, #2 @ r1 = inLen-2
|
||||||
|
LDMFD r13!,{r0,r2-r3,r12,r14}
|
||||||
|
BLT SimpleRate_R_end
|
||||||
|
.ifdef PALMOS_MODE
|
||||||
|
MOV r10,#0
|
||||||
|
.endif
|
||||||
|
SUBS r2, r2, #1 @ r2 = opos--
|
||||||
|
ADDGE r0, r0, #4 @ if (r2 >= 0) { sr.inPtr += 2
|
||||||
|
BGE SimpleRate_R_loop @ and loop }
|
||||||
|
B SimpleRate_R_read_return
|
||||||
|
|
||||||
|
|
||||||
|
_ARM_LinearRate_M:
|
||||||
|
@ r0 = AudioStream &input
|
||||||
|
@ r1 = input.readBuffer
|
||||||
|
@ r2 = input->sr
|
||||||
|
@ r3 = obuf
|
||||||
|
@ <> = osamp
|
||||||
|
@ <> = vol_l
|
||||||
|
@ <> = vol_r
|
||||||
|
MOV r12,r13
|
||||||
|
STMFD r13!,{r0-r1,r4-r11,r14}
|
||||||
|
LDMFD r12,{r11,r12,r14} @ r11= osamp
|
||||||
|
@ r12= vol_l
|
||||||
|
@ r14= vol_r
|
||||||
|
LDMIA r2,{r0,r1,r8} @ r0 = inPtr
|
||||||
|
@ r1 = inLen
|
||||||
|
@ r8 = opos
|
||||||
|
MOV r10,#0
|
||||||
|
CMP r11,#0 @ if (osamp <= 0)
|
||||||
|
BLE LinearRate_M_end @ bale
|
||||||
|
ORR r12,r12,r12,LSL #8 @ r12= vol_l as 16 bits
|
||||||
|
ORR r14,r14,r14,LSL #8 @ r14= vol_r as 16 bits
|
||||||
|
CMP r1,#0
|
||||||
|
BGT LinearRate_M_part2
|
||||||
|
|
||||||
|
@ part1 - read input samples
|
||||||
|
LinearRate_M_loop:
|
||||||
|
SUBS r1, r1, #1 @ r1 = inLen -= 1
|
||||||
|
BLT LinearRate_M_read
|
||||||
|
LinearRate_M_read_return:
|
||||||
|
LDRH r4, [r2, #16] @ r4 = icur[0]
|
||||||
|
LDRSH r5, [r0],#2 @ r5 = tmp1 = *inPtr++
|
||||||
|
SUBS r8, r8, #65536 @ r8 = opos--
|
||||||
|
STRH r4, [r2,#22] @ ilast[0] = icur[0]
|
||||||
|
STRH r5, [r2,#16] @ icur[0] = tmp1
|
||||||
|
BGE LinearRate_M_loop
|
||||||
|
|
||||||
|
@ part2 - form output samples
|
||||||
|
LinearRate_M_part2:
|
||||||
|
@ We are guaranteed that opos < 0 here
|
||||||
|
LDR r6, [r2,#20] @ r6 = ilast[0]<<16 + 32768
|
||||||
|
LDRSH r5, [r2,#16] @ r5 = icur[0]
|
||||||
|
MOV r4, r8, LSL #16
|
||||||
|
MOV r4, r4, LSR #16
|
||||||
|
SUB r5, r5, r6, ASR #16 @ r5 = icur[0] - ilast[0]
|
||||||
|
MLA r6, r4, r5, r6 @ r6 = (icur[0]-ilast[0])*opos_frac+ilast[0]
|
||||||
|
|
||||||
|
LDRSH r4, [r3] @ r4 = obuf[0]
|
||||||
|
LDRSH r5, [r3,#2] @ r5 = obuf[1]
|
||||||
|
MOV r6, r6, ASR #16 @ r6 = tmp0 = tmp1 >>= 16
|
||||||
|
MUL r7, r12,r6 @ r7 = tmp0*vol_l
|
||||||
|
MUL r6, r14,r6 @ r6 = tmp1*vol_r
|
||||||
|
|
||||||
|
ADDS r7, r7, r4, LSL #16 @ r7 = obuf[0]<<16 + tmp0*vol_l
|
||||||
|
RSCVS r7, r10, #0x80000000 @ Clamp r7
|
||||||
|
ADDS r6, r6, r5, LSL #16 @ r6 = obuf[1]<<16 + tmp1*vol_r
|
||||||
|
RSCVS r6, r10, #0x80000000 @ Clamp r6
|
||||||
|
|
||||||
|
MOV r7, r7, LSR #16 @ Shift back to halfword
|
||||||
|
MOV r6, r6, LSR #16 @ Shift back to halfword
|
||||||
|
|
||||||
|
LDR r5, [r2,#12] @ r5 = opos_inc
|
||||||
|
STRH r7, [r3],#2 @ Store output value
|
||||||
|
STRH r6, [r3],#2 @ Store output value
|
||||||
|
SUBS r11, r11,#1 @ osamp--
|
||||||
|
BLE LinearRate_M_end @ end if needed
|
||||||
|
|
||||||
|
ADDS r8, r8, r5 @ r8 = opos += opos_inc
|
||||||
|
BLT LinearRate_M_part2
|
||||||
|
B LinearRate_M_loop
|
||||||
|
LinearRate_M_end:
|
||||||
|
ADD r13,r13,#8
|
||||||
|
STMIA r2,{r0,r1,r8}
|
||||||
|
MOV r0, r3 @ return obuf
|
||||||
|
LDMFD r13!,{r4-r11,PC}
|
||||||
|
LinearRate_M_read:
|
||||||
|
ADD r0, r2, #28 @ r0 = inPtr = inBuf
|
||||||
|
.ifdef PALMOS_MODE
|
||||||
|
LDR r10,[r13,#4*8] @ restore r10
|
||||||
|
.endif
|
||||||
|
STMFD r13!,{r0,r2-r3,r12,r14}
|
||||||
|
|
||||||
|
MOV r1, r0 @ r1 = inBuf
|
||||||
|
LDR r0, [r13,#20] @ r0 = AudioStream & input (20 = 4*5)
|
||||||
|
MOV r2, #512 @ r2 = ARRAYSIZE(inBuf)
|
||||||
|
|
||||||
|
@ Calling back into C++ here. WinCE is fairly easy about such things
|
||||||
|
@ but other OS are more awkward. r9 is preserved for Symbian, and
|
||||||
|
@ we have 2+9+5 = 16 things on the stack (an even number).
|
||||||
|
MOV r14,PC
|
||||||
|
LDR PC,[r13,#24] @ inLen = input.readBuffer(inBuf,512) (24 = 4*6)
|
||||||
|
SUBS r1, r0, #1 @ r1 = inLen-1
|
||||||
|
LDMFD r13!,{r0,r2-r3,r12,r14}
|
||||||
|
BLT LinearRate_M_end
|
||||||
|
.ifdef PALMOS_MODE
|
||||||
|
MOV r10,#0
|
||||||
|
.endif
|
||||||
|
B LinearRate_M_read_return
|
||||||
|
|
||||||
|
_ARM_LinearRate_S:
|
||||||
|
@ r0 = AudioStream &input
|
||||||
|
@ r1 = input.readBuffer
|
||||||
|
@ r2 = input->sr
|
||||||
|
@ r3 = obuf
|
||||||
|
@ <> = osamp
|
||||||
|
@ <> = vol_l
|
||||||
|
@ <> = vol_r
|
||||||
|
MOV r12,r13
|
||||||
|
STMFD r13!,{r0-r1,r4-r11,r14}
|
||||||
|
LDMFD r12,{r11,r12,r14} @ r11= osamp
|
||||||
|
@ r12= vol_l
|
||||||
|
@ r14= vol_r
|
||||||
|
LDMIA r2,{r0,r1,r8} @ r0 = inPtr
|
||||||
|
@ r1 = inLen
|
||||||
|
@ r8 = opos
|
||||||
|
CMP r11,#0 @ if (osamp <= 0)
|
||||||
|
BLE LinearRate_S_end @ bale
|
||||||
|
ORR r12,r12,r12,LSL #8 @ r12= vol_l as 16 bits
|
||||||
|
ORR r14,r14,r14,LSL #8 @ r14= vol_r as 16 bits
|
||||||
|
CMP r1,#0
|
||||||
|
BGT LinearRate_S_part2
|
||||||
|
|
||||||
|
@ part1 - read input samples
|
||||||
|
LinearRate_S_loop:
|
||||||
|
SUBS r1, r1, #2 @ r1 = inLen -= 2
|
||||||
|
BLT LinearRate_S_read
|
||||||
|
LinearRate_S_read_return:
|
||||||
|
LDR r10,[r2, #16] @ r10= icur[0,1]
|
||||||
|
LDRSH r5, [r0],#2 @ r5 = tmp0 = *inPtr++
|
||||||
|
LDRSH r6, [r0],#2 @ r5 = tmp1 = *inPtr++
|
||||||
|
SUBS r8, r8, #65536 @ r8 = opos--
|
||||||
|
STRH r10,[r2,#22] @ ilast[0] = icur[0]
|
||||||
|
MOV r10,r10,LSR #16
|
||||||
|
STRH r10,[r2,#26] @ ilast[1] = icur[1]
|
||||||
|
STRH r5, [r2,#16] @ icur[0] = tmp0
|
||||||
|
STRH r6, [r2,#18] @ icur[1] = tmp1
|
||||||
|
BGE LinearRate_S_loop
|
||||||
|
|
||||||
|
@ part2 - form output samples
|
||||||
|
LinearRate_S_part2:
|
||||||
|
@ We are guaranteed that opos < 0 here
|
||||||
|
LDR r6, [r2,#20] @ r6 = ilast[0]<<16 + 32768
|
||||||
|
LDRSH r5, [r2,#16] @ r5 = icur[0]
|
||||||
|
MOV r4, r8, LSL #16
|
||||||
|
MOV r4, r4, LSR #16
|
||||||
|
SUB r5, r5, r6, ASR #16 @ r5 = icur[0] - ilast[0]
|
||||||
|
MLA r6, r4, r5, r6 @ r6 = (icur[0]-ilast[0])*opos_frac+ilast[0]
|
||||||
|
|
||||||
|
LDR r7, [r2,#24] @ r7 = ilast[1]<<16 + 32768
|
||||||
|
LDRSH r5, [r2,#18] @ r5 = icur[1]
|
||||||
|
LDRSH r10,[r3] @ r10= obuf[0]
|
||||||
|
MOV r6, r6, ASR #16 @ r6 = tmp1 >>= 16
|
||||||
|
SUB r5, r5, r7, ASR #16 @ r5 = icur[1] - ilast[1]
|
||||||
|
MLA r7, r4, r5, r7 @ r7 = (icur[1]-ilast[1])*opos_frac+ilast[1]
|
||||||
|
|
||||||
|
LDRSH r5, [r3,#2] @ r5 = obuf[1]
|
||||||
|
MOV r7, r7, ASR #16 @ r7 = tmp0 >>= 16
|
||||||
|
MUL r7, r12,r7 @ r7 = tmp0*vol_l
|
||||||
|
MUL r6, r14,r6 @ r6 = tmp1*vol_r
|
||||||
|
|
||||||
|
ADDS r7, r7, r10, LSL #16 @ r7 = obuf[0]<<16 + tmp0*vol_l
|
||||||
|
MOV r4, #0
|
||||||
|
RSCVS r7, r4, #0x80000000 @ Clamp r7
|
||||||
|
ADDS r6, r6, r5, LSL #16 @ r6 = obuf[1]<<16 + tmp1*vol_r
|
||||||
|
RSCVS r6, r4, #0x80000000 @ Clamp r6
|
||||||
|
|
||||||
|
MOV r7, r7, LSR #16 @ Shift back to halfword
|
||||||
|
MOV r6, r6, LSR #16 @ Shift back to halfword
|
||||||
|
|
||||||
|
LDR r5, [r2,#12] @ r5 = opos_inc
|
||||||
|
STRH r7, [r3],#2 @ Store output value
|
||||||
|
STRH r6, [r3],#2 @ Store output value
|
||||||
|
SUBS r11, r11,#1 @ osamp--
|
||||||
|
BLE LinearRate_S_end @ and loop
|
||||||
|
|
||||||
|
ADDS r8, r8, r5 @ r8 = opos += opos_inc
|
||||||
|
BLT LinearRate_S_part2
|
||||||
|
B LinearRate_S_loop
|
||||||
|
LinearRate_S_end:
|
||||||
|
ADD r13,r13,#8
|
||||||
|
STMIA r2,{r0,r1,r8}
|
||||||
|
MOV r0, r3 @ return obuf
|
||||||
|
LDMFD r13!,{r4-r11,PC}
|
||||||
|
LinearRate_S_read:
|
||||||
|
ADD r0, r2, #28 @ r0 = inPtr = inBuf
|
||||||
|
.ifdef PALMOS_MODE
|
||||||
|
LDR r10,[r13,#4*8] @ restore r10
|
||||||
|
.endif
|
||||||
|
STMFD r13!,{r0,r2-r3,r12,r14}
|
||||||
|
|
||||||
|
MOV r1, r0 @ r1 = inBuf
|
||||||
|
LDR r0, [r13,#20] @ r0 = AudioStream & input (20 = 4*5)
|
||||||
|
MOV r2, #512 @ r2 = ARRAYSIZE(inBuf)
|
||||||
|
|
||||||
|
@ Calling back into C++ here. WinCE is fairly easy about such things
|
||||||
|
@ but other OS are more awkward. r9 is preserved for Symbian, and
|
||||||
|
@ we have 2+9+5 = 16 things on the stack (an even number).
|
||||||
|
MOV r14,PC
|
||||||
|
LDR PC,[r13,#24] @ inLen = input.readBuffer(inBuf,512) (24 = 4*6)
|
||||||
|
SUBS r1, r0, #2 @ r1 = inLen-2
|
||||||
|
LDMFD r13!,{r0,r2-r3,r12,r14}
|
||||||
|
BLT LinearRate_S_end
|
||||||
|
B LinearRate_S_read_return
|
||||||
|
|
||||||
|
_ARM_LinearRate_R:
|
||||||
|
@ r0 = AudioStream &input
|
||||||
|
@ r1 = input.readBuffer
|
||||||
|
@ r2 = input->sr
|
||||||
|
@ r3 = obuf
|
||||||
|
@ <> = osamp
|
||||||
|
@ <> = vol_l
|
||||||
|
@ <> = vol_r
|
||||||
|
MOV r12,r13
|
||||||
|
STMFD r13!,{r0-r1,r4-r11,r14}
|
||||||
|
LDMFD r12,{r11,r12,r14} @ r11= osamp
|
||||||
|
@ r12= vol_l
|
||||||
|
@ r14= vol_r
|
||||||
|
LDMIA r2,{r0,r1,r8} @ r0 = inPtr
|
||||||
|
@ r1 = inLen
|
||||||
|
@ r8 = opos
|
||||||
|
CMP r11,#0 @ if (osamp <= 0)
|
||||||
|
BLE LinearRate_R_end @ bale
|
||||||
|
ORR r12,r12,r12,LSL #8 @ r12= vol_l as 16 bits
|
||||||
|
ORR r14,r14,r14,LSL #8 @ r14= vol_r as 16 bits
|
||||||
|
CMP r1,#0
|
||||||
|
BGT LinearRate_R_part2
|
||||||
|
|
||||||
|
@ part1 - read input samples
|
||||||
|
LinearRate_R_loop:
|
||||||
|
SUBS r1, r1, #2 @ r1 = inLen -= 2
|
||||||
|
BLT LinearRate_R_read
|
||||||
|
LinearRate_R_read_return:
|
||||||
|
LDR r10,[r2, #16] @ r10= icur[0,1]
|
||||||
|
LDRSH r5, [r0],#2 @ r5 = tmp0 = *inPtr++
|
||||||
|
LDRSH r6, [r0],#2 @ r5 = tmp1 = *inPtr++
|
||||||
|
SUBS r8, r8, #65536 @ r8 = opos--
|
||||||
|
STRH r10,[r2,#22] @ ilast[0] = icur[0]
|
||||||
|
MOV r10,r10,LSR #16
|
||||||
|
STRH r10,[r2,#22] @ ilast[1] = icur[1]
|
||||||
|
STRH r5, [r2,#16] @ icur[0] = tmp0
|
||||||
|
STRH r6, [r2,#18] @ icur[1] = tmp1
|
||||||
|
BGE LinearRate_R_loop
|
||||||
|
|
||||||
|
@ part2 - form output samples
|
||||||
|
LinearRate_R_part2:
|
||||||
|
@ We are guaranteed that opos < 0 here
|
||||||
|
LDR r6, [r2,#20] @ r6 = ilast[0]
|
||||||
|
LDRSH r5, [r2,#16] @ r5 = icur[0]
|
||||||
|
MOV r4, r8, LSL #16
|
||||||
|
MOV r4, r4, LSR #16
|
||||||
|
SUB r5, r5, r6, ASR #16 @ r5 = icur[0] - ilast[0]
|
||||||
|
MLA r6, r4, r5, r6 @ r6 = (icur[0]-ilast[0])*opos_frac+ilast[0]
|
||||||
|
|
||||||
|
LDR r7, [r2,#24] @ r7 = ilast[1]
|
||||||
|
LDRSH r5, [r2,#18] @ r5 = icur[1]
|
||||||
|
LDR r10,[r3] @ r10= obuf[0]
|
||||||
|
MOV r6, r6, ASR #16 @ r6 = tmp1 >>= 16
|
||||||
|
SUB r5, r5, r7, ASR #16 @ r5 = icur[1] - ilast[1]
|
||||||
|
MLA r7, r4, r5, r7 @ r7 = (icur[1]-ilast[1])*opos_frac+ilast[1]
|
||||||
|
|
||||||
|
LDRSH r5, [r3,#2] @ r5 = obuf[1]
|
||||||
|
MOV r7, r7, ASR #16 @ r7 = tmp0 >>= 16
|
||||||
|
MUL r7, r12,r7 @ r7 = tmp0*vol_l
|
||||||
|
MUL r6, r14,r6 @ r6 = tmp1*vol_r
|
||||||
|
|
||||||
|
ADDS r7, r7, r10, LSL #16 @ r7 = obuf[0]<<16 + tmp0*vol_l
|
||||||
|
MOV r4, #0
|
||||||
|
RSCVS r7, r4, #0x80000000 @ Clamp r7
|
||||||
|
ADDS r6, r6, r5, LSL #16 @ r6 = obuf[1]<<16 + tmp1*vol_r
|
||||||
|
RSCVS r6, r4, #0x80000000 @ Clamp r6
|
||||||
|
|
||||||
|
MOV r7, r7, LSR #16 @ Shift back to halfword
|
||||||
|
MOV r6, r6, LSR #16 @ Shift back to halfword
|
||||||
|
|
||||||
|
LDR r5, [r2,#12] @ r5 = opos_inc
|
||||||
|
STRH r6, [r3],#2 @ Store output value
|
||||||
|
STRH r7, [r3],#2 @ Store output value
|
||||||
|
SUBS r11, r11,#1 @ osamp--
|
||||||
|
BLE LinearRate_R_end @ and loop
|
||||||
|
|
||||||
|
ADDS r8, r8, r5 @ r8 = opos += opos_inc
|
||||||
|
BLT LinearRate_R_part2
|
||||||
|
B LinearRate_R_loop
|
||||||
|
LinearRate_R_end:
|
||||||
|
ADD r13,r13,#8
|
||||||
|
STMIA r2,{r0,r1,r8}
|
||||||
|
MOV r0, r3 @ return obuf
|
||||||
|
LDMFD r13!,{r4-r11,PC}
|
||||||
|
LinearRate_R_read:
|
||||||
|
ADD r0, r2, #28 @ r0 = inPtr = inBuf
|
||||||
|
.ifdef PALMOS_MODE
|
||||||
|
LDR r10,[r13,#4*8] @ restore r10
|
||||||
|
.endif
|
||||||
|
STMFD r13!,{r0,r2-r3,r12,r14}
|
||||||
|
|
||||||
|
MOV r1, r0 @ r1 = inBuf
|
||||||
|
LDR r0, [r13,#20] @ r0 = AudioStream & input (20 = 4*5)
|
||||||
|
MOV r2, #512 @ r2 = ARRAYSIZE(inBuf)
|
||||||
|
|
||||||
|
@ Calling back into C++ here. WinCE is fairly easy about such things
|
||||||
|
@ but other OS are more awkward. r9 is preserved for Symbian, and
|
||||||
|
@ we have 2+9+5 = 16 things on the stack (an even number).
|
||||||
|
MOV r14,PC
|
||||||
|
LDR PC,[r13,#24] @ inLen = input.readBuffer(inBuf,512) (24 = 4*6)
|
||||||
|
SUBS r1, r0, #2 @ r1 = inLen-2
|
||||||
|
LDMFD r13!,{r0,r2-r3,r12,r14}
|
||||||
|
BLT LinearRate_R_end
|
||||||
|
B LinearRate_R_read_return
|