aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordhewg2011-03-20 21:54:59 +0100
committerdhewg2011-03-25 14:43:52 +0100
commit0e6cdfd67580f75e912c5e92abb26821d032f74b (patch)
tree9303f1ea6c1be30d28712b908d1480427eec5aae
parent7949d7c6def2df6f3d44db4f55e9d1d3a87a0412 (diff)
downloadscummvm-rg350-0e6cdfd67580f75e912c5e92abb26821d032f74b.tar.gz
scummvm-rg350-0e6cdfd67580f75e912c5e92abb26821d032f74b.tar.bz2
scummvm-rg350-0e6cdfd67580f75e912c5e92abb26821d032f74b.zip
ANDROID: Experimental MIDI Driver
Based on the SONiVOX® Embedded Audio Synthesis (EAS™) library, which is part of the base Android OS. CPU stats (Cortex A8 1GHz, monkey1 intro, peak values): MAME OPL: 30% DosBox OPL: 26% EAS: 19%
-rw-r--r--audio/module.mk1
-rw-r--r--audio/softsynth/eas.cpp420
-rw-r--r--base/plugins.cpp3
3 files changed, 424 insertions, 0 deletions
diff --git a/audio/module.mk b/audio/module.mk
index 661d3ccad4..a9d9bfc869 100644
--- a/audio/module.mk
+++ b/audio/module.mk
@@ -45,6 +45,7 @@ MODULE_OBJS := \
softsynth/ym2612.o \
softsynth/fluidsynth.o \
softsynth/mt32.o \
+ softsynth/eas.o \
softsynth/pcspk.o \
softsynth/sid.o \
softsynth/wave6581.o
diff --git a/audio/softsynth/eas.cpp b/audio/softsynth/eas.cpp
new file mode 100644
index 0000000000..c5c60b253d
--- /dev/null
+++ b/audio/softsynth/eas.cpp
@@ -0,0 +1,420 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/scummsys.h"
+
+#if defined(__ANDROID__)
+
+#include <dlfcn.h>
+
+#include "common/debug.h"
+#include "common/endian.h"
+#include "common/config-manager.h"
+#include "audio/audiostream.h"
+#include "audio/mpu401.h"
+#include "audio/musicplugin.h"
+#include "audio/mixer.h"
+
+// NOTE:
+// EAS's render function *only* accepts one mix buffer size. it's defined at
+// compile time of the library and can be retrieved via EASLibConfig.bufSize
+// (seen: 128 bytes).
+// to avoid local intermediate buffers, this implementation insists on a fixed
+// buffer size of the calling rate converter, which in return must be a
+// multiple of EAS's. that may change if there're hickups because slower
+// devices can't render fast enough
+
+// from rate_arm.cpp
+#define INTERMEDIATE_BUFFER_SIZE 512
+
+// so far all android versions have the very same library version
+#define EAS_LIBRARY "libsonivox.so"
+#define EAS_KNOWNVERSION 0x03060a0e
+
+#define EAS_REVERB 2
+#define EAS_REVERB_BYPASS 0
+#define EAS_REVERB_PRESET 1
+#define EAS_REVERB_CHAMBER 2
+
+class MidiDriver_EAS : public MidiDriver_MPU401, Audio::AudioStream {
+public:
+ MidiDriver_EAS();
+ virtual ~MidiDriver_EAS();
+
+ // MidiDriver
+ virtual int open();
+ virtual bool isOpen() const;
+ virtual void close();
+ virtual void send(uint32 b);
+ virtual void sysEx(const byte *msg, uint16 length);
+ virtual void setTimerCallback(void *timerParam,
+ Common::TimerManager::TimerProc timerProc);
+ virtual uint32 getBaseTempo();
+
+ // AudioStream
+ virtual int readBuffer(int16 *buffer, const int numSamples);
+ virtual bool isStereo() const;
+ virtual int getRate() const;
+ virtual bool endOfData() const;
+
+private:
+ struct EASLibConfig {
+ uint32 version;
+ uint32 debug;
+ int32 voices;
+ int32 channels;
+ int32 rate;
+ int32 bufSize;
+ uint32 filter;
+ uint32 timeStamp;
+ char *GUID;
+ };
+
+ typedef void * EASDataHandle;
+ typedef void * EASHandle;
+
+ typedef EASLibConfig *(*ConfigFunc)();
+ typedef int32 (*InitFunc)(EASDataHandle *);
+ typedef int32 (*ShutdownFunc)(EASDataHandle);
+ typedef int32 (*SetParameterFunc)(EASDataHandle, int32, int32, int32);
+ typedef int32 (*OpenStreamFunc)(EASDataHandle, EASHandle *, EASHandle);
+ typedef int32 (*WriteStreamFunc)(EASDataHandle, EASHandle, byte *, int32);
+ typedef int32 (*CloseStreamFunc)(EASDataHandle, EASHandle);
+ typedef int32 (*RenderFunc)(EASDataHandle, int16 *, int32, int32 *);
+
+ template<typename T>
+ void sym(T &t, const char *symbol) {
+ union {
+ void *v;
+ T t;
+ } u;
+
+ assert(sizeof(u.v) == sizeof(u.t));
+
+ u.v = dlsym(_dlHandle, symbol);
+
+ if (!u.v)
+ warning("couldn't resolve %s from " EAS_LIBRARY, symbol);
+
+ t = u.t;
+ }
+
+ void *_dlHandle;
+
+ ConfigFunc _configFunc;
+ InitFunc _initFunc;
+ ShutdownFunc _shutdownFunc;
+ SetParameterFunc _setParameterFunc;
+ OpenStreamFunc _openStreamFunc;
+ WriteStreamFunc _writeStreamFunc;
+ CloseStreamFunc _closeStreamFunc;
+ RenderFunc _renderFunc;
+
+ const EASLibConfig *_config;
+ EASDataHandle _EASHandle;
+ EASHandle _midiStream;
+
+ Common::TimerManager::TimerProc _timerProc;
+ void *_timerParam;
+ uint32 _baseTempo;
+ uint _rounds;
+ Audio::SoundHandle _soundHandle;
+};
+
+MidiDriver_EAS::MidiDriver_EAS() :
+ MidiDriver_MPU401(),
+ _dlHandle(0),
+ _configFunc(0),
+ _initFunc(0),
+ _shutdownFunc(0),
+ _setParameterFunc(0),
+ _openStreamFunc(0),
+ _writeStreamFunc(0),
+ _closeStreamFunc(0),
+ _renderFunc(0),
+ _config(0),
+ _EASHandle(0),
+ _midiStream(0),
+ _timerProc(0),
+ _timerParam(0),
+ _baseTempo(0),
+ _rounds(0),
+ _soundHandle() {
+}
+
+MidiDriver_EAS::~MidiDriver_EAS() {
+}
+
+int MidiDriver_EAS::open() {
+ if (isOpen())
+ return MERR_ALREADY_OPEN;
+
+ _dlHandle = dlopen(EAS_LIBRARY, RTLD_LAZY);
+ if (!_dlHandle) {
+ warning("error opening " EAS_LIBRARY ": %s", dlerror());
+ return MERR_DEVICE_NOT_AVAILABLE;
+ }
+
+ sym(_configFunc, "EAS_Config");
+ if (!_configFunc) {
+ close();
+ return -1;
+ }
+
+ _config = _configFunc();
+ if (!_config) {
+ close();
+ warning("error retrieving EAS library configuration");
+ return -1;
+ }
+
+ if (_config->version != EAS_KNOWNVERSION) {
+ close();
+ warning("unknown EAS library version: 0x%08x", _config->version);
+ return -1;
+ }
+
+ if (_config->channels > 2) {
+ close();
+ warning("unsupported number of EAS channels: %d", _config->channels);
+ return -1;
+ }
+
+ // see note at top of this file
+ if (INTERMEDIATE_BUFFER_SIZE % (_config->bufSize * _config->channels)) {
+ close();
+ warning("unsupported EAS buffer size: %d", _config->bufSize);
+ return -1;
+ }
+
+ sym(_initFunc, "EAS_Init");
+ sym(_shutdownFunc, "EAS_Shutdown");
+ sym(_setParameterFunc, "EAS_SetParameter");
+ sym(_openStreamFunc, "EAS_OpenMIDIStream");
+ sym(_writeStreamFunc, "EAS_WriteMIDIStream");
+ sym(_closeStreamFunc, "EAS_CloseMIDIStream");
+ sym(_renderFunc, "EAS_Render");
+
+ if (!_initFunc || !_shutdownFunc || !_setParameterFunc ||
+ !_openStreamFunc || !_writeStreamFunc || !_closeStreamFunc ||
+ !_renderFunc) {
+ close();
+ return -1;
+ }
+
+ int32 res = _initFunc(&_EASHandle);
+ if (res) {
+ close();
+ warning("error initializing the EAS library: %d", res);
+ return -1;
+ }
+
+ _setParameterFunc(_EASHandle, EAS_REVERB, EAS_REVERB_PRESET,
+ EAS_REVERB_CHAMBER);
+ if (res)
+ warning("error setting reverb preset: %d", res);
+
+ _setParameterFunc(_EASHandle, EAS_REVERB, EAS_REVERB_BYPASS, 0);
+ if (res)
+ warning("error disabling reverb bypass: %d", res);
+
+ res = _openStreamFunc(_EASHandle, &_midiStream, 0);
+ if (res) {
+ close();
+ warning("error opening EAS MIDI stream: %d", res);
+ return -1;
+ }
+
+ // set the timer frequency to match a single buffer size
+ _baseTempo = (1000000 * _config->bufSize) / _config->rate;
+
+ // number of buffer fills per readBuffer()
+ _rounds = INTERMEDIATE_BUFFER_SIZE / (_config->bufSize * _config->channels);
+
+ debug("EAS initialized (voices:%d channels:%d rate:%d buffer:%d) "
+ "tempo:%u rounds:%u", _config->voices, _config->channels,
+ _config->rate, _config->bufSize, _baseTempo, _rounds);
+
+ g_system->getMixer()->playStream(Audio::Mixer::kMusicSoundType,
+ &_soundHandle, this, -1,
+ Audio::Mixer::kMaxChannelVolume, 0,
+ DisposeAfterUse::NO, true);
+
+ return 0;
+}
+
+bool MidiDriver_EAS::isOpen() const {
+ return _dlHandle != 0;
+}
+
+void MidiDriver_EAS::close() {
+ MidiDriver_MPU401::close();
+
+ if (!isOpen())
+ return;
+
+ g_system->getMixer()->stopHandle(_soundHandle);
+
+ // not pretty, but better than a mutex
+ g_system->delayMillis((_baseTempo * _rounds) / 1000);
+
+ if (_midiStream) {
+ int32 res = _closeStreamFunc(_EASHandle, _midiStream);
+ if (res)
+ warning("error closing EAS MIDI stream: %d", res);
+
+ _midiStream = 0;
+ }
+
+ if (_EASHandle) {
+ int32 res = _shutdownFunc(_EASHandle);
+ if (res)
+ warning("error shutting down the EAS library: %d", res);
+
+ _EASHandle = 0;
+ }
+
+ if (dlclose(_dlHandle))
+ warning("error closing " EAS_LIBRARY ": %s", dlerror());
+
+ _dlHandle = 0;
+}
+
+void MidiDriver_EAS::send(uint32 b) {
+ byte buf[4];
+
+ WRITE_UINT32(buf, b);
+
+ int32 res = _writeStreamFunc(_EASHandle, _midiStream, buf, 4);
+ if (res)
+ warning("error writing to EAS MIDI stream: %d", res);
+}
+
+void MidiDriver_EAS::sysEx(const byte *msg, uint16 length) {
+ byte buf[266];
+
+ assert(length + 2 <= ARRAYSIZE(buf));
+
+ buf[0] = 0xF0;
+ memcpy(buf + 1, msg, length);
+ buf[length + 1] = 0xF7;
+
+ int32 res = _writeStreamFunc(_EASHandle, _midiStream, buf, length + 2);
+ if (res)
+ warning("error writing to EAS MIDI stream: %d", res);
+}
+
+void MidiDriver_EAS::setTimerCallback(void *timerParam,
+ Common::TimerManager::TimerProc timerProc) {
+ _timerParam = timerParam;
+ _timerProc = timerProc;
+}
+
+uint32 MidiDriver_EAS::getBaseTempo() {
+ return _baseTempo;
+}
+
+int MidiDriver_EAS::readBuffer(int16 *buffer, const int numSamples) {
+ // see note at top of this file
+ assert(numSamples == INTERMEDIATE_BUFFER_SIZE);
+
+ int32 res, c;
+
+ for (uint i = 0; i < _rounds; ++i) {
+ // pull in MIDI events for exactly one buffer size
+ if (_timerProc)
+ (*_timerProc)(_timerParam);
+
+ // if there are no MIDI events, this just renders silence
+ res = _renderFunc(_EASHandle, buffer, _config->bufSize, &c);
+ if (res) {
+ warning("error rendering EAS samples: %d", res);
+ return -1;
+ }
+
+ buffer += c * _config->channels;
+ }
+
+ return numSamples;
+}
+
+bool MidiDriver_EAS::isStereo() const {
+ return _config->channels == 2;
+}
+
+int MidiDriver_EAS::getRate() const {
+ return _config->rate;
+}
+
+bool MidiDriver_EAS::endOfData() const {
+ return false;
+}
+
+class EASMusicPlugin : public MusicPluginObject {
+public:
+ EASMusicPlugin();
+ virtual ~EASMusicPlugin();
+
+ const char *getName() const;
+ const char *getId() const;
+ MusicDevices getDevices() const;
+ Common::Error createInstance(MidiDriver **mididriver,
+ MidiDriver::DeviceHandle = 0) const;
+};
+
+EASMusicPlugin::EASMusicPlugin() {
+}
+
+EASMusicPlugin::~EASMusicPlugin() {
+}
+
+const char *EASMusicPlugin::getName() const {
+ return "Embedded Audio Synthesis";
+}
+
+const char *EASMusicPlugin::getId() const {
+ return "eas";
+}
+
+MusicDevices EASMusicPlugin::getDevices() const {
+ MusicDevices devices;
+ devices.push_back(MusicDevice(this, "", MT_GM));
+
+ return devices;
+}
+
+Common::Error EASMusicPlugin::createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle) const {
+ *mididriver = new MidiDriver_EAS();
+
+ return Common::kNoError;
+}
+
+//#if PLUGIN_ENABLED_DYNAMIC(EAS)
+ //REGISTER_PLUGIN_DYNAMIC(EAS, PLUGIN_TYPE_MUSIC, EASMusicPlugin);
+//#else
+ REGISTER_PLUGIN_STATIC(EAS, PLUGIN_TYPE_MUSIC, EASMusicPlugin);
+//#endif
+
+#endif
+
diff --git a/base/plugins.cpp b/base/plugins.cpp
index 1db9c0d499..7d0557c474 100644
--- a/base/plugins.cpp
+++ b/base/plugins.cpp
@@ -212,6 +212,9 @@ public:
#ifdef USE_MT32EMU
LINK_PLUGIN(MT32)
#endif
+ #if defined(__ANDROID__)
+ LINK_PLUGIN(EAS)
+ #endif
LINK_PLUGIN(ADLIB)
LINK_PLUGIN(PCSPK)
LINK_PLUGIN(PCJR)