diff --git a/.gitignore b/.gitignore
index e95faf9a840..3f33da09518 100644
--- a/.gitignore
+++ b/.gitignore
@@ -122,6 +122,7 @@ ipch/
*.bat
*.tss
*.VC.db
+.vs/
#Ignore default Visual Studio build folders
[Dd]ebug/
diff --git a/.travis.yml b/.travis.yml
index f7ce86ec7d1..7c10aebd883 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -24,6 +24,11 @@ addons:
- libfreetype6-dev
- zlib1g-dev
+before_install:
+ - "if [ ${TRAVIS_OS_NAME:-'linux'} = 'osx' ]; then brew update; fi"
+ - "if [ ${TRAVIS_OS_NAME:-'linux'} = 'osx' ]; then brew install sdl2 sdl2_net libvorbis flac mad theora faad2 libmpeg2 glew; fi"
+
+
branches:
only:
- master
@@ -34,6 +39,7 @@ compiler:
os:
- linux
+ - osx
dist: trusty
@@ -42,3 +48,10 @@ script:
- make -j 2
- make test
- make devtools
+
+notifications:
+ irc:
+ channels:
+ - "chat.freenode.net#residualvm"
+ on_success: change
+ on_failure: always
diff --git a/Makefile.common b/Makefile.common
index 36cb138e4ca..3c46010bee6 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -25,8 +25,8 @@ MODULES += \
backends \
engines \
video \
- graphics \
image \
+ graphics \
audio \
math \
common \
diff --git a/audio/decoders/qdm2.cpp b/audio/decoders/qdm2.cpp
index a2f43261529..b8d007da51d 100644
--- a/audio/decoders/qdm2.cpp
+++ b/audio/decoders/qdm2.cpp
@@ -213,9 +213,9 @@ private:
void fill_coding_method_array(sb_int8_array tone_level_idx, sb_int8_array tone_level_idx_temp,
sb_int8_array coding_method, int nb_channels,
int c, int superblocktype_2_3, int cm_table_select);
- void synthfilt_build_sb_samples(Common::BitStream *gb, int length, int sb_min, int sb_max);
- void init_quantized_coeffs_elem0(int8 *quantized_coeffs, Common::BitStream *gb, int length);
- void init_tone_level_dequantization(Common::BitStream *gb, int length);
+ void synthfilt_build_sb_samples(Common::BitStream32LELSB *gb, int length, int sb_min, int sb_max);
+ void init_quantized_coeffs_elem0(int8 *quantized_coeffs, Common::BitStream32LELSB *gb, int length);
+ void init_tone_level_dequantization(Common::BitStream32LELSB *gb, int length);
void process_subpacket_9(QDM2SubPNode *node);
void process_subpacket_10(QDM2SubPNode *node, int length);
void process_subpacket_11(QDM2SubPNode *node, int length);
@@ -224,7 +224,7 @@ private:
void qdm2_decode_super_block(void);
void qdm2_fft_init_coefficient(int sub_packet, int offset, int duration,
int channel, int exp, int phase);
- void qdm2_fft_decode_tones(int duration, Common::BitStream *gb, int b);
+ void qdm2_fft_decode_tones(int duration, Common::BitStream32LELSB *gb, int b);
void qdm2_decode_fft_packets(void);
void qdm2_fft_generate_tone(FFTTone *tone);
void qdm2_fft_tone_synthesizer(uint8 sub_packet);
@@ -677,7 +677,7 @@ void ff_mpa_synth_filter(int16 *synth_buf_ptr, int *synth_buf_offset,
* read the longest vlc code
* = (max_vlc_length + bits - 1) / bits
*/
-static int getVlc2(Common::BitStream *s, int16 (*table)[2], int bits, int maxDepth) {
+static int getVlc2(Common::BitStream32LELSB *s, int16 (*table)[2], int bits, int maxDepth) {
int index = s->peekBits(bits);
int code = table[index][0];
int n = table[index][1];
@@ -1227,7 +1227,7 @@ QDM2Stream::~QDM2Stream() {
delete[] _compressedData;
}
-static int qdm2_get_vlc(Common::BitStream *gb, VLC *vlc, int flag, int depth) {
+static int qdm2_get_vlc(Common::BitStream32LELSB *gb, VLC *vlc, int flag, int depth) {
int value = getVlc2(gb, vlc->table, vlc->bits, depth);
// stage-2, 3 bits exponent escape sequence
@@ -1246,7 +1246,7 @@ static int qdm2_get_vlc(Common::BitStream *gb, VLC *vlc, int flag, int depth) {
return value;
}
-static int qdm2_get_se_vlc(VLC *vlc, Common::BitStream *gb, int depth)
+static int qdm2_get_se_vlc(VLC *vlc, Common::BitStream32LELSB *gb, int depth)
{
int value = qdm2_get_vlc(gb, vlc, 0, depth);
@@ -1612,7 +1612,7 @@ void QDM2Stream::fill_coding_method_array(sb_int8_array tone_level_idx, sb_int8_
* @param sb_min lower subband processed (sb_min included)
* @param sb_max higher subband processed (sb_max excluded)
*/
-void QDM2Stream::synthfilt_build_sb_samples(Common::BitStream *gb, int length, int sb_min, int sb_max) {
+void QDM2Stream::synthfilt_build_sb_samples(Common::BitStream32LELSB *gb, int length, int sb_min, int sb_max) {
int sb, j, k, n, ch, run, channels;
int joined_stereo, zero_encoding, chs;
int type34_first;
@@ -1792,7 +1792,7 @@ void QDM2Stream::synthfilt_build_sb_samples(Common::BitStream *gb, int length, i
* @param gb bitreader context
* @param length packet length in bits
*/
-void QDM2Stream::init_quantized_coeffs_elem0(int8 *quantized_coeffs, Common::BitStream *gb, int length) {
+void QDM2Stream::init_quantized_coeffs_elem0(int8 *quantized_coeffs, Common::BitStream32LELSB *gb, int length) {
int i, k, run, level, diff;
if ((length - gb->pos()) < 16)
@@ -1826,7 +1826,7 @@ void QDM2Stream::init_quantized_coeffs_elem0(int8 *quantized_coeffs, Common::Bit
* @param gb bitreader context
* @param length packet length in bits
*/
-void QDM2Stream::init_tone_level_dequantization(Common::BitStream *gb, int length) {
+void QDM2Stream::init_tone_level_dequantization(Common::BitStream32LELSB *gb, int length) {
int sb, j, k, n, ch;
for (ch = 0; ch < _channels; ch++) {
@@ -2021,7 +2021,7 @@ void QDM2Stream::qdm2_decode_super_block(void) {
average_quantized_coeffs(); // average elements in quantized_coeffs[max_ch][10][8]
Common::MemoryReadStream *d = new Common::MemoryReadStream(_compressedData, _packetSize*8);
- Common::BitStream *gb = new Common::BitStream32LELSB(d);
+ Common::BitStream32LELSB *gb = new Common::BitStream32LELSB(d);
//qdm2_decode_sub_packet_header
header.type = gb->getBits(8);
@@ -2188,7 +2188,7 @@ void QDM2Stream::qdm2_fft_init_coefficient(int sub_packet, int offset, int durat
_fftCoefsIndex++;
}
-void QDM2Stream::qdm2_fft_decode_tones(int duration, Common::BitStream *gb, int b) {
+void QDM2Stream::qdm2_fft_decode_tones(int duration, Common::BitStream32LELSB *gb, int b) {
int channel, stereo, phase, exp;
int local_int_4, local_int_8, stereo_phase, local_int_10;
int local_int_14, stereo_exp, local_int_20, local_int_28;
diff --git a/audio/decoders/wave.cpp b/audio/decoders/wave.cpp
index 803bdf3cf02..55c7034df69 100644
--- a/audio/decoders/wave.cpp
+++ b/audio/decoders/wave.cpp
@@ -61,6 +61,13 @@ bool loadWAVFromStream(Common::SeekableReadStream &stream, int &size, int &rate,
}
stream.read(buf, 4);
+ if (memcmp(buf, "fact", 4) == 0) {
+ // Initial fact chunk, so skip over it
+ uint32 factLen = stream.readUint32LE();
+ stream.skip(factLen);
+ stream.read(buf, 4);
+ }
+
if (memcmp(buf, "fmt ", 4) != 0) {
warning("getWavInfo: No 'fmt' header");
return false;
diff --git a/audio/decoders/wma.cpp b/audio/decoders/wma.cpp
index ee8e35fbf23..5b16ecb5d4e 100644
--- a/audio/decoders/wma.cpp
+++ b/audio/decoders/wma.cpp
@@ -29,7 +29,6 @@
#include "common/error.h"
#include "common/memstream.h"
#include "common/mdct.h"
-#include "common/bitstream.h"
#include "common/huffman.h"
#include "audio/audiostream.h"
@@ -684,7 +683,7 @@ Common::SeekableReadStream *WMACodec::decodeSuperFrame(Common::SeekableReadStrea
return new Common::MemoryReadStream((byte *) outputData, outputDataSize * 2, DisposeAfterUse::YES);
}
-bool WMACodec::decodeFrame(Common::BitStream &bits, int16 *outputData) {
+bool WMACodec::decodeFrame(Common::BitStream8MSB &bits, int16 *outputData) {
_framePos = 0;
_curBlock = 0;
@@ -714,7 +713,7 @@ bool WMACodec::decodeFrame(Common::BitStream &bits, int16 *outputData) {
return true;
}
-int WMACodec::decodeBlock(Common::BitStream &bits) {
+int WMACodec::decodeBlock(Common::BitStream8MSB &bits) {
// Computer new block length
if (!evalBlockLength(bits))
return -1;
@@ -767,7 +766,7 @@ int WMACodec::decodeBlock(Common::BitStream &bits) {
return 0;
}
-bool WMACodec::decodeChannels(Common::BitStream &bits, int bSize,
+bool WMACodec::decodeChannels(Common::BitStream8MSB &bits, int bSize,
bool msStereo, bool *hasChannel) {
int totalGain = readTotalGain(bits);
@@ -823,7 +822,7 @@ bool WMACodec::calculateIMDCT(int bSize, bool msStereo, bool *hasChannel) {
return true;
}
-bool WMACodec::evalBlockLength(Common::BitStream &bits) {
+bool WMACodec::evalBlockLength(Common::BitStream8MSB &bits) {
if (_useVariableBlockLen) {
// Variable block lengths
@@ -899,7 +898,7 @@ void WMACodec::calculateCoefCount(int *coefCount, int bSize) const {
coefCount[i] = coefN;
}
-bool WMACodec::decodeNoise(Common::BitStream &bits, int bSize,
+bool WMACodec::decodeNoise(Common::BitStream8MSB &bits, int bSize,
bool *hasChannel, int *coefCount) {
if (!_useNoiseCoding)
return true;
@@ -950,7 +949,7 @@ bool WMACodec::decodeNoise(Common::BitStream &bits, int bSize,
return true;
}
-bool WMACodec::decodeExponents(Common::BitStream &bits, int bSize, bool *hasChannel) {
+bool WMACodec::decodeExponents(Common::BitStream8MSB &bits, int bSize, bool *hasChannel) {
// Exponents can be reused in short blocks
if (!((_blockLenBits == _frameLenBits) || bits.getBit()))
return true;
@@ -973,7 +972,7 @@ bool WMACodec::decodeExponents(Common::BitStream &bits, int bSize, bool *hasChan
return true;
}
-bool WMACodec::decodeSpectralCoef(Common::BitStream &bits, bool msStereo, bool *hasChannel,
+bool WMACodec::decodeSpectralCoef(Common::BitStream8MSB &bits, bool msStereo, bool *hasChannel,
int *coefCount, int coefBitCount) {
// Simple RLE encoding
@@ -1213,7 +1212,7 @@ static const float powTab[] = {
7.4989420933246e+05, 8.6596432336007e+05,
};
-bool WMACodec::decodeExpHuffman(Common::BitStream &bits, int ch) {
+bool WMACodec::decodeExpHuffman(Common::BitStream8MSB &bits, int ch) {
const float *ptab = powTab + 60;
const uint32 *iptab = (const uint32 *) ptab;
@@ -1311,7 +1310,7 @@ void WMACodec::lspToCurve(float *out, float *val_max_ptr, int n, float *lsp) {
}
// Decode exponents coded with LSP coefficients (same idea as Vorbis)
-bool WMACodec::decodeExpLSP(Common::BitStream &bits, int ch) {
+bool WMACodec::decodeExpLSP(Common::BitStream8MSB &bits, int ch) {
float lspCoefs[kLSPCoefCount];
for (int i = 0; i < kLSPCoefCount; i++) {
@@ -1329,7 +1328,7 @@ bool WMACodec::decodeExpLSP(Common::BitStream &bits, int ch) {
return true;
}
-bool WMACodec::decodeRunLevel(Common::BitStream &bits, const Common::Huffman &huffman,
+bool WMACodec::decodeRunLevel(Common::BitStream8MSB &bits, const Common::Huffman &huffman,
const float *levelTable, const uint16 *runTable, int version, float *ptr,
int offset, int numCoefs, int blockLen, int frameLenBits, int coefNbBits) {
@@ -1471,7 +1470,7 @@ float WMACodec::pow_m1_4(float x) const {
return _lspPowETable[e] * (a + b * t.f);
}
-int WMACodec::readTotalGain(Common::BitStream &bits) {
+int WMACodec::readTotalGain(Common::BitStream8MSB &bits) {
int totalGain = 1;
int v = 127;
@@ -1492,7 +1491,7 @@ int WMACodec::totalGainToBits(int totalGain) {
else return 9;
}
-uint32 WMACodec::getLargeVal(Common::BitStream &bits) {
+uint32 WMACodec::getLargeVal(Common::BitStream8MSB &bits) {
// Consumes up to 34 bits
int count = 8;
diff --git a/audio/decoders/wma.h b/audio/decoders/wma.h
index 8ea82538c67..286903c4828 100644
--- a/audio/decoders/wma.h
+++ b/audio/decoders/wma.h
@@ -27,11 +27,11 @@
#define AUDIO_DECODERS_WMA_H
#include "common/array.h"
+#include "common/bitstream.h"
#include "audio/decoders/codec.h"
namespace Common {
- class BitStream;
class Huffman;
class MDCT;
}
@@ -181,27 +181,27 @@ private:
// Decoding
Common::SeekableReadStream *decodeSuperFrame(Common::SeekableReadStream &data);
- bool decodeFrame(Common::BitStream &bits, int16 *outputData);
- int decodeBlock(Common::BitStream &bits);
+ bool decodeFrame(Common::BitStream8MSB &bits, int16 *outputData);
+ int decodeBlock(Common::BitStream8MSB &bits);
// Decoding helpers
- bool evalBlockLength(Common::BitStream &bits);
- bool decodeChannels(Common::BitStream &bits, int bSize, bool msStereo, bool *hasChannel);
+ bool evalBlockLength(Common::BitStream8MSB &bits);
+ bool decodeChannels(Common::BitStream8MSB &bits, int bSize, bool msStereo, bool *hasChannel);
bool calculateIMDCT(int bSize, bool msStereo, bool *hasChannel);
void calculateCoefCount(int *coefCount, int bSize) const;
- bool decodeNoise(Common::BitStream &bits, int bSize, bool *hasChannel, int *coefCount);
- bool decodeExponents(Common::BitStream &bits, int bSize, bool *hasChannel);
- bool decodeSpectralCoef(Common::BitStream &bits, bool msStereo, bool *hasChannel,
+ bool decodeNoise(Common::BitStream8MSB &bits, int bSize, bool *hasChannel, int *coefCount);
+ bool decodeExponents(Common::BitStream8MSB &bits, int bSize, bool *hasChannel);
+ bool decodeSpectralCoef(Common::BitStream8MSB &bits, bool msStereo, bool *hasChannel,
int *coefCount, int coefBitCount);
float getNormalizedMDCTLength() const;
void calculateMDCTCoefficients(int bSize, bool *hasChannel,
int *coefCount, int totalGain, float mdctNorm);
- bool decodeExpHuffman(Common::BitStream &bits, int ch);
- bool decodeExpLSP(Common::BitStream &bits, int ch);
- bool decodeRunLevel(Common::BitStream &bits, const Common::Huffman &huffman,
+ bool decodeExpHuffman(Common::BitStream8MSB &bits, int ch);
+ bool decodeExpLSP(Common::BitStream8MSB &bits, int ch);
+ bool decodeRunLevel(Common::BitStream8MSB &bits, const Common::Huffman &huffman,
const float *levelTable, const uint16 *runTable, int version, float *ptr,
int offset, int numCoefs, int blockLen, int frameLenBits, int coefNbBits);
@@ -211,9 +211,9 @@ private:
float pow_m1_4(float x) const;
- static int readTotalGain(Common::BitStream &bits);
+ static int readTotalGain(Common::BitStream8MSB &bits);
static int totalGainToBits(int totalGain);
- static uint32 getLargeVal(Common::BitStream &bits);
+ static uint32 getLargeVal(Common::BitStream8MSB &bits);
};
} // End of namespace Audio
diff --git a/audio/softsynth/mt32.cpp b/audio/softsynth/mt32.cpp
index 9471fcb6a78..1b2e6929a31 100644
--- a/audio/softsynth/mt32.cpp
+++ b/audio/softsynth/mt32.cpp
@@ -25,9 +25,6 @@
#ifdef USE_MT32EMU
-#include "audio/softsynth/mt32/mt32emu.h"
-#include "audio/softsynth/mt32/ROMInfo.h"
-
#include "audio/softsynth/emumidi.h"
#include "audio/musicplugin.h"
#include "audio/mpu401.h"
@@ -52,16 +49,16 @@
#include "gui/message.h"
+// prevents load of unused FileStream API because it includes a standard library
+// include, per _sev
+#define MT32EMU_FILE_STREAM_H
+
+#include "audio/softsynth/mt32/c_interface/cpp_interface.h"
+
namespace MT32Emu {
-class ReportHandlerScummVM : public ReportHandler {
-friend class Synth;
-
+class ScummVMReportHandler : public MT32Emu::IReportHandler {
public:
- virtual ~ReportHandlerScummVM() {}
-
-protected:
-
// Callback for debug messages, in vprintf() format
void printDebug(const char *fmt, va_list list) {
Common::String out = Common::String::vformat(fmt, list);
@@ -70,18 +67,32 @@ protected:
// Callbacks for reporting various errors and information
void onErrorControlROM() {
- GUI::MessageDialog dialog("MT32emu: Init Error - Missing or invalid Control ROM image", "OK");
+ GUI::MessageDialog dialog("MT32Emu: Init Error - Missing or invalid Control ROM image", "OK");
dialog.runModal();
error("MT32emu: Init Error - Missing or invalid Control ROM image");
}
void onErrorPCMROM() {
- GUI::MessageDialog dialog("MT32emu: Init Error - Missing PCM ROM image", "OK");
+ GUI::MessageDialog dialog("MT32Emu: Init Error - Missing PCM ROM image", "OK");
dialog.runModal();
error("MT32emu: Init Error - Missing PCM ROM image");
}
void showLCDMessage(const char *message) {
Common::OSDMessageQueue::instance().addMessage(message);
}
+
+ // Unused callbacks
+ virtual void onMIDIMessagePlayed() {}
+ virtual bool onMIDIQueueOverflow() { return false; }
+ virtual void onMIDISystemRealtime(Bit8u /* system_realtime */) {}
+ virtual void onDeviceReset() {}
+ virtual void onDeviceReconfig() {}
+ virtual void onNewReverbMode(Bit8u /* mode */) {}
+ virtual void onNewReverbTime(Bit8u /* time */) {}
+ virtual void onNewReverbLevel(Bit8u /* level */) {}
+ virtual void onPolyStateChanged(Bit8u /* part_num */) {}
+ virtual void onProgramChanged(Bit8u /* part_num */, const char * /* sound_group_name */, const char * /* patch_name */) {}
+
+ virtual ~ScummVMReportHandler() {}
};
} // end of namespace MT32Emu
@@ -95,11 +106,10 @@ class MidiDriver_MT32 : public MidiDriver_Emulated {
private:
MidiChannel_MT32 _midiChannels[16];
uint16 _channelMask;
- MT32Emu::Synth *_synth;
- MT32Emu::ReportHandlerScummVM *_reportHandler;
- const MT32Emu::ROMImage *_controlROM, *_pcmROM;
- Common::File *_controlFile, *_pcmFile;
- void deleteMuntStructures();
+ MT32Emu::Service _service;
+ MT32Emu::ScummVMReportHandler _reportHandler;
+ byte *_controlData, *_pcmData;
+ Common::Mutex _mutex;
int _outputRate;
@@ -107,15 +117,13 @@ protected:
void generateSamples(int16 *buf, int len);
public:
- bool _initializing;
-
MidiDriver_MT32(Audio::Mixer *mixer);
virtual ~MidiDriver_MT32();
int open();
void close();
void send(uint32 b);
- void setPitchBendRange (byte channel, uint range);
+ void setPitchBendRange(byte channel, uint range);
void sysEx(const byte *msg, uint16 length);
uint32 property(int prop, uint32 param);
@@ -139,48 +147,19 @@ MidiDriver_MT32::MidiDriver_MT32(Audio::Mixer *mixer) : MidiDriver_Emulated(mixe
for (i = 0; i < ARRAYSIZE(_midiChannels); ++i) {
_midiChannels[i].init(this, i);
}
- _reportHandler = NULL;
- _synth = NULL;
_outputRate = 0;
- _initializing = false;
-
- // Initialized in open()
- _controlROM = NULL;
- _pcmROM = NULL;
- _controlFile = NULL;
- _pcmFile = NULL;
+ _controlData = nullptr;
+ _pcmData = nullptr;
}
MidiDriver_MT32::~MidiDriver_MT32() {
- deleteMuntStructures();
-}
-
-void MidiDriver_MT32::deleteMuntStructures() {
- delete _synth;
- _synth = NULL;
- delete _reportHandler;
- _reportHandler = NULL;
-
- if (_controlROM)
- MT32Emu::ROMImage::freeROMImage(_controlROM);
- _controlROM = NULL;
- if (_pcmROM)
- MT32Emu::ROMImage::freeROMImage(_pcmROM);
- _pcmROM = NULL;
-
- delete _controlFile;
- _controlFile = NULL;
- delete _pcmFile;
- _pcmFile = NULL;
+ close();
}
int MidiDriver_MT32::open() {
if (_isOpen)
return MERR_ALREADY_OPEN;
- _reportHandler = new MT32Emu::ReportHandlerScummVM();
- _synth = new MT32Emu::Synth(_reportHandler);
-
Graphics::PixelFormat screenFormat = g_system->getScreenFormat();
if (screenFormat.bytesPerPixel == 1) {
@@ -193,75 +172,92 @@ int MidiDriver_MT32::open() {
g_system->getPaletteManager()->setPalette(dummy_palette, 0, 3);
}
- _initializing = true;
debug(4, _s("Initializing MT-32 Emulator"));
- _controlFile = new Common::File();
- if (!_controlFile->open("CM32L_CONTROL.ROM") && !_controlFile->open("MT32_CONTROL.ROM"))
- error("Error opening MT32_CONTROL.ROM / CM32L_CONTROL.ROM");
- _pcmFile = new Common::File();
- if (!_pcmFile->open("CM32L_PCM.ROM") && !_pcmFile->open("MT32_PCM.ROM"))
- error("Error opening MT32_PCM.ROM / CM32L_PCM.ROM");
- _controlROM = MT32Emu::ROMImage::makeROMImage(_controlFile);
- _pcmROM = MT32Emu::ROMImage::makeROMImage(_pcmFile);
- if (!_synth->open(*_controlROM, *_pcmROM))
+
+ Common::File controlFile;
+ if (!controlFile.open("CM32L_CONTROL.ROM") && !controlFile.open("MT32_CONTROL.ROM"))
+ error("Error opening MT32_CONTROL.ROM / CM32L_CONTROL.ROM. Check that your Extra Path in Paths settings is set to the correct directory");
+
+ Common::File pcmFile;
+ if (!pcmFile.open("CM32L_PCM.ROM") && !pcmFile.open("MT32_PCM.ROM"))
+ error("Error opening MT32_PCM.ROM / CM32L_PCM.ROM. Check that your Extra Path in Paths settings is set to the correct directory");
+
+ _controlData = new byte[controlFile.size()];
+ controlFile.read(_controlData, controlFile.size());
+ _pcmData = new byte[pcmFile.size()];
+ pcmFile.read(_pcmData, pcmFile.size());
+
+ _service.createContext(_reportHandler);
+
+ if (_service.addROMData(_controlData, controlFile.size()) != MT32EMU_RC_ADDED_CONTROL_ROM) {
+ error("Adding control ROM failed. Check that your control ROM is valid");
+ }
+
+ controlFile.close();
+
+ if (_service.addROMData(_pcmData, pcmFile.size()) != MT32EMU_RC_ADDED_PCM_ROM) {
+ error("Adding PCM ROM failed. Check that your PCM ROM is valid");
+ }
+
+ pcmFile.close();
+
+ if (_service.openSynth() != MT32EMU_RC_OK)
return MERR_DEVICE_NOT_AVAILABLE;
double gain = (double)ConfMan.getInt("midi_gain") / 100.0;
- _synth->setOutputGain(1.0f * gain);
- _synth->setReverbOutputGain(0.68f * gain);
+ _service.setOutputGain(1.0f * gain);
+ _service.setReverbOutputGain(1.0f * gain);
// We let the synthesizer play MIDI messages immediately. Our MIDI
// handling is synchronous to sample generation. This makes delaying MIDI
// events result in odd sound output in some cases. For example, the
// shattering window in the Indiana Jones and the Fate of Atlantis intro
// will sound like a bell if we use any delay here.
// Bug #6242 "AUDIO: Built-In MT-32 MUNT Produces Wrong Sounds".
- _synth->setMIDIDelayMode(MT32Emu::MIDIDelayMode_IMMEDIATE);
+ _service.setMIDIDelayMode(MT32Emu::MIDIDelayMode_IMMEDIATE);
// We need to report the sample rate MUNT renders at as sample rate of our
// AudioStream.
- _outputRate = _synth->getStereoOutputSampleRate();
+ _outputRate = _service.getActualStereoOutputSamplerate();
+
MidiDriver_Emulated::open();
- _initializing = false;
-
- if (screenFormat.bytesPerPixel > 1)
- g_system->fillScreen(screenFormat.RGBToColor(0, 0, 0));
- else
- g_system->fillScreen(0);
-
- g_system->updateScreen();
-
_mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
return 0;
}
void MidiDriver_MT32::send(uint32 b) {
- _synth->playMsg(b);
+ Common::StackLock lock(_mutex);
+ _service.playMsg(b);
}
+// Indiana Jones and the Fate of Atlantis (including the demo) uses
+// setPitchBendRange, if you need a game for testing purposes
void MidiDriver_MT32::setPitchBendRange(byte channel, uint range) {
if (range > 24) {
warning("setPitchBendRange() called with range > 24: %d", range);
}
- byte benderRangeSysex[9];
- benderRangeSysex[0] = 0x41; // Roland
- benderRangeSysex[1] = channel;
- benderRangeSysex[2] = 0x16; // MT-32
- benderRangeSysex[3] = 0x12; // Write
- benderRangeSysex[4] = 0x00;
- benderRangeSysex[5] = 0x00;
- benderRangeSysex[6] = 0x04;
- benderRangeSysex[7] = (byte)range;
- benderRangeSysex[8] = MT32Emu::Synth::calcSysexChecksum(&benderRangeSysex[4], 4, 0);
- sysEx(benderRangeSysex, 9);
+ byte benderRangeSysex[4] = { 0, 0, 4, (uint8)range };
+ Common::StackLock lock(_mutex);
+ _service.writeSysex(channel, benderRangeSysex, 4);
}
void MidiDriver_MT32::sysEx(const byte *msg, uint16 length) {
if (msg[0] == 0xf0) {
- _synth->playSysex(msg, length);
+ Common::StackLock lock(_mutex);
+ _service.playSysex(msg, length);
} else {
- _synth->playSysexWithoutFraming(msg, length);
+ enum {
+ SYSEX_CMD_DT1 = 0x12,
+ SYSEX_CMD_DAT = 0x42
+ };
+
+ if (msg[3] == SYSEX_CMD_DT1 || msg[3] == SYSEX_CMD_DAT) {
+ Common::StackLock lock(_mutex);
+ _service.writeSysex(msg[1], msg + 4, length - 5);
+ } else {
+ warning("Unused sysEx command %d", msg[3]);
+ }
}
}
@@ -275,12 +271,18 @@ void MidiDriver_MT32::close() {
// Detach the mixer callback handler
_mixer->stopHandle(_mixerSoundHandle);
- _synth->close();
- deleteMuntStructures();
+ Common::StackLock lock(_mutex);
+ _service.closeSynth();
+ _service.freeContext();
+ delete[] _controlData;
+ _controlData = nullptr;
+ delete[] _pcmData;
+ _pcmData = nullptr;
}
void MidiDriver_MT32::generateSamples(int16 *data, int len) {
- _synth->render(data, len);
+ Common::StackLock lock(_mutex);
+ _service.renderBit16s(data, len);
}
uint32 MidiDriver_MT32::property(int prop, uint32 param) {
diff --git a/audio/softsynth/mt32/Analog.cpp b/audio/softsynth/mt32/Analog.cpp
index 8ac28e401a2..31e88561c46 100644
--- a/audio/softsynth/mt32/Analog.cpp
+++ b/audio/softsynth/mt32/Analog.cpp
@@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -15,8 +15,12 @@
* along with this program. If not, see .
*/
-//#include
+#include
+
+#include "internals.h"
+
#include "Analog.h"
+#include "Synth.h"
namespace MT32Emu {
@@ -106,7 +110,6 @@ static const Bit32u ACCURATE_LPF_DELTAS_OVERSAMPLED[][ACCURATE_LPF_NUMBER_OF_PHA
class AbstractLowPassFilter {
public:
static AbstractLowPassFilter &createLowPassFilter(AnalogOutputMode mode, bool oldMT32AnalogLPF);
- static void muteRingBuffer(SampleEx *ringBuffer, unsigned int length);
virtual ~AbstractLowPassFilter() {}
virtual SampleEx process(SampleEx sample) = 0;
@@ -152,9 +155,9 @@ public:
void addPositionIncrement(unsigned int positionIncrement);
};
-Analog::Analog(const AnalogOutputMode mode, const ControlROMFeatureSet *controlROMFeatures) :
- leftChannelLPF(AbstractLowPassFilter::createLowPassFilter(mode, controlROMFeatures->isOldMT32AnalogLPF())),
- rightChannelLPF(AbstractLowPassFilter::createLowPassFilter(mode, controlROMFeatures->isOldMT32AnalogLPF())),
+Analog::Analog(const AnalogOutputMode mode, const bool oldMT32AnalogLPF) :
+ leftChannelLPF(AbstractLowPassFilter::createLowPassFilter(mode, oldMT32AnalogLPF)),
+ rightChannelLPF(AbstractLowPassFilter::createLowPassFilter(mode, oldMT32AnalogLPF)),
synthGain(0),
reverbGain(0)
{}
@@ -164,7 +167,7 @@ Analog::~Analog() {
delete &rightChannelLPF;
}
-void Analog::process(Sample **outStream, const Sample *nonReverbLeft, const Sample *nonReverbRight, const Sample *reverbDryLeft, const Sample *reverbDryRight, const Sample *reverbWetLeft, const Sample *reverbWetRight, Bit32u outLength) {
+void Analog::process(Sample *outStream, const Sample *nonReverbLeft, const Sample *nonReverbRight, const Sample *reverbDryLeft, const Sample *reverbDryRight, const Sample *reverbWetLeft, const Sample *reverbWetRight, Bit32u outLength) {
if (outStream == NULL) {
leftChannelLPF.addPositionIncrement(outLength);
rightChannelLPF.addPositionIncrement(outLength);
@@ -179,8 +182,8 @@ void Analog::process(Sample **outStream, const Sample *nonReverbLeft, const Samp
outSampleL = leftChannelLPF.process(0);
outSampleR = rightChannelLPF.process(0);
} else {
- SampleEx inSampleL = ((SampleEx)*(nonReverbLeft++) + (SampleEx)*(reverbDryLeft++)) * synthGain + (SampleEx)*(reverbWetLeft++) * reverbGain;
- SampleEx inSampleR = ((SampleEx)*(nonReverbRight++) + (SampleEx)*(reverbDryRight++)) * synthGain + (SampleEx)*(reverbWetRight++) * reverbGain;
+ SampleEx inSampleL = (SampleEx(*(nonReverbLeft++)) + SampleEx(*(reverbDryLeft++))) * synthGain + SampleEx(*(reverbWetLeft++)) * reverbGain;
+ SampleEx inSampleR = (SampleEx(*(nonReverbRight++)) + SampleEx(*(reverbDryRight++))) * synthGain + SampleEx(*(reverbWetRight++)) * reverbGain;
#if !MT32EMU_USE_FLOAT_SAMPLES
inSampleL >>= OUTPUT_GAIN_FRACTION_BITS;
@@ -191,8 +194,8 @@ void Analog::process(Sample **outStream, const Sample *nonReverbLeft, const Samp
outSampleR = rightChannelLPF.process(inSampleR);
}
- *((*outStream)++) = Synth::clipSampleEx(outSampleL);
- *((*outStream)++) = Synth::clipSampleEx(outSampleR);
+ *(outStream++) = Synth::clipSampleEx(outSampleL);
+ *(outStream++) = Synth::clipSampleEx(outSampleR);
}
}
@@ -236,23 +239,6 @@ AbstractLowPassFilter &AbstractLowPassFilter::createLowPassFilter(AnalogOutputMo
}
}
-void AbstractLowPassFilter::muteRingBuffer(SampleEx *ringBuffer, unsigned int length) {
-
-#if MT32EMU_USE_FLOAT_SAMPLES
-
- SampleEx *p = ringBuffer;
- while (length--) {
- *(p++) = 0.0f;
- }
-
-#else
-
- memset(ringBuffer, 0, length * sizeof(SampleEx));
-
-#endif
-
-}
-
bool AbstractLowPassFilter::hasNextSample() const {
return false;
}
@@ -273,7 +259,7 @@ CoarseLowPassFilter::CoarseLowPassFilter(bool oldMT32AnalogLPF) :
LPF_TAPS(oldMT32AnalogLPF ? COARSE_LPF_TAPS_MT32 : COARSE_LPF_TAPS_CM32L),
ringBufferPosition(0)
{
- muteRingBuffer(ringBuffer, COARSE_LPF_DELAY_LINE_LENGTH);
+ Synth::muteSampleBuffer(ringBuffer, COARSE_LPF_DELAY_LINE_LENGTH);
}
SampleEx CoarseLowPassFilter::process(const SampleEx inSample) {
@@ -303,7 +289,7 @@ AccurateLowPassFilter::AccurateLowPassFilter(const bool oldMT32AnalogLPF, const
ringBufferPosition(0),
phase(0)
{
- muteRingBuffer(ringBuffer, ACCURATE_LPF_DELAY_LINE_LENGTH);
+ Synth::muteSampleBuffer(ringBuffer, ACCURATE_LPF_DELAY_LINE_LENGTH);
}
SampleEx AccurateLowPassFilter::process(const SampleEx inSample) {
@@ -345,4 +331,4 @@ void AccurateLowPassFilter::addPositionIncrement(const unsigned int positionIncr
phase = (phase + positionIncrement * phaseIncrement) % ACCURATE_LPF_NUMBER_OF_PHASES;
}
-}
+} // namespace MT32Emu
diff --git a/audio/softsynth/mt32/Analog.h b/audio/softsynth/mt32/Analog.h
index a48db724855..ee642f280d0 100644
--- a/audio/softsynth/mt32/Analog.h
+++ b/audio/softsynth/mt32/Analog.h
@@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -18,7 +18,10 @@
#ifndef MT32EMU_ANALOG_H
#define MT32EMU_ANALOG_H
-#include "mt32emu.h"
+#include "globals.h"
+#include "internals.h"
+#include "Types.h"
+#include "Enumerations.h"
namespace MT32Emu {
@@ -35,9 +38,9 @@ class AbstractLowPassFilter;
*/
class Analog {
public:
- Analog(AnalogOutputMode mode, const ControlROMFeatureSet *controlROMFeatures);
+ Analog(const AnalogOutputMode mode, const bool oldMT32AnalogLPF);
~Analog();
- void process(Sample **outStream, const Sample *nonReverbLeft, const Sample *nonReverbRight, const Sample *reverbDryLeft, const Sample *reverbDryRight, const Sample *reverbWetLeft, const Sample *reverbWetRight, const Bit32u outLength);
+ void process(Sample *outStream, const Sample *nonReverbLeft, const Sample *nonReverbRight, const Sample *reverbDryLeft, const Sample *reverbDryRight, const Sample *reverbWetLeft, const Sample *reverbWetRight, Bit32u outLength);
unsigned int getOutputSampleRate() const;
Bit32u getDACStreamsLength(Bit32u outputLength) const;
void setSynthOutputGain(float synthGain);
@@ -52,6 +55,6 @@ private:
Analog(Analog &);
};
-}
+} // namespace MT32Emu
-#endif
+#endif // #ifndef MT32EMU_ANALOG_H
diff --git a/audio/softsynth/mt32/BReverbModel.cpp b/audio/softsynth/mt32/BReverbModel.cpp
index 5e02db8f990..2be1b418a23 100644
--- a/audio/softsynth/mt32/BReverbModel.cpp
+++ b/audio/softsynth/mt32/BReverbModel.cpp
@@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -15,9 +15,12 @@
* along with this program. If not, see .
*/
-//#include
-#include "mt32emu.h"
+#include
+
+#include "internals.h"
+
#include "BReverbModel.h"
+#include "Synth.h"
// Analysing of state of reverb RAM address lines gives exact sizes of the buffers of filters used. This also indicates that
// the reverb model implemented in the real devices consists of three series allpass filters preceded by a non-feedback comb (or a delay with a LPF)
@@ -43,14 +46,14 @@ const BReverbSettings &BReverbModel::getCM32L_LAPCSettings(const ReverbMode mode
static const Bit32u MODE_0_COMBS[] = {705 + PROCESS_DELAY, 2349, 2839, 3632};
static const Bit32u MODE_0_OUTL[] = {2349, 141, 1960};
static const Bit32u MODE_0_OUTR[] = {1174, 1570, 145};
- static const Bit32u MODE_0_COMB_FACTOR[] = {0xA0, 0x60, 0x60, 0x60};
- static const Bit32u MODE_0_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ static const Bit8u MODE_0_COMB_FACTOR[] = {0xA0, 0x60, 0x60, 0x60};
+ static const Bit8u MODE_0_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98,
0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98,
0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98};
- static const Bit32u MODE_0_DRY_AMP[] = {0xA0, 0xA0, 0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xD0};
- static const Bit32u MODE_0_WET_AMP[] = {0x10, 0x30, 0x50, 0x70, 0x90, 0xC0, 0xF0, 0xF0};
- static const Bit32u MODE_0_LPF_AMP = 0x60;
+ static const Bit8u MODE_0_DRY_AMP[] = {0xA0, 0xA0, 0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xD0};
+ static const Bit8u MODE_0_WET_AMP[] = {0x10, 0x30, 0x50, 0x70, 0x90, 0xC0, 0xF0, 0xF0};
+ static const Bit8u MODE_0_LPF_AMP = 0x60;
static const Bit32u MODE_1_NUMBER_OF_ALLPASSES = 3;
static const Bit32u MODE_1_ALLPASSES[] = {1324, 809, 176};
@@ -58,14 +61,14 @@ const BReverbSettings &BReverbModel::getCM32L_LAPCSettings(const ReverbMode mode
static const Bit32u MODE_1_COMBS[] = {961 + PROCESS_DELAY, 2619, 3545, 4519};
static const Bit32u MODE_1_OUTL[] = {2618, 1760, 4518};
static const Bit32u MODE_1_OUTR[] = {1300, 3532, 2274};
- static const Bit32u MODE_1_COMB_FACTOR[] = {0x80, 0x60, 0x60, 0x60};
- static const Bit32u MODE_1_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ static const Bit8u MODE_1_COMB_FACTOR[] = {0x80, 0x60, 0x60, 0x60};
+ static const Bit8u MODE_1_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98,
0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98,
0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98};
- static const Bit32u MODE_1_DRY_AMP[] = {0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xE0};
- static const Bit32u MODE_1_WET_AMP[] = {0x10, 0x30, 0x50, 0x70, 0x90, 0xC0, 0xF0, 0xF0};
- static const Bit32u MODE_1_LPF_AMP = 0x60;
+ static const Bit8u MODE_1_DRY_AMP[] = {0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xE0};
+ static const Bit8u MODE_1_WET_AMP[] = {0x10, 0x30, 0x50, 0x70, 0x90, 0xC0, 0xF0, 0xF0};
+ static const Bit8u MODE_1_LPF_AMP = 0x60;
static const Bit32u MODE_2_NUMBER_OF_ALLPASSES = 3;
static const Bit32u MODE_2_ALLPASSES[] = {969, 644, 157};
@@ -73,25 +76,25 @@ const BReverbSettings &BReverbModel::getCM32L_LAPCSettings(const ReverbMode mode
static const Bit32u MODE_2_COMBS[] = {116 + PROCESS_DELAY, 2259, 2839, 3539};
static const Bit32u MODE_2_OUTL[] = {2259, 718, 1769};
static const Bit32u MODE_2_OUTR[] = {1136, 2128, 1};
- static const Bit32u MODE_2_COMB_FACTOR[] = {0, 0x20, 0x20, 0x20};
- static const Bit32u MODE_2_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ static const Bit8u MODE_2_COMB_FACTOR[] = {0, 0x20, 0x20, 0x20};
+ static const Bit8u MODE_2_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0,
0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0,
0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0};
- static const Bit32u MODE_2_DRY_AMP[] = {0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xB0, 0xC0, 0xE0};
- static const Bit32u MODE_2_WET_AMP[] = {0x10, 0x30, 0x50, 0x70, 0x90, 0xC0, 0xF0, 0xF0};
- static const Bit32u MODE_2_LPF_AMP = 0x80;
+ static const Bit8u MODE_2_DRY_AMP[] = {0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xB0, 0xC0, 0xE0};
+ static const Bit8u MODE_2_WET_AMP[] = {0x10, 0x30, 0x50, 0x70, 0x90, 0xC0, 0xF0, 0xF0};
+ static const Bit8u MODE_2_LPF_AMP = 0x80;
static const Bit32u MODE_3_NUMBER_OF_ALLPASSES = 0;
static const Bit32u MODE_3_NUMBER_OF_COMBS = 1;
static const Bit32u MODE_3_DELAY[] = {16000 + MODE_3_FEEDBACK_DELAY + PROCESS_DELAY + MODE_3_ADDITIONAL_DELAY};
static const Bit32u MODE_3_OUTL[] = {400, 624, 960, 1488, 2256, 3472, 5280, 8000};
static const Bit32u MODE_3_OUTR[] = {800, 1248, 1920, 2976, 4512, 6944, 10560, 16000};
- static const Bit32u MODE_3_COMB_FACTOR[] = {0x68};
- static const Bit32u MODE_3_COMB_FEEDBACK[] = {0x68, 0x60};
- static const Bit32u MODE_3_DRY_AMP[] = {0x20, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50,
+ static const Bit8u MODE_3_COMB_FACTOR[] = {0x68};
+ static const Bit8u MODE_3_COMB_FEEDBACK[] = {0x68, 0x60};
+ static const Bit8u MODE_3_DRY_AMP[] = {0x20, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50,
0x20, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50};
- static const Bit32u MODE_3_WET_AMP[] = {0x18, 0x18, 0x28, 0x40, 0x60, 0x80, 0xA8, 0xF8};
+ static const Bit8u MODE_3_WET_AMP[] = {0x18, 0x18, 0x28, 0x40, 0x60, 0x80, 0xA8, 0xF8};
static const BReverbSettings REVERB_MODE_0_SETTINGS = {MODE_0_NUMBER_OF_ALLPASSES, MODE_0_ALLPASSES, MODE_0_NUMBER_OF_COMBS, MODE_0_COMBS, MODE_0_OUTL, MODE_0_OUTR, MODE_0_COMB_FACTOR, MODE_0_COMB_FEEDBACK, MODE_0_DRY_AMP, MODE_0_WET_AMP, MODE_0_LPF_AMP};
static const BReverbSettings REVERB_MODE_1_SETTINGS = {MODE_1_NUMBER_OF_ALLPASSES, MODE_1_ALLPASSES, MODE_1_NUMBER_OF_COMBS, MODE_1_COMBS, MODE_1_OUTL, MODE_1_OUTR, MODE_1_COMB_FACTOR, MODE_1_COMB_FEEDBACK, MODE_1_DRY_AMP, MODE_1_WET_AMP, MODE_1_LPF_AMP};
@@ -112,14 +115,14 @@ const BReverbSettings &BReverbModel::getMT32Settings(const ReverbMode mode) {
static const Bit32u MODE_0_COMBS[] = {575 + PROCESS_DELAY, 2040, 2752, 3629};
static const Bit32u MODE_0_OUTL[] = {2040, 687, 1814};
static const Bit32u MODE_0_OUTR[] = {1019, 2072, 1};
- static const Bit32u MODE_0_COMB_FACTOR[] = {0xB0, 0x60, 0x60, 0x60};
- static const Bit32u MODE_0_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ static const Bit8u MODE_0_COMB_FACTOR[] = {0xB0, 0x60, 0x60, 0x60};
+ static const Bit8u MODE_0_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98,
0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98,
0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98};
- static const Bit32u MODE_0_DRY_AMP[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80};
- static const Bit32u MODE_0_WET_AMP[] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x70, 0xA0, 0xE0};
- static const Bit32u MODE_0_LPF_AMP = 0x80;
+ static const Bit8u MODE_0_DRY_AMP[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80};
+ static const Bit8u MODE_0_WET_AMP[] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x70, 0xA0, 0xE0};
+ static const Bit8u MODE_0_LPF_AMP = 0x80;
static const Bit32u MODE_1_NUMBER_OF_ALLPASSES = 3;
static const Bit32u MODE_1_ALLPASSES[] = {1324, 809, 176};
@@ -127,14 +130,14 @@ const BReverbSettings &BReverbModel::getMT32Settings(const ReverbMode mode) {
static const Bit32u MODE_1_COMBS[] = {961 + PROCESS_DELAY, 2619, 3545, 4519};
static const Bit32u MODE_1_OUTL[] = {2618, 1760, 4518};
static const Bit32u MODE_1_OUTR[] = {1300, 3532, 2274};
- static const Bit32u MODE_1_COMB_FACTOR[] = {0x90, 0x60, 0x60, 0x60};
- static const Bit32u MODE_1_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ static const Bit8u MODE_1_COMB_FACTOR[] = {0x90, 0x60, 0x60, 0x60};
+ static const Bit8u MODE_1_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98,
0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98,
0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98};
- static const Bit32u MODE_1_DRY_AMP[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80};
- static const Bit32u MODE_1_WET_AMP[] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x70, 0xA0, 0xE0};
- static const Bit32u MODE_1_LPF_AMP = 0x80;
+ static const Bit8u MODE_1_DRY_AMP[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80};
+ static const Bit8u MODE_1_WET_AMP[] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x70, 0xA0, 0xE0};
+ static const Bit8u MODE_1_LPF_AMP = 0x80;
static const Bit32u MODE_2_NUMBER_OF_ALLPASSES = 3;
static const Bit32u MODE_2_ALLPASSES[] = {969, 644, 157};
@@ -142,25 +145,25 @@ const BReverbSettings &BReverbModel::getMT32Settings(const ReverbMode mode) {
static const Bit32u MODE_2_COMBS[] = {116 + PROCESS_DELAY, 2259, 2839, 3539};
static const Bit32u MODE_2_OUTL[] = {2259, 718, 1769};
static const Bit32u MODE_2_OUTR[] = {1136, 2128, 1};
- static const Bit32u MODE_2_COMB_FACTOR[] = {0, 0x60, 0x60, 0x60};
- static const Bit32u MODE_2_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ static const Bit8u MODE_2_COMB_FACTOR[] = {0, 0x60, 0x60, 0x60};
+ static const Bit8u MODE_2_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98,
0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98,
0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98};
- static const Bit32u MODE_2_DRY_AMP[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80};
- static const Bit32u MODE_2_WET_AMP[] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x70, 0xA0, 0xE0};
- static const Bit32u MODE_2_LPF_AMP = 0x80;
+ static const Bit8u MODE_2_DRY_AMP[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80};
+ static const Bit8u MODE_2_WET_AMP[] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x70, 0xA0, 0xE0};
+ static const Bit8u MODE_2_LPF_AMP = 0x80;
static const Bit32u MODE_3_NUMBER_OF_ALLPASSES = 0;
static const Bit32u MODE_3_NUMBER_OF_COMBS = 1;
static const Bit32u MODE_3_DELAY[] = {16000 + MODE_3_FEEDBACK_DELAY + PROCESS_DELAY + MODE_3_ADDITIONAL_DELAY};
static const Bit32u MODE_3_OUTL[] = {400, 624, 960, 1488, 2256, 3472, 5280, 8000};
static const Bit32u MODE_3_OUTR[] = {800, 1248, 1920, 2976, 4512, 6944, 10560, 16000};
- static const Bit32u MODE_3_COMB_FACTOR[] = {0x68};
- static const Bit32u MODE_3_COMB_FEEDBACK[] = {0x68, 0x60};
- static const Bit32u MODE_3_DRY_AMP[] = {0x10, 0x10, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ static const Bit8u MODE_3_COMB_FACTOR[] = {0x68};
+ static const Bit8u MODE_3_COMB_FEEDBACK[] = {0x68, 0x60};
+ static const Bit8u MODE_3_DRY_AMP[] = {0x10, 0x10, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x10, 0x20, 0x20, 0x10, 0x20, 0x10, 0x20, 0x10};
- static const Bit32u MODE_3_WET_AMP[] = {0x08, 0x18, 0x28, 0x40, 0x60, 0x80, 0xA8, 0xF8};
+ static const Bit8u MODE_3_WET_AMP[] = {0x08, 0x18, 0x28, 0x40, 0x60, 0x80, 0xA8, 0xF8};
static const BReverbSettings REVERB_MODE_0_SETTINGS = {MODE_0_NUMBER_OF_ALLPASSES, MODE_0_ALLPASSES, MODE_0_NUMBER_OF_COMBS, MODE_0_COMBS, MODE_0_OUTL, MODE_0_OUTR, MODE_0_COMB_FACTOR, MODE_0_COMB_FEEDBACK, MODE_0_DRY_AMP, MODE_0_WET_AMP, MODE_0_LPF_AMP};
static const BReverbSettings REVERB_MODE_1_SETTINGS = {MODE_1_NUMBER_OF_ALLPASSES, MODE_1_ALLPASSES, MODE_1_NUMBER_OF_COMBS, MODE_1_COMBS, MODE_1_OUTL, MODE_1_OUTR, MODE_1_COMB_FACTOR, MODE_1_COMB_FEEDBACK, MODE_1_DRY_AMP, MODE_1_WET_AMP, MODE_1_LPF_AMP};
@@ -189,7 +192,7 @@ static Sample weirdMul(Sample a, Bit8u addMask, Bit8u carryMask) {
}
return res;
#else
- return Sample(((Bit32s)a * addMask) >> 8);
+ return Sample((Bit32s(a) * addMask) >> 8);
#endif
}
@@ -252,7 +255,7 @@ Sample AllpassFilter::process(const Sample in) {
#endif
}
-CombFilter::CombFilter(const Bit32u useSize, const Bit32u useFilterFactor) : RingBuffer(useSize), filterFactor(useFilterFactor) {}
+CombFilter::CombFilter(const Bit32u useSize, const Bit8u useFilterFactor) : RingBuffer(useSize), filterFactor(useFilterFactor) {}
void CombFilter::process(const Sample in) {
// This model corresponds to the comb filter implementation of the real CM-32L device
@@ -271,11 +274,11 @@ Sample CombFilter::getOutputAt(const Bit32u outIndex) const {
return buffer[(size + index - outIndex) % size];
}
-void CombFilter::setFeedbackFactor(const Bit32u useFeedbackFactor) {
+void CombFilter::setFeedbackFactor(const Bit8u useFeedbackFactor) {
feedbackFactor = useFeedbackFactor;
}
-DelayWithLowPassFilter::DelayWithLowPassFilter(const Bit32u useSize, const Bit32u useFilterFactor, const Bit32u useAmp)
+DelayWithLowPassFilter::DelayWithLowPassFilter(const Bit32u useSize, const Bit8u useFilterFactor, const Bit8u useAmp)
: CombFilter(useSize, useFilterFactor), amp(useAmp) {}
void DelayWithLowPassFilter::process(const Sample in) {
@@ -292,7 +295,7 @@ void DelayWithLowPassFilter::process(const Sample in) {
buffer[index] = weirdMul(lpfOut, amp, 0xFF);
}
-TapDelayCombFilter::TapDelayCombFilter(const Bit32u useSize, const Bit32u useFilterFactor) : CombFilter(useSize, useFilterFactor) {}
+TapDelayCombFilter::TapDelayCombFilter(const Bit32u useSize, const Bit8u useFilterFactor) : CombFilter(useSize, useFilterFactor) {}
void TapDelayCombFilter::process(const Sample in) {
// the previously stored value
@@ -430,7 +433,7 @@ bool BReverbModel::isMT32Compatible(const ReverbMode mode) const {
return ¤tSettings == &getMT32Settings(mode);
}
-void BReverbModel::process(const Sample *inLeft, const Sample *inRight, Sample *outLeft, Sample *outRight, unsigned long numSamples) {
+void BReverbModel::process(const Sample *inLeft, const Sample *inRight, Sample *outLeft, Sample *outRight, Bit32u numSamples) {
if (combs == NULL) {
Synth::muteSampleBuffer(outLeft, numSamples);
Synth::muteSampleBuffer(outRight, numSamples);
@@ -501,9 +504,9 @@ void BReverbModel::process(const Sample *inLeft, const Sample *inRight, Sample *
* Analysing of the algorithm suggests that the overflow is most probable when the combs output is added below.
* So, despite this isn't actually accurate, we only add the check here for performance reasons.
*/
- Sample outSample = Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx((SampleEx)outL1 + SampleEx(outL1 >> 1)) + (SampleEx)outL2) + SampleEx(outL2 >> 1)) + (SampleEx)outL3);
+ Sample outSample = Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx(SampleEx(outL1) + (SampleEx(outL1) >> 1)) + SampleEx(outL2)) + (SampleEx(outL2) >> 1)) + SampleEx(outL3));
#else
- Sample outSample = Synth::clipSampleEx((SampleEx)outL1 + SampleEx(outL1 >> 1) + (SampleEx)outL2 + SampleEx(outL2 >> 1) + (SampleEx)outL3);
+ Sample outSample = Synth::clipSampleEx(SampleEx(outL1) + (SampleEx(outL1) >> 1) + SampleEx(outL2) + (SampleEx(outL2) >> 1) + SampleEx(outL3));
#endif
*(outLeft++) = weirdMul(outSample, wetLevel, 0xFF);
}
@@ -515,9 +518,9 @@ void BReverbModel::process(const Sample *inLeft, const Sample *inRight, Sample *
Sample outSample = 1.5f * (outR1 + outR2) + outR3;
#elif MT32EMU_BOSS_REVERB_PRECISE_MODE
// See the note above for the left channel output.
- Sample outSample = Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx((SampleEx)outR1 + SampleEx(outR1 >> 1)) + (SampleEx)outR2) + SampleEx(outR2 >> 1)) + (SampleEx)outR3);
+ Sample outSample = Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx(SampleEx(outR1) + (SampleEx(outR1) >> 1)) + SampleEx(outR2)) + (SampleEx(outR2) >> 1)) + SampleEx(outR3));
#else
- Sample outSample = Synth::clipSampleEx((SampleEx)outR1 + SampleEx(outR1 >> 1) + (SampleEx)outR2 + SampleEx(outR2 >> 1) + (SampleEx)outR3);
+ Sample outSample = Synth::clipSampleEx(SampleEx(outR1) + (SampleEx(outR1) >> 1) + SampleEx(outR2) + (SampleEx(outR2) >> 1) + SampleEx(outR3));
#endif
*(outRight++) = weirdMul(outSample, wetLevel, 0xFF);
}
@@ -525,4 +528,4 @@ void BReverbModel::process(const Sample *inLeft, const Sample *inRight, Sample *
}
}
-}
+} // namespace MT32Emu
diff --git a/audio/softsynth/mt32/BReverbModel.h b/audio/softsynth/mt32/BReverbModel.h
index 764daf1a9ec..8cfc5da8a33 100644
--- a/audio/softsynth/mt32/BReverbModel.h
+++ b/audio/softsynth/mt32/BReverbModel.h
@@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -18,6 +18,10 @@
#ifndef MT32EMU_B_REVERB_MODEL_H
#define MT32EMU_B_REVERB_MODEL_H
+#include "globals.h"
+#include "internals.h"
+#include "Types.h"
+
namespace MT32Emu {
struct BReverbSettings {
@@ -27,11 +31,11 @@ struct BReverbSettings {
const Bit32u * const combSizes;
const Bit32u * const outLPositions;
const Bit32u * const outRPositions;
- const Bit32u * const filterFactors;
- const Bit32u * const feedbackFactors;
- const Bit32u * const dryAmps;
- const Bit32u * const wetLevels;
- const Bit32u lpfAmp;
+ const Bit8u * const filterFactors;
+ const Bit8u * const feedbackFactors;
+ const Bit8u * const dryAmps;
+ const Bit8u * const wetLevels;
+ const Bit8u lpfAmp;
};
class RingBuffer {
@@ -56,23 +60,23 @@ public:
class CombFilter : public RingBuffer {
protected:
- const Bit32u filterFactor;
- Bit32u feedbackFactor;
+ const Bit8u filterFactor;
+ Bit8u feedbackFactor;
public:
- CombFilter(const Bit32u size, const Bit32u useFilterFactor);
+ CombFilter(const Bit32u size, const Bit8u useFilterFactor);
virtual void process(const Sample in);
Sample getOutputAt(const Bit32u outIndex) const;
- void setFeedbackFactor(const Bit32u useFeedbackFactor);
+ void setFeedbackFactor(const Bit8u useFeedbackFactor);
};
class DelayWithLowPassFilter : public CombFilter {
- Bit32u amp;
+ Bit8u amp;
public:
- DelayWithLowPassFilter(const Bit32u useSize, const Bit32u useFilterFactor, const Bit32u useAmp);
+ DelayWithLowPassFilter(const Bit32u useSize, const Bit8u useFilterFactor, const Bit8u useAmp);
void process(const Sample in);
- void setFeedbackFactor(const Bit32u) {}
+ void setFeedbackFactor(const Bit8u) {}
};
class TapDelayCombFilter : public CombFilter {
@@ -80,7 +84,7 @@ class TapDelayCombFilter : public CombFilter {
Bit32u outR;
public:
- TapDelayCombFilter(const Bit32u useSize, const Bit32u useFilterFactor);
+ TapDelayCombFilter(const Bit32u useSize, const Bit8u useFilterFactor);
void process(const Sample in);
Sample getLeftOutput() const;
Sample getRightOutput() const;
@@ -93,8 +97,8 @@ class BReverbModel {
const BReverbSettings ¤tSettings;
const bool tapDelayMode;
- Bit32u dryAmp;
- Bit32u wetLevel;
+ Bit8u dryAmp;
+ Bit8u wetLevel;
static const BReverbSettings &getCM32L_LAPCSettings(const ReverbMode mode);
static const BReverbSettings &getMT32Settings(const ReverbMode mode);
@@ -108,11 +112,11 @@ public:
void close();
void mute();
void setParameters(Bit8u time, Bit8u level);
- void process(const Sample *inLeft, const Sample *inRight, Sample *outLeft, Sample *outRight, unsigned long numSamples);
+ void process(const Sample *inLeft, const Sample *inRight, Sample *outLeft, Sample *outRight, Bit32u numSamples);
bool isActive() const;
bool isMT32Compatible(const ReverbMode mode) const;
};
-}
+} // namespace MT32Emu
-#endif
+#endif // #ifndef MT32EMU_B_REVERB_MODEL_H
diff --git a/audio/softsynth/mt32/Enumerations.h b/audio/softsynth/mt32/Enumerations.h
new file mode 100644
index 00000000000..9b0a35d0bfe
--- /dev/null
+++ b/audio/softsynth/mt32/Enumerations.h
@@ -0,0 +1,155 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+/* Using two guards since this file may be included twice with different MT32EMU_C_ENUMERATIONS define. */
+
+#if (!defined MT32EMU_CPP_ENUMERATIONS_H && !defined MT32EMU_C_ENUMERATIONS) || (!defined MT32EMU_C_ENUMERATIONS_H && defined MT32EMU_C_ENUMERATIONS)
+
+#ifdef MT32EMU_C_ENUMERATIONS
+
+#define MT32EMU_C_ENUMERATIONS_H
+
+#define MT32EMU_DAC_INPUT_MODE_NAME mt32emu_dac_input_mode
+#define MT32EMU_DAC_INPUT_MODE(ident) MT32EMU_DAC_##ident
+
+#define MT32EMU_MIDI_DELAY_MODE_NAME mt32emu_midi_delay_mode
+#define MT32EMU_MIDI_DELAY_MODE(ident) MT32EMU_MDM_##ident
+
+#define MT32EMU_ANALOG_OUTPUT_MODE_NAME mt32emu_analog_output_mode
+#define MT32EMU_ANALOG_OUTPUT_MODE(ident) MT32EMU_AOM_##ident
+
+#define MT32EMU_PARTIAL_STATE_NAME mt32emu_partial_state
+#define MT32EMU_PARTIAL_STATE(ident) MT32EMU_PS_##ident
+
+#else /* #ifdef MT32EMU_C_ENUMERATIONS */
+
+#define MT32EMU_CPP_ENUMERATIONS_H
+
+#define MT32EMU_DAC_INPUT_MODE_NAME DACInputMode
+#define MT32EMU_DAC_INPUT_MODE(ident) DACInputMode_##ident
+
+#define MT32EMU_MIDI_DELAY_MODE_NAME MIDIDelayMode
+#define MT32EMU_MIDI_DELAY_MODE(ident) MIDIDelayMode_##ident
+
+#define MT32EMU_ANALOG_OUTPUT_MODE_NAME AnalogOutputMode
+#define MT32EMU_ANALOG_OUTPUT_MODE(ident) AnalogOutputMode_##ident
+
+#define MT32EMU_PARTIAL_STATE_NAME PartialState
+#define MT32EMU_PARTIAL_STATE(ident) PartialState_##ident
+
+namespace MT32Emu {
+
+#endif /* #ifdef MT32EMU_C_ENUMERATIONS */
+
+/**
+ * Methods for emulating the connection between the LA32 and the DAC, which involves
+ * some hacks in the real devices for doubling the volume.
+ * See also http://en.wikipedia.org/wiki/Roland_MT-32#Digital_overflow
+ */
+enum MT32EMU_DAC_INPUT_MODE_NAME {
+ /**
+ * Produces samples at double the volume, without tricks.
+ * Nicer overdrive characteristics than the DAC hacks (it simply clips samples within range)
+ * Higher quality than the real devices
+ */
+ MT32EMU_DAC_INPUT_MODE(NICE),
+
+ /**
+ * Produces samples that exactly match the bits output from the emulated LA32.
+ * Nicer overdrive characteristics than the DAC hacks (it simply clips samples within range)
+ * Much less likely to overdrive than any other mode.
+ * Half the volume of any of the other modes.
+ * Output gain is ignored for both LA32 and reverb output.
+ * Perfect for developers while debugging :)
+ */
+ MT32EMU_DAC_INPUT_MODE(PURE),
+
+ /**
+ * Re-orders the LA32 output bits as in early generation MT-32s (according to Wikipedia).
+ * Bit order at DAC (where each number represents the original LA32 output bit number, and XX means the bit is always low):
+ * 15 13 12 11 10 09 08 07 06 05 04 03 02 01 00 XX
+ */
+ MT32EMU_DAC_INPUT_MODE(GENERATION1),
+
+ /**
+ * Re-orders the LA32 output bits as in later generations (personally confirmed on my CM-32L - KG).
+ * Bit order at DAC (where each number represents the original LA32 output bit number):
+ * 15 13 12 11 10 09 08 07 06 05 04 03 02 01 00 14
+ */
+ MT32EMU_DAC_INPUT_MODE(GENERATION2)
+};
+
+/** Methods for emulating the effective delay of incoming MIDI messages introduced by a MIDI interface. */
+enum MT32EMU_MIDI_DELAY_MODE_NAME {
+ /** Process incoming MIDI events immediately. */
+ MT32EMU_MIDI_DELAY_MODE(IMMEDIATE),
+
+ /**
+ * Delay incoming short MIDI messages as if they where transferred via a MIDI cable to a real hardware unit and immediate sysex processing.
+ * This ensures more accurate timing of simultaneous NoteOn messages.
+ */
+ MT32EMU_MIDI_DELAY_MODE(DELAY_SHORT_MESSAGES_ONLY),
+
+ /** Delay all incoming MIDI events as if they where transferred via a MIDI cable to a real hardware unit.*/
+ MT32EMU_MIDI_DELAY_MODE(DELAY_ALL)
+};
+
+/** Methods for emulating the effects of analogue circuits of real hardware units on the output signal. */
+enum MT32EMU_ANALOG_OUTPUT_MODE_NAME {
+ /** Only digital path is emulated. The output samples correspond to the digital signal at the DAC entrance. */
+ MT32EMU_ANALOG_OUTPUT_MODE(DIGITAL_ONLY),
+ /** Coarse emulation of LPF circuit. High frequencies are boosted, sample rate remains unchanged. */
+ MT32EMU_ANALOG_OUTPUT_MODE(COARSE),
+ /**
+ * Finer emulation of LPF circuit. Output signal is upsampled to 48 kHz to allow emulation of audible mirror spectra above 16 kHz,
+ * which is passed through the LPF circuit without significant attenuation.
+ */
+ MT32EMU_ANALOG_OUTPUT_MODE(ACCURATE),
+ /**
+ * Same as AnalogOutputMode_ACCURATE mode but the output signal is 2x oversampled, i.e. the output sample rate is 96 kHz.
+ * This makes subsequent resampling easier. Besides, due to nonlinear passband of the LPF emulated, it takes fewer number of MACs
+ * compared to a regular LPF FIR implementations.
+ */
+ MT32EMU_ANALOG_OUTPUT_MODE(OVERSAMPLED)
+};
+
+enum MT32EMU_PARTIAL_STATE_NAME {
+ MT32EMU_PARTIAL_STATE(INACTIVE),
+ MT32EMU_PARTIAL_STATE(ATTACK),
+ MT32EMU_PARTIAL_STATE(SUSTAIN),
+ MT32EMU_PARTIAL_STATE(RELEASE)
+};
+
+#ifndef MT32EMU_C_ENUMERATIONS
+
+} // namespace MT32Emu
+
+#endif
+
+#undef MT32EMU_DAC_INPUT_MODE_NAME
+#undef MT32EMU_DAC_INPUT_MODE
+
+#undef MT32EMU_MIDI_DELAY_MODE_NAME
+#undef MT32EMU_MIDI_DELAY_MODE
+
+#undef MT32EMU_ANALOG_OUTPUT_MODE_NAME
+#undef MT32EMU_ANALOG_OUTPUT_MODE
+
+#undef MT32EMU_PARTIAL_STATE_NAME
+#undef MT32EMU_PARTIAL_STATE
+
+#endif /* #if (!defined MT32EMU_CPP_ENUMERATIONS_H && !defined MT32EMU_C_ENUMERATIONS) || (!defined MT32EMU_C_ENUMERATIONS_H && defined MT32EMU_C_ENUMERATIONS) */
diff --git a/audio/softsynth/mt32/File.cpp b/audio/softsynth/mt32/File.cpp
new file mode 100644
index 00000000000..07164823d30
--- /dev/null
+++ b/audio/softsynth/mt32/File.cpp
@@ -0,0 +1,77 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+
+#include "internals.h"
+
+#include "File.h"
+#include "sha1/sha1.h"
+
+namespace MT32Emu {
+
+AbstractFile::AbstractFile() : sha1DigestCalculated(false) {
+ sha1Digest[0] = 0;
+
+ reserved = NULL;
+}
+
+AbstractFile::AbstractFile(const SHA1Digest &useSHA1Digest) : sha1DigestCalculated(true) {
+ memcpy(sha1Digest, useSHA1Digest, sizeof(SHA1Digest) - 1);
+ sha1Digest[sizeof(SHA1Digest) - 1] = 0; // Ensure terminator char.
+
+ reserved = NULL;
+}
+
+const File::SHA1Digest &AbstractFile::getSHA1() {
+ if (sha1DigestCalculated) {
+ return sha1Digest;
+ }
+ sha1DigestCalculated = true;
+
+ size_t size = getSize();
+ if (size == 0) {
+ return sha1Digest;
+ }
+
+ const Bit8u *data = getData();
+ if (data == NULL) {
+ return sha1Digest;
+ }
+
+ unsigned char fileDigest[20];
+
+ sha1::calc(data, int(size), fileDigest);
+ sha1::toHexString(fileDigest, sha1Digest);
+ return sha1Digest;
+}
+
+ArrayFile::ArrayFile(const Bit8u *useData, size_t useSize) : data(useData), size(useSize)
+{}
+
+ArrayFile::ArrayFile(const Bit8u *useData, size_t useSize, const SHA1Digest &useSHA1Digest) : AbstractFile(useSHA1Digest), data(useData), size(useSize)
+{}
+
+size_t ArrayFile::getSize() {
+ return size;
+}
+
+const Bit8u *ArrayFile::getData() {
+ return data;
+}
+
+} // namespace MT32Emu
diff --git a/audio/softsynth/mt32/File.h b/audio/softsynth/mt32/File.h
new file mode 100644
index 00000000000..c9a7d582b4c
--- /dev/null
+++ b/audio/softsynth/mt32/File.h
@@ -0,0 +1,73 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef MT32EMU_FILE_H
+#define MT32EMU_FILE_H
+
+#include
+
+#include "globals.h"
+#include "Types.h"
+
+namespace MT32Emu {
+
+class MT32EMU_EXPORT File {
+public:
+ // Includes terminator char.
+ typedef char SHA1Digest[41];
+
+ virtual ~File() {}
+ virtual size_t getSize() = 0;
+ virtual const Bit8u *getData() = 0;
+ virtual const SHA1Digest &getSHA1() = 0;
+
+ virtual void close() = 0;
+};
+
+class MT32EMU_EXPORT AbstractFile : public File {
+public:
+ const SHA1Digest &getSHA1();
+
+protected:
+ AbstractFile();
+ AbstractFile(const SHA1Digest &sha1Digest);
+
+private:
+ bool sha1DigestCalculated;
+ SHA1Digest sha1Digest;
+
+ // Binary compatibility helper.
+ void *reserved;
+};
+
+class MT32EMU_EXPORT ArrayFile : public AbstractFile {
+public:
+ ArrayFile(const Bit8u *data, size_t size);
+ ArrayFile(const Bit8u *data, size_t size, const SHA1Digest &sha1Digest);
+
+ size_t getSize();
+ const Bit8u *getData();
+ void close() {}
+
+private:
+ const Bit8u *data;
+ size_t size;
+};
+
+} // namespace MT32Emu
+
+#endif // #ifndef MT32EMU_FILE_H
diff --git a/audio/softsynth/mt32/FileStream.cpp b/audio/softsynth/mt32/FileStream.cpp
new file mode 100644
index 00000000000..768c5fcdabc
--- /dev/null
+++ b/audio/softsynth/mt32/FileStream.cpp
@@ -0,0 +1,83 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+#include "internals.h"
+
+#include "FileStream.h"
+
+namespace MT32Emu {
+
+using std::ios_base;
+
+FileStream::FileStream() : ifsp(*new std::ifstream), data(NULL), size(0)
+{}
+
+FileStream::~FileStream() {
+ // destructor closes ifsp
+ delete &ifsp;
+ delete[] data;
+}
+
+size_t FileStream::getSize() {
+ if (size != 0) {
+ return size;
+ }
+ if (!ifsp.is_open()) {
+ return 0;
+ }
+ ifsp.seekg(0, ios_base::end);
+ size = size_t(ifsp.tellg());
+ return size;
+}
+
+const Bit8u *FileStream::getData() {
+ if (data != NULL) {
+ return data;
+ }
+ if (!ifsp.is_open()) {
+ return NULL;
+ }
+ if (getSize() == 0) {
+ return NULL;
+ }
+ Bit8u *fileData = new Bit8u[size];
+ if (fileData == NULL) {
+ return NULL;
+ }
+ ifsp.seekg(0);
+ ifsp.read(reinterpret_cast(fileData), std::streamsize(size));
+ if (size_t(ifsp.tellg()) != size) {
+ delete[] fileData;
+ return NULL;
+ }
+ data = fileData;
+ close();
+ return data;
+}
+
+bool FileStream::open(const char *filename) {
+ ifsp.clear();
+ ifsp.open(filename, ios_base::in | ios_base::binary);
+ return !ifsp.fail();
+}
+
+void FileStream::close() {
+ ifsp.close();
+ ifsp.clear();
+}
+
+} // namespace MT32Emu
diff --git a/audio/softsynth/mt32/FileStream.h b/audio/softsynth/mt32/FileStream.h
new file mode 100644
index 00000000000..2de6e801ffc
--- /dev/null
+++ b/audio/softsynth/mt32/FileStream.h
@@ -0,0 +1,46 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef MT32EMU_FILE_STREAM_H
+#define MT32EMU_FILE_STREAM_H
+
+#include
+
+#include "globals.h"
+#include "Types.h"
+#include "File.h"
+
+namespace MT32Emu {
+
+class FileStream : public AbstractFile {
+public:
+ MT32EMU_EXPORT FileStream();
+ MT32EMU_EXPORT ~FileStream();
+ MT32EMU_EXPORT size_t getSize();
+ MT32EMU_EXPORT const Bit8u *getData();
+ MT32EMU_EXPORT bool open(const char *filename);
+ MT32EMU_EXPORT void close();
+
+private:
+ std::ifstream &ifsp;
+ const Bit8u *data;
+ size_t size;
+};
+
+} // namespace MT32Emu
+
+#endif // #ifndef MT32EMU_FILE_STREAM_H
diff --git a/audio/softsynth/mt32/LA32FloatWaveGenerator.cpp b/audio/softsynth/mt32/LA32FloatWaveGenerator.cpp
index 42d820ebad9..824204e81bb 100644
--- a/audio/softsynth/mt32/LA32FloatWaveGenerator.cpp
+++ b/audio/softsynth/mt32/LA32FloatWaveGenerator.cpp
@@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -15,10 +15,11 @@
* along with this program. If not, see .
*/
-//#include
-#include "mt32emu.h"
+#ifndef MT32EMU_LA32_WAVE_GENERATOR_CPP
+#error This file should be included from LA32WaveGenerator.cpp only.
+#endif
+
#include "mmath.h"
-#include "internals.h"
namespace MT32Emu {
@@ -38,10 +39,10 @@ float LA32WaveGenerator::getPCMSample(unsigned int position) {
return ((pcmSample & 32768) == 0) ? sampleValue : -sampleValue;
}
-void LA32WaveGenerator::initSynth(const bool sawtoothWaveform, const Bit8u pulseWidth, const Bit8u resonance) {
- this->sawtoothWaveform = sawtoothWaveform;
- this->pulseWidth = pulseWidth;
- this->resonance = resonance;
+void LA32WaveGenerator::initSynth(const bool useSawtoothWaveform, const Bit8u usePulseWidth, const Bit8u useResonance) {
+ sawtoothWaveform = useSawtoothWaveform;
+ pulseWidth = usePulseWidth;
+ resonance = useResonance;
wavePos = 0.0f;
lastFreq = 0.0f;
@@ -50,24 +51,24 @@ void LA32WaveGenerator::initSynth(const bool sawtoothWaveform, const Bit8u pulse
active = true;
}
-void LA32WaveGenerator::initPCM(const Bit16s * const pcmWaveAddress, const Bit32u pcmWaveLength, const bool pcmWaveLooped, const bool pcmWaveInterpolated) {
- this->pcmWaveAddress = pcmWaveAddress;
- this->pcmWaveLength = pcmWaveLength;
- this->pcmWaveLooped = pcmWaveLooped;
- this->pcmWaveInterpolated = pcmWaveInterpolated;
+void LA32WaveGenerator::initPCM(const Bit16s * const usePCMWaveAddress, const Bit32u usePCMWaveLength, const bool usePCMWaveLooped, const bool usePCMWaveInterpolated) {
+ pcmWaveAddress = usePCMWaveAddress;
+ pcmWaveLength = usePCMWaveLength;
+ pcmWaveLooped = usePCMWaveLooped;
+ pcmWaveInterpolated = usePCMWaveInterpolated;
pcmPosition = 0.0f;
active = true;
}
+// ampVal - Logarithmic amp of the wave generator
+// pitch - Logarithmic frequency of the resulting wave
+// cutoffRampVal - Composed of the base cutoff in range [78..178] left-shifted by 18 bits and the TVF modifier
float LA32WaveGenerator::generateNextSample(const Bit32u ampVal, const Bit16u pitch, const Bit32u cutoffRampVal) {
if (!active) {
return 0.0f;
}
- this->amp = amp;
- this->pitch = pitch;
-
float sample = 0.0f;
// SEMI-CONFIRMED: From sample analysis:
@@ -284,9 +285,9 @@ bool LA32WaveGenerator::isPCMWave() const {
return pcmWaveAddress != NULL;
}
-void LA32PartialPair::init(const bool ringModulated, const bool mixed) {
- this->ringModulated = ringModulated;
- this->mixed = mixed;
+void LA32PartialPair::init(const bool useRingModulated, const bool useMixed) {
+ ringModulated = useRingModulated;
+ mixed = useMixed;
masterOutputSample = 0.0f;
slaveOutputSample = 0.0f;
}
@@ -354,4 +355,4 @@ bool LA32PartialPair::isActive(const PairType useMaster) const {
return useMaster == MASTER ? master.isActive() : slave.isActive();
}
-}
+} // namespace MT32Emu
diff --git a/audio/softsynth/mt32/LA32FloatWaveGenerator.h b/audio/softsynth/mt32/LA32FloatWaveGenerator.h
index 329e5de2585..89b6fe479a1 100644
--- a/audio/softsynth/mt32/LA32FloatWaveGenerator.h
+++ b/audio/softsynth/mt32/LA32FloatWaveGenerator.h
@@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -16,14 +16,15 @@
*/
#ifndef MT32EMU_LA32_WAVE_GENERATOR_H
-#define MT32EMU_LA32_WAVE_GENERATOR_H
+#error This file should be included from LA32WaveGenerator.h only.
+#endif
namespace MT32Emu {
/**
* LA32WaveGenerator is aimed to represent the exact model of LA32 wave generator.
* The output square wave is created by adding high / low linear segments in-between
- * the rising and falling cosine segments. Basically, it’s very similar to the phase distortion synthesis.
+ * the rising and falling cosine segments. Basically, it's very similar to the phase distortion synthesis.
* Behaviour of a true resonance filter is emulated by adding decaying sine wave.
* The beginning and the ending of the resonant sine is multiplied by a cosine window.
* To synthesise sawtooth waves, the resulting square wave is multiplied by synchronous cosine wave.
@@ -38,12 +39,6 @@ class LA32WaveGenerator {
// True means the resulting square wave is to be multiplied by the synchronous cosine
bool sawtoothWaveform;
- // Logarithmic amp of the wave generator
- Bit32u amp;
-
- // Logarithmic frequency of the resulting wave
- Bit16u pitch;
-
// Values in range [1..31]
// Value 1 correspong to the minimum resonance
Bit8u resonance;
@@ -53,9 +48,6 @@ class LA32WaveGenerator {
// Value 255 corresponds to the maximum possible asymmetric of the resulting wave
Bit8u pulseWidth;
- // Composed of the base cutoff in range [78..178] left-shifted by 18 bits and the TVF modifier
- Bit32u cutoffVal;
-
// Logarithmic PCM sample start address
const Bit16s *pcmWaveAddress;
@@ -96,7 +88,7 @@ public:
// Return true if the WG engine generates PCM wave samples
bool isPCMWave() const;
-};
+}; // class LA32WaveGenerator
// LA32PartialPair contains a structure of two partials being mixed / ring modulated
class LA32PartialPair {
@@ -135,8 +127,6 @@ public:
// Return active state of the WG engine
bool isActive(const PairType master) const;
-};
+}; // class LA32PartialPair
} // namespace MT32Emu
-
-#endif // #ifndef MT32EMU_LA32_WAVE_GENERATOR_H
diff --git a/audio/softsynth/mt32/LA32Ramp.cpp b/audio/softsynth/mt32/LA32Ramp.cpp
index 2b31a330d2f..a4da4f57a88 100644
--- a/audio/softsynth/mt32/LA32Ramp.cpp
+++ b/audio/softsynth/mt32/LA32Ramp.cpp
@@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -47,12 +47,12 @@ We haven't fully explored:
- Values when ramping between levels (though this is probably correct).
- Transition timing (may not be 100% accurate, especially for very fast ramps).
*/
-//#include
-#include "mt32emu.h"
-#include "mmath.h"
#include "internals.h"
+#include "LA32Ramp.h"
+#include "Tables.h"
+
namespace MT32Emu {
// SEMI-CONFIRMED from sample analysis.
@@ -152,4 +152,4 @@ void LA32Ramp::reset() {
interruptRaised = false;
}
-}
+} // namespace MT32Emu
diff --git a/audio/softsynth/mt32/LA32Ramp.h b/audio/softsynth/mt32/LA32Ramp.h
index 796b4d1eec1..5e4ddf720ee 100644
--- a/audio/softsynth/mt32/LA32Ramp.h
+++ b/audio/softsynth/mt32/LA32Ramp.h
@@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -18,6 +18,9 @@
#ifndef MT32EMU_LA32RAMP_H
#define MT32EMU_LA32RAMP_H
+#include "globals.h"
+#include "Types.h"
+
namespace MT32Emu {
class LA32Ramp {
@@ -38,6 +41,6 @@ public:
void reset();
};
-}
+} // namespace MT32Emu
-#endif /* TVA_H_ */
+#endif // #ifndef MT32EMU_LA32RAMP_H
diff --git a/audio/softsynth/mt32/LA32WaveGenerator.cpp b/audio/softsynth/mt32/LA32WaveGenerator.cpp
index 765f75fa618..a9c425beac5 100644
--- a/audio/softsynth/mt32/LA32WaveGenerator.cpp
+++ b/audio/softsynth/mt32/LA32WaveGenerator.cpp
@@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -15,15 +15,19 @@
* along with this program. If not, see .
*/
-#if MT32EMU_USE_FLOAT_SAMPLES
-#include "LA32FloatWaveGenerator.cpp"
-#else
+#include
-//#include
-#include "mt32emu.h"
-#include "mmath.h"
#include "internals.h"
+#include "LA32WaveGenerator.h"
+#include "Tables.h"
+
+#if MT32EMU_USE_FLOAT_SAMPLES
+#define MT32EMU_LA32_WAVE_GENERATOR_CPP
+#include "LA32FloatWaveGenerator.cpp"
+#undef MT32EMU_LA32_WAVE_GENERATOR_CPP
+#else
+
namespace MT32Emu {
static const Bit32u SINE_SEGMENT_RELATIVE_LENGTH = 1 << 18;
@@ -50,7 +54,7 @@ Bit16s LA32Utilites::unlog(const LogSample &logSample) {
void LA32Utilites::addLogSamples(LogSample &logSample1, const LogSample &logSample2) {
Bit32u logSampleValue = logSample1.logValue + logSample2.logValue;
- logSample1.logValue = logSampleValue < 65536 ? (Bit16u)logSampleValue : 65535;
+ logSample1.logValue = logSampleValue < 65536 ? Bit16u(logSampleValue) : 65535;
logSample1.sign = logSample1.sign == logSample2.sign ? LogSample::POSITIVE : LogSample::NEGATIVE;
}
@@ -130,9 +134,7 @@ void LA32WaveGenerator::advancePosition() {
Bit32u lowLinearLength = (resonanceWaveLengthFactor << 8) - 4 * SINE_SEGMENT_RELATIVE_LENGTH - highLinearLength;
computePositions(highLinearLength, lowLinearLength, resonanceWaveLengthFactor);
- // resonancePhase computation hack
- int *resonancePhaseAlias = (int *)&resonancePhase;
- *resonancePhaseAlias = ((resonanceSinePosition >> 18) + (phase > POSITIVE_FALLING_SINE_SEGMENT ? 2 : 0)) & 3;
+ resonancePhase = ResonancePhase(((resonanceSinePosition >> 18) + (phase > POSITIVE_FALLING_SINE_SEGMENT ? 2 : 0)) & 3);
}
void LA32WaveGenerator::generateNextSquareWaveLogSample() {
@@ -158,7 +160,7 @@ void LA32WaveGenerator::generateNextSquareWaveLogSample() {
logSampleValue += (MIDDLE_CUTOFF_VALUE - cutoffVal) >> 9;
}
- squareLogSample.logValue = logSampleValue < 65536 ? (Bit16u)logSampleValue : 65535;
+ squareLogSample.logValue = logSampleValue < 65536 ? Bit16u(logSampleValue) : 65535;
squareLogSample.sign = phase < NEGATIVE_FALLING_SINE_SEGMENT ? LogSample::POSITIVE : LogSample::NEGATIVE;
}
@@ -198,7 +200,7 @@ void LA32WaveGenerator::generateNextResonanceWaveLogSample() {
// After all the amp decrements are added, it should be safe now to adjust the amp of the resonance wave to what we see on captures
logSampleValue -= 1 << 12;
- resonanceLogSample.logValue = logSampleValue < 65536 ? (Bit16u)logSampleValue : 65535;
+ resonanceLogSample.logValue = logSampleValue < 65536 ? Bit16u(logSampleValue) : 65535;
resonanceLogSample.sign = resonancePhase < NEGATIVE_FALLING_RESONANCE_SINE_SEGMENT ? LogSample::POSITIVE : LogSample::NEGATIVE;
}
@@ -216,7 +218,7 @@ void LA32WaveGenerator::generateNextSawtoothCosineLogSample(LogSample &logSample
void LA32WaveGenerator::pcmSampleToLogSample(LogSample &logSample, const Bit16s pcmSample) const {
Bit32u logSampleValue = (32787 - (pcmSample & 32767)) << 1;
logSampleValue += amp >> 10;
- logSample.logValue = logSampleValue < 65536 ? (Bit16u)logSampleValue : 65535;
+ logSample.logValue = logSampleValue < 65536 ? Bit16u(logSampleValue) : 65535;
logSample.sign = pcmSample < 0 ? LogSample::NEGATIVE : LogSample::POSITIVE;
}
@@ -377,7 +379,7 @@ Bit16s LA32PartialPair::unlogAndMixWGOutput(const LA32WaveGenerator &wg) {
Bit16s firstSample = LA32Utilites::unlog(wg.getOutputLogSample(true));
Bit16s secondSample = LA32Utilites::unlog(wg.getOutputLogSample(false));
if (wg.isPCMWave()) {
- return Bit16s(firstSample + ((Bit32s(secondSample - firstSample) * wg.getPCMInterpolationFactor()) >> 7));
+ return Bit16s(firstSample + (((Bit32s(secondSample) - Bit32s(firstSample)) * wg.getPCMInterpolationFactor()) >> 7));
}
return firstSample + secondSample;
}
@@ -407,7 +409,7 @@ Bit16s LA32PartialPair::nextOutSample() {
Bit16s slaveSample = slave.isPCMWave() ? LA32Utilites::unlog(slave.getOutputLogSample(true)) : unlogAndMixWGOutput(slave);
slaveSample <<= 2;
slaveSample >>= 2;
- Bit16s ringModulatedSample = Bit16s(((Bit32s)masterSample * (Bit32s)slaveSample) >> 13);
+ Bit16s ringModulatedSample = Bit16s((Bit32s(masterSample) * Bit32s(slaveSample)) >> 13);
return mixed ? nonOverdrivenMasterSample + ringModulatedSample : ringModulatedSample;
}
@@ -423,6 +425,6 @@ bool LA32PartialPair::isActive(const PairType useMaster) const {
return useMaster == MASTER ? master.isActive() : slave.isActive();
}
-}
+} // namespace MT32Emu
#endif // #if MT32EMU_USE_FLOAT_SAMPLES
diff --git a/audio/softsynth/mt32/LA32WaveGenerator.h b/audio/softsynth/mt32/LA32WaveGenerator.h
index 212abe2b19c..6a40e44c98d 100644
--- a/audio/softsynth/mt32/LA32WaveGenerator.h
+++ b/audio/softsynth/mt32/LA32WaveGenerator.h
@@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -15,13 +15,17 @@
* along with this program. If not, see .
*/
+#ifndef MT32EMU_LA32_WAVE_GENERATOR_H
+#define MT32EMU_LA32_WAVE_GENERATOR_H
+
+#include "globals.h"
+#include "internals.h"
+#include "Types.h"
+
#if MT32EMU_USE_FLOAT_SAMPLES
#include "LA32FloatWaveGenerator.h"
#else
-#ifndef MT32EMU_LA32_WAVE_GENERATOR_H
-#define MT32EMU_LA32_WAVE_GENERATOR_H
-
namespace MT32Emu {
/**
@@ -55,7 +59,7 @@ public:
/**
* LA32WaveGenerator is aimed to represent the exact model of LA32 wave generator.
* The output square wave is created by adding high / low linear segments in-between
- * the rising and falling cosine segments. Basically, it’s very similar to the phase distortion synthesis.
+ * the rising and falling cosine segments. Basically, it's very similar to the phase distortion synthesis.
* Behaviour of a true resonance filter is emulated by adding decaying sine wave.
* The beginning and the ending of the resonant sine is multiplied by a cosine window.
* To synthesise sawtooth waves, the resulting square wave is multiplied by synchronous cosine wave.
@@ -143,7 +147,7 @@ class LA32WaveGenerator {
} phase;
// Current phase of the resonance wave
- enum {
+ enum ResonancePhase {
POSITIVE_RISING_RESONANCE_SINE_SEGMENT,
POSITIVE_FALLING_RESONANCE_SINE_SEGMENT,
NEGATIVE_FALLING_RESONANCE_SINE_SEGMENT,
@@ -200,7 +204,7 @@ public:
// Return current PCM interpolation factor
Bit32u getPCMInterpolationFactor() const;
-};
+}; // class LA32WaveGenerator
// LA32PartialPair contains a structure of two partials being mixed / ring modulated
class LA32PartialPair {
@@ -239,10 +243,10 @@ public:
// Return active state of the WG engine
bool isActive(const PairType master) const;
-};
+}; // class LA32PartialPair
} // namespace MT32Emu
-#endif // #ifndef MT32EMU_LA32_WAVE_GENERATOR_H
-
#endif // #if MT32EMU_USE_FLOAT_SAMPLES
+
+#endif // #ifndef MT32EMU_LA32_WAVE_GENERATOR_H
diff --git a/audio/softsynth/mt32/MemoryRegion.h b/audio/softsynth/mt32/MemoryRegion.h
index c0cb041e115..f8d7da18f83 100644
--- a/audio/softsynth/mt32/MemoryRegion.h
+++ b/audio/softsynth/mt32/MemoryRegion.h
@@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -18,12 +18,20 @@
#ifndef MT32EMU_MEMORY_REGION_H
#define MT32EMU_MEMORY_REGION_H
+#include
+
+#include "globals.h"
+#include "Types.h"
+#include "Structures.h"
+
namespace MT32Emu {
enum MemoryRegionType {
MR_PatchTemp, MR_RhythmTemp, MR_TimbreTemp, MR_Patches, MR_Timbres, MR_System, MR_Display, MR_Reset
};
+class Synth;
+
class MemoryRegion {
private:
Synth *synth;
@@ -84,7 +92,7 @@ public:
}
void read(unsigned int entry, unsigned int off, Bit8u *dst, unsigned int len) const;
void write(unsigned int entry, unsigned int off, const Bit8u *src, unsigned int len, bool init = false) const;
-};
+}; // class MemoryRegion
class PatchTempMemoryRegion : public MemoryRegion {
public:
@@ -112,13 +120,13 @@ public:
};
class DisplayMemoryRegion : public MemoryRegion {
public:
- DisplayMemoryRegion(Synth *useSynth) : MemoryRegion(useSynth, NULL, NULL, MR_Display, MT32EMU_MEMADDR(0x200000), MAX_SYSEX_SIZE - 1, 1) {}
+ DisplayMemoryRegion(Synth *useSynth) : MemoryRegion(useSynth, NULL, NULL, MR_Display, MT32EMU_MEMADDR(0x200000), SYSEX_BUFFER_SIZE - 1, 1) {}
};
class ResetMemoryRegion : public MemoryRegion {
public:
ResetMemoryRegion(Synth *useSynth) : MemoryRegion(useSynth, NULL, NULL, MR_Reset, MT32EMU_MEMADDR(0x7F0000), 0x3FFF, 1) {}
};
-}
+} // namespace MT32Emu
-#endif
+#endif // #ifndef MT32EMU_MEMORY_REGION_H
diff --git a/audio/softsynth/mt32/MidiEventQueue.h b/audio/softsynth/mt32/MidiEventQueue.h
index b1948c5f8ea..1a5ff0ad335 100644
--- a/audio/softsynth/mt32/MidiEventQueue.h
+++ b/audio/softsynth/mt32/MidiEventQueue.h
@@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -18,6 +18,9 @@
#ifndef MT32EMU_MIDI_EVENT_QUEUE_H
#define MT32EMU_MIDI_EVENT_QUEUE_H
+#include "globals.h"
+#include "Types.h"
+
namespace MT32Emu {
/**
@@ -60,8 +63,9 @@ public:
const MidiEvent *peekMidiEvent();
void dropMidiEvent();
bool isFull() const;
+ bool inline isEmpty() const;
};
-}
+} // namespace MT32Emu
-#endif
+#endif // #ifndef MT32EMU_MIDI_EVENT_QUEUE_H
diff --git a/audio/softsynth/mt32/MidiStreamParser.cpp b/audio/softsynth/mt32/MidiStreamParser.cpp
new file mode 100644
index 00000000000..f9ead3369c0
--- /dev/null
+++ b/audio/softsynth/mt32/MidiStreamParser.cpp
@@ -0,0 +1,289 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include
+
+#include "internals.h"
+
+#include "MidiStreamParser.h"
+#include "Synth.h"
+
+using namespace MT32Emu;
+
+DefaultMidiStreamParser::DefaultMidiStreamParser(Synth &useSynth, Bit32u initialStreamBufferCapacity) :
+ MidiStreamParser(initialStreamBufferCapacity), synth(useSynth), timestampSet(false) {}
+
+void DefaultMidiStreamParser::setTimestamp(const Bit32u useTimestamp) {
+ timestampSet = true;
+ timestamp = useTimestamp;
+}
+
+void DefaultMidiStreamParser::resetTimestamp() {
+ timestampSet = false;
+}
+
+void DefaultMidiStreamParser::handleShortMessage(const Bit32u message) {
+ do {
+ if (timestampSet) {
+ if (synth.playMsg(message, timestamp)) return;
+ }
+ else {
+ if (synth.playMsg(message)) return;
+ }
+ } while (synth.reportHandler->onMIDIQueueOverflow());
+}
+
+void DefaultMidiStreamParser::handleSysex(const Bit8u *stream, const Bit32u length) {
+ do {
+ if (timestampSet) {
+ if (synth.playSysex(stream, length, timestamp)) return;
+ }
+ else {
+ if (synth.playSysex(stream, length)) return;
+ }
+ } while (synth.reportHandler->onMIDIQueueOverflow());
+}
+
+void DefaultMidiStreamParser::handleSystemRealtimeMessage(const Bit8u realtime) {
+ synth.reportHandler->onMIDISystemRealtime(realtime);
+}
+
+void DefaultMidiStreamParser::printDebug(const char *debugMessage) {
+ synth.printDebug("%s", debugMessage);
+}
+
+MidiStreamParser::MidiStreamParser(Bit32u initialStreamBufferCapacity) :
+ MidiStreamParserImpl(*this, *this, initialStreamBufferCapacity) {}
+
+MidiStreamParserImpl::MidiStreamParserImpl(MidiReceiver &useReceiver, MidiReporter &useReporter, Bit32u initialStreamBufferCapacity) :
+ midiReceiver(useReceiver), midiReporter(useReporter)
+{
+ if (initialStreamBufferCapacity < SYSEX_BUFFER_SIZE) initialStreamBufferCapacity = SYSEX_BUFFER_SIZE;
+ if (MAX_STREAM_BUFFER_SIZE < initialStreamBufferCapacity) initialStreamBufferCapacity = MAX_STREAM_BUFFER_SIZE;
+ streamBufferCapacity = initialStreamBufferCapacity;
+ streamBuffer = new Bit8u[streamBufferCapacity];
+ streamBufferSize = 0;
+ runningStatus = 0;
+
+ reserved = NULL;
+}
+
+MidiStreamParserImpl::~MidiStreamParserImpl() {
+ delete[] streamBuffer;
+}
+
+void MidiStreamParserImpl::parseStream(const Bit8u *stream, Bit32u length) {
+ while (length > 0) {
+ Bit32u parsedMessageLength = 0;
+ if (0xF8 <= *stream) {
+ // Process System Realtime immediately and go on
+ midiReceiver.handleSystemRealtimeMessage(*stream);
+ parsedMessageLength = 1;
+ // No effect on the running status
+ } else if (streamBufferSize > 0) {
+ // Check if there is something in streamBuffer waiting for being processed
+ if (*streamBuffer == 0xF0) {
+ parsedMessageLength = parseSysexFragment(stream, length);
+ } else {
+ parsedMessageLength = parseShortMessageDataBytes(stream, length);
+ }
+ } else {
+ if (*stream == 0xF0) {
+ runningStatus = 0; // SysEx clears the running status
+ parsedMessageLength = parseSysex(stream, length);
+ } else {
+ parsedMessageLength = parseShortMessageStatus(stream);
+ }
+ }
+
+ // Parsed successfully
+ stream += parsedMessageLength;
+ length -= parsedMessageLength;
+ }
+}
+
+void MidiStreamParserImpl::processShortMessage(const Bit32u message) {
+ // Adds running status to the MIDI message if it doesn't contain one
+ Bit8u status = Bit8u(message);
+ if (0xF8 <= status) {
+ midiReceiver.handleSystemRealtimeMessage(status);
+ } else if (processStatusByte(status)) {
+ midiReceiver.handleShortMessage((message << 8) | status);
+ } else if (0x80 <= status) { // If no running status available yet, skip this message
+ midiReceiver.handleShortMessage(message);
+ }
+}
+
+// We deal with SysEx messages below 512 bytes long in most cases. Nevertheless, it seems reasonable to support a possibility
+// to load bulk dumps using a single message. However, this is known to fail with a real device due to limited input buffer size.
+bool MidiStreamParserImpl::checkStreamBufferCapacity(const bool preserveContent) {
+ if (streamBufferSize < streamBufferCapacity) return true;
+ if (streamBufferCapacity < MAX_STREAM_BUFFER_SIZE) {
+ Bit8u *oldStreamBuffer = streamBuffer;
+ streamBufferCapacity = MAX_STREAM_BUFFER_SIZE;
+ streamBuffer = new Bit8u[streamBufferCapacity];
+ if (preserveContent) memcpy(streamBuffer, oldStreamBuffer, streamBufferSize);
+ delete[] oldStreamBuffer;
+ return true;
+ }
+ return false;
+}
+
+// Checks input byte whether it is a status byte. If not, replaces it with running status when available.
+// Returns true if the input byte was changed to running status.
+bool MidiStreamParserImpl::processStatusByte(Bit8u &status) {
+ if (status < 0x80) {
+ // First byte isn't status, try running status
+ if (runningStatus < 0x80) {
+ // No running status available yet
+ midiReporter.printDebug("processStatusByte: No valid running status yet, MIDI message ignored");
+ return false;
+ }
+ status = runningStatus;
+ return true;
+ } else if (status < 0xF0) {
+ // Store current status as running for a Voice message
+ runningStatus = status;
+ } else if (status < 0xF8) {
+ // System Common clears running status
+ runningStatus = 0;
+ } // System Realtime doesn't affect running status
+ return false;
+}
+
+// Returns # of bytes parsed
+Bit32u MidiStreamParserImpl::parseShortMessageStatus(const Bit8u stream[]) {
+ Bit8u status = *stream;
+ Bit32u parsedLength = processStatusByte(status) ? 0 : 1;
+ if (0x80 <= status) { // If no running status available yet, skip one byte
+ *streamBuffer = status;
+ ++streamBufferSize;
+ }
+ return parsedLength;
+}
+
+// Returns # of bytes parsed
+Bit32u MidiStreamParserImpl::parseShortMessageDataBytes(const Bit8u stream[], Bit32u length) {
+ const Bit32u shortMessageLength = Synth::getShortMessageLength(*streamBuffer);
+ Bit32u parsedLength = 0;
+
+ // Append incoming bytes to streamBuffer
+ while ((streamBufferSize < shortMessageLength) && (length-- > 0)) {
+ Bit8u dataByte = *(stream++);
+ if (dataByte < 0x80) {
+ // Add data byte to streamBuffer
+ streamBuffer[streamBufferSize++] = dataByte;
+ } else if (dataByte < 0xF8) {
+ // Discard invalid bytes and start over
+ char s[128];
+ sprintf(s, "parseShortMessageDataBytes: Invalid short message: status %02x, expected length %i, actual %i -> ignored", *streamBuffer, shortMessageLength, streamBufferSize);
+ midiReporter.printDebug(s);
+ streamBufferSize = 0; // Clear streamBuffer
+ return parsedLength;
+ } else {
+ // Bypass System Realtime message
+ midiReceiver.handleSystemRealtimeMessage(dataByte);
+ }
+ ++parsedLength;
+ }
+ if (streamBufferSize < shortMessageLength) return parsedLength; // Still lacks data bytes
+
+ // Assemble short message
+ Bit32u shortMessage = streamBuffer[0];
+ for (Bit32u i = 1; i < shortMessageLength; ++i) {
+ shortMessage |= streamBuffer[i] << (i << 3);
+ }
+ midiReceiver.handleShortMessage(shortMessage);
+ streamBufferSize = 0; // Clear streamBuffer
+ return parsedLength;
+}
+
+// Returns # of bytes parsed
+Bit32u MidiStreamParserImpl::parseSysex(const Bit8u stream[], const Bit32u length) {
+ // Find SysEx length
+ Bit32u sysexLength = 1;
+ while (sysexLength < length) {
+ Bit8u nextByte = stream[sysexLength++];
+ if (0x80 <= nextByte) {
+ if (nextByte == 0xF7) {
+ // End of SysEx
+ midiReceiver.handleSysex(stream, sysexLength);
+ return sysexLength;
+ }
+ if (0xF8 <= nextByte) {
+ // The System Realtime message must be processed right after return
+ // but the SysEx is actually fragmented and to be reconstructed in streamBuffer
+ --sysexLength;
+ break;
+ }
+ // Illegal status byte in SysEx message, aborting
+ midiReporter.printDebug("parseSysex: SysEx message lacks end-of-sysex (0xf7), ignored");
+ // Continue parsing from that point
+ return sysexLength - 1;
+ }
+ }
+
+ // Store incomplete SysEx message for further processing
+ streamBufferSize = sysexLength;
+ if (checkStreamBufferCapacity(false)) {
+ memcpy(streamBuffer, stream, sysexLength);
+ } else {
+ // Not enough buffer capacity, don't care about the real buffer content, just mark the first byte
+ *streamBuffer = *stream;
+ streamBufferSize = streamBufferCapacity;
+ }
+ return sysexLength;
+}
+
+// Returns # of bytes parsed
+Bit32u MidiStreamParserImpl::parseSysexFragment(const Bit8u stream[], const Bit32u length) {
+ Bit32u parsedLength = 0;
+ while (parsedLength < length) {
+ Bit8u nextByte = stream[parsedLength++];
+ if (nextByte < 0x80) {
+ // Add SysEx data byte to streamBuffer
+ if (checkStreamBufferCapacity(true)) streamBuffer[streamBufferSize++] = nextByte;
+ continue;
+ }
+ if (0xF8 <= nextByte) {
+ // Bypass System Realtime message
+ midiReceiver.handleSystemRealtimeMessage(nextByte);
+ continue;
+ }
+ if (nextByte != 0xF7) {
+ // Illegal status byte in SysEx message, aborting
+ midiReporter.printDebug("parseSysexFragment: SysEx message lacks end-of-sysex (0xf7), ignored");
+ // Clear streamBuffer and continue parsing from that point
+ streamBufferSize = 0;
+ --parsedLength;
+ break;
+ }
+ // End of SysEx
+ if (checkStreamBufferCapacity(true)) {
+ streamBuffer[streamBufferSize++] = nextByte;
+ midiReceiver.handleSysex(streamBuffer, streamBufferSize);
+ streamBufferSize = 0; // Clear streamBuffer
+ break;
+ }
+ // Encountered streamBuffer overrun
+ midiReporter.printDebug("parseSysexFragment: streamBuffer overrun while receiving SysEx message, ignored. Max allowed size of fragmented SysEx is 32768 bytes.");
+ streamBufferSize = 0; // Clear streamBuffer
+ break;
+ }
+ return parsedLength;
+}
diff --git a/audio/softsynth/mt32/MidiStreamParser.h b/audio/softsynth/mt32/MidiStreamParser.h
new file mode 100644
index 00000000000..bd31b77c897
--- /dev/null
+++ b/audio/softsynth/mt32/MidiStreamParser.h
@@ -0,0 +1,124 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef MT32EMU_MIDI_STREAM_PARSER_H
+#define MT32EMU_MIDI_STREAM_PARSER_H
+
+#include "globals.h"
+#include "Types.h"
+
+namespace MT32Emu {
+
+class Synth;
+
+// Interface for a user-supplied class to receive parsed well-formed MIDI messages.
+class MT32EMU_EXPORT MidiReceiver {
+public:
+ // Invoked when a complete short MIDI message is parsed in the input MIDI stream.
+ virtual void handleShortMessage(const Bit32u message) = 0;
+
+ // Invoked when a complete well-formed System Exclusive MIDI message is parsed in the input MIDI stream.
+ virtual void handleSysex(const Bit8u stream[], const Bit32u length) = 0;
+
+ // Invoked when a System Realtime MIDI message is parsed in the input MIDI stream.
+ virtual void handleSystemRealtimeMessage(const Bit8u realtime) = 0;
+
+protected:
+ ~MidiReceiver() {}
+};
+
+// Interface for a user-supplied class to receive notifications of input MIDI stream parse errors.
+class MT32EMU_EXPORT MidiReporter {
+public:
+ // Invoked when an error occurs during processing the input MIDI stream.
+ virtual void printDebug(const char *debugMessage) = 0;
+
+protected:
+ ~MidiReporter() {}
+};
+
+// Provides a context for parsing a stream of MIDI events coming from a single source.
+// There can be multiple MIDI sources feeding MIDI events to a single Synth object.
+// NOTE: Calls from multiple threads which feed a single Synth object with data must be explicitly synchronised,
+// although, no synchronisation is required with the rendering thread.
+class MT32EMU_EXPORT MidiStreamParserImpl {
+public:
+ // The first two arguments provide for implementations of essential interfaces needed.
+ // The third argument specifies streamBuffer initial capacity. The buffer capacity should be large enough to fit the longest SysEx expected.
+ // If a longer SysEx occurs, streamBuffer is reallocated to the maximum size of MAX_STREAM_BUFFER_SIZE (32768 bytes).
+ // Default capacity is SYSEX_BUFFER_SIZE (1000 bytes) which is enough to fit SysEx messages in common use.
+ MidiStreamParserImpl(MidiReceiver &, MidiReporter &, Bit32u initialStreamBufferCapacity = SYSEX_BUFFER_SIZE);
+ virtual ~MidiStreamParserImpl();
+
+ // Parses a block of raw MIDI bytes. All the parsed MIDI messages are sent in sequence to the user-supplied methods for further processing.
+ // SysEx messages are allowed to be fragmented across several calls to this method. Running status is also handled for short messages.
+ // NOTE: the total length of a SysEx message being fragmented shall not exceed MAX_STREAM_BUFFER_SIZE (32768 bytes).
+ void parseStream(const Bit8u *stream, Bit32u length);
+
+ // Convenience method which accepts a Bit32u-encoded short MIDI message and sends it to the user-supplied method for further processing.
+ // The short MIDI message may contain no status byte, the running status is used in this case.
+ void processShortMessage(const Bit32u message);
+
+private:
+ Bit8u runningStatus;
+ Bit8u *streamBuffer;
+ Bit32u streamBufferCapacity;
+ Bit32u streamBufferSize;
+ MidiReceiver &midiReceiver;
+ MidiReporter &midiReporter;
+
+ // Binary compatibility helper.
+ void *reserved;
+
+ bool checkStreamBufferCapacity(const bool preserveContent);
+ bool processStatusByte(Bit8u &status);
+ Bit32u parseShortMessageStatus(const Bit8u stream[]);
+ Bit32u parseShortMessageDataBytes(const Bit8u stream[], Bit32u length);
+ Bit32u parseSysex(const Bit8u stream[], const Bit32u length);
+ Bit32u parseSysexFragment(const Bit8u stream[], const Bit32u length);
+}; // class MidiStreamParserImpl
+
+// An abstract class that provides a context for parsing a stream of MIDI events coming from a single source.
+class MT32EMU_EXPORT MidiStreamParser : public MidiStreamParserImpl, protected MidiReceiver, protected MidiReporter {
+public:
+ // The argument specifies streamBuffer initial capacity. The buffer capacity should be large enough to fit the longest SysEx expected.
+ // If a longer SysEx occurs, streamBuffer is reallocated to the maximum size of MAX_STREAM_BUFFER_SIZE (32768 bytes).
+ // Default capacity is SYSEX_BUFFER_SIZE (1000 bytes) which is enough to fit SysEx messages in common use.
+ explicit MidiStreamParser(Bit32u initialStreamBufferCapacity = SYSEX_BUFFER_SIZE);
+};
+
+class MT32EMU_EXPORT DefaultMidiStreamParser : public MidiStreamParser {
+public:
+ explicit DefaultMidiStreamParser(Synth &synth, Bit32u initialStreamBufferCapacity = SYSEX_BUFFER_SIZE);
+ void setTimestamp(const Bit32u useTimestamp);
+ void resetTimestamp();
+
+protected:
+ void handleShortMessage(const Bit32u message);
+ void handleSysex(const Bit8u *stream, const Bit32u length);
+ void handleSystemRealtimeMessage(const Bit8u realtime);
+ void printDebug(const char *debugMessage);
+
+private:
+ Synth &synth;
+ bool timestampSet;
+ Bit32u timestamp;
+};
+
+} // namespace MT32Emu
+
+#endif // MT32EMU_MIDI_STREAM_PARSER_H
diff --git a/audio/softsynth/mt32/Part.cpp b/audio/softsynth/mt32/Part.cpp
index cffc3ed7440..9b5119ee8bb 100644
--- a/audio/softsynth/mt32/Part.cpp
+++ b/audio/softsynth/mt32/Part.cpp
@@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -15,12 +15,16 @@
* along with this program. If not, see .
*/
-//#include
-//#include
+#include
+#include
-#include "mt32emu.h"
#include "internals.h"
+
+#include "Part.h"
+#include "Partial.h"
#include "PartialManager.h"
+#include "Poly.h"
+#include "Synth.h"
namespace MT32Emu {
@@ -112,7 +116,7 @@ Bit32s Part::getPitchBend() const {
void Part::setBend(unsigned int midiBend) {
// CONFIRMED:
- pitchBend = (((signed)midiBend - 8192) * pitchBenderRange) >> 14; // PORTABILITY NOTE: Assumes arithmetic shift
+ pitchBend = ((signed(midiBend) - 8192) * pitchBenderRange) >> 14; // PORTABILITY NOTE: Assumes arithmetic shift
}
Bit8u Part::getModulation() const {
@@ -120,7 +124,7 @@ Bit8u Part::getModulation() const {
}
void Part::setModulation(unsigned int midiModulation) {
- modulation = (Bit8u)midiModulation;
+ modulation = Bit8u(midiModulation);
}
void Part::resetAllControllers() {
@@ -162,7 +166,7 @@ void Part::refresh() {
patchCache[t].reverb = patchTemp->patch.reverbSwitch > 0;
}
memcpy(currentInstr, timbreTemp->common.name, 10);
- synth->newTimbreSet(partNum, patchTemp->patch.timbreGroup, currentInstr);
+ synth->newTimbreSet(partNum, patchTemp->patch.timbreGroup, patchTemp->patch.timbreNum, currentInstr);
updatePitchBenderRange();
}
@@ -255,26 +259,26 @@ void Part::cacheTimbre(PatchCache cache[4], const TimbreParam *timbre) {
switch (t) {
case 0:
- cache[t].PCMPartial = (PartialStruct[(int)timbre->common.partialStructure12] & 0x2) ? true : false;
- cache[t].structureMix = PartialMixStruct[(int)timbre->common.partialStructure12];
+ cache[t].PCMPartial = (PartialStruct[int(timbre->common.partialStructure12)] & 0x2) ? true : false;
+ cache[t].structureMix = PartialMixStruct[int(timbre->common.partialStructure12)];
cache[t].structurePosition = 0;
cache[t].structurePair = 1;
break;
case 1:
- cache[t].PCMPartial = (PartialStruct[(int)timbre->common.partialStructure12] & 0x1) ? true : false;
- cache[t].structureMix = PartialMixStruct[(int)timbre->common.partialStructure12];
+ cache[t].PCMPartial = (PartialStruct[int(timbre->common.partialStructure12)] & 0x1) ? true : false;
+ cache[t].structureMix = PartialMixStruct[int(timbre->common.partialStructure12)];
cache[t].structurePosition = 1;
cache[t].structurePair = 0;
break;
case 2:
- cache[t].PCMPartial = (PartialStruct[(int)timbre->common.partialStructure34] & 0x2) ? true : false;
- cache[t].structureMix = PartialMixStruct[(int)timbre->common.partialStructure34];
+ cache[t].PCMPartial = (PartialStruct[int(timbre->common.partialStructure34)] & 0x2) ? true : false;
+ cache[t].structureMix = PartialMixStruct[int(timbre->common.partialStructure34)];
cache[t].structurePosition = 0;
cache[t].structurePair = 3;
break;
case 3:
- cache[t].PCMPartial = (PartialStruct[(int)timbre->common.partialStructure34] & 0x1) ? true : false;
- cache[t].structureMix = PartialMixStruct[(int)timbre->common.partialStructure34];
+ cache[t].PCMPartial = (PartialStruct[int(timbre->common.partialStructure34)] & 0x1) ? true : false;
+ cache[t].structureMix = PartialMixStruct[int(timbre->common.partialStructure34)];
cache[t].structurePosition = 1;
cache[t].structurePair = 2;
break;
@@ -308,7 +312,7 @@ const char *Part::getName() const {
void Part::setVolume(unsigned int midiVolume) {
// CONFIRMED: This calculation matches the table used in the control ROM
- patchTemp->outputLevel = (Bit8u)(midiVolume * 100 / 127);
+ patchTemp->outputLevel = Bit8u(midiVolume * 100 / 127);
//synth->printDebug("%s (%s): Set volume to %d", name, currentInstr, midiVolume);
}
@@ -322,7 +326,7 @@ Bit8u Part::getExpression() const {
void Part::setExpression(unsigned int midiExpression) {
// CONFIRMED: This calculation matches the table used in the control ROM
- expression = (Bit8u)(midiExpression * 100 / 127);
+ expression = Bit8u(midiExpression * 100 / 127);
}
void RhythmPart::setPan(unsigned int midiPan) {
@@ -337,9 +341,9 @@ void Part::setPan(unsigned int midiPan) {
// NOTE: Panning is inverted compared to GM.
// CM-32L: Divide by 8.5
- patchTemp->panpot = (Bit8u)((midiPan << 3) / 68);
+ patchTemp->panpot = Bit8u((midiPan << 3) / 68);
// FIXME: MT-32: Divide by 9
- //patchTemp->panpot = (Bit8u)(midiPan / 9);
+ //patchTemp->panpot = Bit8u(midiPan / 9);
//synth->printDebug("%s (%s): Set pan to %d", name, currentInstr, panpot);
}
@@ -372,7 +376,8 @@ void RhythmPart::noteOn(unsigned int midiKey, unsigned int velocity) {
unsigned int key = midiKey;
unsigned int drumNum = key - 24;
int drumTimbreNum = rhythmTemp[drumNum].timbre;
- if (drumTimbreNum >= 127) { // 94 on MT-32
+ const int drumTimbreCount = 64 + synth->controlROMMap->timbreRCount; // 94 on MT-32, 128 on LAPC-I/CM32-L
+ if (drumTimbreNum == 127 || drumTimbreNum >= drumTimbreCount) { // timbre #127 is OFF, no sense to play it
synth->printDebug("%s: Attempted to play unmapped key %d (velocity %d)", name, midiKey, velocity);
return;
}
@@ -507,7 +512,7 @@ void Part::playPoly(const PatchCache cache[4], const MemParams::RhythmTemp *rhyt
#if MT32EMU_MONITOR_PARTIALS > 1
synth->printPartialUsage();
#endif
- synth->polyStateChanged(partNum);
+ synth->reportHandler->onPolyStateChanged(Bit8u(partNum));
}
void Part::allNotesOff() {
@@ -593,16 +598,14 @@ void Part::partialDeactivated(Poly *poly) {
if (!poly->isActive()) {
activePolys.remove(poly);
synth->partialManager->polyFreed(poly);
- synth->polyStateChanged(partNum);
+ synth->reportHandler->onPolyStateChanged(Bit8u(partNum));
}
}
-//#define POLY_LIST_DEBUG
-
PolyList::PolyList() : firstPoly(NULL), lastPoly(NULL) {}
bool PolyList::isEmpty() const {
-#ifdef POLY_LIST_DEBUG
+#ifdef MT32EMU_POLY_LIST_DEBUG
if ((firstPoly == NULL || lastPoly == NULL) && firstPoly != lastPoly) {
printf("PolyList: desynchronised firstPoly & lastPoly pointers\n");
}
@@ -619,7 +622,7 @@ Poly *PolyList::getLast() const {
}
void PolyList::prepend(Poly *poly) {
-#ifdef POLY_LIST_DEBUG
+#ifdef MT32EMU_POLY_LIST_DEBUG
if (poly->getNext() != NULL) {
printf("PolyList: Non-NULL next field in a Poly being prepended is ignored\n");
}
@@ -632,14 +635,14 @@ void PolyList::prepend(Poly *poly) {
}
void PolyList::append(Poly *poly) {
-#ifdef POLY_LIST_DEBUG
+#ifdef MT32EMU_POLY_LIST_DEBUG
if (poly->getNext() != NULL) {
printf("PolyList: Non-NULL next field in a Poly being appended is ignored\n");
}
#endif
poly->setNext(NULL);
if (lastPoly != NULL) {
-#ifdef POLY_LIST_DEBUG
+#ifdef MT32EMU_POLY_LIST_DEBUG
if (lastPoly->getNext() != NULL) {
printf("PolyList: Non-NULL next field in the lastPoly\n");
}
@@ -656,7 +659,7 @@ Poly *PolyList::takeFirst() {
Poly *oldFirst = firstPoly;
firstPoly = oldFirst->getNext();
if (firstPoly == NULL) {
-#ifdef POLY_LIST_DEBUG
+#ifdef MT32EMU_POLY_LIST_DEBUG
if (lastPoly != oldFirst) {
printf("PolyList: firstPoly != lastPoly in a list with a single Poly\n");
}
@@ -675,7 +678,7 @@ void PolyList::remove(Poly * const polyToRemove) {
for (Poly *poly = firstPoly; poly != NULL; poly = poly->getNext()) {
if (poly->getNext() == polyToRemove) {
if (polyToRemove == lastPoly) {
-#ifdef POLY_LIST_DEBUG
+#ifdef MT32EMU_POLY_LIST_DEBUG
if (lastPoly->getNext() != NULL) {
printf("PolyList: Non-NULL next field in the lastPoly\n");
}
@@ -689,4 +692,4 @@ void PolyList::remove(Poly * const polyToRemove) {
}
}
-}
+} // namespace MT32Emu
diff --git a/audio/softsynth/mt32/Part.h b/audio/softsynth/mt32/Part.h
index b458fb39882..f5171589d63 100644
--- a/audio/softsynth/mt32/Part.h
+++ b/audio/softsynth/mt32/Part.h
@@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -18,9 +18,14 @@
#ifndef MT32EMU_PART_H
#define MT32EMU_PART_H
+#include "globals.h"
+#include "internals.h"
+#include "Types.h"
+#include "Structures.h"
+
namespace MT32Emu {
-class PartialManager;
+class Poly;
class Synth;
class PolyList {
@@ -123,7 +128,7 @@ public:
// Abort the first poly in PolyState_HELD, or if none exists, the first active poly in any state.
bool abortFirstPolyPreferHeld();
bool abortFirstPoly();
-};
+}; // class Part
class RhythmPart: public Part {
// Pointer to the area of the MT-32's memory dedicated to rhythm
@@ -143,5 +148,6 @@ public:
void setProgram(unsigned int patchNum);
};
-}
-#endif
+} // namespace MT32Emu
+
+#endif // #ifndef MT32EMU_PART_H
diff --git a/audio/softsynth/mt32/Partial.cpp b/audio/softsynth/mt32/Partial.cpp
index 73480875098..6afef364df1 100644
--- a/audio/softsynth/mt32/Partial.cpp
+++ b/audio/softsynth/mt32/Partial.cpp
@@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -15,14 +15,19 @@
* along with this program. If not, see .
*/
-//#include
-//#include
-//#include
+#include
-#include "mt32emu.h"
-#include "mmath.h"
#include "internals.h"
+#include "Partial.h"
+#include "Part.h"
+#include "Poly.h"
+#include "Synth.h"
+#include "Tables.h"
+#include "TVA.h"
+#include "TVF.h"
+#include "TVP.h"
+
namespace MT32Emu {
static const Bit8u PAN_NUMERATOR_MASTER[] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7};
@@ -54,7 +59,7 @@ int Partial::debugGetPartialNum() const {
}
// Only used for debugging purposes
-unsigned long Partial::debugGetSampleNum() const {
+Bit32u Partial::debugGetSampleNum() const {
return sampleNum;
}
@@ -266,7 +271,7 @@ void Partial::backupCache(const PatchCache &cache) {
}
}
-bool Partial::produceOutput(Sample *leftBuf, Sample *rightBuf, unsigned long length) {
+bool Partial::produceOutput(Sample *leftBuf, Sample *rightBuf, Bit32u length) {
if (!isActive() || alreadyOutputed || isRingModulatingSlave()) {
return false;
}
@@ -313,8 +318,8 @@ bool Partial::produceOutput(Sample *leftBuf, Sample *rightBuf, unsigned long len
// Though, it is unknown whether this overflow is exploited somewhere.
Sample leftOut = Sample((sample * leftPanValue) >> 8);
Sample rightOut = Sample((sample * rightPanValue) >> 8);
- *leftBuf = Synth::clipSampleEx((SampleEx)*leftBuf + (SampleEx)leftOut);
- *rightBuf = Synth::clipSampleEx((SampleEx)*rightBuf + (SampleEx)rightOut);
+ *leftBuf = Synth::clipSampleEx(SampleEx(*leftBuf) + SampleEx(leftOut));
+ *rightBuf = Synth::clipSampleEx(SampleEx(*rightBuf) + SampleEx(rightOut));
leftBuf++;
rightBuf++;
#endif
@@ -341,4 +346,4 @@ void Partial::startDecayAll() {
tvf->startDecay();
}
-}
+} // namespace MT32Emu
diff --git a/audio/softsynth/mt32/Partial.h b/audio/softsynth/mt32/Partial.h
index 749468135dd..187665de512 100644
--- a/audio/softsynth/mt32/Partial.h
+++ b/audio/softsynth/mt32/Partial.h
@@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -18,11 +18,21 @@
#ifndef MT32EMU_PARTIAL_H
#define MT32EMU_PARTIAL_H
+#include "globals.h"
+#include "internals.h"
+#include "Types.h"
+#include "Structures.h"
+#include "LA32Ramp.h"
+#include "LA32WaveGenerator.h"
+
namespace MT32Emu {
-class Synth;
class Part;
+class Poly;
+class Synth;
class TVA;
+class TVF;
+class TVP;
struct ControlROMPCMStruct;
// A partial represents one of up to four waveform generators currently playing within a poly.
@@ -32,7 +42,7 @@ private:
const int debugPartialNum; // Only used for debugging
// Number of the sample currently being rendered by produceOutput(), or 0 if no run is in progress
// This is only kept available for debugging purposes.
- unsigned long sampleNum;
+ Bit32u sampleNum;
// Actually, this is a 4-bit register but we abuse this to emulate inverted mixing.
// Also we double the value to enable INACCURATE_SMOOTH_PAN, with respect to MoK.
@@ -77,7 +87,7 @@ public:
~Partial();
int debugGetPartialNum() const;
- unsigned long debugGetSampleNum() const;
+ Bit32u debugGetSampleNum() const;
int getOwnerPart() const;
const Poly *getPoly() const;
@@ -100,9 +110,9 @@ public:
// Returns true only if data written to buffer
// This function (unlike the one below it) returns processed stereo samples
// made from combining this single partial with its pair, if it has one.
- bool produceOutput(Sample *leftBuf, Sample *rightBuf, unsigned long length);
-};
+ bool produceOutput(Sample *leftBuf, Sample *rightBuf, Bit32u length);
+}; // class Partial
-}
+} // namespace MT32Emu
-#endif
+#endif // #ifndef MT32EMU_PARTIAL_H
diff --git a/audio/softsynth/mt32/PartialManager.cpp b/audio/softsynth/mt32/PartialManager.cpp
index 8ca6e4e3d7b..7c702f75821 100644
--- a/audio/softsynth/mt32/PartialManager.cpp
+++ b/audio/softsynth/mt32/PartialManager.cpp
@@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -15,11 +15,16 @@
* along with this program. If not, see .
*/
-//#include
+#include
+#include
-#include "mt32emu.h"
#include "internals.h"
+
#include "PartialManager.h"
+#include "Part.h"
+#include "Partial.h"
+#include "Poly.h"
+#include "Synth.h"
namespace MT32Emu {
@@ -275,7 +280,7 @@ void PartialManager::polyFreed(Poly *poly) {
const Poly *activePoly = synth->getPart(partNum)->getFirstActivePoly();
Bit32u polyCount = 0;
while (activePoly != NULL) {
- activePoly->getNext();
+ activePoly = activePoly->getNext();
polyCount++;
}
synth->printDebug("Part: %i, active poly count: %i\n", partNum, polyCount);
@@ -286,4 +291,4 @@ void PartialManager::polyFreed(Poly *poly) {
freePolys[firstFreePolyIndex] = poly;
}
-}
+} // namespace MT32Emu
diff --git a/audio/softsynth/mt32/PartialManager.h b/audio/softsynth/mt32/PartialManager.h
index f2e8767bbb5..b2908a5c213 100644
--- a/audio/softsynth/mt32/PartialManager.h
+++ b/audio/softsynth/mt32/PartialManager.h
@@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -18,8 +18,15 @@
#ifndef MT32EMU_PARTIALMANAGER_H
#define MT32EMU_PARTIALMANAGER_H
+#include "globals.h"
+#include "internals.h"
+#include "Types.h"
+
namespace MT32Emu {
+class Part;
+class Partial;
+class Poly;
class Synth;
class PartialManager {
@@ -49,8 +56,8 @@ public:
const Partial *getPartial(unsigned int partialNum) const;
Poly *assignPolyToPart(Part *part);
void polyFreed(Poly *poly);
-};
+}; // class PartialManager
-}
+} // namespace MT32Emu
-#endif
+#endif // #ifndef MT32EMU_PARTIALMANAGER_H
diff --git a/audio/softsynth/mt32/Poly.cpp b/audio/softsynth/mt32/Poly.cpp
index badcd8fb965..9a3948cf114 100644
--- a/audio/softsynth/mt32/Poly.cpp
+++ b/audio/softsynth/mt32/Poly.cpp
@@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -15,9 +15,15 @@
* along with this program. If not, see .
*/
-#include "mt32emu.h"
+#include
+
#include "internals.h"
+#include "Poly.h"
+#include "Part.h"
+#include "Partial.h"
+#include "Synth.h"
+
namespace MT32Emu {
Poly::Poly() {
@@ -181,4 +187,4 @@ void Poly::setNext(Poly *poly) {
next = poly;
}
-}
+} // namespace MT32Emu
diff --git a/audio/softsynth/mt32/Poly.h b/audio/softsynth/mt32/Poly.h
index e2614369bb7..4b283231c80 100644
--- a/audio/softsynth/mt32/Poly.h
+++ b/audio/softsynth/mt32/Poly.h
@@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -18,17 +18,14 @@
#ifndef MT32EMU_POLY_H
#define MT32EMU_POLY_H
+#include "globals.h"
+#include "internals.h"
+
namespace MT32Emu {
class Part;
class Partial;
-
-enum PolyState {
- POLY_Playing,
- POLY_Held, // This marks keys that have been released on the keyboard, but are being held by the pedal
- POLY_Releasing,
- POLY_Inactive
-};
+struct PatchCache;
class Poly {
private:
@@ -66,8 +63,8 @@ public:
Poly *getNext() const;
void setNext(Poly *poly);
-};
+}; // class Poly
-}
+} // namespace MT32Emu
-#endif /* POLY_H_ */
+#endif // #ifndef MT32EMU_POLY_H
diff --git a/audio/softsynth/mt32/ROMInfo.cpp b/audio/softsynth/mt32/ROMInfo.cpp
index f6817c1a4d0..ce78e693aa1 100644
--- a/audio/softsynth/mt32/ROMInfo.cpp
+++ b/audio/softsynth/mt32/ROMInfo.cpp
@@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -15,27 +15,27 @@
* along with this program. If not, see .
*/
-//#include
+#include
+
+#include "internals.h"
+
#include "ROMInfo.h"
namespace MT32Emu {
-static const ROMInfo *getKnownROMInfoFromList(unsigned int index) {
- static const ControlROMFeatureSet MT32_COMPATIBLE(true, true);
- static const ControlROMFeatureSet CM32L_COMPATIBLE(false, false);
-
+static const ROMInfo *getKnownROMInfoFromList(Bit32u index) {
// Known ROMs
- static const ROMInfo CTRL_MT32_V1_04 = {65536, "5a5cb5a77d7d55ee69657c2f870416daed52dea7", ROMInfo::Control, "ctrl_mt32_1_04", "MT-32 Control v1.04", ROMInfo::Full, NULL, &MT32_COMPATIBLE};
- static const ROMInfo CTRL_MT32_V1_05 = {65536, "e17a3a6d265bf1fa150312061134293d2b58288c", ROMInfo::Control, "ctrl_mt32_1_05", "MT-32 Control v1.05", ROMInfo::Full, NULL, &MT32_COMPATIBLE};
- static const ROMInfo CTRL_MT32_V1_06 = {65536, "a553481f4e2794c10cfe597fef154eef0d8257de", ROMInfo::Control, "ctrl_mt32_1_06", "MT-32 Control v1.06", ROMInfo::Full, NULL, &MT32_COMPATIBLE};
- static const ROMInfo CTRL_MT32_V1_07 = {65536, "b083518fffb7f66b03c23b7eb4f868e62dc5a987", ROMInfo::Control, "ctrl_mt32_1_07", "MT-32 Control v1.07", ROMInfo::Full, NULL, &MT32_COMPATIBLE};
- static const ROMInfo CTRL_MT32_BLUER = {65536, "7b8c2a5ddb42fd0732e2f22b3340dcf5360edf92", ROMInfo::Control, "ctrl_mt32_bluer", "MT-32 Control BlueRidge", ROMInfo::Full, NULL, &MT32_COMPATIBLE};
+ static const ROMInfo CTRL_MT32_V1_04 = {65536, "5a5cb5a77d7d55ee69657c2f870416daed52dea7", ROMInfo::Control, "ctrl_mt32_1_04", "MT-32 Control v1.04", ROMInfo::Full, NULL};
+ static const ROMInfo CTRL_MT32_V1_05 = {65536, "e17a3a6d265bf1fa150312061134293d2b58288c", ROMInfo::Control, "ctrl_mt32_1_05", "MT-32 Control v1.05", ROMInfo::Full, NULL};
+ static const ROMInfo CTRL_MT32_V1_06 = {65536, "a553481f4e2794c10cfe597fef154eef0d8257de", ROMInfo::Control, "ctrl_mt32_1_06", "MT-32 Control v1.06", ROMInfo::Full, NULL};
+ static const ROMInfo CTRL_MT32_V1_07 = {65536, "b083518fffb7f66b03c23b7eb4f868e62dc5a987", ROMInfo::Control, "ctrl_mt32_1_07", "MT-32 Control v1.07", ROMInfo::Full, NULL};
+ static const ROMInfo CTRL_MT32_BLUER = {65536, "7b8c2a5ddb42fd0732e2f22b3340dcf5360edf92", ROMInfo::Control, "ctrl_mt32_bluer", "MT-32 Control BlueRidge", ROMInfo::Full, NULL};
- static const ROMInfo CTRL_CM32L_V1_00 = {65536, "73683d585cd6948cc19547942ca0e14a0319456d", ROMInfo::Control, "ctrl_cm32l_1_00", "CM-32L/LAPC-I Control v1.00", ROMInfo::Full, NULL, &CM32L_COMPATIBLE};
- static const ROMInfo CTRL_CM32L_V1_02 = {65536, "a439fbb390da38cada95a7cbb1d6ca199cd66ef8", ROMInfo::Control, "ctrl_cm32l_1_02", "CM-32L/LAPC-I Control v1.02", ROMInfo::Full, NULL, &CM32L_COMPATIBLE};
+ static const ROMInfo CTRL_CM32L_V1_00 = {65536, "73683d585cd6948cc19547942ca0e14a0319456d", ROMInfo::Control, "ctrl_cm32l_1_00", "CM-32L/LAPC-I Control v1.00", ROMInfo::Full, NULL};
+ static const ROMInfo CTRL_CM32L_V1_02 = {65536, "a439fbb390da38cada95a7cbb1d6ca199cd66ef8", ROMInfo::Control, "ctrl_cm32l_1_02", "CM-32L/LAPC-I Control v1.02", ROMInfo::Full, NULL};
- static const ROMInfo PCM_MT32 = {524288, "f6b1eebc4b2d200ec6d3d21d51325d5b48c60252", ROMInfo::PCM, "pcm_mt32", "MT-32 PCM ROM", ROMInfo::Full, NULL, NULL};
- static const ROMInfo PCM_CM32L = {1048576, "289cc298ad532b702461bfc738009d9ebe8025ea", ROMInfo::PCM, "pcm_cm32l", "CM-32L/CM-64/LAPC-I PCM ROM", ROMInfo::Full, NULL, NULL};
+ static const ROMInfo PCM_MT32 = {524288, "f6b1eebc4b2d200ec6d3d21d51325d5b48c60252", ROMInfo::PCM, "pcm_mt32", "MT-32 PCM ROM", ROMInfo::Full, NULL};
+ static const ROMInfo PCM_CM32L = {1048576, "289cc298ad532b702461bfc738009d9ebe8025ea", ROMInfo::PCM, "pcm_cm32l", "CM-32L/CM-64/LAPC-I PCM ROM", ROMInfo::Full, NULL};
static const ROMInfo * const ROM_INFOS[] = {
&CTRL_MT32_V1_04,
@@ -52,23 +52,11 @@ static const ROMInfo *getKnownROMInfoFromList(unsigned int index) {
return ROM_INFOS[index];
}
-const ROMInfo* ROMInfo::getROMInfo(Common::File *file) {
- size_t fileSize = file->size();
- Common::String fileName = file->getName();
- fileName.toUppercase();
- bool isCM32LROM = fileName.hasPrefix("CM32L_");
- // We haven't added the SHA1 checksum code in ScummVM, as the file size
- // and ROM name suffices for our needs for now.
- //const char *fileDigest = file->getSHA1();
- for (int i = 0; getKnownROMInfoFromList(i) != NULL; i++) {
+const ROMInfo* ROMInfo::getROMInfo(File *file) {
+ size_t fileSize = file->getSize();
+ for (Bit32u i = 0; getKnownROMInfoFromList(i) != NULL; i++) {
const ROMInfo *romInfo = getKnownROMInfoFromList(i);
- if (fileSize == romInfo->fileSize /*&& !strcmp(fileDigest, romInfo->sha1Digest)*/) {
- if (fileSize == 65536) {
- // If we are looking for a CM-32L ROM, make sure we return the first matching
- // CM-32L ROM from the list, instead of the first matching MT-32 ROM
- if (isCM32LROM && romInfo->controlROMFeatures->isDefaultReverbMT32Compatible())
- continue;
- }
+ if (fileSize == romInfo->fileSize && !strcmp(file->getSHA1(), romInfo->sha1Digest)) {
return romInfo;
}
}
@@ -79,17 +67,17 @@ void ROMInfo::freeROMInfo(const ROMInfo *romInfo) {
(void) romInfo;
}
-static int getROMCount() {
- int count;
+static Bit32u getROMCount() {
+ Bit32u count;
for(count = 0; getKnownROMInfoFromList(count) != NULL; count++) {
}
return count;
}
-const ROMInfo** ROMInfo::getROMInfoList(unsigned int types, unsigned int pairTypes) {
+const ROMInfo** ROMInfo::getROMInfoList(Bit32u types, Bit32u pairTypes) {
const ROMInfo **romInfoList = new const ROMInfo*[getROMCount() + 1];
const ROMInfo **currentROMInList = romInfoList;
- for(int i = 0; getKnownROMInfoFromList(i) != NULL; i++) {
+ for (Bit32u i = 0; getKnownROMInfoFromList(i) != NULL; i++) {
const ROMInfo *romInfo = getKnownROMInfoFromList(i);
if ((types & (1 << romInfo->type)) && (pairTypes & (1 << romInfo->pairType))) {
*currentROMInList++ = romInfo;
@@ -103,19 +91,22 @@ void ROMInfo::freeROMInfoList(const ROMInfo **romInfoList) {
delete[] romInfoList;
}
-const ROMImage* ROMImage::makeROMImage(Common::File *file) {
- ROMImage *romImage = new ROMImage;
- romImage->file = file;
- romImage->romInfo = ROMInfo::getROMInfo(romImage->file);
- return romImage;
+ROMImage::ROMImage(File *useFile) : file(useFile), romInfo(ROMInfo::getROMInfo(file))
+{}
+
+ROMImage::~ROMImage() {
+ ROMInfo::freeROMInfo(romInfo);
+}
+
+const ROMImage* ROMImage::makeROMImage(File *file) {
+ return new ROMImage(file);
}
void ROMImage::freeROMImage(const ROMImage *romImage) {
- ROMInfo::freeROMInfo(romImage->romInfo);
delete romImage;
}
-Common::File* ROMImage::getFile() const {
+File* ROMImage::getFile() const {
return file;
}
@@ -123,17 +114,4 @@ const ROMInfo* ROMImage::getROMInfo() const {
return romInfo;
}
-ControlROMFeatureSet::ControlROMFeatureSet(bool useDefaultReverbMT32Compatible, bool useOldMT32AnalogLPF) :
- defaultReverbMT32Compatible(useDefaultReverbMT32Compatible),
- oldMT32AnalogLPF(useOldMT32AnalogLPF)
-{}
-
-bool ControlROMFeatureSet::isDefaultReverbMT32Compatible() const {
- return defaultReverbMT32Compatible;
-}
-
-bool ControlROMFeatureSet::isOldMT32AnalogLPF() const {
- return oldMT32AnalogLPF;
-}
-
-}
+} // namespace MT32Emu
diff --git a/audio/softsynth/mt32/ROMInfo.h b/audio/softsynth/mt32/ROMInfo.h
index 4682620a159..8a5ad141b6e 100644
--- a/audio/softsynth/mt32/ROMInfo.h
+++ b/audio/softsynth/mt32/ROMInfo.h
@@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -18,73 +18,63 @@
#ifndef MT32EMU_ROMINFO_H
#define MT32EMU_ROMINFO_H
-//#include
-#include "common/file.h"
+#include
+
+#include "globals.h"
+#include "File.h"
namespace MT32Emu {
-struct ControlROMFeatureSet;
-
// Defines vital info about ROM file to be used by synth and applications
struct ROMInfo {
public:
size_t fileSize;
- const char *sha1Digest;
+ const File::SHA1Digest &sha1Digest;
enum Type {PCM, Control, Reverb} type;
const char *shortName;
const char *description;
enum PairType {Full, FirstHalf, SecondHalf, Mux0, Mux1} pairType;
ROMInfo *pairROMInfo;
- const ControlROMFeatureSet *controlROMFeatures;
// Returns a ROMInfo struct by inspecting the size and the SHA1 hash
- static const ROMInfo* getROMInfo(Common::File *file);
+ MT32EMU_EXPORT static const ROMInfo* getROMInfo(File *file);
// Currently no-op
- static void freeROMInfo(const ROMInfo *romInfo);
+ MT32EMU_EXPORT static void freeROMInfo(const ROMInfo *romInfo);
// Allows retrieving a NULL-terminated list of ROMInfos for a range of types and pairTypes
// (specified by bitmasks)
// Useful for GUI/console app to output information on what ROMs it supports
- static const ROMInfo** getROMInfoList(unsigned int types, unsigned int pairTypes);
+ MT32EMU_EXPORT static const ROMInfo** getROMInfoList(Bit32u types, Bit32u pairTypes);
// Frees the list of ROMInfos given
- static void freeROMInfoList(const ROMInfo **romInfos);
+ MT32EMU_EXPORT static void freeROMInfoList(const ROMInfo **romInfos);
};
// Synth::open() is to require a full control ROMImage and a full PCM ROMImage to work
class ROMImage {
private:
- Common::File *file;
- const ROMInfo *romInfo;
+ File * const file;
+ const ROMInfo * const romInfo;
+
+ ROMImage(File *file);
+ ~ROMImage();
public:
-
// Creates a ROMImage object given a ROMInfo and a File. Keeps a reference
// to the File and ROMInfo given, which must be freed separately by the user
// after the ROMImage is freed
- static const ROMImage* makeROMImage(Common::File *file);
+ MT32EMU_EXPORT static const ROMImage* makeROMImage(File *file);
// Must only be done after all Synths using the ROMImage are deleted
- static void freeROMImage(const ROMImage *romImage);
+ MT32EMU_EXPORT static void freeROMImage(const ROMImage *romImage);
- Common::File *getFile() const;
- const ROMInfo *getROMInfo() const;
+ MT32EMU_EXPORT File *getFile() const;
+ MT32EMU_EXPORT const ROMInfo *getROMInfo() const;
};
-struct ControlROMFeatureSet {
-private:
- unsigned int defaultReverbMT32Compatible : 1;
- unsigned int oldMT32AnalogLPF : 1;
+} // namespace MT32Emu
-public:
- ControlROMFeatureSet(bool defaultReverbMT32Compatible, bool oldMT32AnalogLPF);
- bool isDefaultReverbMT32Compatible() const;
- bool isOldMT32AnalogLPF() const;
-};
-
-}
-
-#endif
+#endif // #ifndef MT32EMU_ROMINFO_H
diff --git a/audio/softsynth/mt32/SampleRateConverter.cpp b/audio/softsynth/mt32/SampleRateConverter.cpp
new file mode 100644
index 00000000000..70f860f3952
--- /dev/null
+++ b/audio/softsynth/mt32/SampleRateConverter.cpp
@@ -0,0 +1,97 @@
+/* Copyright (C) 2015-2017 Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+#include "SampleRateConverter.h"
+
+#if MT32EMU_WITH_LIBSOXR_RESAMPLER
+#include "srchelper/SoxrAdapter.h"
+#elif MT32EMU_WITH_LIBSAMPLERATE_RESAMPLER
+#include "srchelper/SamplerateAdapter.h"
+#else
+#include "srchelper/InternalResampler.h"
+#endif
+
+#include "Synth.h"
+
+using namespace MT32Emu;
+
+static inline void *createDelegate(Synth &synth, double targetSampleRate, SampleRateConverter::Quality quality) {
+#if MT32EMU_WITH_LIBSOXR_RESAMPLER
+ return new SoxrAdapter(synth, targetSampleRate, quality);
+#elif MT32EMU_WITH_LIBSAMPLERATE_RESAMPLER
+ return new SamplerateAdapter(synth, targetSampleRate, quality);
+#else
+ return new InternalResampler(synth, targetSampleRate, quality);
+#endif
+}
+
+AnalogOutputMode SampleRateConverter::getBestAnalogOutputMode(double targetSampleRate) {
+ if (Synth::getStereoOutputSampleRate(AnalogOutputMode_ACCURATE) < targetSampleRate) {
+ return AnalogOutputMode_OVERSAMPLED;
+ } else if (Synth::getStereoOutputSampleRate(AnalogOutputMode_COARSE) < targetSampleRate) {
+ return AnalogOutputMode_ACCURATE;
+ }
+ return AnalogOutputMode_COARSE;
+}
+
+SampleRateConverter::SampleRateConverter(Synth &useSynth, double targetSampleRate, Quality useQuality) :
+ synthInternalToTargetSampleRateRatio(SAMPLE_RATE / targetSampleRate),
+ srcDelegate(createDelegate(useSynth, targetSampleRate, useQuality))
+{}
+
+SampleRateConverter::~SampleRateConverter() {
+#if MT32EMU_WITH_LIBSOXR_RESAMPLER
+ delete static_cast(srcDelegate);
+#elif MT32EMU_WITH_LIBSAMPLERATE_RESAMPLER
+ delete static_cast(srcDelegate);
+#else
+ delete static_cast(srcDelegate);
+#endif
+}
+
+void SampleRateConverter::getOutputSamples(float *buffer, unsigned int length) {
+#if MT32EMU_WITH_LIBSOXR_RESAMPLER
+ static_cast(srcDelegate)->getOutputSamples(buffer, length);
+#elif MT32EMU_WITH_LIBSAMPLERATE_RESAMPLER
+ static_cast(srcDelegate)->getOutputSamples(buffer, length);
+#else
+ static_cast(srcDelegate)->getOutputSamples(buffer, length);
+#endif
+}
+
+void SampleRateConverter::getOutputSamples(Bit16s *outBuffer, unsigned int length) {
+ static const unsigned int CHANNEL_COUNT = 2;
+
+ float floatBuffer[CHANNEL_COUNT * MAX_SAMPLES_PER_RUN];
+ while (length > 0) {
+ const unsigned int size = MAX_SAMPLES_PER_RUN < length ? MAX_SAMPLES_PER_RUN : length;
+ getOutputSamples(floatBuffer, size);
+ float *outs = floatBuffer;
+ float *ends = floatBuffer + CHANNEL_COUNT * size;
+ while (outs < ends) {
+ *(outBuffer++) = Synth::convertSample(*(outs++));
+ }
+ length -= size;
+ }
+}
+
+double SampleRateConverter::convertOutputToSynthTimestamp(double outputTimestamp) const {
+ return outputTimestamp * synthInternalToTargetSampleRateRatio;
+}
+
+double SampleRateConverter::convertSynthToOutputTimestamp(double synthTimestamp) const {
+ return synthTimestamp / synthInternalToTargetSampleRateRatio;
+}
diff --git a/audio/softsynth/mt32/SampleRateConverter.h b/audio/softsynth/mt32/SampleRateConverter.h
new file mode 100644
index 00000000000..24bce0891b9
--- /dev/null
+++ b/audio/softsynth/mt32/SampleRateConverter.h
@@ -0,0 +1,78 @@
+/* Copyright (C) 2015-2017 Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef SAMPLE_RATE_CONVERTER_H
+#define SAMPLE_RATE_CONVERTER_H
+
+#include "globals.h"
+#include "Types.h"
+#include "Enumerations.h"
+
+namespace MT32Emu {
+
+class Synth;
+
+/* SampleRateConverter class allows to convert the synthesiser output to any desired sample rate.
+ * It processes the completely mixed stereo output signal as it passes the analogue circuit emulation,
+ * so emulating the synthesiser output signal passing further through an ADC.
+ * Several conversion quality options are provided which allow to trade-off the conversion speed vs. the passband width.
+ * All the options except FASTEST guarantee full suppression of the aliasing noise in terms of the 16-bit integer samples.
+ */
+class MT32EMU_EXPORT SampleRateConverter {
+public:
+ enum Quality {
+ // Use this only when the speed is more important than the audio quality.
+ FASTEST,
+ FAST,
+ GOOD,
+ BEST
+ };
+
+ // Returns the value of AnalogOutputMode for which the output signal may retain its full frequency spectrum
+ // at the sample rate specified by the targetSampleRate argument.
+ static AnalogOutputMode getBestAnalogOutputMode(double targetSampleRate);
+
+ // Creates a SampleRateConverter instance that converts output signal from the synth to the given sample rate
+ // with the specified conversion quality.
+ SampleRateConverter(Synth &synth, double targetSampleRate, Quality quality);
+ ~SampleRateConverter();
+
+ // Fills the provided output buffer with the results of the sample rate conversion.
+ // The input samples are automatically retrieved from the synth as necessary.
+ void getOutputSamples(MT32Emu::Bit16s *buffer, unsigned int length);
+
+ // Fills the provided output buffer with the results of the sample rate conversion.
+ // The input samples are automatically retrieved from the synth as necessary.
+ void getOutputSamples(float *buffer, unsigned int length);
+
+ // Returns the number of samples produced at the internal synth sample rate (32000 Hz)
+ // that correspond to the number of samples at the target sample rate.
+ // Intended to facilitate audio time synchronisation.
+ double convertOutputToSynthTimestamp(double outputTimestamp) const;
+
+ // Returns the number of samples produced at the target sample rate
+ // that correspond to the number of samples at the internal synth sample rate (32000 Hz).
+ // Intended to facilitate audio time synchronisation.
+ double convertSynthToOutputTimestamp(double synthTimestamp) const;
+
+private:
+ const double synthInternalToTargetSampleRateRatio;
+ void * const srcDelegate;
+}; // class SampleRateConverter
+
+} // namespace MT32Emu
+
+#endif // SAMPLE_RATE_CONVERTER_H
diff --git a/audio/softsynth/mt32/Structures.h b/audio/softsynth/mt32/Structures.h
index 4dada3a8478..6dcb9c86a91 100644
--- a/audio/softsynth/mt32/Structures.h
+++ b/audio/softsynth/mt32/Structures.h
@@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -18,6 +18,9 @@
#ifndef MT32EMU_STRUCTURES_H
#define MT32EMU_STRUCTURES_H
+#include "globals.h"
+#include "Types.h"
+
namespace MT32Emu {
// MT32EMU_MEMADDR() converts from sysex-padded, MT32EMU_SYSEXMEMADDR converts to it
@@ -102,8 +105,8 @@ struct TimbreParam {
Bit8u envTime[5]; // 0-100
Bit8u envLevel[4]; // 0-100 // [3]: SUSTAIN LEVEL
} MT32EMU_ALIGN_PACKED tva;
- } MT32EMU_ALIGN_PACKED partial[4];
-} MT32EMU_ALIGN_PACKED;
+ } MT32EMU_ALIGN_PACKED partial[4]; // struct PartialParam
+} MT32EMU_ALIGN_PACKED; // struct TimbreParam
struct PatchParam {
Bit8u timbreGroup; // TIMBRE GROUP 0-3 (group A, group B, Memory, Rhythm)
@@ -163,7 +166,16 @@ struct MemParams {
Bit8u chanAssign[9]; // MIDI CHANNEL (PART1) 0-16 (1-16,OFF)
Bit8u masterVol; // MASTER VOLUME 0-100
} MT32EMU_ALIGN_PACKED system;
-};
+}; // struct MemParams
+
+struct SoundGroup {
+ Bit8u timbreNumberTableAddrLow;
+ Bit8u timbreNumberTableAddrHigh;
+ Bit8u displayPosition;
+ Bit8u name[9];
+ Bit8u timbreCount;
+ Bit8u pad;
+} MT32EMU_ALIGN_PACKED;
#if defined(_MSC_VER) || defined(__MINGW32__)
#pragma pack(pop)
@@ -171,10 +183,17 @@ struct MemParams {
#pragma pack()
#endif
+struct ControlROMFeatureSet {
+ unsigned int quirkPitchEnvelopeOverflow : 1;
+
+ // Features below don't actually depend on control ROM version, which is used to identify hardware model
+ unsigned int defaultReverbMT32Compatible : 1;
+ unsigned int oldMT32AnalogLPF : 1;
+};
+
struct ControlROMMap {
- Bit16u idPos;
- Bit16u idLen;
- const char *idBytes;
+ const char *shortName;
+ const ControlROMFeatureSet &featureSet;
Bit16u pcmTable; // 4 * pcmCount bytes
Bit16u pcmCount;
Bit16u timbreAMap; // 128 bytes
@@ -194,6 +213,8 @@ struct ControlROMMap {
Bit16u patchMaxTable; // 16 bytes
Bit16u systemMaxTable; // 23 bytes
Bit16u timbreMaxTable; // 72 bytes
+ Bit16u soundGroupsTable; // 14 bytes each entry
+ Bit16u soundGroupsCount;
};
struct ControlROMPCMStruct {
@@ -215,7 +236,7 @@ struct PatchCache {
bool playPartial;
bool PCMPartial;
int pcm;
- char waveform;
+ Bit8u waveform;
Bit32u structureMix;
int structurePosition;
@@ -233,6 +254,6 @@ struct PatchCache {
const TimbreParam::PartialParam *partialParam;
};
-}
+} // namespace MT32Emu
-#endif
+#endif // #ifndef MT32EMU_STRUCTURES_H
diff --git a/audio/softsynth/mt32/Synth.cpp b/audio/softsynth/mt32/Synth.cpp
index 6df7eb9e310..3a478b5b624 100644
--- a/audio/softsynth/mt32/Synth.cpp
+++ b/audio/softsynth/mt32/Synth.cpp
@@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -15,42 +15,136 @@
* along with this program. If not, see .
*/
-//#include
-//#include
-//#include
-//#include
+#include
-#include "mt32emu.h"
-#include "mmath.h"
#include "internals.h"
+#include "Synth.h"
#include "Analog.h"
#include "BReverbModel.h"
+#include "File.h"
#include "MemoryRegion.h"
#include "MidiEventQueue.h"
+#include "Part.h"
+#include "Partial.h"
#include "PartialManager.h"
+#include "Poly.h"
+#include "ROMInfo.h"
+#include "TVA.h"
namespace MT32Emu {
// MIDI interface data transfer rate in samples. Used to simulate the transfer delay.
-static const double MIDI_DATA_TRANSFER_RATE = (double)SAMPLE_RATE / 31250.0 * 8.0;
+static const double MIDI_DATA_TRANSFER_RATE = double(SAMPLE_RATE) / 31250.0 * 8.0;
+
+// FIXME: there should be more specific feature sets for various MT-32 control ROM versions
+static const ControlROMFeatureSet OLD_MT32_COMPATIBLE = { true, true, true };
+static const ControlROMFeatureSet CM32L_COMPATIBLE = { false, false, false };
static const ControlROMMap ControlROMMaps[7] = {
- // ID IDc IDbytes PCMmap PCMc tmbrA tmbrAO, tmbrAC tmbrB tmbrBO, tmbrBC tmbrR trC rhythm rhyC rsrv panpot prog rhyMax patMax sysMax timMax
- {0x4014, 22, "\000 ver1.04 14 July 87 ", 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x73A6, 85, 0x57C7, 0x57E2, 0x57D0, 0x5252, 0x525E, 0x526E, 0x520A},
- {0x4014, 22, "\000 ver1.05 06 Aug, 87 ", 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x7414, 85, 0x57C7, 0x57E2, 0x57D0, 0x5252, 0x525E, 0x526E, 0x520A},
- {0x4014, 22, "\000 ver1.06 31 Aug, 87 ", 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x7414, 85, 0x57D9, 0x57F4, 0x57E2, 0x5264, 0x5270, 0x5280, 0x521C},
- {0x4010, 22, "\000 ver1.07 10 Oct, 87 ", 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x73fe, 85, 0x57B1, 0x57CC, 0x57BA, 0x523C, 0x5248, 0x5258, 0x51F4}, // MT-32 revision 1
- {0x4010, 22, "\000verX.XX 30 Sep, 88 ", 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x741C, 85, 0x57E5, 0x5800, 0x57EE, 0x5270, 0x527C, 0x528C, 0x5228}, // MT-32 Blue Ridge mod
- {0x2205, 22, "\000CM32/LAPC1.00 890404", 0x8100, 256, 0x8000, 0x8000, false, 0x8080, 0x8000, false, 0x8500, 64, 0x8580, 85, 0x4F65, 0x4F80, 0x4F6E, 0x48A1, 0x48A5, 0x48BE, 0x48D5},
- {0x2205, 22, "\000CM32/LAPC1.02 891205", 0x8100, 256, 0x8000, 0x8000, true, 0x8080, 0x8000, true, 0x8500, 64, 0x8580, 85, 0x4F93, 0x4FAE, 0x4F9C, 0x48CB, 0x48CF, 0x48E8, 0x48FF} // CM-32L
+ // ID Features PCMmap PCMc tmbrA tmbrAO, tmbrAC tmbrB tmbrBO tmbrBC tmbrR trC rhythm rhyC rsrv panpot prog rhyMax patMax sysMax timMax sndGrp sGC
+ { "ctrl_mt32_1_04", OLD_MT32_COMPATIBLE, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x73A6, 85, 0x57C7, 0x57E2, 0x57D0, 0x5252, 0x525E, 0x526E, 0x520A, 0x7064, 19 },
+ { "ctrl_mt32_1_05", OLD_MT32_COMPATIBLE, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x7414, 85, 0x57C7, 0x57E2, 0x57D0, 0x5252, 0x525E, 0x526E, 0x520A, 0x70CA, 19 },
+ { "ctrl_mt32_1_06", OLD_MT32_COMPATIBLE, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x7414, 85, 0x57D9, 0x57F4, 0x57E2, 0x5264, 0x5270, 0x5280, 0x521C, 0x70CA, 19 },
+ { "ctrl_mt32_1_07", OLD_MT32_COMPATIBLE, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x73fe, 85, 0x57B1, 0x57CC, 0x57BA, 0x523C, 0x5248, 0x5258, 0x51F4, 0x70B0, 19 }, // MT-32 revision 1
+ {"ctrl_mt32_bluer", OLD_MT32_COMPATIBLE, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x741C, 85, 0x57E5, 0x5800, 0x57EE, 0x5270, 0x527C, 0x528C, 0x5228, 0x70CE, 19 }, // MT-32 Blue Ridge mod
+ {"ctrl_cm32l_1_00", CM32L_COMPATIBLE, 0x8100, 256, 0x8000, 0x8000, true, 0x8080, 0x8000, true, 0x8500, 64, 0x8580, 85, 0x4F65, 0x4F80, 0x4F6E, 0x48A1, 0x48A5, 0x48BE, 0x48D5, 0x5A6C, 19 },
+ {"ctrl_cm32l_1_02", CM32L_COMPATIBLE, 0x8100, 256, 0x8000, 0x8000, true, 0x8080, 0x8000, true, 0x8500, 64, 0x8580, 85, 0x4F93, 0x4FAE, 0x4F9C, 0x48CB, 0x48CF, 0x48E8, 0x48FF, 0x5A96, 19 } // CM-32L
// (Note that all but CM-32L ROM actually have 86 entries for rhythmTemp)
};
-static inline void advanceStreamPosition(Sample *&stream, Bit32u posDelta) {
- if (stream != NULL) {
- stream += posDelta;
+static const PartialState PARTIAL_PHASE_TO_STATE[8] = {
+ PartialState_ATTACK, PartialState_ATTACK, PartialState_ATTACK, PartialState_ATTACK,
+ PartialState_SUSTAIN, PartialState_SUSTAIN, PartialState_RELEASE, PartialState_INACTIVE
+};
+
+static inline PartialState getPartialState(PartialManager *partialManager, unsigned int partialNum) {
+ const Partial *partial = partialManager->getPartial(partialNum);
+ return partial->isActive() ? PARTIAL_PHASE_TO_STATE[partial->getTVA()->getPhase()] : PartialState_INACTIVE;
+}
+
+class SampleFormatConverter {
+protected:
+#if MT32EMU_USE_FLOAT_SAMPLES
+ Bit16s *outBuffer;
+#else
+ float *outBuffer;
+#endif
+
+public:
+ Sample *sampleBuffer;
+
+ SampleFormatConverter(Sample *buffer) : outBuffer(NULL), sampleBuffer(buffer) {}
+
+ inline bool isConversionNeeded() {
+ return outBuffer != NULL;
}
+
+ inline void convert(Bit32u len) {
+ if (sampleBuffer == NULL) return;
+ if (outBuffer == NULL) {
+ sampleBuffer += len;
+ return;
+ }
+ Sample *inBuffer = sampleBuffer;
+ while (len--) {
+ *(outBuffer++) = Synth::convertSample(*(inBuffer++));
+ }
+ }
+
+ inline void addSilence(Bit32u len) {
+ if (outBuffer != NULL) {
+ Synth::muteSampleBuffer(outBuffer, len);
+ outBuffer += len;
+ } else if (sampleBuffer != NULL) {
+ Synth::muteSampleBuffer(sampleBuffer, len);
+ sampleBuffer += len;
+ }
+ }
+};
+
+template
+class BufferedSampleFormatConverter : public SampleFormatConverter {
+ Sample renderingBuffer[BUFFER_SIZE_MULTIPLIER * MAX_SAMPLES_PER_RUN];
+
+public:
+#if MT32EMU_USE_FLOAT_SAMPLES
+ BufferedSampleFormatConverter(Bit16s *buffer)
+#else
+ BufferedSampleFormatConverter(float *buffer)
+#endif
+ : SampleFormatConverter(renderingBuffer)
+ {
+ outBuffer = buffer;
+ if (buffer == NULL) sampleBuffer = NULL;
+ }
+};
+
+class Renderer {
+ Synth &synth;
+
+ // These buffers are used for building the output streams as they are found at the DAC entrance.
+ // The output is mixed down to stereo interleaved further in the analog circuitry emulation.
+ Sample tmpNonReverbLeft[MAX_SAMPLES_PER_RUN], tmpNonReverbRight[MAX_SAMPLES_PER_RUN];
+ Sample tmpReverbDryLeft[MAX_SAMPLES_PER_RUN], tmpReverbDryRight[MAX_SAMPLES_PER_RUN];
+ Sample tmpReverbWetLeft[MAX_SAMPLES_PER_RUN], tmpReverbWetRight[MAX_SAMPLES_PER_RUN];
+
+public:
+ Renderer(Synth &useSynth) : synth(useSynth) {}
+
+ void render(SampleFormatConverter &converter, Bit32u len);
+ void renderStreams(SampleFormatConverter &nonReverbLeft, SampleFormatConverter &nonReverbRight, SampleFormatConverter &reverbDryLeft, SampleFormatConverter &reverbDryRight, SampleFormatConverter &reverbWetLeft, SampleFormatConverter &reverbWetRight, Bit32u len);
+ void produceLA32Output(Sample *buffer, Bit32u len);
+ void convertSamplesToOutput(Sample *buffer, Bit32u len);
+ void doRenderStreams(DACOutputStreams &streams, Bit32u len);
+};
+
+Bit32u Synth::getLibraryVersionInt() {
+ return (MT32EMU_VERSION_MAJOR << 16) | (MT32EMU_VERSION_MINOR << 8) | (MT32EMU_VERSION_PATCH);
+}
+
+const char *Synth::getLibraryVersionString() {
+ return MT32EMU_VERSION;
}
Bit8u Synth::calcSysexChecksum(const Bit8u *data, const Bit32u len, const Bit8u initChecksum) {
@@ -61,10 +155,17 @@ Bit8u Synth::calcSysexChecksum(const Bit8u *data, const Bit32u len, const Bit8u
return Bit8u(checksum & 0x7f);
}
-Synth::Synth(ReportHandler *useReportHandler) : mt32ram(*new MemParams()), mt32default(*new MemParams()) {
- isOpen = false;
+Bit32u Synth::getStereoOutputSampleRate(AnalogOutputMode analogOutputMode) {
+ static const unsigned int SAMPLE_RATES[] = {SAMPLE_RATE, SAMPLE_RATE, SAMPLE_RATE * 3 / 2, SAMPLE_RATE * 3};
+
+ return SAMPLE_RATES[analogOutputMode];
+}
+
+Synth::Synth(ReportHandler *useReportHandler) : mt32ram(*new MemParams), mt32default(*new MemParams), renderer(*new Renderer(*this)) {
+ opened = false;
reverbOverridden = false;
partialCount = DEFAULT_MAX_PARTIALS;
+ controlROMMap = NULL;
controlROMFeatures = NULL;
if (useReportHandler == NULL) {
@@ -85,11 +186,27 @@ Synth::Synth(ReportHandler *useReportHandler) : mt32ram(*new MemParams()), mt32d
setOutputGain(1.0f);
setReverbOutputGain(1.0f);
setReversedStereoEnabled(false);
+
+ patchTempMemoryRegion = NULL;
+ rhythmTempMemoryRegion = NULL;
+ timbreTempMemoryRegion = NULL;
+ patchesMemoryRegion = NULL;
+ timbresMemoryRegion = NULL;
+ systemMemoryRegion = NULL;
+ displayMemoryRegion = NULL;
+ resetMemoryRegion = NULL;
+ paddedTimbreMaxTable = NULL;
+
partialManager = NULL;
+ pcmWaves = NULL;
+ pcmROMData = NULL;
+ soundGroupNames = NULL;
midiQueue = NULL;
lastReceivedMIDIEventTimestamp = 0;
memset(parts, 0, sizeof(parts));
renderedSampleCount = 0;
+
+ reserved = NULL;
}
Synth::~Synth() {
@@ -99,47 +216,52 @@ Synth::~Synth() {
}
delete &mt32ram;
delete &mt32default;
+ delete &renderer;
}
void ReportHandler::showLCDMessage(const char *data) {
- // We cannot use printf here. Since we already implement our own
- // ReportHandler we simply disable the default implementation since it is
- // never called anyway.
-#if 0
- printf("WRITE-LCD: %s", data);
- printf("\n");
-#endif
+ printf("WRITE-LCD: %s\n", data);
}
void ReportHandler::printDebug(const char *fmt, va_list list) {
- // We cannot use (v)printf here. Since we already implement our own
- // ReportHandler we simply disable the default implementation since it is
- // never called anyway.
-#if 0
vprintf(fmt, list);
printf("\n");
-#endif
}
-void Synth::polyStateChanged(int partNum) {
- reportHandler->onPolyStateChanged(partNum);
-}
-
-void Synth::newTimbreSet(int partNum, Bit8u timbreGroup, const char patchName[]) {
- reportHandler->onProgramChanged(partNum, timbreGroup, patchName);
+void Synth::newTimbreSet(Bit8u partNum, Bit8u timbreGroup, Bit8u timbreNumber, const char patchName[]) {
+ const char *soundGroupName;
+ switch (timbreGroup) {
+ case 1:
+ timbreNumber += 64;
+ // Fall-through
+ case 0:
+ soundGroupName = soundGroupNames[soundGroupIx[timbreNumber]];
+ break;
+ case 2:
+ soundGroupName = soundGroupNames[controlROMMap->soundGroupsCount - 2];
+ break;
+ case 3:
+ soundGroupName = soundGroupNames[controlROMMap->soundGroupsCount - 1];
+ break;
+ default:
+ soundGroupName = NULL;
+ break;
+ }
+ reportHandler->onProgramChanged(partNum, soundGroupName, patchName);
}
void Synth::printDebug(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
#if MT32EMU_DEBUG_SAMPLESTAMPS > 0
- reportHandler->printDebug("[%u] ", (char *)&renderedSampleCount);
+ reportHandler->printDebug("[%u]", (va_list)&renderedSampleCount);
#endif
reportHandler->printDebug(fmt, ap);
va_end(ap);
}
void Synth::setReverbEnabled(bool newReverbEnabled) {
+ if (!opened) return;
if (isReverbEnabled() == newReverbEnabled) return;
if (newReverbEnabled) {
bool oldReverbOverridden = reverbOverridden;
@@ -167,30 +289,23 @@ bool Synth::isReverbOverridden() const {
}
void Synth::setReverbCompatibilityMode(bool mt32CompatibleMode) {
- if (reverbModels[REVERB_MODE_ROOM] != NULL) {
- if (isMT32ReverbCompatibilityMode() == mt32CompatibleMode) return;
- setReverbEnabled(false);
- for (int i = 0; i < 4; i++) {
- delete reverbModels[i];
- }
- }
- reverbModels[REVERB_MODE_ROOM] = new BReverbModel(REVERB_MODE_ROOM, mt32CompatibleMode);
- reverbModels[REVERB_MODE_HALL] = new BReverbModel(REVERB_MODE_HALL, mt32CompatibleMode);
- reverbModels[REVERB_MODE_PLATE] = new BReverbModel(REVERB_MODE_PLATE, mt32CompatibleMode);
- reverbModels[REVERB_MODE_TAP_DELAY] = new BReverbModel(REVERB_MODE_TAP_DELAY, mt32CompatibleMode);
-#if !MT32EMU_REDUCE_REVERB_MEMORY
- for (int i = REVERB_MODE_ROOM; i <= REVERB_MODE_TAP_DELAY; i++) {
- reverbModels[i]->open();
- }
-#endif
- if (isOpen) {
- setReverbOutputGain(reverbOutputGain);
- setReverbEnabled(true);
+ if (!opened || (isMT32ReverbCompatibilityMode() == mt32CompatibleMode)) return;
+ bool oldReverbEnabled = isReverbEnabled();
+ setReverbEnabled(false);
+ for (int i = 0; i < 4; i++) {
+ delete reverbModels[i];
}
+ initReverbModels(mt32CompatibleMode);
+ setReverbEnabled(oldReverbEnabled);
+ setReverbOutputGain(reverbOutputGain);
}
bool Synth::isMT32ReverbCompatibilityMode() const {
- return isOpen && (reverbModels[REVERB_MODE_ROOM]->isMT32Compatible(REVERB_MODE_ROOM));
+ return opened && (reverbModels[REVERB_MODE_ROOM]->isMT32Compatible(REVERB_MODE_ROOM));
+}
+
+bool Synth::isDefaultReverbMT32Compatible() const {
+ return opened && controlROMFeatures->defaultReverbMT32Compatible;
}
void Synth::setDACInputMode(DACInputMode mode) {
@@ -239,22 +354,18 @@ void Synth::setReversedStereoEnabled(bool enabled) {
reversedStereoEnabled = enabled;
}
-bool Synth::isReversedStereoEnabled() {
+bool Synth::isReversedStereoEnabled() const {
return reversedStereoEnabled;
}
bool Synth::loadControlROM(const ROMImage &controlROMImage) {
- Common::File *file = controlROMImage.getFile();
+ File *file = controlROMImage.getFile();
const ROMInfo *controlROMInfo = controlROMImage.getROMInfo();
if ((controlROMInfo == NULL)
|| (controlROMInfo->type != ROMInfo::Control)
|| (controlROMInfo->pairType != ROMInfo::Full)) {
- return false;
- }
- controlROMFeatures = controlROMImage.getROMInfo()->controlROMFeatures;
- if (controlROMFeatures == NULL) {
#if MT32EMU_MONITOR_INIT
- printDebug("Invalid Control ROM Info provided without feature set");
+ printDebug("Invalid Control ROM Info provided");
#endif
return false;
}
@@ -262,13 +373,16 @@ bool Synth::loadControlROM(const ROMImage &controlROMImage) {
#if MT32EMU_MONITOR_INIT
printDebug("Found Control ROM: %s, %s", controlROMInfo->shortName, controlROMInfo->description);
#endif
- file->read(controlROMData, CONTROL_ROM_SIZE);
+ const Bit8u *fileData = file->getData();
+ memcpy(controlROMData, fileData, CONTROL_ROM_SIZE);
// Control ROM successfully loaded, now check whether it's a known type
controlROMMap = NULL;
+ controlROMFeatures = NULL;
for (unsigned int i = 0; i < sizeof(ControlROMMaps) / sizeof(ControlROMMaps[0]); i++) {
- if (memcmp(&controlROMData[ControlROMMaps[i].idPos], ControlROMMaps[i].idBytes, ControlROMMaps[i].idLen) == 0) {
+ if (strcmp(controlROMInfo->shortName, ControlROMMaps[i].shortName) == 0) {
controlROMMap = &ControlROMMaps[i];
+ controlROMFeatures = &controlROMMap->featureSet;
return true;
}
}
@@ -279,7 +393,7 @@ bool Synth::loadControlROM(const ROMImage &controlROMImage) {
}
bool Synth::loadPCMROM(const ROMImage &pcmROMImage) {
- Common::File *file = pcmROMImage.getFile();
+ File *file = pcmROMImage.getFile();
const ROMInfo *pcmROMInfo = pcmROMImage.getROMInfo();
if ((pcmROMInfo == NULL)
|| (pcmROMInfo->type != ROMInfo::PCM)
@@ -289,24 +403,21 @@ bool Synth::loadPCMROM(const ROMImage &pcmROMImage) {
#if MT32EMU_MONITOR_INIT
printDebug("Found PCM ROM: %s, %s", pcmROMInfo->shortName, pcmROMInfo->description);
#endif
- size_t fileSize = file->size();
+ size_t fileSize = file->getSize();
if (fileSize != (2 * pcmROMSize)) {
#if MT32EMU_MONITOR_INIT
printDebug("PCM ROM file has wrong size (expected %d, got %d)", 2 * pcmROMSize, fileSize);
#endif
return false;
}
-
- byte *buffer = new byte[file->size()];
- file->read(buffer, file->size());
- const byte *fileData = buffer;
+ const Bit8u *fileData = file->getData();
for (size_t i = 0; i < pcmROMSize; i++) {
Bit8u s = *(fileData++);
Bit8u c = *(fileData++);
int order[16] = {0, 9, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 8};
- signed short log = 0;
+ Bit16s log = 0;
for (int u = 0; u < 15; u++) {
int bit;
if (order[u] < 8) {
@@ -314,18 +425,15 @@ bool Synth::loadPCMROM(const ROMImage &pcmROMImage) {
} else {
bit = (c >> (7 - (order[u] - 8))) & 0x1;
}
- log = log | (short)(bit << (15 - u));
+ log = log | Bit16s(bit << (15 - u));
}
pcmROMData[i] = log;
}
-
- delete[] buffer;
-
return true;
}
bool Synth::initPCMList(Bit16u mapAddress, Bit16u count) {
- ControlROMPCMStruct *tps = (ControlROMPCMStruct *)&controlROMData[mapAddress];
+ ControlROMPCMStruct *tps = reinterpret_cast(&controlROMData[mapAddress]);
for (int i = 0; i < count; i++) {
Bit32u rAddr = tps[i].pos * 0x800;
Bit32u rLenExp = (tps[i].len & 0x70) >> 4;
@@ -345,7 +453,7 @@ bool Synth::initPCMList(Bit16u mapAddress, Bit16u count) {
return false;
}
-bool Synth::initCompressedTimbre(int timbreNum, const Bit8u *src, unsigned int srcLen) {
+bool Synth::initCompressedTimbre(Bit16u timbreNum, const Bit8u *src, Bit32u srcLen) {
// "Compressed" here means that muted partials aren't present in ROM (except in the case of partial 0 being muted).
// Instead the data from the previous unmuted partial is used.
if (srcLen < sizeof(TimbreParam::CommonParam)) {
@@ -369,7 +477,7 @@ bool Synth::initCompressedTimbre(int timbreNum, const Bit8u *src, unsigned int s
return true;
}
-bool Synth::initTimbres(Bit16u mapAddress, Bit16u offset, int count, int startTimbre, bool compressed) {
+bool Synth::initTimbres(Bit16u mapAddress, Bit16u offset, Bit16u count, Bit16u startTimbre, bool compressed) {
const Bit8u *timbreMap = &controlROMData[mapAddress];
for (Bit16u i = 0; i < count * 2; i += 2) {
Bit16u address = (timbreMap[i + 1] << 8) | timbreMap[i];
@@ -391,12 +499,32 @@ bool Synth::initTimbres(Bit16u mapAddress, Bit16u offset, int count, int startTi
return true;
}
+void Synth::initReverbModels(bool mt32CompatibleMode) {
+ reverbModels[REVERB_MODE_ROOM] = new BReverbModel(REVERB_MODE_ROOM, mt32CompatibleMode);
+ reverbModels[REVERB_MODE_HALL] = new BReverbModel(REVERB_MODE_HALL, mt32CompatibleMode);
+ reverbModels[REVERB_MODE_PLATE] = new BReverbModel(REVERB_MODE_PLATE, mt32CompatibleMode);
+ reverbModels[REVERB_MODE_TAP_DELAY] = new BReverbModel(REVERB_MODE_TAP_DELAY, mt32CompatibleMode);
+#if !MT32EMU_REDUCE_REVERB_MEMORY
+ for (int i = REVERB_MODE_ROOM; i <= REVERB_MODE_TAP_DELAY; i++) {
+ reverbModels[i]->open();
+ }
+#endif
+}
+
+void Synth::initSoundGroups(char newSoundGroupNames[][9]) {
+ memcpy(soundGroupIx, &controlROMData[controlROMMap->soundGroupsTable - sizeof(soundGroupIx)], sizeof(soundGroupIx));
+ const SoundGroup *table = reinterpret_cast(&controlROMData[controlROMMap->soundGroupsTable]);
+ for (unsigned int i = 0; i < controlROMMap->soundGroupsCount; i++) {
+ memcpy(&newSoundGroupNames[i][0], table[i].name, sizeof(table[i].name));
+ }
+}
+
bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, AnalogOutputMode analogOutputMode) {
return open(controlROMImage, pcmROMImage, DEFAULT_MAX_PARTIALS, analogOutputMode);
}
-bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, unsigned int usePartialCount, AnalogOutputMode analogOutputMode) {
- if (isOpen) {
+bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, Bit32u usePartialCount, AnalogOutputMode analogOutputMode) {
+ if (opened) {
return false;
}
partialCount = usePartialCount;
@@ -411,6 +539,7 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, u
if (!loadControlROM(controlROMImage)) {
printDebug("Init Error - Missing or invalid Control ROM image");
reportHandler->onErrorControlROM();
+ dispose();
return false;
}
@@ -428,22 +557,24 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, u
if (!loadPCMROM(pcmROMImage)) {
printDebug("Init Error - Missing PCM ROM image");
reportHandler->onErrorPCMROM();
+ dispose();
return false;
}
#if MT32EMU_MONITOR_INIT
printDebug("Initialising Reverb Models");
#endif
- bool mt32CompatibleReverb = controlROMFeatures->isDefaultReverbMT32Compatible();
+ bool mt32CompatibleReverb = controlROMFeatures->defaultReverbMT32Compatible;
#if MT32EMU_MONITOR_INIT
printDebug("Using %s Compatible Reverb Models", mt32CompatibleReverb ? "MT-32" : "CM-32L");
#endif
- setReverbCompatibilityMode(mt32CompatibleReverb);
+ initReverbModels(mt32CompatibleReverb);
#if MT32EMU_MONITOR_INIT
printDebug("Initialising Timbre Bank A");
#endif
if (!initTimbres(controlROMMap->timbreAMap, controlROMMap->timbreAOffset, 0x40, 0, controlROMMap->timbreACompressed)) {
+ dispose();
return false;
}
@@ -451,6 +582,7 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, u
printDebug("Initialising Timbre Bank B");
#endif
if (!initTimbres(controlROMMap->timbreBMap, controlROMMap->timbreBOffset, 0x40, 64, controlROMMap->timbreBCompressed)) {
+ dispose();
return false;
}
@@ -458,6 +590,7 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, u
printDebug("Initialising Timbre Bank R");
#endif
if (!initTimbres(controlROMMap->timbreRMap, 0, controlROMMap->timbreRCount, 192, true)) {
+ dispose();
return false;
}
@@ -519,6 +652,10 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, u
refreshSystem();
reverbOverridden = oldReverbOverridden;
+ char(*writableSoundGroupNames)[9] = new char[controlROMMap->soundGroupsCount][9];
+ soundGroupNames = writableSoundGroupNames;
+ initSoundGroups(writableSoundGroupNames);
+
for (int i = 0; i < 9; i++) {
MemParams::PatchTemp *patchTemp = &mt32ram.patchTemp[i];
@@ -550,12 +687,12 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, u
midiQueue = new MidiEventQueue();
- analog = new Analog(analogOutputMode, controlROMFeatures);
+ analog = new Analog(analogOutputMode, controlROMFeatures->oldMT32AnalogLPF);
setOutputGain(outputGain);
setReverbOutputGain(reverbOutputGain);
- isOpen = true;
- isEnabled = false;
+ opened = true;
+ activated = false;
#if MT32EMU_MONITOR_INIT
printDebug("*** Initialisation complete ***");
@@ -563,10 +700,8 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, u
return true;
}
-void Synth::close(bool forced) {
- if (!forced && !isOpen) {
- return;
- }
+void Synth::dispose() {
+ opened = false;
delete midiQueue;
midiQueue = NULL;
@@ -582,8 +717,14 @@ void Synth::close(bool forced) {
parts[i] = NULL;
}
+ delete[] soundGroupNames;
+ soundGroupNames = NULL;
+
delete[] pcmWaves;
+ pcmWaves = NULL;
+
delete[] pcmROMData;
+ pcmROMData = NULL;
deleteMemoryRegions();
@@ -593,7 +734,17 @@ void Synth::close(bool forced) {
}
reverbModel = NULL;
controlROMFeatures = NULL;
- isOpen = false;
+ controlROMMap = NULL;
+}
+
+void Synth::close() {
+ if (opened) {
+ dispose();
+ }
+}
+
+bool Synth::isOpen() const {
+ return opened;
}
void Synth::flushMIDIQueue() {
@@ -649,7 +800,7 @@ Bit32u Synth::getShortMessageLength(Bit32u msg) {
}
Bit32u Synth::addMIDIInterfaceDelay(Bit32u len, Bit32u timestamp) {
- Bit32u transferTime = Bit32u((double)len * MIDI_DATA_TRANSFER_RATE);
+ Bit32u transferTime = Bit32u(double(len) * MIDI_DATA_TRANSFER_RATE);
// Dealing with wrapping
if (Bit32s(timestamp - lastReceivedMIDIEventTimestamp) < 0) {
timestamp = lastReceivedMIDIEventTimestamp;
@@ -664,12 +815,19 @@ bool Synth::playMsg(Bit32u msg) {
}
bool Synth::playMsg(Bit32u msg, Bit32u timestamp) {
+ if ((msg & 0xF8) == 0xF8) {
+ reportHandler->onMIDISystemRealtime(Bit8u(msg));
+ return true;
+ }
if (midiQueue == NULL) return false;
if (midiDelayMode != MIDIDelayMode_IMMEDIATE) {
timestamp = addMIDIInterfaceDelay(getShortMessageLength(msg), timestamp);
}
- if (!isEnabled) isEnabled = true;
- return midiQueue->pushShortMessage(msg, timestamp);
+ if (!activated) activated = true;
+ do {
+ if (midiQueue->pushShortMessage(msg, timestamp)) return true;
+ } while (reportHandler->onMIDIQueueOverflow());
+ return false;
}
bool Synth::playSysex(const Bit8u *sysex, Bit32u len) {
@@ -681,24 +839,28 @@ bool Synth::playSysex(const Bit8u *sysex, Bit32u len, Bit32u timestamp) {
if (midiDelayMode == MIDIDelayMode_DELAY_ALL) {
timestamp = addMIDIInterfaceDelay(len, timestamp);
}
- if (!isEnabled) isEnabled = true;
- return midiQueue->pushSysex(sysex, len, timestamp);
+ if (!activated) activated = true;
+ do {
+ if (midiQueue->pushSysex(sysex, len, timestamp)) return true;
+ } while (reportHandler->onMIDIQueueOverflow());
+ return false;
}
void Synth::playMsgNow(Bit32u msg) {
+ if (!opened) return;
+
// NOTE: Active sense IS implemented in real hardware. However, realtime processing is clearly out of the library scope.
// It is assumed that realtime consumers of the library respond to these MIDI events as appropriate.
- unsigned char code = (unsigned char)((msg & 0x0000F0) >> 4);
- unsigned char chan = (unsigned char)(msg & 0x00000F);
- unsigned char note = (unsigned char)((msg & 0x007F00) >> 8);
- unsigned char velocity = (unsigned char)((msg & 0x7F0000) >> 16);
- if (!isEnabled) isEnabled = true;
+ Bit8u code = Bit8u((msg & 0x0000F0) >> 4);
+ Bit8u chan = Bit8u(msg & 0x00000F);
+ Bit8u note = Bit8u((msg & 0x007F00) >> 8);
+ Bit8u velocity = Bit8u((msg & 0x7F0000) >> 16);
//printDebug("Playing chan %d, code 0x%01x note: 0x%02x", chan, code, note);
- char part = chantable[chan];
- if (part < 0 || part > 8) {
+ Bit8u part = chantable[chan];
+ if (part > 8) {
#if MT32EMU_MONITOR_MIDI > 0
printDebug("Play msg on unreg chan %d (%d): code=0x%01x, vel=%d", chan, part, code, velocity);
#endif
@@ -707,9 +869,12 @@ void Synth::playMsgNow(Bit32u msg) {
playMsgOnPart(part, code, note, velocity);
}
-void Synth::playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity) {
+void Synth::playMsgOnPart(Bit8u part, Bit8u code, Bit8u note, Bit8u velocity) {
+ if (!opened) return;
+
Bit32u bend;
+ if (!activated) activated = true;
//printDebug("Synth::playMsgOnPart(%02x, %02x, %02x, %02x)", part, code, note, velocity);
switch (code) {
case 0x8:
@@ -836,23 +1001,23 @@ void Synth::playSysexWithoutFraming(const Bit8u *sysex, Bit32u len) {
return;
}
if (sysex[0] != SYSEX_MANUFACTURER_ROLAND) {
- printDebug("playSysexWithoutFraming: Header not intended for this device manufacturer: %02x %02x %02x %02x", (int)sysex[0], (int)sysex[1], (int)sysex[2], (int)sysex[3]);
+ printDebug("playSysexWithoutFraming: Header not intended for this device manufacturer: %02x %02x %02x %02x", int(sysex[0]), int(sysex[1]), int(sysex[2]), int(sysex[3]));
return;
}
if (sysex[2] == SYSEX_MDL_D50) {
- printDebug("playSysexWithoutFraming: Header is intended for model D-50 (not yet supported): %02x %02x %02x %02x", (int)sysex[0], (int)sysex[1], (int)sysex[2], (int)sysex[3]);
+ printDebug("playSysexWithoutFraming: Header is intended for model D-50 (not yet supported): %02x %02x %02x %02x", int(sysex[0]), int(sysex[1]), int(sysex[2]), int(sysex[3]));
return;
} else if (sysex[2] != SYSEX_MDL_MT32) {
- printDebug("playSysexWithoutFraming: Header not intended for model MT-32: %02x %02x %02x %02x", (int)sysex[0], (int)sysex[1], (int)sysex[2], (int)sysex[3]);
+ printDebug("playSysexWithoutFraming: Header not intended for model MT-32: %02x %02x %02x %02x", int(sysex[0]), int(sysex[1]), int(sysex[2]), int(sysex[3]));
return;
}
playSysexWithoutHeader(sysex[1], sysex[3], sysex + 4, len - 4);
}
-void Synth::playSysexWithoutHeader(unsigned char device, unsigned char command, const Bit8u *sysex, Bit32u len) {
+void Synth::playSysexWithoutHeader(Bit8u device, Bit8u command, const Bit8u *sysex, Bit32u len) {
if (device > 0x10) {
// We have device ID 0x10 (default, but changeable, on real MT-32), < 0x10 is for channels
- printDebug("playSysexWithoutHeader: Message is not intended for this device ID (provided: %02x, expected: 0x10 or channel)", (int)device);
+ printDebug("playSysexWithoutHeader: Message is not intended for this device ID (provided: %02x, expected: 0x10 or channel)", int(device));
return;
}
// This is checked early in the real devices (before any sysex length checks or further processing)
@@ -861,6 +1026,13 @@ void Synth::playSysexWithoutHeader(unsigned char device, unsigned char command,
reset();
return;
}
+
+ if (command == SYSEX_CMD_EOD) {
+#if MT32EMU_MONITOR_SYSEX > 0
+ printDebug("playSysexWithoutHeader: Ignored unsupported command %02x", command);
+#endif
+ return;
+ }
if (len < 4) {
printDebug("playSysexWithoutHeader: Message is too short (%d bytes)!", len);
return;
@@ -872,13 +1044,20 @@ void Synth::playSysexWithoutHeader(unsigned char device, unsigned char command,
}
len -= 1; // Exclude checksum
switch (command) {
+ case SYSEX_CMD_WSD:
+#if MT32EMU_MONITOR_SYSEX > 0
+ printDebug("playSysexWithoutHeader: Ignored unsupported command %02x", command);
+#endif
+ break;
case SYSEX_CMD_DAT:
+ /* Outcommented until we (ever) actually implement handshake communication
if (hasActivePartials()) {
printDebug("playSysexWithoutHeader: Got SYSEX_CMD_DAT but partials are active - ignoring");
// FIXME: We should send SYSEX_CMD_RJC in this case
break;
}
- // Deliberate fall-through
+ */
+ // fall through
case SYSEX_CMD_DT1:
writeSysex(device, sysex, len);
break;
@@ -888,7 +1067,7 @@ void Synth::playSysexWithoutHeader(unsigned char device, unsigned char command,
// FIXME: We should send SYSEX_CMD_RJC in this case
break;
}
- // Deliberate fall-through
+ // fall through
case SYSEX_CMD_RQ1:
readSysex(device, sysex, len);
break;
@@ -898,11 +1077,12 @@ void Synth::playSysexWithoutHeader(unsigned char device, unsigned char command,
}
}
-void Synth::readSysex(unsigned char /*device*/, const Bit8u * /*sysex*/, Bit32u /*len*/) const {
+void Synth::readSysex(Bit8u /*device*/, const Bit8u * /*sysex*/, Bit32u /*len*/) const {
// NYI
}
-void Synth::writeSysex(unsigned char device, const Bit8u *sysex, Bit32u len) {
+void Synth::writeSysex(Bit8u device, const Bit8u *sysex, Bit32u len) {
+ if (!opened) return;
reportHandler->onMIDIMessagePlayed();
Bit32u addr = (sysex[0] << 16) | (sysex[1] << 8) | (sysex[2]);
addr = MT32EMU_MEMADDR(addr);
@@ -918,7 +1098,7 @@ void Synth::writeSysex(unsigned char device, const Bit8u *sysex, Bit32u len) {
#endif
if (/*addr >= MT32EMU_MEMADDR(0x000000) && */addr < MT32EMU_MEMADDR(0x010000)) {
int offset;
- if (chantable[device] == -1) {
+ if (chantable[device] > 8) {
#if MT32EMU_MONITOR_SYSEX > 0
printDebug(" (Channel not mapped to a part... 0 offset)");
#endif
@@ -939,7 +1119,7 @@ void Synth::writeSysex(unsigned char device, const Bit8u *sysex, Bit32u len) {
addr += MT32EMU_MEMADDR(0x030110) - MT32EMU_MEMADDR(0x010000);
} else if (/*addr >= MT32EMU_MEMADDR(0x020000) && */ addr < MT32EMU_MEMADDR(0x030000)) {
int offset;
- if (chantable[device] == -1) {
+ if (chantable[device] > 8) {
#if MT32EMU_MONITOR_SYSEX > 0
printDebug(" (Channel not mapped to a part... 0 offset)");
#endif
@@ -986,6 +1166,7 @@ void Synth::writeSysex(unsigned char device, const Bit8u *sysex, Bit32u len) {
}
void Synth::readMemory(Bit32u addr, Bit32u len, Bit8u *data) {
+ if (!opened) return;
const MemoryRegion *region = findMemoryRegion(addr);
if (region != NULL) {
readMemoryRegion(region, addr, len, data);
@@ -1004,12 +1185,12 @@ void Synth::initMemoryRegions() {
pos += sizeof(TimbreParam::PartialParam);
}
memset(&paddedTimbreMaxTable[pos], 0, 10); // Padding
- patchTempMemoryRegion = new PatchTempMemoryRegion(this, (Bit8u *)&mt32ram.patchTemp[0], &controlROMData[controlROMMap->patchMaxTable]);
- rhythmTempMemoryRegion = new RhythmTempMemoryRegion(this, (Bit8u *)&mt32ram.rhythmTemp[0], &controlROMData[controlROMMap->rhythmMaxTable]);
- timbreTempMemoryRegion = new TimbreTempMemoryRegion(this, (Bit8u *)&mt32ram.timbreTemp[0], paddedTimbreMaxTable);
- patchesMemoryRegion = new PatchesMemoryRegion(this, (Bit8u *)&mt32ram.patches[0], &controlROMData[controlROMMap->patchMaxTable]);
- timbresMemoryRegion = new TimbresMemoryRegion(this, (Bit8u *)&mt32ram.timbres[0], paddedTimbreMaxTable);
- systemMemoryRegion = new SystemMemoryRegion(this, (Bit8u *)&mt32ram.system, &controlROMData[controlROMMap->systemMaxTable]);
+ patchTempMemoryRegion = new PatchTempMemoryRegion(this, reinterpret_cast(&mt32ram.patchTemp[0]), &controlROMData[controlROMMap->patchMaxTable]);
+ rhythmTempMemoryRegion = new RhythmTempMemoryRegion(this, reinterpret_cast(&mt32ram.rhythmTemp[0]), &controlROMData[controlROMMap->rhythmMaxTable]);
+ timbreTempMemoryRegion = new TimbreTempMemoryRegion(this, reinterpret_cast(&mt32ram.timbreTemp[0]), paddedTimbreMaxTable);
+ patchesMemoryRegion = new PatchesMemoryRegion(this, reinterpret_cast(&mt32ram.patches[0]), &controlROMData[controlROMMap->patchMaxTable]);
+ timbresMemoryRegion = new TimbresMemoryRegion(this, reinterpret_cast(&mt32ram.timbres[0]), paddedTimbreMaxTable);
+ systemMemoryRegion = new SystemMemoryRegion(this, reinterpret_cast(&mt32ram.system), &controlROMData[controlROMMap->systemMaxTable]);
displayMemoryRegion = new DisplayMemoryRegion(this);
resetMemoryRegion = new ResetMemoryRegion(this);
}
@@ -1071,7 +1252,7 @@ void Synth::readMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len
for (m = 0; m < len; m += 2) {
data[m] = 0xff;
if (m + 1 < len) {
- data[m+1] = (Bit8u)region->type;
+ data[m+1] = Bit8u(region->type);
}
}
}
@@ -1279,16 +1460,16 @@ void Synth::writeMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u le
if(firstPart < 0)
firstPart = 0;
int lastPart = off + len - SYSTEM_CHAN_ASSIGN_START_OFF;
- if(lastPart > 9)
- lastPart = 9;
- refreshSystemChanAssign(firstPart, lastPart);
+ if(lastPart > 8)
+ lastPart = 8;
+ refreshSystemChanAssign(Bit8u(firstPart), Bit8u(lastPart));
}
if (off <= SYSTEM_MASTER_VOL_OFF && off + len > SYSTEM_MASTER_VOL_OFF) {
refreshSystemMasterVol();
}
break;
case MR_Display:
- char buf[MAX_SYSEX_SIZE];
+ char buf[SYSEX_BUFFER_SIZE];
memcpy(&buf, &data[0], len);
buf[len] = 0;
#if MT32EMU_MONITOR_SYSEX > 0
@@ -1361,19 +1542,19 @@ void Synth::refreshSystemReserveSettings() {
partialManager->setReserve(rset);
}
-void Synth::refreshSystemChanAssign(unsigned int firstPart, unsigned int lastPart) {
- memset(chantable, -1, sizeof(chantable));
+void Synth::refreshSystemChanAssign(Bit8u firstPart, Bit8u lastPart) {
+ memset(chantable, 0xFF, sizeof(chantable));
// CONFIRMED: In the case of assigning a channel to multiple parts, the lower part wins.
- for (unsigned int i = 0; i <= 8; i++) {
+ for (Bit32u i = 0; i <= 8; i++) {
if (parts[i] != NULL && i >= firstPart && i <= lastPart) {
// CONFIRMED: Decay is started for all polys, and all controllers are reset, for every part whose assignment was touched by the sysex write.
parts[i]->allSoundOff();
parts[i]->resetAllControllers();
}
- int chan = mt32ram.system.chanAssign[i];
- if (chan != 16 && chantable[chan] == -1) {
- chantable[chan] = i;
+ Bit8u chan = mt32ram.system.chanAssign[i];
+ if (chan < 16 && chantable[chan] > 8) {
+ chantable[chan] = Bit8u(i);
}
}
@@ -1398,6 +1579,7 @@ void Synth::refreshSystem() {
}
void Synth::reset() {
+ if (!opened) return;
#if MT32EMU_MONITOR_SYSEX > 0
printDebug("RESET");
#endif
@@ -1413,7 +1595,7 @@ void Synth::reset() {
}
}
refreshSystem();
- isEnabled = false;
+ isActive();
}
MidiEvent::~MidiEvent() {
@@ -1477,7 +1659,7 @@ bool MidiEventQueue::pushSysex(const Bit8u *sysexData, Bit32u sysexLength, Bit32
}
const MidiEvent *MidiEventQueue::peekMidiEvent() {
- return (startPosition == endPosition) ? NULL : &ringBuffer[startPosition];
+ return isEmpty() ? NULL : &ringBuffer[startPosition];
}
void MidiEventQueue::dropMidiEvent() {
@@ -1491,76 +1673,164 @@ bool MidiEventQueue::isFull() const {
return startPosition == ((endPosition + 1) & ringBufferMask);
}
-unsigned int Synth::getStereoOutputSampleRate() const {
+bool MidiEventQueue::isEmpty() const {
+ return startPosition == endPosition;
+}
+
+Bit32u Synth::getStereoOutputSampleRate() const {
return (analog == NULL) ? SAMPLE_RATE : analog->getOutputSampleRate();
}
-void Synth::render(Sample *stream, Bit32u len) {
- if (!isEnabled) {
- renderedSampleCount += analog->getDACStreamsLength(len);
- analog->process(NULL, NULL, NULL, NULL, NULL, NULL, NULL, len);
- muteSampleBuffer(stream, len << 1);
+void Renderer::render(SampleFormatConverter &converter, Bit32u len) {
+ if (!synth.opened) {
+ converter.addSilence(len << 1);
return;
}
- // As in AnalogOutputMode_ACCURATE mode output is upsampled, buffer size MAX_SAMPLES_PER_RUN is more than enough.
- Sample tmpNonReverbLeft[MAX_SAMPLES_PER_RUN], tmpNonReverbRight[MAX_SAMPLES_PER_RUN];
- Sample tmpReverbDryLeft[MAX_SAMPLES_PER_RUN], tmpReverbDryRight[MAX_SAMPLES_PER_RUN];
- Sample tmpReverbWetLeft[MAX_SAMPLES_PER_RUN], tmpReverbWetRight[MAX_SAMPLES_PER_RUN];
+ if (!synth.activated) {
+ synth.renderedSampleCount += synth.analog->getDACStreamsLength(len);
+ synth.analog->process(NULL, NULL, NULL, NULL, NULL, NULL, NULL, len);
+ converter.addSilence(len << 1);
+ return;
+ }
while (len > 0) {
+ // As in AnalogOutputMode_ACCURATE mode output is upsampled, MAX_SAMPLES_PER_RUN is more than enough for the temp buffers.
Bit32u thisPassLen = len > MAX_SAMPLES_PER_RUN ? MAX_SAMPLES_PER_RUN : len;
- renderStreams(tmpNonReverbLeft, tmpNonReverbRight, tmpReverbDryLeft, tmpReverbDryRight, tmpReverbWetLeft, tmpReverbWetRight, analog->getDACStreamsLength(thisPassLen));
- analog->process(&stream, tmpNonReverbLeft, tmpNonReverbRight, tmpReverbDryLeft, tmpReverbDryRight, tmpReverbWetLeft, tmpReverbWetRight, thisPassLen);
+ synth.renderStreams(tmpNonReverbLeft, tmpNonReverbRight, tmpReverbDryLeft, tmpReverbDryRight, tmpReverbWetLeft, tmpReverbWetRight, synth.analog->getDACStreamsLength(thisPassLen));
+ synth.analog->process(converter.sampleBuffer, tmpNonReverbLeft, tmpNonReverbRight, tmpReverbDryLeft, tmpReverbDryRight, tmpReverbWetLeft, tmpReverbWetRight, thisPassLen);
+ converter.convert(thisPassLen << 1);
len -= thisPassLen;
}
}
-void Synth::renderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample *reverbDryLeft, Sample *reverbDryRight, Sample *reverbWetLeft, Sample *reverbWetRight, Bit32u len) {
+void Synth::render(Bit16s *stream, Bit32u len) {
+#if MT32EMU_USE_FLOAT_SAMPLES
+ BufferedSampleFormatConverter<2> converter(stream);
+#else
+ SampleFormatConverter converter(stream);
+#endif
+ renderer.render(converter, len);
+}
+
+void Synth::render(float *stream, Bit32u len) {
+#if MT32EMU_USE_FLOAT_SAMPLES
+ SampleFormatConverter converter(stream);
+#else
+ BufferedSampleFormatConverter<2> converter(stream);
+#endif
+ renderer.render(converter, len);
+}
+
+void Renderer::renderStreams(
+ SampleFormatConverter &nonReverbLeft, SampleFormatConverter &nonReverbRight,
+ SampleFormatConverter &reverbDryLeft, SampleFormatConverter &reverbDryRight,
+ SampleFormatConverter &reverbWetLeft, SampleFormatConverter &reverbWetRight,
+ Bit32u len)
+{
+ if (!synth.opened) {
+ nonReverbLeft.addSilence(len);
+ nonReverbRight.addSilence(len);
+ reverbDryLeft.addSilence(len);
+ reverbDryRight.addSilence(len);
+ reverbWetLeft.addSilence(len);
+ reverbWetRight.addSilence(len);
+ return;
+ }
+
while (len > 0) {
// We need to ensure zero-duration notes will play so add minimum 1-sample delay.
Bit32u thisLen = 1;
- if (!isAbortingPoly()) {
- const MidiEvent *nextEvent = midiQueue->peekMidiEvent();
- Bit32s samplesToNextEvent = (nextEvent != NULL) ? Bit32s(nextEvent->timestamp - renderedSampleCount) : MAX_SAMPLES_PER_RUN;
+ if (!synth.isAbortingPoly()) {
+ const MidiEvent *nextEvent = synth.midiQueue->peekMidiEvent();
+ Bit32s samplesToNextEvent = (nextEvent != NULL) ? Bit32s(nextEvent->timestamp - synth.renderedSampleCount) : MAX_SAMPLES_PER_RUN;
if (samplesToNextEvent > 0) {
thisLen = len > MAX_SAMPLES_PER_RUN ? MAX_SAMPLES_PER_RUN : len;
- if (thisLen > (Bit32u)samplesToNextEvent) {
+ if (thisLen > Bit32u(samplesToNextEvent)) {
thisLen = samplesToNextEvent;
}
} else {
if (nextEvent->sysexData == NULL) {
- playMsgNow(nextEvent->shortMessageData);
+ synth.playMsgNow(nextEvent->shortMessageData);
// If a poly is aborting we don't drop the event from the queue.
// Instead, we'll return to it again when the abortion is done.
- if (!isAbortingPoly()) {
- midiQueue->dropMidiEvent();
+ if (!synth.isAbortingPoly()) {
+ synth.midiQueue->dropMidiEvent();
}
} else {
- playSysexNow(nextEvent->sysexData, nextEvent->sysexLength);
- midiQueue->dropMidiEvent();
+ synth.playSysexNow(nextEvent->sysexData, nextEvent->sysexLength);
+ synth.midiQueue->dropMidiEvent();
}
}
}
- doRenderStreams(nonReverbLeft, nonReverbRight, reverbDryLeft, reverbDryRight, reverbWetLeft, reverbWetRight, thisLen);
- advanceStreamPosition(nonReverbLeft, thisLen);
- advanceStreamPosition(nonReverbRight, thisLen);
- advanceStreamPosition(reverbDryLeft, thisLen);
- advanceStreamPosition(reverbDryRight, thisLen);
- advanceStreamPosition(reverbWetLeft, thisLen);
- advanceStreamPosition(reverbWetRight, thisLen);
+ DACOutputStreams streams = {
+ nonReverbLeft.sampleBuffer, nonReverbRight.sampleBuffer,
+ reverbDryLeft.sampleBuffer, reverbDryRight.sampleBuffer,
+ reverbWetLeft.sampleBuffer, reverbWetRight.sampleBuffer
+ };
+ doRenderStreams(streams, thisLen);
+ nonReverbLeft.convert(thisLen);
+ nonReverbRight.convert(thisLen);
+ reverbDryLeft.convert(thisLen);
+ reverbDryRight.convert(thisLen);
+ reverbWetLeft.convert(thisLen);
+ reverbWetRight.convert(thisLen);
len -= thisLen;
}
}
+void Synth::renderStreams(
+ Bit16s *nonReverbLeft, Bit16s *nonReverbRight,
+ Bit16s *reverbDryLeft, Bit16s *reverbDryRight,
+ Bit16s *reverbWetLeft, Bit16s *reverbWetRight,
+ Bit32u len)
+{
+#if MT32EMU_USE_FLOAT_SAMPLES
+ BufferedSampleFormatConverter<> convNonReverbLeft(nonReverbLeft), convNonReverbRight(nonReverbRight);
+ BufferedSampleFormatConverter<> convReverbDryLeft(reverbDryLeft), convReverbDryRight(reverbDryRight);
+ BufferedSampleFormatConverter<> convReverbWetLeft(reverbWetLeft), convReverbWetRight(reverbWetRight);
+#else
+ SampleFormatConverter convNonReverbLeft(nonReverbLeft), convNonReverbRight(nonReverbRight);
+ SampleFormatConverter convReverbDryLeft(reverbDryLeft), convReverbDryRight(reverbDryRight);
+ SampleFormatConverter convReverbWetLeft(reverbWetLeft), convReverbWetRight(reverbWetRight);
+#endif
+ renderer.renderStreams(
+ convNonReverbLeft, convNonReverbRight,
+ convReverbDryLeft, convReverbDryRight,
+ convReverbWetLeft, convReverbWetRight,
+ len);
+}
+
+void Synth::renderStreams(
+ float *nonReverbLeft, float *nonReverbRight,
+ float *reverbDryLeft, float *reverbDryRight,
+ float *reverbWetLeft, float *reverbWetRight,
+ Bit32u len)
+{
+#if MT32EMU_USE_FLOAT_SAMPLES
+ SampleFormatConverter convNonReverbLeft(nonReverbLeft), convNonReverbRight(nonReverbRight);
+ SampleFormatConverter convReverbDryLeft(reverbDryLeft), convReverbDryRight(reverbDryRight);
+ SampleFormatConverter convReverbWetLeft(reverbWetLeft), convReverbWetRight(reverbWetRight);
+#else
+ BufferedSampleFormatConverter<> convNonReverbLeft(nonReverbLeft), convNonReverbRight(nonReverbRight);
+ BufferedSampleFormatConverter<> convReverbDryLeft(reverbDryLeft), convReverbDryRight(reverbDryRight);
+ BufferedSampleFormatConverter<> convReverbWetLeft(reverbWetLeft), convReverbWetRight(reverbWetRight);
+#endif
+ renderer.renderStreams(
+ convNonReverbLeft, convNonReverbRight,
+ convReverbDryLeft, convReverbDryRight,
+ convReverbWetLeft, convReverbWetRight,
+ len);
+}
+
// In GENERATION2 units, the output from LA32 goes to the Boss chip already bit-shifted.
// In NICE mode, it's also better to increase volume before the reverb processing to preserve accuracy.
-void Synth::produceLA32Output(Sample *buffer, Bit32u len) {
+void Renderer::produceLA32Output(Sample *buffer, Bit32u len) {
#if MT32EMU_USE_FLOAT_SAMPLES
(void)buffer;
(void)len;
#else
- switch (dacInputMode) {
+ switch (synth.dacInputMode) {
case DACInputMode_GENERATION2:
while (len--) {
*buffer = (*buffer & 0x8000) | ((*buffer << 1) & 0x7FFE) | ((*buffer >> 14) & 0x0001);
@@ -1569,7 +1839,7 @@ void Synth::produceLA32Output(Sample *buffer, Bit32u len) {
break;
case DACInputMode_NICE:
while (len--) {
- *buffer = clipSampleEx(SampleEx(*buffer) << 1);
+ *buffer = Synth::clipSampleEx(SampleEx(*buffer) << 1);
++buffer;
}
break;
@@ -1579,12 +1849,12 @@ void Synth::produceLA32Output(Sample *buffer, Bit32u len) {
#endif
}
-void Synth::convertSamplesToOutput(Sample *buffer, Bit32u len) {
+void Renderer::convertSamplesToOutput(Sample *buffer, Bit32u len) {
#if MT32EMU_USE_FLOAT_SAMPLES
(void)buffer;
(void)len;
#else
- if (dacInputMode == DACInputMode_GENERATION1) {
+ if (synth.dacInputMode == DACInputMode_GENERATION1) {
while (len--) {
*buffer = Sample((*buffer & 0x8000) | ((*buffer << 1) & 0x7FFE));
++buffer;
@@ -1593,78 +1863,77 @@ void Synth::convertSamplesToOutput(Sample *buffer, Bit32u len) {
#endif
}
-void Synth::doRenderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample *reverbDryLeft, Sample *reverbDryRight, Sample *reverbWetLeft, Sample *reverbWetRight, Bit32u len) {
- // Even if LA32 output isn't desired, we proceed anyway with temp buffers
- Sample tmpBufNonReverbLeft[MAX_SAMPLES_PER_RUN], tmpBufNonReverbRight[MAX_SAMPLES_PER_RUN];
- if (nonReverbLeft == NULL) nonReverbLeft = tmpBufNonReverbLeft;
- if (nonReverbRight == NULL) nonReverbRight = tmpBufNonReverbRight;
+void Renderer::doRenderStreams(DACOutputStreams &streams, Bit32u len) {
+ if (synth.activated) {
+ // Even if LA32 output isn't desired, we proceed anyway with temp buffers
+ Sample *nonReverbLeft = streams.nonReverbLeft == NULL ? tmpNonReverbLeft : streams.nonReverbLeft;
+ Sample *nonReverbRight = streams.nonReverbRight == NULL ? tmpNonReverbRight : streams.nonReverbRight;
+ Sample *reverbDryLeft = streams.reverbDryLeft == NULL ? tmpReverbDryLeft : streams.reverbDryLeft;
+ Sample *reverbDryRight = streams.reverbDryRight == NULL ? tmpReverbDryRight : streams.reverbDryRight;
- Sample tmpBufReverbDryLeft[MAX_SAMPLES_PER_RUN], tmpBufReverbDryRight[MAX_SAMPLES_PER_RUN];
- if (reverbDryLeft == NULL) reverbDryLeft = tmpBufReverbDryLeft;
- if (reverbDryRight == NULL) reverbDryRight = tmpBufReverbDryRight;
+ Synth::muteSampleBuffer(nonReverbLeft, len);
+ Synth::muteSampleBuffer(nonReverbRight, len);
+ Synth::muteSampleBuffer(reverbDryLeft, len);
+ Synth::muteSampleBuffer(reverbDryRight, len);
- if (isEnabled) {
- muteSampleBuffer(nonReverbLeft, len);
- muteSampleBuffer(nonReverbRight, len);
- muteSampleBuffer(reverbDryLeft, len);
- muteSampleBuffer(reverbDryRight, len);
-
- for (unsigned int i = 0; i < getPartialCount(); i++) {
- if (partialManager->shouldReverb(i)) {
- partialManager->produceOutput(i, reverbDryLeft, reverbDryRight, len);
+ for (unsigned int i = 0; i < synth.getPartialCount(); i++) {
+ if (synth.partialManager->shouldReverb(i)) {
+ synth.partialManager->produceOutput(i, reverbDryLeft, reverbDryRight, len);
} else {
- partialManager->produceOutput(i, nonReverbLeft, nonReverbRight, len);
+ synth.partialManager->produceOutput(i, nonReverbLeft, nonReverbRight, len);
}
}
produceLA32Output(reverbDryLeft, len);
produceLA32Output(reverbDryRight, len);
- if (isReverbEnabled()) {
- reverbModel->process(reverbDryLeft, reverbDryRight, reverbWetLeft, reverbWetRight, len);
- if (reverbWetLeft != NULL) convertSamplesToOutput(reverbWetLeft, len);
- if (reverbWetRight != NULL) convertSamplesToOutput(reverbWetRight, len);
+ if (synth.isReverbEnabled()) {
+ synth.reverbModel->process(reverbDryLeft, reverbDryRight, streams.reverbWetLeft, streams.reverbWetRight, len);
+ if (streams.reverbWetLeft != NULL) convertSamplesToOutput(streams.reverbWetLeft, len);
+ if (streams.reverbWetRight != NULL) convertSamplesToOutput(streams.reverbWetRight, len);
} else {
- muteSampleBuffer(reverbWetLeft, len);
- muteSampleBuffer(reverbWetRight, len);
+ Synth::muteSampleBuffer(streams.reverbWetLeft, len);
+ Synth::muteSampleBuffer(streams.reverbWetRight, len);
}
// Don't bother with conversion if the output is going to be unused
- if (nonReverbLeft != tmpBufNonReverbLeft) {
+ if (streams.nonReverbLeft != NULL) {
produceLA32Output(nonReverbLeft, len);
convertSamplesToOutput(nonReverbLeft, len);
}
- if (nonReverbRight != tmpBufNonReverbRight) {
+ if (streams.nonReverbRight != NULL) {
produceLA32Output(nonReverbRight, len);
convertSamplesToOutput(nonReverbRight, len);
}
- if (reverbDryLeft != tmpBufReverbDryLeft) convertSamplesToOutput(reverbDryLeft, len);
- if (reverbDryRight != tmpBufReverbDryRight) convertSamplesToOutput(reverbDryRight, len);
+ if (streams.reverbDryLeft != NULL) convertSamplesToOutput(reverbDryLeft, len);
+ if (streams.reverbDryRight != NULL) convertSamplesToOutput(reverbDryRight, len);
} else {
- // Avoid muting buffers that wasn't requested
- if (nonReverbLeft != tmpBufNonReverbLeft) muteSampleBuffer(nonReverbLeft, len);
- if (nonReverbRight != tmpBufNonReverbRight) muteSampleBuffer(nonReverbRight, len);
- if (reverbDryLeft != tmpBufReverbDryLeft) muteSampleBuffer(reverbDryLeft, len);
- if (reverbDryRight != tmpBufReverbDryRight) muteSampleBuffer(reverbDryRight, len);
- muteSampleBuffer(reverbWetLeft, len);
- muteSampleBuffer(reverbWetRight, len);
+ Synth::muteSampleBuffer(streams.nonReverbLeft, len);
+ Synth::muteSampleBuffer(streams.nonReverbRight, len);
+ Synth::muteSampleBuffer(streams.reverbDryLeft, len);
+ Synth::muteSampleBuffer(streams.reverbDryRight, len);
+ Synth::muteSampleBuffer(streams.reverbWetLeft, len);
+ Synth::muteSampleBuffer(streams.reverbWetRight, len);
}
- partialManager->clearAlreadyOutputed();
- renderedSampleCount += len;
+ synth.partialManager->clearAlreadyOutputed();
+ synth.renderedSampleCount += len;
}
-void Synth::printPartialUsage(unsigned long sampleOffset) {
+void Synth::printPartialUsage(Bit32u sampleOffset) {
unsigned int partialUsage[9];
partialManager->getPerPartPartialUsage(partialUsage);
if (sampleOffset > 0) {
- printDebug("[+%lu] Partial Usage: 1:%02d 2:%02d 3:%02d 4:%02d 5:%02d 6:%02d 7:%02d 8:%02d R: %02d TOTAL: %02d", sampleOffset, partialUsage[0], partialUsage[1], partialUsage[2], partialUsage[3], partialUsage[4], partialUsage[5], partialUsage[6], partialUsage[7], partialUsage[8], getPartialCount() - partialManager->getFreePartialCount());
+ printDebug("[+%u] Partial Usage: 1:%02d 2:%02d 3:%02d 4:%02d 5:%02d 6:%02d 7:%02d 8:%02d R: %02d TOTAL: %02d", sampleOffset, partialUsage[0], partialUsage[1], partialUsage[2], partialUsage[3], partialUsage[4], partialUsage[5], partialUsage[6], partialUsage[7], partialUsage[8], getPartialCount() - partialManager->getFreePartialCount());
} else {
printDebug("Partial Usage: 1:%02d 2:%02d 3:%02d 4:%02d 5:%02d 6:%02d 7:%02d 8:%02d R: %02d TOTAL: %02d", partialUsage[0], partialUsage[1], partialUsage[2], partialUsage[3], partialUsage[4], partialUsage[5], partialUsage[6], partialUsage[7], partialUsage[8], getPartialCount() - partialManager->getFreePartialCount());
}
}
bool Synth::hasActivePartials() const {
+ if (!opened) {
+ return false;
+ }
for (unsigned int partialNum = 0; partialNum < getPartialCount(); partialNum++) {
if (partialManager->getPartial(partialNum)->isActive()) {
return true;
@@ -1673,51 +1942,81 @@ bool Synth::hasActivePartials() const {
return false;
}
-bool Synth::isAbortingPoly() const {
- return abortingPoly != NULL;
-}
-
-bool Synth::isActive() const {
- if (hasActivePartials()) {
+bool Synth::isActive() {
+ if (!opened) {
+ return false;
+ }
+ if (!midiQueue->isEmpty() || hasActivePartials()) {
return true;
}
- if (isReverbEnabled()) {
- return reverbModel->isActive();
+ if (isReverbEnabled() && reverbModel->isActive()) {
+ return true;
}
+ activated = false;
return false;
}
-unsigned int Synth::getPartialCount() const {
+Bit32u Synth::getPartialCount() const {
return partialCount;
}
void Synth::getPartStates(bool *partStates) const {
+ if (!opened) {
+ memset(partStates, 0, 9 * sizeof(bool));
+ return;
+ }
for (int partNumber = 0; partNumber < 9; partNumber++) {
const Part *part = parts[partNumber];
partStates[partNumber] = part->getActiveNonReleasingPartialCount() > 0;
}
}
-void Synth::getPartialStates(PartialState *partialStates) const {
- static const PartialState partialPhaseToState[8] = {
- PartialState_ATTACK, PartialState_ATTACK, PartialState_ATTACK, PartialState_ATTACK,
- PartialState_SUSTAIN, PartialState_SUSTAIN, PartialState_RELEASE, PartialState_INACTIVE
- };
+Bit32u Synth::getPartStates() const {
+ if (!opened) return 0;
+ bool partStates[9];
+ getPartStates(partStates);
+ Bit32u bitSet = 0;
+ for (int partNumber = 8; partNumber >= 0; partNumber--) {
+ bitSet = (bitSet << 1) | (partStates[partNumber] ? 1 : 0);
+ }
+ return bitSet;
+}
- for (unsigned int partialNum = 0; partialNum < getPartialCount(); partialNum++) {
- const Partial *partial = partialManager->getPartial(partialNum);
- partialStates[partialNum] = partial->isActive() ? partialPhaseToState[partial->getTVA()->getPhase()] : PartialState_INACTIVE;
+void Synth::getPartialStates(PartialState *partialStates) const {
+ if (!opened) {
+ memset(partialStates, 0, partialCount * sizeof(PartialState));
+ return;
+ }
+ for (unsigned int partialNum = 0; partialNum < partialCount; partialNum++) {
+ partialStates[partialNum] = getPartialState(partialManager, partialNum);
}
}
-unsigned int Synth::getPlayingNotes(unsigned int partNumber, Bit8u *keys, Bit8u *velocities) const {
- unsigned int playingNotes = 0;
- if (isOpen && (partNumber < 9)) {
+void Synth::getPartialStates(Bit8u *partialStates) const {
+ if (!opened) {
+ memset(partialStates, 0, ((partialCount + 3) >> 2));
+ return;
+ }
+ for (unsigned int quartNum = 0; (4 * quartNum) < partialCount; quartNum++) {
+ Bit8u packedStates = 0;
+ for (unsigned int i = 0; i < 4; i++) {
+ unsigned int partialNum = (4 * quartNum) + i;
+ if (partialCount <= partialNum) break;
+ PartialState partialState = getPartialState(partialManager, partialNum);
+ packedStates |= (partialState & 3) << (2 * i);
+ }
+ partialStates[quartNum] = packedStates;
+ }
+}
+
+Bit32u Synth::getPlayingNotes(Bit8u partNumber, Bit8u *keys, Bit8u *velocities) const {
+ Bit32u playingNotes = 0;
+ if (opened && (partNumber < 9)) {
const Part *part = parts[partNumber];
const Poly *poly = part->getFirstActivePoly();
while (poly != NULL) {
- keys[playingNotes] = (Bit8u)poly->getKey();
- velocities[playingNotes] = (Bit8u)poly->getVelocity();
+ keys[playingNotes] = Bit8u(poly->getKey());
+ velocities[playingNotes] = Bit8u(poly->getVelocity());
playingNotes++;
poly = poly->getNext();
}
@@ -1725,11 +2024,11 @@ unsigned int Synth::getPlayingNotes(unsigned int partNumber, Bit8u *keys, Bit8u
return playingNotes;
}
-const char *Synth::getPatchName(unsigned int partNumber) const {
- return (!isOpen || partNumber > 8) ? NULL : parts[partNumber]->getCurrentInstr();
+const char *Synth::getPatchName(Bit8u partNumber) const {
+ return (!opened || partNumber > 8) ? NULL : parts[partNumber]->getCurrentInstr();
}
-const Part *Synth::getPart(unsigned int partNum) const {
+const Part *Synth::getPart(Bit8u partNum) const {
if (partNum > 8) {
return NULL;
}
@@ -1783,6 +2082,7 @@ void MemoryRegion::write(unsigned int entry, unsigned int off, const Bit8u *src,
#if MT32EMU_MONITOR_SYSEX > 0
synth->printDebug("write[%d]: unwritable region: entry=%d, off=%d, len=%d", type, entry, off, len);
#endif
+ return;
}
for (unsigned int i = 0; i < len; i++) {
@@ -1807,4 +2107,4 @@ void MemoryRegion::write(unsigned int entry, unsigned int off, const Bit8u *src,
}
}
-}
+} // namespace MT32Emu
diff --git a/audio/softsynth/mt32/Synth.h b/audio/softsynth/mt32/Synth.h
index 97d4644ee2b..fe31d99f022 100644
--- a/audio/softsynth/mt32/Synth.h
+++ b/audio/softsynth/mt32/Synth.h
@@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -18,8 +18,13 @@
#ifndef MT32EMU_SYNTH_H
#define MT32EMU_SYNTH_H
-//#include
-//#include
+#include
+#include
+#include
+
+#include "globals.h"
+#include "Types.h"
+#include "Enumerations.h"
namespace MT32Emu {
@@ -31,6 +36,8 @@ class Part;
class Poly;
class Partial;
class PartialManager;
+class Renderer;
+class ROMImage;
class PatchTempMemoryRegion;
class RhythmTempMemoryRegion;
@@ -41,82 +48,11 @@ class SystemMemoryRegion;
class DisplayMemoryRegion;
class ResetMemoryRegion;
+struct ControlROMFeatureSet;
struct ControlROMMap;
struct PCMWaveEntry;
struct MemParams;
-/**
- * Methods for emulating the connection between the LA32 and the DAC, which involves
- * some hacks in the real devices for doubling the volume.
- * See also http://en.wikipedia.org/wiki/Roland_MT-32#Digital_overflow
- */
-enum DACInputMode {
- // Produces samples at double the volume, without tricks.
- // * Nicer overdrive characteristics than the DAC hacks (it simply clips samples within range)
- // * Higher quality than the real devices
- DACInputMode_NICE,
-
- // Produces samples that exactly match the bits output from the emulated LA32.
- // * Nicer overdrive characteristics than the DAC hacks (it simply clips samples within range)
- // * Much less likely to overdrive than any other mode.
- // * Half the volume of any of the other modes.
- // * Output gain is ignored for both LA32 and reverb output.
- // * Perfect for developers while debugging :)
- DACInputMode_PURE,
-
- // Re-orders the LA32 output bits as in early generation MT-32s (according to Wikipedia).
- // Bit order at DAC (where each number represents the original LA32 output bit number, and XX means the bit is always low):
- // 15 13 12 11 10 09 08 07 06 05 04 03 02 01 00 XX
- DACInputMode_GENERATION1,
-
- // Re-orders the LA32 output bits as in later generations (personally confirmed on my CM-32L - KG).
- // Bit order at DAC (where each number represents the original LA32 output bit number):
- // 15 13 12 11 10 09 08 07 06 05 04 03 02 01 00 14
- DACInputMode_GENERATION2
-};
-
-// Methods for emulating the effective delay of incoming MIDI messages introduced by a MIDI interface.
-enum MIDIDelayMode {
- // Process incoming MIDI events immediately.
- MIDIDelayMode_IMMEDIATE,
-
- // Delay incoming short MIDI messages as if they where transferred via a MIDI cable to a real hardware unit and immediate sysex processing.
- // This ensures more accurate timing of simultaneous NoteOn messages.
- MIDIDelayMode_DELAY_SHORT_MESSAGES_ONLY,
-
- // Delay all incoming MIDI events as if they where transferred via a MIDI cable to a real hardware unit.
- MIDIDelayMode_DELAY_ALL
-};
-
-// Methods for emulating the effects of analogue circuits of real hardware units on the output signal.
-enum AnalogOutputMode {
- // Only digital path is emulated. The output samples correspond to the digital signal at the DAC entrance.
- AnalogOutputMode_DIGITAL_ONLY,
- // Coarse emulation of LPF circuit. High frequencies are boosted, sample rate remains unchanged.
- AnalogOutputMode_COARSE,
- // Finer emulation of LPF circuit. Output signal is upsampled to 48 kHz to allow emulation of audible mirror spectra above 16 kHz,
- // which is passed through the LPF circuit without significant attenuation.
- AnalogOutputMode_ACCURATE,
- // Same as AnalogOutputMode_ACCURATE mode but the output signal is 2x oversampled, i.e. the output sample rate is 96 kHz.
- // This makes subsequent resampling easier. Besides, due to nonlinear passband of the LPF emulated, it takes fewer number of MACs
- // compared to a regular LPF FIR implementations.
- AnalogOutputMode_OVERSAMPLED
-};
-
-enum ReverbMode {
- REVERB_MODE_ROOM,
- REVERB_MODE_HALL,
- REVERB_MODE_PLATE,
- REVERB_MODE_TAP_DELAY
-};
-
-enum PartialState {
- PartialState_INACTIVE,
- PartialState_ATTACK,
- PartialState_SUSTAIN,
- PartialState_RELEASE
-};
-
const Bit8u SYSEX_MANUFACTURER_ROLAND = 0x41;
const Bit8u SYSEX_MDL_MT32 = 0x16;
@@ -132,47 +68,66 @@ const Bit8u SYSEX_CMD_EOD = 0x45; // End of data
const Bit8u SYSEX_CMD_ERR = 0x4E; // Communications error
const Bit8u SYSEX_CMD_RJC = 0x4F; // Rejection
-const int MAX_SYSEX_SIZE = 512; // FIXME: Does this correspond to a real MIDI buffer used in h/w devices?
+const Bit32u CONTROL_ROM_SIZE = 64 * 1024;
-const unsigned int CONTROL_ROM_SIZE = 64 * 1024;
-
-class ReportHandler {
-friend class Synth;
+// Set of multiplexed output streams appeared at the DAC entrance.
+template
+struct DACOutputStreams {
+ T *nonReverbLeft;
+ T *nonReverbRight;
+ T *reverbDryLeft;
+ T *reverbDryRight;
+ T *reverbWetLeft;
+ T *reverbWetRight;
+};
+// Class for the client to supply callbacks for reporting various errors and information
+class MT32EMU_EXPORT ReportHandler {
public:
virtual ~ReportHandler() {}
-protected:
-
// Callback for debug messages, in vprintf() format
virtual void printDebug(const char *fmt, va_list list);
-
- // Callbacks for reporting various errors and information
+ // Callbacks for reporting errors
virtual void onErrorControlROM() {}
virtual void onErrorPCMROM() {}
+ // Callback for reporting about displaying a new custom message on LCD
virtual void showLCDMessage(const char *message);
+ // Callback for reporting actual processing of a MIDI message
virtual void onMIDIMessagePlayed() {}
+ // Callback for reporting an overflow of the input MIDI queue.
+ // Returns true if a recovery action was taken and yet another attempt to enqueue the MIDI event is desired.
+ virtual bool onMIDIQueueOverflow() { return false; }
+ // Callback invoked when a System Realtime MIDI message is detected at the input.
+ virtual void onMIDISystemRealtime(Bit8u /* systemRealtime */) {}
+ // Callbacks for reporting system events
virtual void onDeviceReset() {}
virtual void onDeviceReconfig() {}
+ // Callbacks for reporting changes of reverb settings
virtual void onNewReverbMode(Bit8u /* mode */) {}
virtual void onNewReverbTime(Bit8u /* time */) {}
virtual void onNewReverbLevel(Bit8u /* level */) {}
- virtual void onPolyStateChanged(int /* partNum */) {}
- virtual void onProgramChanged(int /* partNum */, int /* bankNum */, const char * /* patchName */) {}
+ // Callbacks for reporting various information
+ virtual void onPolyStateChanged(Bit8u /* partNum */) {}
+ virtual void onProgramChanged(Bit8u /* partNum */, const char * /* soundGroupName */, const char * /* patchName */) {}
};
class Synth {
+friend class DefaultMidiStreamParser;
friend class Part;
-friend class RhythmPart;
-friend class Poly;
friend class Partial;
friend class PartialManager;
-friend class Tables;
-friend class MemoryRegion;
+friend class Poly;
+friend class Renderer;
+friend class RhythmPart;
+friend class SamplerateAdapter;
+friend class SoxrAdapter;
friend class TVA;
-friend class TVF;
friend class TVP;
+
private:
+ // **************************** Implementation fields **************************
+
PatchTempMemoryRegion *patchTempMemoryRegion;
RhythmTempMemoryRegion *rhythmTempMemoryRegion;
TimbreTempMemoryRegion *timbreTempMemoryRegion;
@@ -184,8 +139,6 @@ private:
Bit8u *paddedTimbreMaxTable;
- bool isEnabled;
-
PCMWaveEntry *pcmWaves; // Array
const ControlROMFeatureSet *controlROMFeatures;
@@ -194,8 +147,11 @@ private:
Bit16s *pcmROMData;
size_t pcmROMSize; // This is in 16-bit samples, therefore half the number of bytes in the ROM
- unsigned int partialCount;
- Bit8s chantable[32]; // FIXME: Need explanation why 32 is set, obviously it should be 16
+ Bit8u soundGroupIx[128]; // For each standard timbre
+ const char (*soundGroupNames)[9]; // Array
+
+ Bit32u partialCount;
+ Bit8u chantable[16]; // NOTE: value above 8 means that the channel is not assigned
MidiEventQueue *midiQueue;
volatile Bit32u lastReceivedMIDIEventTimestamp;
@@ -215,7 +171,8 @@ private:
bool reversedStereoEnabled;
- bool isOpen;
+ bool opened;
+ bool activated;
bool isDefaultReportHandler;
ReportHandler *reportHandler;
@@ -229,15 +186,17 @@ private:
Poly *abortingPoly;
Analog *analog;
+ Renderer &renderer;
+
+ // Binary compatibility helper.
+ void *reserved;
+
+ // **************************** Implementation methods **************************
Bit32u addMIDIInterfaceDelay(Bit32u len, Bit32u timestamp);
+ bool isAbortingPoly() const { return abortingPoly != NULL; }
- void produceLA32Output(Sample *buffer, Bit32u len);
- void convertSamplesToOutput(Sample *buffer, Bit32u len);
- bool isAbortingPoly() const;
- void doRenderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample *reverbDryLeft, Sample *reverbDryRight, Sample *reverbWetLeft, Sample *reverbWetRight, Bit32u len);
-
- void readSysex(unsigned char channel, const Bit8u *sysex, Bit32u len) const;
+ void readSysex(Bit8u channel, const Bit8u *sysex, Bit32u len) const;
void initMemoryRegions();
void deleteMemoryRegions();
MemoryRegion *findMemoryRegion(Bit32u addr);
@@ -248,79 +207,105 @@ private:
bool loadPCMROM(const ROMImage &pcmROMImage);
bool initPCMList(Bit16u mapAddress, Bit16u count);
- bool initTimbres(Bit16u mapAddress, Bit16u offset, int timbreCount, int startTimbre, bool compressed);
- bool initCompressedTimbre(int drumNum, const Bit8u *mem, unsigned int memLen);
+ bool initTimbres(Bit16u mapAddress, Bit16u offset, Bit16u timbreCount, Bit16u startTimbre, bool compressed);
+ bool initCompressedTimbre(Bit16u drumNum, const Bit8u *mem, Bit32u memLen);
+ void initReverbModels(bool mt32CompatibleMode);
+ void initSoundGroups(char newSoundGroupNames[][9]);
void refreshSystemMasterTune();
void refreshSystemReverbParameters();
void refreshSystemReserveSettings();
- void refreshSystemChanAssign(unsigned int firstPart, unsigned int lastPart);
+ void refreshSystemChanAssign(Bit8u firstPart, Bit8u lastPart);
void refreshSystemMasterVol();
void refreshSystem();
void reset();
+ void dispose();
- void printPartialUsage(unsigned long sampleOffset = 0);
+ void printPartialUsage(Bit32u sampleOffset = 0);
- void polyStateChanged(int partNum);
- void newTimbreSet(int partNum, Bit8u timbreGroup, const char patchName[]);
+ void newTimbreSet(Bit8u partNum, Bit8u timbreGroup, Bit8u timbreNumber, const char patchName[]);
void printDebug(const char *fmt, ...);
// partNum should be 0..7 for Part 1..8, or 8 for Rhythm
- const Part *getPart(unsigned int partNum) const;
+ const Part *getPart(Bit8u partNum) const;
public:
- static inline Sample clipSampleEx(SampleEx sampleEx) {
-#if MT32EMU_USE_FLOAT_SAMPLES
- return sampleEx;
-#else
+ static inline Bit16s clipSampleEx(Bit32s sampleEx) {
// Clamp values above 32767 to 32767, and values below -32768 to -32768
// FIXME: Do we really need this stuff? I think these branches are very well predicted. Instead, this introduces a chain.
// The version below is actually a bit faster on my system...
- //return ((sampleEx + 0x8000) & ~0xFFFF) ? (sampleEx >> 31) ^ 0x7FFF : (Sample)sampleEx;
- return ((-0x8000 <= sampleEx) && (sampleEx <= 0x7FFF)) ? (Sample)sampleEx : (sampleEx >> 31) ^ 0x7FFF;
-#endif
+ //return ((sampleEx + 0x8000) & ~0xFFFF) ? Bit16s((sampleEx >> 31) ^ 0x7FFF) : (Bit16s)sampleEx;
+ return ((-0x8000 <= sampleEx) && (sampleEx <= 0x7FFF)) ? Bit16s(sampleEx) : Bit16s((sampleEx >> 31) ^ 0x7FFF);
}
- static inline void muteSampleBuffer(Sample *buffer, Bit32u len) {
- if (buffer == NULL) return;
+ static inline float clipSampleEx(float sampleEx) {
+ return sampleEx;
+ }
-#if MT32EMU_USE_FLOAT_SAMPLES
+ template
+ static inline void muteSampleBuffer(S *buffer, Bit32u len) {
+ if (buffer == NULL) return;
+ memset(buffer, 0, len * sizeof(S));
+ }
+
+ static inline void muteSampleBuffer(float *buffer, Bit32u len) {
+ if (buffer == NULL) return;
// FIXME: Use memset() where compatibility is guaranteed (if this turns out to be a win)
while (len--) {
*(buffer++) = 0.0f;
}
-#else
- memset(buffer, 0, len * sizeof(Sample));
-#endif
}
- static Bit32u getShortMessageLength(Bit32u msg);
- static Bit8u calcSysexChecksum(const Bit8u *data, const Bit32u len, const Bit8u initChecksum = 0);
+ static inline Bit16s convertSample(float sample) {
+ return Synth::clipSampleEx(Bit32s(sample * 16384.0f)); // This multiplier takes into account the DAC bit shift
+ }
+
+ static inline float convertSample(Bit16s sample) {
+ return float(sample) / 16384.0f; // This multiplier takes into account the DAC bit shift
+ }
+
+ // Returns library version as an integer in format: 0x00MMmmpp, where:
+ // MM - major version number
+ // mm - minor version number
+ // pp - patch number
+ MT32EMU_EXPORT static Bit32u getLibraryVersionInt();
+ // Returns library version as a C-string in format: "MAJOR.MINOR.PATCH"
+ MT32EMU_EXPORT static const char *getLibraryVersionString();
+
+ MT32EMU_EXPORT static Bit32u getShortMessageLength(Bit32u msg);
+ MT32EMU_EXPORT static Bit8u calcSysexChecksum(const Bit8u *data, const Bit32u len, const Bit8u initChecksum = 0);
+
+ // Returns output sample rate used in emulation of stereo analog circuitry of hardware units.
+ // See comment for AnalogOutputMode.
+ MT32EMU_EXPORT static Bit32u getStereoOutputSampleRate(AnalogOutputMode analogOutputMode);
// Optionally sets callbacks for reporting various errors, information and debug messages
- Synth(ReportHandler *useReportHandler = NULL);
- ~Synth();
+ MT32EMU_EXPORT explicit Synth(ReportHandler *useReportHandler = NULL);
+ MT32EMU_EXPORT ~Synth();
// Used to initialise the MT-32. Must be called before any other function.
// Returns true if initialization was sucessful, otherwise returns false.
// controlROMImage and pcmROMImage represent Control and PCM ROM images for use by synth.
// usePartialCount sets the maximum number of partials playing simultaneously for this session (optional).
// analogOutputMode sets the mode for emulation of analogue circuitry of the hardware units (optional).
- bool open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, unsigned int usePartialCount = DEFAULT_MAX_PARTIALS, AnalogOutputMode analogOutputMode = AnalogOutputMode_COARSE);
+ MT32EMU_EXPORT bool open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, Bit32u usePartialCount = DEFAULT_MAX_PARTIALS, AnalogOutputMode analogOutputMode = AnalogOutputMode_COARSE);
// Overloaded method which opens the synth with default partial count.
- bool open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, AnalogOutputMode analogOutputMode);
+ MT32EMU_EXPORT bool open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, AnalogOutputMode analogOutputMode);
// Closes the MT-32 and deallocates any memory used by the synthesizer
- void close(bool forced = false);
+ MT32EMU_EXPORT void close();
+
+ // Returns true if the synth is in completely initialized state, otherwise returns false.
+ MT32EMU_EXPORT bool isOpen() const;
// All the enqueued events are processed by the synth immediately.
- void flushMIDIQueue();
+ MT32EMU_EXPORT void flushMIDIQueue();
// Sets size of the internal MIDI event queue. The queue size is set to the minimum power of 2 that is greater or equal to the size specified.
// The queue is flushed before reallocation.
// Returns the actual queue size being used.
- Bit32u setMIDIEventQueueSize(Bit32u);
+ MT32EMU_EXPORT Bit32u setMIDIEventQueueSize(Bit32u);
// Enqueues a MIDI event for subsequent playback.
// The MIDI event will be processed not before the specified timestamp.
@@ -330,14 +315,15 @@ public:
// Calls from multiple threads must be synchronised, although, no synchronisation is required with the rendering thread.
// The methods return false if the MIDI event queue is full and the message cannot be enqueued.
- // Enqueues a single short MIDI message. The message must contain a status byte.
- bool playMsg(Bit32u msg, Bit32u timestamp);
- // Enqueues a single well formed System Exclusive MIDI message.
- bool playSysex(const Bit8u *sysex, Bit32u len, Bit32u timestamp);
+ // Enqueues a single short MIDI message to play at specified time. The message must contain a status byte.
+ MT32EMU_EXPORT bool playMsg(Bit32u msg, Bit32u timestamp);
+ // Enqueues a single well formed System Exclusive MIDI message to play at specified time.
+ MT32EMU_EXPORT bool playSysex(const Bit8u *sysex, Bit32u len, Bit32u timestamp);
- // Overloaded methods for the MIDI events to be processed ASAP.
- bool playMsg(Bit32u msg);
- bool playSysex(const Bit8u *sysex, Bit32u len);
+ // Enqueues a single short MIDI message to be processed ASAP. The message must contain a status byte.
+ MT32EMU_EXPORT bool playMsg(Bit32u msg);
+ // Enqueues a single well formed System Exclusive MIDI message to be processed ASAP.
+ MT32EMU_EXPORT bool playSysex(const Bit8u *sysex, Bit32u len);
// WARNING:
// The methods below don't ensure minimum 1-sample delay between sequential MIDI events,
@@ -345,40 +331,60 @@ public:
// A thread that invokes these methods must be explicitly synchronised with the thread performing sample rendering.
// Sends a short MIDI message to the synth for immediate playback. The message must contain a status byte.
- void playMsgNow(Bit32u msg);
- void playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity);
+ // See the WARNING above.
+ MT32EMU_EXPORT void playMsgNow(Bit32u msg);
+ // Sends unpacked short MIDI message to the synth for immediate playback. The message must contain a status byte.
+ // See the WARNING above.
+ MT32EMU_EXPORT void playMsgOnPart(Bit8u part, Bit8u code, Bit8u note, Bit8u velocity);
- // Sends a string of Sysex commands to the MT-32 for immediate interpretation
- // The length is in bytes
- void playSysexNow(const Bit8u *sysex, Bit32u len);
- void playSysexWithoutFraming(const Bit8u *sysex, Bit32u len);
- void playSysexWithoutHeader(unsigned char device, unsigned char command, const Bit8u *sysex, Bit32u len);
- void writeSysex(unsigned char channel, const Bit8u *sysex, Bit32u len);
+ // Sends a single well formed System Exclusive MIDI message for immediate processing. The length is in bytes.
+ // See the WARNING above.
+ MT32EMU_EXPORT void playSysexNow(const Bit8u *sysex, Bit32u len);
+ // Sends inner body of a System Exclusive MIDI message for direct processing. The length is in bytes.
+ // See the WARNING above.
+ MT32EMU_EXPORT void playSysexWithoutFraming(const Bit8u *sysex, Bit32u len);
+ // Sends inner body of a System Exclusive MIDI message for direct processing. The length is in bytes.
+ // See the WARNING above.
+ MT32EMU_EXPORT void playSysexWithoutHeader(Bit8u device, Bit8u command, const Bit8u *sysex, Bit32u len);
+ // Sends inner body of a System Exclusive MIDI message for direct processing. The length is in bytes.
+ // See the WARNING above.
+ MT32EMU_EXPORT void writeSysex(Bit8u channel, const Bit8u *sysex, Bit32u len);
- void setReverbEnabled(bool reverbEnabled);
- bool isReverbEnabled() const;
+ // Allows to disable wet reverb output altogether.
+ MT32EMU_EXPORT void setReverbEnabled(bool reverbEnabled);
+ // Returns whether wet reverb output is enabled.
+ MT32EMU_EXPORT bool isReverbEnabled() const;
// Sets override reverb mode. In this mode, emulation ignores sysexes (or the related part of them) which control the reverb parameters.
// This mode is in effect until it is turned off. When the synth is re-opened, the override mode is unchanged but the state
// of the reverb model is reset to default.
- void setReverbOverridden(bool reverbOverridden);
- bool isReverbOverridden() const;
+ MT32EMU_EXPORT void setReverbOverridden(bool reverbOverridden);
+ // Returns whether reverb settings are overridden.
+ MT32EMU_EXPORT bool isReverbOverridden() const;
// Forces reverb model compatibility mode. By default, the compatibility mode corresponds to the used control ROM version.
// Invoking this method with the argument set to true forces emulation of old MT-32 reverb circuit.
// When the argument is false, emulation of the reverb circuit used in new generation of MT-32 compatible modules is enforced
// (these include CM-32L and LAPC-I).
- void setReverbCompatibilityMode(bool mt32CompatibleMode);
- bool isMT32ReverbCompatibilityMode() const;
- void setDACInputMode(DACInputMode mode);
- DACInputMode getDACInputMode() const;
- void setMIDIDelayMode(MIDIDelayMode mode);
- MIDIDelayMode getMIDIDelayMode() const;
+ MT32EMU_EXPORT void setReverbCompatibilityMode(bool mt32CompatibleMode);
+ // Returns whether reverb is in old MT-32 compatibility mode.
+ MT32EMU_EXPORT bool isMT32ReverbCompatibilityMode() const;
+ // Returns whether default reverb compatibility mode is the old MT-32 compatibility mode.
+ MT32EMU_EXPORT bool isDefaultReverbMT32Compatible() const;
+ // Sets new DAC input mode. See DACInputMode for details.
+ MT32EMU_EXPORT void setDACInputMode(DACInputMode mode);
+ // Returns current DAC input mode. See DACInputMode for details.
+ MT32EMU_EXPORT DACInputMode getDACInputMode() const;
+ // Sets new MIDI delay mode. See MIDIDelayMode for details.
+ MT32EMU_EXPORT void setMIDIDelayMode(MIDIDelayMode mode);
+ // Returns current MIDI delay mode. See MIDIDelayMode for details.
+ MT32EMU_EXPORT MIDIDelayMode getMIDIDelayMode() const;
// Sets output gain factor for synth output channels. Applied to all output samples and unrelated with the synth's Master volume,
// it rather corresponds to the gain of the output analog circuitry of the hardware units. However, together with setReverbOutputGain()
// it offers to the user a capability to control the gain of reverb and non-reverb output channels independently.
// Ignored in DACInputMode_PURE
- void setOutputGain(float);
- float getOutputGain() const;
+ MT32EMU_EXPORT void setOutputGain(float gain);
+ // Returns current output gain factor for synth output channels.
+ MT32EMU_EXPORT float getOutputGain() const;
// Sets output gain factor for the reverb wet output channels. It rather corresponds to the gain of the output
// analog circuitry of the hardware units. However, together with setOutputGain() it offers to the user a capability
@@ -389,59 +395,85 @@ public:
// there is a difference in the reverb analogue circuit, and the resulting output gain is 0.68
// of that for LA32 analogue output. This factor is applied to the reverb output gain.
// Ignored in DACInputMode_PURE
- void setReverbOutputGain(float);
- float getReverbOutputGain() const;
+ MT32EMU_EXPORT void setReverbOutputGain(float gain);
+ // Returns current output gain factor for reverb wet output channels.
+ MT32EMU_EXPORT float getReverbOutputGain() const;
- void setReversedStereoEnabled(bool enabled);
- bool isReversedStereoEnabled();
+ // Swaps left and right output channels.
+ MT32EMU_EXPORT void setReversedStereoEnabled(bool enabled);
+ // Returns whether left and right output channels are swapped.
+ MT32EMU_EXPORT bool isReversedStereoEnabled() const;
// Returns actual sample rate used in emulation of stereo analog circuitry of hardware units.
// See comment for render() below.
- unsigned int getStereoOutputSampleRate() const;
+ MT32EMU_EXPORT Bit32u getStereoOutputSampleRate() const;
// Renders samples to the specified output stream as if they were sampled at the analog stereo output.
- // When AnalogOutputMode is set to ACCURATE, the output signal is upsampled to 48 kHz in order
+ // When AnalogOutputMode is set to ACCURATE (OVERSAMPLED), the output signal is upsampled to 48 (96) kHz in order
// to retain emulation accuracy in whole audible frequency spectra. Otherwise, native digital signal sample rate is retained.
// getStereoOutputSampleRate() can be used to query actual sample rate of the output signal.
- // The length is in frames, not bytes (in 16-bit stereo, one frame is 4 bytes).
- void render(Sample *stream, Bit32u len);
+ // The length is in frames, not bytes (in 16-bit stereo, one frame is 4 bytes). Uses NATIVE byte ordering.
+ MT32EMU_EXPORT void render(Bit16s *stream, Bit32u len);
+ // Same as above but outputs to a float stereo stream.
+ MT32EMU_EXPORT void render(float *stream, Bit32u len);
// Renders samples to the specified output streams as if they appeared at the DAC entrance.
// No further processing performed in analog circuitry emulation is applied to the signal.
- // NULL may be specified in place of any or all of the stream buffers.
- // The length is in samples, not bytes.
- void renderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample *reverbDryLeft, Sample *reverbDryRight, Sample *reverbWetLeft, Sample *reverbWetRight, Bit32u len);
+ // NULL may be specified in place of any or all of the stream buffers to skip it.
+ // The length is in samples, not bytes. Uses NATIVE byte ordering.
+ MT32EMU_EXPORT void renderStreams(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u len);
+ void renderStreams(const DACOutputStreams &streams, Bit32u len) {
+ renderStreams(streams.nonReverbLeft, streams.nonReverbRight, streams.reverbDryLeft, streams.reverbDryRight, streams.reverbWetLeft, streams.reverbWetRight, len);
+ }
+ // Same as above but outputs to float streams.
+ MT32EMU_EXPORT void renderStreams(float *nonReverbLeft, float *nonReverbRight, float *reverbDryLeft, float *reverbDryRight, float *reverbWetLeft, float *reverbWetRight, Bit32u len);
+ void renderStreams(const DACOutputStreams &streams, Bit32u len) {
+ renderStreams(streams.nonReverbLeft, streams.nonReverbRight, streams.reverbDryLeft, streams.reverbDryRight, streams.reverbWetLeft, streams.reverbWetRight, len);
+ }
// Returns true when there is at least one active partial, otherwise false.
- bool hasActivePartials() const;
+ MT32EMU_EXPORT bool hasActivePartials() const;
- // Returns true if hasActivePartials() returns true, or reverb is (somewhat unreliably) detected as being active.
- bool isActive() const;
+ // Returns true if the synth is active and subsequent calls to render() may result in non-trivial output (i.e. silence).
+ // The synth is considered active when either there are pending MIDI events in the queue, there is at least one active partial,
+ // or the reverb is (somewhat unreliably) detected as being active.
+ MT32EMU_EXPORT bool isActive();
// Returns the maximum number of partials playing simultaneously.
- unsigned int getPartialCount() const;
+ MT32EMU_EXPORT Bit32u getPartialCount() const;
// Fills in current states of all the parts into the array provided. The array must have at least 9 entries to fit values for all the parts.
// If the value returned for a part is true, there is at least one active non-releasing partial playing on this part.
// This info is useful in emulating behaviour of LCD display of the hardware units.
- void getPartStates(bool *partStates) const;
+ MT32EMU_EXPORT void getPartStates(bool *partStates) const;
+
+ // Returns current states of all the parts as a bit set. The least significant bit corresponds to the state of part 1,
+ // total of 9 bits hold the states of all the parts. If the returned bit for a part is set, there is at least one active
+ // non-releasing partial playing on this part. This info is useful in emulating behaviour of LCD display of the hardware units.
+ MT32EMU_EXPORT Bit32u getPartStates() const;
// Fills in current states of all the partials into the array provided. The array must be large enough to accommodate states of all the partials.
- void getPartialStates(PartialState *partialStates) const;
+ MT32EMU_EXPORT void getPartialStates(PartialState *partialStates) const;
+
+ // Fills in current states of all the partials into the array provided. Each byte in the array holds states of 4 partials
+ // starting from the least significant bits. The state of each partial is packed in a pair of bits.
+ // The array must be large enough to accommodate states of all the partials (see getPartialCount()).
+ MT32EMU_EXPORT void getPartialStates(Bit8u *partialStates) const;
// Fills in information about currently playing notes on the specified part into the arrays provided. The arrays must be large enough
// to accommodate data for all the playing notes. The maximum number of simultaneously playing notes cannot exceed the number of partials.
// Argument partNumber should be 0..7 for Part 1..8, or 8 for Rhythm.
// Returns the number of currently playing notes on the specified part.
- unsigned int getPlayingNotes(unsigned int partNumber, Bit8u *keys, Bit8u *velocities) const;
+ MT32EMU_EXPORT Bit32u getPlayingNotes(Bit8u partNumber, Bit8u *keys, Bit8u *velocities) const;
// Returns name of the patch set on the specified part.
// Argument partNumber should be 0..7 for Part 1..8, or 8 for Rhythm.
- const char *getPatchName(unsigned int partNumber) const;
+ MT32EMU_EXPORT const char *getPatchName(Bit8u partNumber) const;
- void readMemory(Bit32u addr, Bit32u len, Bit8u *data);
-};
+ // Stores internal state of emulated synth into an array provided (as it would be acquired from hardware).
+ MT32EMU_EXPORT void readMemory(Bit32u addr, Bit32u len, Bit8u *data);
+}; // class Synth
-}
+} // namespace MT32Emu
-#endif
+#endif // #ifndef MT32EMU_SYNTH_H
diff --git a/audio/softsynth/mt32/TVA.cpp b/audio/softsynth/mt32/TVA.cpp
index 894e53f14a4..c20b8b63933 100644
--- a/audio/softsynth/mt32/TVA.cpp
+++ b/audio/softsynth/mt32/TVA.cpp
@@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -19,19 +19,23 @@
* This class emulates the calculations performed by the 8095 microcontroller in order to configure the LA-32's amplitude ramp for a single partial at each stage of its TVA envelope.
* Unless we introduced bugs, it should be pretty much 100% accurate according to Mok's specifications.
*/
-//#include
-#include "mt32emu.h"
-#include "mmath.h"
#include "internals.h"
+#include "TVA.h"
+#include "Part.h"
+#include "Partial.h"
+#include "Poly.h"
+#include "Synth.h"
+#include "Tables.h"
+
namespace MT32Emu {
// CONFIRMED: Matches a table in ROM - haven't got around to coming up with a formula for it yet.
static Bit8u biasLevelToAmpSubtractionCoeff[13] = {255, 187, 137, 100, 74, 54, 40, 29, 21, 15, 10, 5, 0};
TVA::TVA(const Partial *usePartial, LA32Ramp *useAmpRamp) :
- partial(usePartial), ampRamp(useAmpRamp), system_(&usePartial->getSynth()->mt32ram.system), phase(TVA_PHASE_DEAD) {
+ partial(usePartial), ampRamp(useAmpRamp), system(&usePartial->getSynth()->mt32ram.system), phase(TVA_PHASE_DEAD) {
}
void TVA::startRamp(Bit8u newTarget, Bit8u newIncrement, int newPhase) {
@@ -91,15 +95,15 @@ static int calcVeloAmpSubtraction(Bit8u veloSensitivity, unsigned int velocity)
// FIXME:KG: Better variable names
int velocityMult = veloSensitivity - 50;
int absVelocityMult = velocityMult < 0 ? -velocityMult : velocityMult;
- velocityMult = (signed)((unsigned)(velocityMult * ((signed)velocity - 64)) << 2);
+ velocityMult = signed(unsigned(velocityMult * (signed(velocity) - 64)) << 2);
return absVelocityMult - (velocityMult >> 8); // PORTABILITY NOTE: Assumes arithmetic shift
}
-static int calcBasicAmp(const Tables *tables, const Partial *partial, const MemParams::System *system_, const TimbreParam::PartialParam *partialParam, const MemParams::PatchTemp *patchTemp, const MemParams::RhythmTemp *rhythmTemp, int biasAmpSubtraction, int veloAmpSubtraction, Bit8u expression) {
+static int calcBasicAmp(const Tables *tables, const Partial *partial, const MemParams::System *system, const TimbreParam::PartialParam *partialParam, const MemParams::PatchTemp *patchTemp, const MemParams::RhythmTemp *rhythmTemp, int biasAmpSubtraction, int veloAmpSubtraction, Bit8u expression) {
int amp = 155;
if (!partial->isRingModulatingSlave()) {
- amp -= tables->masterVolToAmpSubtraction[system_->masterVol];
+ amp -= tables->masterVolToAmpSubtraction[system->masterVol];
if (amp < 0) {
return 0;
}
@@ -140,7 +144,7 @@ static int calcBasicAmp(const Tables *tables, const Partial *partial, const MemP
return amp;
}
-int calcKeyTimeSubtraction(Bit8u envTimeKeyfollow, int key) {
+static int calcKeyTimeSubtraction(Bit8u envTimeKeyfollow, int key) {
if (envTimeKeyfollow == 0) {
return 0;
}
@@ -165,7 +169,7 @@ void TVA::reset(const Part *newPart, const TimbreParam::PartialParam *newPartial
biasAmpSubtraction = calcBiasAmpSubtractions(partialParam, key);
veloAmpSubtraction = calcVeloAmpSubtraction(partialParam->tva.veloSensitivity, velocity);
- int newTarget = calcBasicAmp(tables, partial, system_, partialParam, patchTemp, newRhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression());
+ int newTarget = calcBasicAmp(tables, partial, system, partialParam, patchTemp, newRhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression());
int newPhase;
if (partialParam->tva.envTime[0] == 0) {
// Initially go to the TVA_PHASE_ATTACK target amp, and spend the next phase going from there to the TVA_PHASE_2 target amp
@@ -182,7 +186,7 @@ void TVA::reset(const Part *newPart, const TimbreParam::PartialParam *newPartial
// "Go downward as quickly as possible".
// Since the current value is 0, the LA32Ramp will notice that we're already at or below the target and trying to go downward,
// and therefore jump to the target immediately and raise an interrupt.
- startRamp((Bit8u)newTarget, 0x80 | 127, newPhase);
+ startRamp(Bit8u(newTarget), 0x80 | 127, newPhase);
}
void TVA::startAbort() {
@@ -217,7 +221,7 @@ void TVA::recalcSustain() {
}
// We're sustaining. Recalculate all the values
const Tables *tables = &Tables::getInstance();
- int newTarget = calcBasicAmp(tables, partial, system_, partialParam, patchTemp, rhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression());
+ int newTarget = calcBasicAmp(tables, partial, system, partialParam, patchTemp, rhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression());
newTarget += partialParam->tva.envLevel[3];
// Since we're in TVA_PHASE_SUSTAIN at this point, we know that target has been reached and an interrupt fired, so we can rely on it being the current amp.
int targetDelta = newTarget - target;
@@ -225,9 +229,9 @@ void TVA::recalcSustain() {
// Calculate an increment to get to the new amp value in a short, more or less consistent amount of time
Bit8u newIncrement;
if (targetDelta >= 0) {
- newIncrement = tables->envLogarithmicTime[(Bit8u)targetDelta] - 2;
+ newIncrement = tables->envLogarithmicTime[Bit8u(targetDelta)] - 2;
} else {
- newIncrement = (tables->envLogarithmicTime[(Bit8u)-targetDelta] - 2) | 0x80;
+ newIncrement = (tables->envLogarithmicTime[Bit8u(-targetDelta)] - 2) | 0x80;
}
// Configure so that once the transition's complete and nextPhase() is called, we'll just re-enter sustain phase (or decay phase, depending on parameters at the time).
startRamp(newTarget, newIncrement, TVA_PHASE_SUSTAIN - 1);
@@ -279,7 +283,7 @@ void TVA::nextPhase() {
int envPointIndex = phase;
if (!allLevelsZeroFromNowOn) {
- newTarget = calcBasicAmp(tables, partial, system_, partialParam, patchTemp, rhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression());
+ newTarget = calcBasicAmp(tables, partial, system, partialParam, patchTemp, rhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression());
if (newPhase == TVA_PHASE_SUSTAIN || newPhase == TVA_PHASE_RELEASE) {
if (partialParam->tva.envLevel[3] == 0) {
@@ -311,7 +315,7 @@ void TVA::nextPhase() {
int envTimeSetting = partialParam->tva.envTime[envPointIndex];
if (newPhase == TVA_PHASE_ATTACK) {
- envTimeSetting -= ((signed)partial->getPoly()->getVelocity() - 64) >> (6 - partialParam->tva.envTimeVeloSensitivity); // PORTABILITY NOTE: Assumes arithmetic shift
+ envTimeSetting -= (signed(partial->getPoly()->getVelocity()) - 64) >> (6 - partialParam->tva.envTimeVeloSensitivity); // PORTABILITY NOTE: Assumes arithmetic shift
if (envTimeSetting <= 0 && partialParam->tva.envTime[envPointIndex] != 0) {
envTimeSetting = 1;
@@ -338,14 +342,14 @@ void TVA::nextPhase() {
}
}
targetDelta = -targetDelta;
- newIncrement = tables->envLogarithmicTime[(Bit8u)targetDelta] - envTimeSetting;
+ newIncrement = tables->envLogarithmicTime[Bit8u(targetDelta)] - envTimeSetting;
if (newIncrement <= 0) {
newIncrement = 1;
}
newIncrement = newIncrement | 0x80;
} else {
// FIXME: The last 22 or so entries in this table are 128 - surely that fucks things up, since that ends up being -128 signed?
- newIncrement = tables->envLogarithmicTime[(Bit8u)targetDelta] - envTimeSetting;
+ newIncrement = tables->envLogarithmicTime[Bit8u(targetDelta)] - envTimeSetting;
if (newIncrement <= 0) {
newIncrement = 1;
}
@@ -360,7 +364,7 @@ void TVA::nextPhase() {
}
}
- startRamp((Bit8u)newTarget, (Bit8u)newIncrement, newPhase);
+ startRamp(Bit8u(newTarget), Bit8u(newIncrement), newPhase);
}
-}
+} // namespace MT32Emu
diff --git a/audio/softsynth/mt32/TVA.h b/audio/softsynth/mt32/TVA.h
index a100107a699..f593b4e7d18 100644
--- a/audio/softsynth/mt32/TVA.h
+++ b/audio/softsynth/mt32/TVA.h
@@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -18,9 +18,15 @@
#ifndef MT32EMU_TVA_H
#define MT32EMU_TVA_H
+#include "globals.h"
+#include "Types.h"
+#include "Structures.h"
+
namespace MT32Emu {
+class LA32Ramp;
class Part;
+class Partial;
// Note that when entering nextPhase(), newPhase is set to phase + 1, and the descriptions/names below refer to
// newPhase's value.
@@ -57,7 +63,7 @@ class TVA {
private:
const Partial * const partial;
LA32Ramp *ampRamp;
- const MemParams::System * const system_;
+ const MemParams::System * const system;
const Part *part;
const TimbreParam::PartialParam *partialParam;
@@ -87,8 +93,8 @@ public:
bool isPlaying() const;
int getPhase() const;
-};
+}; // class TVA
-}
+} // namespace MT32Emu
-#endif /* TVA_H_ */
+#endif // #ifndef MT32EMU_TVA_H
diff --git a/audio/softsynth/mt32/TVF.cpp b/audio/softsynth/mt32/TVF.cpp
index 164cf2b4cbb..b296c34132a 100644
--- a/audio/softsynth/mt32/TVF.cpp
+++ b/audio/softsynth/mt32/TVF.cpp
@@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -15,12 +15,14 @@
* along with this program. If not, see .
*/
-//#include
-
-#include "mt32emu.h"
-#include "mmath.h"
#include "internals.h"
+#include "TVF.h"
+#include "LA32Ramp.h"
+#include "Partial.h"
+#include "Poly.h"
+#include "Tables.h"
+
namespace MT32Emu {
// Note that when entering nextPhase(), newPhase is set to phase + 1, and the descriptions/names below refer to
@@ -60,7 +62,7 @@ static int calcBaseCutoff(const TimbreParam::PartialParam *partialParam, Bit32u
static const Bit8s keyfollowMult21[] = {-21, -10, -5, 0, 2, 5, 8, 10, 13, 16, 18, 21, 26, 32, 42, 21, 21};
int baseCutoff = keyfollowMult21[partialParam->tvf.keyfollow] - keyfollowMult21[partialParam->wg.pitchKeyfollow];
// baseCutoff range now: -63 to 63
- baseCutoff *= (int)key - 60;
+ baseCutoff *= int(key) - 60;
// baseCutoff range now: -3024 to 3024
int biasPoint = partialParam->tvf.biasPoint;
if ((biasPoint & 0x40) == 0) {
@@ -75,7 +77,7 @@ static int calcBaseCutoff(const TimbreParam::PartialParam *partialParam, Bit32u
// biasPoint range here: 64 to 127
int bias = biasPoint - 31 - key; // bias range here: -75 to 84
if (bias < 0) {
- baseCutoff += bias * biasLevelToBiasMult[partialParam->tvf.biasLevel]; // Calculation range: −6375 to 6375
+ baseCutoff += bias * biasLevelToBiasMult[partialParam->tvf.biasLevel]; // Calculation range: -6375 to 6375
// baseCutoff range now: -9399 to 9399
}
}
@@ -96,7 +98,7 @@ static int calcBaseCutoff(const TimbreParam::PartialParam *partialParam, Bit32u
if (baseCutoff > 255) {
baseCutoff = 255;
}
- return (Bit8u)baseCutoff;
+ return Bit8u(baseCutoff);
}
TVF::TVF(const Partial *usePartial, LA32Ramp *useCutoffModifierRamp) :
@@ -128,7 +130,7 @@ void TVF::reset(const TimbreParam::PartialParam *newPartialParam, unsigned int b
int newLevelMult = velocity * newPartialParam->tvf.envVeloSensitivity;
newLevelMult >>= 6;
newLevelMult += 109 - newPartialParam->tvf.envVeloSensitivity;
- newLevelMult += ((signed)key - 60) >> (4 - newPartialParam->tvf.envDepthKeyfollow);
+ newLevelMult += (signed(key) - 60) >> (4 - newPartialParam->tvf.envDepthKeyfollow);
if (newLevelMult < 0) {
newLevelMult = 0;
}
@@ -140,7 +142,7 @@ void TVF::reset(const TimbreParam::PartialParam *newPartialParam, unsigned int b
levelMult = newLevelMult;
if (newPartialParam->tvf.envTimeKeyfollow != 0) {
- keyTimeSubtraction = ((signed)key - 60) >> (5 - newPartialParam->tvf.envTimeKeyfollow);
+ keyTimeSubtraction = (signed(key) - 60) >> (5 - newPartialParam->tvf.envTimeKeyfollow);
} else {
keyTimeSubtraction = 0;
}
@@ -228,4 +230,4 @@ void TVF::nextPhase() {
startRamp(newTarget, newIncrement, newPhase);
}
-}
+} // namespace MT32Emu
diff --git a/audio/softsynth/mt32/TVF.h b/audio/softsynth/mt32/TVF.h
index 1e2c6d17287..38dcef708cd 100644
--- a/audio/softsynth/mt32/TVF.h
+++ b/audio/softsynth/mt32/TVF.h
@@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -18,8 +18,15 @@
#ifndef MT32EMU_TVF_H
#define MT32EMU_TVF_H
+#include "globals.h"
+#include "Types.h"
+#include "Structures.h"
+
namespace MT32Emu {
+class LA32Ramp;
+class Partial;
+
class TVF {
private:
const Partial * const partial;
@@ -47,8 +54,8 @@ public:
Bit8u getBaseCutoff() const;
void handleInterrupt();
void startDecay();
-};
+}; // class TVF
-}
+} // namespace MT32Emu
-#endif
+#endif // #ifndef MT32EMU_TVF_H
diff --git a/audio/softsynth/mt32/TVP.cpp b/audio/softsynth/mt32/TVP.cpp
index a8003d96dc5..dca0003843d 100644
--- a/audio/softsynth/mt32/TVP.cpp
+++ b/audio/softsynth/mt32/TVP.cpp
@@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -15,12 +15,17 @@
* along with this program. If not, see .
*/
-//#include
-//#include
+#include
-#include "mt32emu.h"
#include "internals.h"
+#include "TVP.h"
+#include "Part.h"
+#include "Partial.h"
+#include "Poly.h"
+#include "Synth.h"
+#include "TVA.h"
+
namespace MT32Emu {
// FIXME: Add Explanation
@@ -47,7 +52,7 @@ static Bit16u keyToPitchTable[] = {
};
TVP::TVP(const Partial *usePartial) :
- partial(usePartial), system_(&usePartial->getSynth()->mt32ram.system) {
+ partial(usePartial), system(&usePartial->getSynth()->mt32ram.system) {
// We want to do processing 4000 times per second. FIXME: This is pretty arbitrary.
maxCounter = SAMPLE_RATE / 4000;
// The timer runs at 500kHz. We only need to bother updating it every maxCounter samples, before we do processing.
@@ -58,7 +63,7 @@ TVP::TVP(const Partial *usePartial) :
static Bit16s keyToPitch(unsigned int key) {
// We're using a table to do: return round_to_nearest_or_even((key - 60) * (4096.0 / 12.0))
// Banker's rounding is just slightly annoying to do in C++
- int k = (int)key;
+ int k = int(key);
Bit16s pitch = keyToPitchTable[abs(k - 60)];
return key < 60 ? -pitch : pitch;
}
@@ -82,7 +87,7 @@ static Bit32u calcBasePitch(const Partial *partial, const TimbreParam::PartialPa
const ControlROMPCMStruct *controlROMPCMStruct = partial->getControlROMPCMStruct();
if (controlROMPCMStruct != NULL) {
- basePitch += (Bit32s)((((Bit32s)controlROMPCMStruct->pitchMSB) << 8) | (Bit32s)controlROMPCMStruct->pitchLSB);
+ basePitch += (Bit32s(controlROMPCMStruct->pitchMSB) << 8) | Bit32s(controlROMPCMStruct->pitchLSB);
} else {
if ((partialParam->wg.waveform & 1) == 0) {
basePitch += 37133; // This puts Middle C at around 261.64Hz (assuming no other modifications, masterTune of 64, etc.)
@@ -98,7 +103,7 @@ static Bit32u calcBasePitch(const Partial *partial, const TimbreParam::PartialPa
if (basePitch > 59392) {
basePitch = 59392;
}
- return (Bit32u)basePitch;
+ return Bit32u(basePitch);
}
static Bit32u calcVeloMult(Bit8u veloSensitivity, unsigned int velocity) {
@@ -119,7 +124,7 @@ static Bit32u calcVeloMult(Bit8u veloSensitivity, unsigned int velocity) {
static Bit32s calcTargetPitchOffsetWithoutLFO(const TimbreParam::PartialParam *partialParam, int levelIndex, unsigned int velocity) {
int veloMult = calcVeloMult(partialParam->pitchEnv.veloSensitivity, velocity);
int targetPitchOffsetWithoutLFO = partialParam->pitchEnv.level[levelIndex] - 50;
- targetPitchOffsetWithoutLFO = (Bit32s)(targetPitchOffsetWithoutLFO * veloMult) >> (16 - partialParam->pitchEnv.depth); // PORTABILITY NOTE: Assumes arithmetic shift
+ targetPitchOffsetWithoutLFO = (targetPitchOffsetWithoutLFO * veloMult) >> (16 - partialParam->pitchEnv.depth); // PORTABILITY NOTE: Assumes arithmetic shift
return targetPitchOffsetWithoutLFO;
}
@@ -140,7 +145,7 @@ void TVP::reset(const Part *usePart, const TimbreParam::PartialParam *usePartial
phase = 0;
if (partialParam->pitchEnv.timeKeyfollow) {
- timeKeyfollowSubtraction = (key - 60) >> (5 - partialParam->pitchEnv.timeKeyfollow); // PORTABILITY NOTE: Assumes arithmetic shift
+ timeKeyfollowSubtraction = Bit32s(key - 60) >> (5 - partialParam->pitchEnv.timeKeyfollow); // PORTABILITY NOTE: Assumes arithmetic shift
} else {
timeKeyfollowSubtraction = 0;
}
@@ -163,7 +168,7 @@ void TVP::updatePitch() {
if (!partial->isPCM() || (partial->getControlROMPCMStruct()->len & 0x01) == 0) { // FIXME: Use !partial->pcmWaveEntry->unaffectedByMasterTune instead
// FIXME: masterTune recalculation doesn't really happen here, and there are various bugs not yet emulated
// 171 is ~half a semitone.
- newPitch += ((system_->masterTune - 64) * 171) >> 6; // PORTABILITY NOTE: Assumes arithmetic shift.
+ newPitch += ((system->masterTune - 64) * 171) >> 6; // PORTABILITY NOTE: Assumes arithmetic shift.
}
if ((partialParam->wg.pitchBenderEnabled & 1) != 0) {
newPitch += part->getPitchBend();
@@ -172,14 +177,13 @@ void TVP::updatePitch() {
newPitch = 0;
}
-// Note: Temporary #ifdef until we have proper "quirk" configuration
-// This is about right emulation of MT-32 GEN0 quirk exploited in Colonel's Bequest timbre "Lightning"
-#ifndef MT32EMU_QUIRK_PITCH_ENVELOPE_OVERFLOW_MT32
- if (newPitch > 59392) {
- newPitch = 59392;
+ // Skipping this check seems about right emulation of MT-32 GEN0 quirk exploited in Colonel's Bequest timbre "Lightning"
+ if (partial->getSynth()->controlROMFeatures->quirkPitchEnvelopeOverflow == 0) {
+ if (newPitch > 59392) {
+ newPitch = 59392;
+ }
}
-#endif
- pitch = (Bit16u)newPitch;
+ pitch = Bit16u(newPitch);
// FIXME: We're doing this here because that's what the CM-32L does - we should probably move this somewhere more appropriate in future.
partial->getTVA()->recalcSustain();
@@ -317,10 +321,10 @@ void TVP::process() {
negativeBigTicksRemaining = negativeBigTicksRemaining >> rightShifts; // PORTABILITY NOTE: Assumes arithmetic shift
rightShifts = 13;
}
- int newResult = ((Bit32s)(negativeBigTicksRemaining * pitchOffsetChangePerBigTick)) >> rightShifts; // PORTABILITY NOTE: Assumes arithmetic shift
+ int newResult = (negativeBigTicksRemaining * pitchOffsetChangePerBigTick) >> rightShifts; // PORTABILITY NOTE: Assumes arithmetic shift
newResult += targetPitchOffsetWithoutLFO + lfoPitchOffset;
currentPitchOffset = newResult;
updatePitch();
}
-}
+} // namespace MT32Emu
diff --git a/audio/softsynth/mt32/TVP.h b/audio/softsynth/mt32/TVP.h
index e9d05ffa7a1..be90f0ff087 100644
--- a/audio/softsynth/mt32/TVP.h
+++ b/audio/softsynth/mt32/TVP.h
@@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -18,12 +18,19 @@
#ifndef MT32EMU_TVP_H
#define MT32EMU_TVP_H
+#include "globals.h"
+#include "Types.h"
+#include "Structures.h"
+
namespace MT32Emu {
+class Part;
+class Partial;
+
class TVP {
private:
const Partial * const partial;
- const MemParams::System * const system_; // FIXME: Only necessary because masterTune calculation is done in the wrong place atm.
+ const MemParams::System * const system; // FIXME: Only necessary because masterTune calculation is done in the wrong place atm.
const Part *part;
const TimbreParam::PartialParam *partialParam;
@@ -60,8 +67,8 @@ public:
Bit32u getBasePitch() const;
Bit16u nextPitch();
void startDecay();
-};
+}; // class TVP
-}
+} // namespace MT32Emu
-#endif
+#endif // #ifndef MT32EMU_TVP_H
diff --git a/audio/softsynth/mt32/Tables.cpp b/audio/softsynth/mt32/Tables.cpp
index 7e165b5a7a1..cb3493285a8 100644
--- a/audio/softsynth/mt32/Tables.cpp
+++ b/audio/softsynth/mt32/Tables.cpp
@@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -15,11 +15,10 @@
* along with this program. If not, see .
*/
-//#include
+#include "internals.h"
-#include "mt32emu.h"
-#include "mmath.h"
#include "Tables.h"
+#include "mmath.h"
namespace MT32Emu {
@@ -31,24 +30,25 @@ const Tables &Tables::getInstance() {
}
Tables::Tables() {
- int lf;
- for (lf = 0; lf <= 100; lf++) {
+ for (int lf = 0; lf <= 100; lf++) {
// CONFIRMED:KG: This matches a ROM table found by Mok
- float fVal = (2.0f - LOG10F((float)lf + 1.0f)) * 128.0f;
- int val = (int)(fVal + 1.0);
+ float fVal = (2.0f - LOG10F(float(lf) + 1.0f)) * 128.0f;
+ int val = int(fVal + 1.0);
if (val > 255) {
val = 255;
}
- levelToAmpSubtraction[lf] = (Bit8u)val;
+ levelToAmpSubtraction[lf] = Bit8u(val);
}
envLogarithmicTime[0] = 64;
- for (lf = 1; lf <= 255; lf++) {
+ for (int lf = 1; lf <= 255; lf++) {
// CONFIRMED:KG: This matches a ROM table found by Mok
- envLogarithmicTime[lf] = (Bit8u)ceil(64.0f + LOG2F((float)lf) * 8.0f);
+ envLogarithmicTime[lf] = Bit8u(ceil(64.0f + LOG2F(float(lf)) * 8.0f));
}
-#ifdef EMULATE_LAPC_I // Dummy #ifdef - we'll have runtime emulation mode selection in future.
+#if 0
+ // The table below is to be used in conjunction with emulation of VCA of newer generation units which is currently missing.
+ // These relatively small values are rather intended to fine-tune the overall amplification of the VCA.
// CONFIRMED: Based on a table found by Mok in the LAPC-I control ROM
// Note that this matches the MT-32 table, but with the values clamped to a maximum of 8.
memset(masterVolToAmpSubtraction, 8, 71);
@@ -64,12 +64,12 @@ Tables::Tables() {
// CONFIRMED: Based on a table found by Mok in the MT-32 control ROM
masterVolToAmpSubtraction[0] = 255;
for (int masterVol = 1; masterVol <= 100; masterVol++) {
- masterVolToAmpSubtraction[masterVol] = (int)(106.31 - 16.0f * LOG2F((float)masterVol));
+ masterVolToAmpSubtraction[masterVol] = Bit8u(106.31 - 16.0f * LOG2F(float(masterVol)));
}
#endif
for (int i = 0; i <= 100; i++) {
- pulseWidth100To255[i] = (int)(i * 255 / 100.0f + 0.5f);
+ pulseWidth100To255[i] = Bit8u(i * 255 / 100.0f + 0.5f);
//synth->printDebug("%d: %d", i, pulseWidth100To255[i]);
}
@@ -94,4 +94,4 @@ Tables::Tables() {
resAmpDecayFactor = resAmpDecayFactorTable;
}
-}
+} // namespace MT32Emu
diff --git a/audio/softsynth/mt32/Tables.h b/audio/softsynth/mt32/Tables.h
index 8865c7fac82..249e32919a3 100644
--- a/audio/softsynth/mt32/Tables.h
+++ b/audio/softsynth/mt32/Tables.h
@@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -18,6 +18,9 @@
#ifndef MT32EMU_TABLES_H
#define MT32EMU_TABLES_H
+#include "globals.h"
+#include "Types.h"
+
namespace MT32Emu {
class Tables {
@@ -52,8 +55,8 @@ public:
Bit16u logsin9[512];
const Bit8u *resAmpDecayFactor;
-};
+}; // class Tables
-}
+} // namespace MT32Emu
-#endif
+#endif // #ifndef MT32EMU_TABLES_H
diff --git a/audio/softsynth/mt32/Types.h b/audio/softsynth/mt32/Types.h
index 934b1a1173e..f90dce19a4c 100644
--- a/audio/softsynth/mt32/Types.h
+++ b/audio/softsynth/mt32/Types.h
@@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -27,14 +27,6 @@ typedef signed short int Bit16s;
typedef unsigned char Bit8u;
typedef signed char Bit8s;
-#if MT32EMU_USE_FLOAT_SAMPLES
-typedef float Sample;
-typedef float SampleEx;
-#else
-typedef Bit16s Sample;
-typedef Bit32s SampleEx;
-#endif
-
}
#endif
diff --git a/audio/softsynth/mt32/config.h b/audio/softsynth/mt32/config.h
new file mode 100644
index 00000000000..af59f055a0b
--- /dev/null
+++ b/audio/softsynth/mt32/config.h
@@ -0,0 +1,28 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef MT32EMU_CONFIG_H
+#define MT32EMU_CONFIG_H
+
+#define MT32EMU_VERSION "2.0.3"
+#define MT32EMU_VERSION_MAJOR 2
+#define MT32EMU_VERSION_MINOR 0
+#define MT32EMU_VERSION_PATCH 3
+
+#define MT32EMU_EXPORTS_TYPE 3
+
+#endif
diff --git a/audio/softsynth/mt32/globals.h b/audio/softsynth/mt32/globals.h
new file mode 100644
index 00000000000..49a5ecc250e
--- /dev/null
+++ b/audio/softsynth/mt32/globals.h
@@ -0,0 +1,119 @@
+/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef MT32EMU_GLOBALS_H
+#define MT32EMU_GLOBALS_H
+
+#include "config.h"
+
+/* Support for compiling shared library. */
+#ifdef MT32EMU_SHARED
+#if defined _WIN32 || defined __CYGWIN__
+#ifdef _MSC_VER
+#ifdef mt32emu_EXPORTS
+#define MT32EMU_EXPORT_ATTRIBUTE _declspec(dllexport)
+#else /* #ifdef mt32emu_EXPORTS */
+#define MT32EMU_EXPORT_ATTRIBUTE _declspec(dllimport)
+#endif /* #ifdef mt32emu_EXPORTS */
+#else /* #ifdef _MSC_VER */
+#ifdef mt32emu_EXPORTS
+#define MT32EMU_EXPORT_ATTRIBUTE __attribute__ ((dllexport))
+#else /* #ifdef mt32emu_EXPORTS */
+#define MT32EMU_EXPORT_ATTRIBUTE __attribute__ ((dllimport))
+#endif /* #ifdef mt32emu_EXPORTS */
+#endif /* #ifdef _MSC_VER */
+#else /* #if defined _WIN32 || defined __CYGWIN__ */
+#define MT32EMU_EXPORT_ATTRIBUTE __attribute__ ((visibility("default")))
+#endif /* #if defined _WIN32 || defined __CYGWIN__ */
+#else /* #ifdef MT32EMU_SHARED */
+#define MT32EMU_EXPORT_ATTRIBUTE
+#endif /* #ifdef MT32EMU_SHARED */
+
+#if MT32EMU_EXPORTS_TYPE == 1 || MT32EMU_EXPORTS_TYPE == 2
+#define MT32EMU_EXPORT
+#else
+#define MT32EMU_EXPORT MT32EMU_EXPORT_ATTRIBUTE
+#endif
+
+/* Useful constants */
+
+/* Sample rate to use in mixing. With the progress of development, we've found way too many thing dependent.
+ * In order to achieve further advance in emulation accuracy, sample rate made fixed throughout the emulator,
+ * except the emulation of analogue path.
+ * The output from the synth is supposed to be resampled externally in order to convert to the desired sample rate.
+ */
+#define MT32EMU_SAMPLE_RATE 32000
+
+/* The default value for the maximum number of partials playing simultaneously. */
+#define MT32EMU_DEFAULT_MAX_PARTIALS 32
+
+/* The higher this number, the more memory will be used, but the more samples can be processed in one run -
+ * various parts of sample generation can be processed more efficiently in a single run.
+ * A run's maximum length is that given to Synth::render(), so giving a value here higher than render() is ever
+ * called with will give no gain (but simply waste the memory).
+ * Note that this value does *not* in any way impose limitations on the length given to render(), and has no effect
+ * on the generated audio.
+ * This value must be >= 1.
+ */
+#define MT32EMU_MAX_SAMPLES_PER_RUN 4096
+
+/* The default size of the internal MIDI event queue.
+ * It holds the incoming MIDI events before the rendering engine actually processes them.
+ * The main goal is to fairly emulate the real hardware behaviour which obviously
+ * uses an internal MIDI event queue to gather incoming data as well as the delays
+ * introduced by transferring data via the MIDI interface.
+ * This also facilitates building of an external rendering loop
+ * as the queue stores timestamped MIDI events.
+ */
+#define MT32EMU_DEFAULT_MIDI_EVENT_QUEUE_SIZE 1024
+
+/* Maximum allowed size of MIDI parser input stream buffer.
+ * Should suffice for any reasonable bulk dump SysEx, as the h/w units have only 32K of RAM onboard.
+ */
+#define MT32EMU_MAX_STREAM_BUFFER_SIZE 32768
+
+/* This should correspond to the MIDI buffer size used in real h/w devices.
+ * CM-32L control ROM seems using 1000 bytes, old MT-32 isn't confirmed by now.
+ */
+#define MT32EMU_SYSEX_BUFFER_SIZE 1000
+
+#if defined(__cplusplus) && MT32EMU_API_TYPE != 1
+
+namespace MT32Emu
+{
+const unsigned int SAMPLE_RATE = MT32EMU_SAMPLE_RATE;
+#undef MT32EMU_SAMPLE_RATE
+
+const unsigned int DEFAULT_MAX_PARTIALS = MT32EMU_DEFAULT_MAX_PARTIALS;
+#undef MT32EMU_DEFAULT_MAX_PARTIALS
+
+const unsigned int MAX_SAMPLES_PER_RUN = MT32EMU_MAX_SAMPLES_PER_RUN;
+#undef MT32EMU_MAX_SAMPLES_PER_RUN
+
+const unsigned int DEFAULT_MIDI_EVENT_QUEUE_SIZE = MT32EMU_DEFAULT_MIDI_EVENT_QUEUE_SIZE;
+#undef MT32EMU_DEFAULT_MIDI_EVENT_QUEUE_SIZE
+
+const unsigned int MAX_STREAM_BUFFER_SIZE = MT32EMU_MAX_STREAM_BUFFER_SIZE;
+#undef MT32EMU_MAX_STREAM_BUFFER_SIZE
+
+const unsigned int SYSEX_BUFFER_SIZE = MT32EMU_SYSEX_BUFFER_SIZE;
+#undef MT32EMU_SYSEX_BUFFER_SIZE
+}
+
+#endif /* #if defined(__cplusplus) && MT32EMU_API_TYPE != 1 */
+
+#endif /* #ifndef MT32EMU_GLOBALS_H */
diff --git a/audio/softsynth/mt32/internals.h b/audio/softsynth/mt32/internals.h
index ef56819a42d..c64ba392128 100644
--- a/audio/softsynth/mt32/internals.h
+++ b/audio/softsynth/mt32/internals.h
@@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -18,66 +18,111 @@
#ifndef MT32EMU_INTERNALS_H
#define MT32EMU_INTERNALS_H
+#include "Types.h"
+
// Debugging
// 0: Standard debug output is not stamped with the rendered sample count
// 1: Standard debug output is stamped with the rendered sample count
// NOTE: The "samplestamp" corresponds to the end of the last completed rendering run.
// This is important to bear in mind for debug output that occurs during a run.
+#ifndef MT32EMU_DEBUG_SAMPLESTAMPS
#define MT32EMU_DEBUG_SAMPLESTAMPS 0
+#endif
// 0: No debug output for initialisation progress
// 1: Debug output for initialisation progress
+#ifndef MT32EMU_MONITOR_INIT
#define MT32EMU_MONITOR_INIT 0
+#endif
// 0: No debug output for MIDI events
// 1: Debug output for weird MIDI events
+#ifndef MT32EMU_MONITOR_MIDI
#define MT32EMU_MONITOR_MIDI 0
+#endif
// 0: No debug output for note on/off
// 1: Basic debug output for note on/off
// 2: Comprehensive debug output for note on/off
+#ifndef MT32EMU_MONITOR_INSTRUMENTS
#define MT32EMU_MONITOR_INSTRUMENTS 0
+#endif
// 0: No debug output for partial allocations
// 1: Show partial stats when an allocation fails
// 2: Show partial stats with every new poly
// 3: Show individual partial allocations/deactivations
+#ifndef MT32EMU_MONITOR_PARTIALS
#define MT32EMU_MONITOR_PARTIALS 0
+#endif
// 0: No debug output for sysex
// 1: Basic debug output for sysex
+#ifndef MT32EMU_MONITOR_SYSEX
#define MT32EMU_MONITOR_SYSEX 0
+#endif
// 0: No debug output for sysex writes to the timbre areas
// 1: Debug output with the name and location of newly-written timbres
// 2: Complete dump of timbre parameters for newly-written timbres
+#ifndef MT32EMU_MONITOR_TIMBRES
#define MT32EMU_MONITOR_TIMBRES 0
+#endif
// 0: No TVA/TVF-related debug output.
// 1: Shows changes to TVA/TVF target, increment and phase.
+#ifndef MT32EMU_MONITOR_TVA
#define MT32EMU_MONITOR_TVA 0
+#endif
+#ifndef MT32EMU_MONITOR_TVF
#define MT32EMU_MONITOR_TVF 0
+#endif
// Configuration
+// 0: Use 16-bit signed samples and refined wave generator based on logarithmic fixed-point computations and LUTs. Maximum emulation accuracy and speed.
+// 1: Use float samples in the wave generator and renderer. Maximum output quality and minimum noise.
+#ifndef MT32EMU_USE_FLOAT_SAMPLES
+#define MT32EMU_USE_FLOAT_SAMPLES 0
+#endif
+
// If non-zero, deletes reverb buffers that are not in use to save memory.
// If zero, keeps reverb buffers for all modes around all the time to avoid allocating/freeing in the critical path.
+#ifndef MT32EMU_REDUCE_REVERB_MEMORY
#define MT32EMU_REDUCE_REVERB_MEMORY 1
+#endif
// 0: Maximum speed at the cost of a bit lower emulation accuracy.
// 1: Maximum achievable emulation accuracy.
+#ifndef MT32EMU_BOSS_REVERB_PRECISE_MODE
#define MT32EMU_BOSS_REVERB_PRECISE_MODE 0
-
-#include "Structures.h"
-#include "Tables.h"
-#include "Poly.h"
-#include "LA32Ramp.h"
-#include "LA32WaveGenerator.h"
-#include "TVA.h"
-#include "TVP.h"
-#include "TVF.h"
-#include "Partial.h"
-#include "Part.h"
-
#endif
+
+namespace MT32Emu {
+
+enum PolyState {
+ POLY_Playing,
+ POLY_Held, // This marks keys that have been released on the keyboard, but are being held by the pedal
+ POLY_Releasing,
+ POLY_Inactive
+};
+
+enum ReverbMode {
+ REVERB_MODE_ROOM,
+ REVERB_MODE_HALL,
+ REVERB_MODE_PLATE,
+ REVERB_MODE_TAP_DELAY
+};
+
+#if MT32EMU_USE_FLOAT_SAMPLES
+typedef float Sample;
+typedef float SampleEx;
+#else
+typedef Bit16s Sample;
+typedef Bit32s SampleEx;
+#endif
+
+}
+
+#endif // #ifndef MT32EMU_INTERNALS_H
diff --git a/audio/softsynth/mt32/mmath.h b/audio/softsynth/mt32/mmath.h
index 602242e74f8..f233bedcbb0 100644
--- a/audio/softsynth/mt32/mmath.h
+++ b/audio/softsynth/mt32/mmath.h
@@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -18,12 +18,7 @@
#ifndef MT32EMU_MMATH_H
#define MT32EMU_MMATH_H
-#define FIXEDPOINT_UDIV(x, y, point) (((x) << (point)) / ((y)))
-#define FIXEDPOINT_SDIV(x, y, point) (((x) * (1 << point)) / ((y)))
-#define FIXEDPOINT_UMULT(x, y, point) (((x) * (y)) >> point)
-#define FIXEDPOINT_SMULT(x, y, point) (((x) * (y)) / (1 << point))
-
-#define FIXEDPOINT_MAKE(x, point) ((Bit32u)((1 << point) * x))
+#include
namespace MT32Emu {
@@ -46,7 +41,7 @@ static inline float EXPF(float x) {
static inline float EXP2F(float x) {
#ifdef __APPLE__
// on OSX exp2f() is 1.59 times faster than "exp() and the multiplication with FLOAT_LN_2"
- return exp2(x);
+ return exp2f(x);
#else
return exp(FLOAT_LN_2 * x);
#endif
@@ -68,6 +63,6 @@ static inline float LOG10F(float x) {
return log10(x);
}
-}
+} // namespace MT32Emu
-#endif
+#endif // #ifndef MT32EMU_MMATH_H
diff --git a/audio/softsynth/mt32/module.mk b/audio/softsynth/mt32/module.mk
index f966da8d086..7657f5b55f6 100644
--- a/audio/softsynth/mt32/module.mk
+++ b/audio/softsynth/mt32/module.mk
@@ -3,8 +3,11 @@ MODULE := audio/softsynth/mt32
MODULE_OBJS := \
Analog.o \
BReverbModel.o \
+ File.o \
+ FileStream.o \
LA32Ramp.o \
LA32WaveGenerator.o \
+ MidiStreamParser.o \
Part.o \
Partial.o \
PartialManager.o \
@@ -14,7 +17,24 @@ MODULE_OBJS := \
Tables.o \
TVA.o \
TVF.o \
- TVP.o
+ TVP.o \
+ sha1/sha1.o \
+ c_interface/c_interface.o
+
+# SampleRateConverter.o \
+# srchelper/InternalResampler.o \
+# srchelper/SamplerateAdapter.o \
+# srchelper/SoxrAdapter.o \
+# srchelper/srctools/src/FIRResampler.o \
+# srchelper/srctools/src/IIR2xResampler.o \
+# srchelper/srctools/src/LinearResampler.o \
+# srchelper/srctools/src/ResamplerModel.o \
+# srchelper/srctools/src/SincResampler.o
+# TODO: The Munt SampleRateConverter requires these additional -I options.
+# This is not a very nice way of doing that, though, as it adds them globally.
+# INCLUDES += -I $(srcdir)/$(MODULE)/srchelper/srctools/include
+# INCLUDES += -I $(srcdir)/$(MODULE)/
+
# Include common rules
include $(srcdir)/rules.mk
diff --git a/audio/softsynth/mt32/mt32emu.h b/audio/softsynth/mt32/mt32emu.h
index 1574c08f0d9..3f3b6af3449 100644
--- a/audio/softsynth/mt32/mt32emu.h
+++ b/audio/softsynth/mt32/mt32emu.h
@@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
- * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
+ * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -18,44 +18,67 @@
#ifndef MT32EMU_MT32EMU_H
#define MT32EMU_MT32EMU_H
-// Configuration
+#include "config.h"
-// 0: Use 16-bit signed samples and refined wave generator based on logarithmic fixed-point computations and LUTs. Maximum emulation accuracy and speed.
-// 1: Use float samples in the wave generator and renderer. Maximum output quality and minimum noise.
-#define MT32EMU_USE_FLOAT_SAMPLES 0
+/* API Configuration */
-namespace MT32Emu
-{
-// Sample rate to use in mixing. With the progress of development, we've found way too many thing dependent.
-// In order to achieve further advance in emulation accuracy, sample rate made fixed throughout the emulator,
-// except the emulation of analogue path.
-// The output from the synth is supposed to be resampled externally in order to convert to the desired sample rate.
-const unsigned int SAMPLE_RATE = 32000;
+/* 0: Use full-featured C++ API. Well suitable when the library is to be linked statically.
+ * When the library is shared, ABI compatibility may be an issue. Therefore, it should
+ * only be used within a project comprising of several modules to share the library code.
+ * 1: Use C-compatible API. Make the library looks as a regular C library with well-defined ABI.
+ * This is also crucial when the library is to be linked with modules in a different
+ * language, either statically or dynamically.
+ * 2: Use plugin-like API via C-interface wrapped in a C++ class. This is mainly intended
+ * for a shared library being dynamically loaded in run-time. To get access to all the library
+ * services, a client application only needs to bind with a single factory function.
+ * 3: Use optimised C++ API compatible with the plugin API (type 2). The facade class also wraps
+ * the C functions but they are invoked directly. This enables the compiler to generate better
+ * code for the library when linked statically yet being consistent with the plugin-like API.
+ */
-// The default value for the maximum number of partials playing simultaneously.
-const unsigned int DEFAULT_MAX_PARTIALS = 32;
+#ifdef MT32EMU_API_TYPE
+#if MT32EMU_API_TYPE == 0 && (MT32EMU_EXPORTS_TYPE == 1 || MT32EMU_EXPORTS_TYPE == 2)
+#error Incompatible setting MT32EMU_API_TYPE=0
+#elif MT32EMU_API_TYPE == 1 && (MT32EMU_EXPORTS_TYPE == 0 || MT32EMU_EXPORTS_TYPE == 2)
+#error Incompatible setting MT32EMU_API_TYPE=1
+#elif MT32EMU_API_TYPE == 2 && (MT32EMU_EXPORTS_TYPE == 0)
+#error Incompatible setting MT32EMU_API_TYPE=2
+#elif MT32EMU_API_TYPE == 3 && (MT32EMU_EXPORTS_TYPE == 0)
+#error Incompatible setting MT32EMU_API_TYPE=3
+#endif
+#else /* #ifdef MT32EMU_API_TYPE */
+#if 0 < MT32EMU_EXPORTS_TYPE && MT32EMU_EXPORTS_TYPE < 3
+#define MT32EMU_API_TYPE MT32EMU_EXPORTS_TYPE
+#else
+#define MT32EMU_API_TYPE 0
+#endif
+#endif /* #ifdef MT32EMU_API_TYPE */
-// The higher this number, the more memory will be used, but the more samples can be processed in one run -
-// various parts of sample generation can be processed more efficiently in a single run.
-// A run's maximum length is that given to Synth::render(), so giving a value here higher than render() is ever
-// called with will give no gain (but simply waste the memory).
-// Note that this value does *not* in any way impose limitations on the length given to render(), and has no effect
-// on the generated audio.
-// This value must be >= 1.
-const unsigned int MAX_SAMPLES_PER_RUN = 4096;
+/* MT32EMU_SHARED should be defined when building shared library, especially for Windows platforms. */
+/*
+#define MT32EMU_SHARED
+*/
-// The default size of the internal MIDI event queue.
-// It holds the incoming MIDI events before the rendering engine actually processes them.
-// The main goal is to fairly emulate the real hardware behaviour which obviously
-// uses an internal MIDI event queue to gather incoming data as well as the delays
-// introduced by transferring data via the MIDI interface.
-// This also facilitates building of an external rendering loop
-// as the queue stores timestamped MIDI events.
-const unsigned int DEFAULT_MIDI_EVENT_QUEUE_SIZE = 1024;
-}
+#include "globals.h"
+
+#if !defined(__cplusplus) || MT32EMU_API_TYPE == 1
+
+#include "c_interface/c_interface.h"
+
+#elif MT32EMU_API_TYPE == 2 || MT32EMU_API_TYPE == 3
+
+#include "c_interface/cpp_interface.h"
+
+#else /* #if !defined(__cplusplus) || MT32EMU_API_TYPE == 1 */
#include "Types.h"
+#include "File.h"
+#include "FileStream.h"
#include "ROMInfo.h"
#include "Synth.h"
+#include "MidiStreamParser.h"
+#include "SampleRateConverter.h"
-#endif
+#endif /* #if !defined(__cplusplus) || MT32EMU_API_TYPE == 1 */
+
+#endif /* #ifndef MT32EMU_MT32EMU_H */
diff --git a/audio/softsynth/mt32/sha1/sha1.cpp b/audio/softsynth/mt32/sha1/sha1.cpp
new file mode 100755
index 00000000000..9b91cd9f298
--- /dev/null
+++ b/audio/softsynth/mt32/sha1/sha1.cpp
@@ -0,0 +1,185 @@
+/*
+ Copyright (c) 2011, Micael Hildenborg
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of Micael Hildenborg nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY Micael Hildenborg ''AS IS'' AND ANY
+ EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL Micael Hildenborg BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ Contributors:
+ Gustav
+ Several members in the gamedev.se forum.
+ Gregory Petrosyan
+ */
+
+#include "sha1.h"
+
+namespace sha1
+{
+ namespace // local
+ {
+ // Rotate an integer value to left.
+ inline unsigned int rol(const unsigned int value,
+ const unsigned int steps)
+ {
+ return ((value << steps) | (value >> (32 - steps)));
+ }
+
+ // Sets the first 16 integers in the buffert to zero.
+ // Used for clearing the W buffert.
+ inline void clearWBuffert(unsigned int* buffert)
+ {
+ for (int pos = 16; --pos >= 0;)
+ {
+ buffert[pos] = 0;
+ }
+ }
+
+ void innerHash(unsigned int* result, unsigned int* w)
+ {
+ unsigned int a = result[0];
+ unsigned int b = result[1];
+ unsigned int c = result[2];
+ unsigned int d = result[3];
+ unsigned int e = result[4];
+
+ int round = 0;
+
+ #define sha1macro(func,val) \
+ { \
+ const unsigned int t = rol(a, 5) + (func) + e + val + w[round]; \
+ e = d; \
+ d = c; \
+ c = rol(b, 30); \
+ b = a; \
+ a = t; \
+ }
+
+ while (round < 16)
+ {
+ sha1macro((b & c) | (~b & d), 0x5a827999)
+ ++round;
+ }
+ while (round < 20)
+ {
+ w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);
+ sha1macro((b & c) | (~b & d), 0x5a827999)
+ ++round;
+ }
+ while (round < 40)
+ {
+ w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);
+ sha1macro(b ^ c ^ d, 0x6ed9eba1)
+ ++round;
+ }
+ while (round < 60)
+ {
+ w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);
+ sha1macro((b & c) | (b & d) | (c & d), 0x8f1bbcdc)
+ ++round;
+ }
+ while (round < 80)
+ {
+ w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);
+ sha1macro(b ^ c ^ d, 0xca62c1d6)
+ ++round;
+ }
+
+ #undef sha1macro
+
+ result[0] += a;
+ result[1] += b;
+ result[2] += c;
+ result[3] += d;
+ result[4] += e;
+ }
+ } // namespace
+
+ void calc(const void* src, const int bytelength, unsigned char* hash)
+ {
+ // Init the result array.
+ unsigned int result[5] = { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 };
+
+ // Cast the void src pointer to be the byte array we can work with.
+ const unsigned char* sarray = static_cast(src);
+
+ // The reusable round buffer
+ unsigned int w[80];
+
+ // Loop through all complete 64byte blocks.
+ const int endOfFullBlocks = bytelength - 64;
+ int endCurrentBlock;
+ int currentBlock = 0;
+
+ while (currentBlock <= endOfFullBlocks)
+ {
+ endCurrentBlock = currentBlock + 64;
+
+ // Init the round buffer with the 64 byte block data.
+ for (int roundPos = 0; currentBlock < endCurrentBlock; currentBlock += 4)
+ {
+ // This line will swap endian on big endian and keep endian on little endian.
+ w[roundPos++] = static_cast(sarray[currentBlock + 3])
+ | (static_cast(sarray[currentBlock + 2]) << 8)
+ | (static_cast(sarray[currentBlock + 1]) << 16)
+ | (static_cast(sarray[currentBlock]) << 24);
+ }
+ innerHash(result, w);
+ }
+
+ // Handle the last and not full 64 byte block if existing.
+ endCurrentBlock = bytelength - currentBlock;
+ clearWBuffert(w);
+ int lastBlockBytes = 0;
+ for (;lastBlockBytes < endCurrentBlock; ++lastBlockBytes)
+ {
+ w[lastBlockBytes >> 2] |= static_cast(sarray[lastBlockBytes + currentBlock]) << ((3 - (lastBlockBytes & 3)) << 3);
+ }
+ w[lastBlockBytes >> 2] |= 0x80 << ((3 - (lastBlockBytes & 3)) << 3);
+ if (endCurrentBlock >= 56)
+ {
+ innerHash(result, w);
+ clearWBuffert(w);
+ }
+ w[15] = bytelength << 3;
+ innerHash(result, w);
+
+ // Store hash in result pointer, and make sure we get in in the correct order on both endian models.
+ for (int hashByte = 20; --hashByte >= 0;)
+ {
+ hash[hashByte] = (result[hashByte >> 2] >> (((3 - hashByte) & 0x3) << 3)) & 0xff;
+ }
+ }
+
+ void toHexString(const unsigned char* hash, char* hexstring)
+ {
+ const char hexDigits[] = { "0123456789abcdef" };
+
+ for (int hashByte = 20; --hashByte >= 0;)
+ {
+ hexstring[hashByte << 1] = hexDigits[(hash[hashByte] >> 4) & 0xf];
+ hexstring[(hashByte << 1) + 1] = hexDigits[hash[hashByte] & 0xf];
+ }
+ hexstring[40] = 0;
+ }
+} // namespace sha1
diff --git a/audio/softsynth/mt32/sha1/sha1.h b/audio/softsynth/mt32/sha1/sha1.h
new file mode 100755
index 00000000000..96d8ce4da6e
--- /dev/null
+++ b/audio/softsynth/mt32/sha1/sha1.h
@@ -0,0 +1,49 @@
+/*
+ Copyright (c) 2011, Micael Hildenborg
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of Micael Hildenborg nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY Micael Hildenborg ''AS IS'' AND ANY
+ EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL Micael Hildenborg BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SHA1_DEFINED
+#define SHA1_DEFINED
+
+namespace sha1
+{
+
+ /**
+ @param src points to any kind of data to be hashed.
+ @param bytelength the number of bytes to hash from the src pointer.
+ @param hash should point to a buffer of at least 20 bytes of size for storing the sha1 result in.
+ */
+ void calc(const void* src, const int bytelength, unsigned char* hash);
+
+ /**
+ @param hash is 20 bytes of sha1 hash. This is the same data that is the result from the calc function.
+ @param hexstring should point to a buffer of at least 41 bytes of size for storing the hexadecimal representation of the hash. A zero will be written at position 40, so the buffer will be a valid zero ended string.
+ */
+ void toHexString(const unsigned char* hash, char* hexstring);
+
+} // namespace sha1
+
+#endif // SHA1_DEFINED
diff --git a/audio/softsynth/opl/dbopl.cpp b/audio/softsynth/opl/dbopl.cpp
index 43eb40e7ba3..19be94cf698 100644
--- a/audio/softsynth/opl/dbopl.cpp
+++ b/audio/softsynth/opl/dbopl.cpp
@@ -413,6 +413,7 @@ Bits Operator::TemplateVolume( ) {
return vol;
}
//In sustain phase, but not sustaining, do regular release
+ //fall through
case RELEASE:
vol += RateForward( releaseAdd );
if ( GCC_UNLIKELY(vol >= ENV_MAX) ) {
diff --git a/backends/cloud/box/boxstorage.cpp b/backends/cloud/box/boxstorage.cpp
index 598ca55c15c..fb01521016d 100644
--- a/backends/cloud/box/boxstorage.cpp
+++ b/backends/cloud/box/boxstorage.cpp
@@ -67,8 +67,8 @@ void BoxStorage::loadKeyAndSecret() {
#endif
}
-BoxStorage::BoxStorage(Common::String accessToken, Common::String refreshToken):
- _token(accessToken), _refreshToken(refreshToken) {}
+BoxStorage::BoxStorage(Common::String token, Common::String refreshToken):
+ _token(token), _refreshToken(refreshToken) {}
BoxStorage::BoxStorage(Common::String code) {
getAccessToken(
@@ -191,36 +191,36 @@ void BoxStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networking
return;
}
- Common::JSONObject info = json->asObject();
+ Common::JSONObject jsonInfo = json->asObject();
- Common::String uid, name, email;
+ Common::String uid, displayName, email;
uint64 quotaUsed = 0, quotaAllocated = 0;
// can check that "type": "user"
// there is also "max_upload_size", "phone" and "avatar_url"
- if (Networking::CurlJsonRequest::jsonContainsString(info, "id", "BoxStorage::infoInnerCallback"))
- uid = info.getVal("id")->asString();
+ if (Networking::CurlJsonRequest::jsonContainsString(jsonInfo, "id", "BoxStorage::infoInnerCallback"))
+ uid = jsonInfo.getVal("id")->asString();
- if (Networking::CurlJsonRequest::jsonContainsString(info, "name", "BoxStorage::infoInnerCallback"))
- name = info.getVal("name")->asString();
+ if (Networking::CurlJsonRequest::jsonContainsString(jsonInfo, "name", "BoxStorage::infoInnerCallback"))
+ displayName = jsonInfo.getVal("name")->asString();
- if (Networking::CurlJsonRequest::jsonContainsString(info, "login", "BoxStorage::infoInnerCallback"))
- email = info.getVal("login")->asString();
+ if (Networking::CurlJsonRequest::jsonContainsString(jsonInfo, "login", "BoxStorage::infoInnerCallback"))
+ email = jsonInfo.getVal("login")->asString();
- if (Networking::CurlJsonRequest::jsonContainsIntegerNumber(info, "space_amount", "BoxStorage::infoInnerCallback"))
- quotaAllocated = info.getVal("space_amount")->asIntegerNumber();
+ if (Networking::CurlJsonRequest::jsonContainsIntegerNumber(jsonInfo, "space_amount", "BoxStorage::infoInnerCallback"))
+ quotaAllocated = jsonInfo.getVal("space_amount")->asIntegerNumber();
- if (Networking::CurlJsonRequest::jsonContainsIntegerNumber(info, "space_used", "BoxStorage::infoInnerCallback"))
- quotaUsed = info.getVal("space_used")->asIntegerNumber();
+ if (Networking::CurlJsonRequest::jsonContainsIntegerNumber(jsonInfo, "space_used", "BoxStorage::infoInnerCallback"))
+ quotaUsed = jsonInfo.getVal("space_used")->asIntegerNumber();
Common::String username = email;
- if (username == "") username = name;
+ if (username == "") username = displayName;
if (username == "") username = uid;
CloudMan.setStorageUsername(kStorageBoxId, username);
if (outerCallback) {
- (*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, name, email, quotaUsed, quotaAllocated)));
+ (*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, displayName, email, quotaUsed, quotaAllocated)));
delete outerCallback;
}
@@ -245,8 +245,8 @@ void BoxStorage::createDirectoryInnerCallback(BoolCallback outerCallback, Networ
if (outerCallback) {
if (Networking::CurlJsonRequest::jsonIsObject(json, "BoxStorage::createDirectoryInnerCallback")) {
- Common::JSONObject info = json->asObject();
- (*outerCallback)(BoolResponse(nullptr, info.contains("id")));
+ Common::JSONObject jsonInfo = json->asObject();
+ (*outerCallback)(BoolResponse(nullptr, jsonInfo.contains("id")));
} else {
(*outerCallback)(BoolResponse(nullptr, false));
}
@@ -256,7 +256,7 @@ void BoxStorage::createDirectoryInnerCallback(BoolCallback outerCallback, Networ
delete json;
}
-Networking::Request *BoxStorage::createDirectoryWithParentId(Common::String parentId, Common::String name, BoolCallback callback, Networking::ErrorCallback errorCallback) {
+Networking::Request *BoxStorage::createDirectoryWithParentId(Common::String parentId, Common::String directoryName, BoolCallback callback, Networking::ErrorCallback errorCallback) {
if (!errorCallback)
errorCallback = getErrorPrintingCallback();
@@ -270,7 +270,7 @@ Networking::Request *BoxStorage::createDirectoryWithParentId(Common::String pare
parentObject.setVal("id", new Common::JSONValue(parentId));
Common::JSONObject jsonRequestParameters;
- jsonRequestParameters.setVal("name", new Common::JSONValue(name));
+ jsonRequestParameters.setVal("name", new Common::JSONValue(directoryName));
jsonRequestParameters.setVal("parent", new Common::JSONValue(parentObject));
Common::JSONValue value(jsonRequestParameters);
diff --git a/backends/cloud/box/boxstorage.h b/backends/cloud/box/boxstorage.h
index 27bf60e39b4..a641669b2a6 100644
--- a/backends/cloud/box/boxstorage.h
+++ b/backends/cloud/box/boxstorage.h
@@ -74,7 +74,7 @@ public:
/** Public Cloud API comes down there. */
virtual Networking::Request *listDirectoryById(Common::String id, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback);
- virtual Networking::Request *createDirectoryWithParentId(Common::String parentId, Common::String name, BoolCallback callback, Networking::ErrorCallback errorCallback);
+ virtual Networking::Request *createDirectoryWithParentId(Common::String parentId, Common::String directoryName, BoolCallback callback, Networking::ErrorCallback errorCallback);
/** Returns UploadStatus struct with info about uploaded file. */
virtual Networking::Request *upload(Common::String remotePath, Common::String localPath, UploadCallback callback, Networking::ErrorCallback errorCallback);
diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index 5f7d6942cad..5519e4baab3 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -253,19 +253,18 @@ void CloudManager::setStorageLastSync(uint32 index, Common::String date) {
void CloudManager::connectStorage(uint32 index, Common::String code) {
freeStorages();
- Storage *storage = nullptr;
switch (index) {
case kStorageDropboxId:
- storage = new Dropbox::DropboxStorage(code);
+ new Dropbox::DropboxStorage(code);
break;
case kStorageOneDriveId:
- storage = new OneDrive::OneDriveStorage(code);
+ new OneDrive::OneDriveStorage(code);
break;
case kStorageGoogleDriveId:
- storage = new GoogleDrive::GoogleDriveStorage(code);
+ new GoogleDrive::GoogleDriveStorage(code);
break;
case kStorageBoxId:
- storage = new Box::BoxStorage(code);
+ new Box::BoxStorage(code);
break;
}
// in these constructors Storages request token using the passed code
diff --git a/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp b/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
index 411e367a9dd..d6937d9cb2a 100644
--- a/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
+++ b/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
@@ -52,8 +52,8 @@ void DropboxCreateDirectoryRequest::start() {
_ignoreCallback = false;
Networking::JsonCallback innerCallback = new Common::Callback(this, &DropboxCreateDirectoryRequest::responseCallback);
- Networking::ErrorCallback errorCallback = new Common::Callback(this, &DropboxCreateDirectoryRequest::errorCallback);
- Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, DROPBOX_API_CREATE_FOLDER);
+ Networking::ErrorCallback errorResponseCallback = new Common::Callback(this, &DropboxCreateDirectoryRequest::errorCallback);
+ Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorResponseCallback, DROPBOX_API_CREATE_FOLDER);
request->addHeader("Authorization: Bearer " + _token);
request->addHeader("Content-Type: application/json");
diff --git a/backends/cloud/dropbox/dropboxinforequest.cpp b/backends/cloud/dropbox/dropboxinforequest.cpp
index 3d82c8d34be..ebf2d12170e 100644
--- a/backends/cloud/dropbox/dropboxinforequest.cpp
+++ b/backends/cloud/dropbox/dropboxinforequest.cpp
@@ -54,8 +54,8 @@ void DropboxInfoRequest::start() {
_ignoreCallback = false;
Networking::JsonCallback innerCallback = new Common::Callback(this, &DropboxInfoRequest::userResponseCallback);
- Networking::ErrorCallback errorCallback = new Common::Callback(this, &DropboxInfoRequest::errorCallback);
- Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, DROPBOX_API_GET_CURRENT_ACCOUNT);
+ Networking::ErrorCallback errorResponseCallback = new Common::Callback(this, &DropboxInfoRequest::errorCallback);
+ Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorResponseCallback, DROPBOX_API_GET_CURRENT_ACCOUNT);
request->addHeader("Authorization: Bearer " + _token);
request->addHeader("Content-Type: application/json");
request->addPostField("null"); //use POST
@@ -108,8 +108,8 @@ void DropboxInfoRequest::userResponseCallback(Networking::JsonResponse response)
delete json;
Networking::JsonCallback innerCallback = new Common::Callback(this, &DropboxInfoRequest::quotaResponseCallback);
- Networking::ErrorCallback errorCallback = new Common::Callback(this, &DropboxInfoRequest::errorCallback);
- Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, DROPBOX_API_GET_SPACE_USAGE);
+ Networking::ErrorCallback errorResponseCallback = new Common::Callback(this, &DropboxInfoRequest::errorCallback);
+ Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorResponseCallback, DROPBOX_API_GET_SPACE_USAGE);
request->addHeader("Authorization: Bearer " + _token);
request->addHeader("Content-Type: application/json");
request->addPostField("null"); //use POST
diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index ec4c84400ca..c00d9549669 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -68,8 +68,8 @@ void GoogleDriveStorage::loadKeyAndSecret() {
#endif
}
-GoogleDriveStorage::GoogleDriveStorage(Common::String accessToken, Common::String refreshToken):
- _token(accessToken), _refreshToken(refreshToken) {}
+GoogleDriveStorage::GoogleDriveStorage(Common::String token, Common::String refreshToken):
+ _token(token), _refreshToken(refreshToken) {}
GoogleDriveStorage::GoogleDriveStorage(Common::String code) {
getAccessToken(
@@ -192,28 +192,28 @@ void GoogleDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Ne
return;
}
- Common::JSONObject info = json->asObject();
+ Common::JSONObject jsonInfo = json->asObject();
- Common::String uid, name, email;
+ Common::String uid, displayName, email;
uint64 quotaUsed = 0, quotaAllocated = 0;
- if (Networking::CurlJsonRequest::jsonContainsAttribute(info, "user", "GoogleDriveStorage::infoInnerCallback") &&
- Networking::CurlJsonRequest::jsonIsObject(info.getVal("user"), "GoogleDriveStorage::infoInnerCallback")) {
+ if (Networking::CurlJsonRequest::jsonContainsAttribute(jsonInfo, "user", "GoogleDriveStorage::infoInnerCallback") &&
+ Networking::CurlJsonRequest::jsonIsObject(jsonInfo.getVal("user"), "GoogleDriveStorage::infoInnerCallback")) {
//"me":true, "kind":"drive#user","photoLink": "",
//"displayName":"Alexander Tkachev","emailAddress":"alexander@tkachov.ru","permissionId":""
- Common::JSONObject user = info.getVal("user")->asObject();
+ Common::JSONObject user = jsonInfo.getVal("user")->asObject();
if (Networking::CurlJsonRequest::jsonContainsString(user, "permissionId", "GoogleDriveStorage::infoInnerCallback"))
uid = user.getVal("permissionId")->asString(); //not sure it's user's id, but who cares anyway?
if (Networking::CurlJsonRequest::jsonContainsString(user, "displayName", "GoogleDriveStorage::infoInnerCallback"))
- name = user.getVal("displayName")->asString();
+ displayName = user.getVal("displayName")->asString();
if (Networking::CurlJsonRequest::jsonContainsString(user, "emailAddress", "GoogleDriveStorage::infoInnerCallback"))
email = user.getVal("emailAddress")->asString();
}
- if (Networking::CurlJsonRequest::jsonContainsAttribute(info, "storageQuota", "GoogleDriveStorage::infoInnerCallback") &&
- Networking::CurlJsonRequest::jsonIsObject(info.getVal("storageQuota"), "GoogleDriveStorage::infoInnerCallback")) {
+ if (Networking::CurlJsonRequest::jsonContainsAttribute(jsonInfo, "storageQuota", "GoogleDriveStorage::infoInnerCallback") &&
+ Networking::CurlJsonRequest::jsonIsObject(jsonInfo.getVal("storageQuota"), "GoogleDriveStorage::infoInnerCallback")) {
//"usageInDrive":"6332462","limit":"18253611008","usage":"6332462","usageInDriveTrash":"0"
- Common::JSONObject storageQuota = info.getVal("storageQuota")->asObject();
+ Common::JSONObject storageQuota = jsonInfo.getVal("storageQuota")->asObject();
if (Networking::CurlJsonRequest::jsonContainsString(storageQuota, "usage", "GoogleDriveStorage::infoInnerCallback")) {
Common::String usage = storageQuota.getVal("usage")->asString();
@@ -229,7 +229,7 @@ void GoogleDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Ne
CloudMan.setStorageUsername(kStorageGoogleDriveId, email);
if (outerCallback) {
- (*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, name, email, quotaUsed, quotaAllocated)));
+ (*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, displayName, email, quotaUsed, quotaAllocated)));
delete outerCallback;
}
@@ -246,8 +246,8 @@ void GoogleDriveStorage::createDirectoryInnerCallback(BoolCallback outerCallback
if (outerCallback) {
if (Networking::CurlJsonRequest::jsonIsObject(json, "GoogleDriveStorage::createDirectoryInnerCallback")) {
- Common::JSONObject info = json->asObject();
- (*outerCallback)(BoolResponse(nullptr, info.contains("id")));
+ Common::JSONObject jsonInfo = json->asObject();
+ (*outerCallback)(BoolResponse(nullptr, jsonInfo.contains("id")));
} else {
(*outerCallback)(BoolResponse(nullptr, false));
}
@@ -289,7 +289,7 @@ void GoogleDriveStorage::printInfo(StorageInfoResponse response) {
debug(9, "\tdisk usage: %lu/%lu", response.value.used(), response.value.available());
}
-Networking::Request *GoogleDriveStorage::createDirectoryWithParentId(Common::String parentId, Common::String name, BoolCallback callback, Networking::ErrorCallback errorCallback) {
+Networking::Request *GoogleDriveStorage::createDirectoryWithParentId(Common::String parentId, Common::String directoryName, BoolCallback callback, Networking::ErrorCallback errorCallback) {
if (!errorCallback)
errorCallback = getErrorPrintingCallback();
@@ -304,7 +304,7 @@ Networking::Request *GoogleDriveStorage::createDirectoryWithParentId(Common::Str
Common::JSONObject jsonRequestParameters;
jsonRequestParameters.setVal("mimeType", new Common::JSONValue("application/vnd.google-apps.folder"));
- jsonRequestParameters.setVal("name", new Common::JSONValue(name));
+ jsonRequestParameters.setVal("name", new Common::JSONValue(directoryName));
jsonRequestParameters.setVal("parents", new Common::JSONValue(parentsArray));
Common::JSONValue value(jsonRequestParameters);
diff --git a/backends/cloud/googledrive/googledrivestorage.h b/backends/cloud/googledrive/googledrivestorage.h
index 457369dc903..d0585bc403b 100644
--- a/backends/cloud/googledrive/googledrivestorage.h
+++ b/backends/cloud/googledrive/googledrivestorage.h
@@ -86,7 +86,7 @@ public:
virtual Networking::Request *streamFileById(Common::String id, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback);
/** Calls the callback when finished. */
- virtual Networking::Request *createDirectoryWithParentId(Common::String parentId, Common::String name, BoolCallback callback, Networking::ErrorCallback errorCallback);
+ virtual Networking::Request *createDirectoryWithParentId(Common::String parentId, Common::String directoryName, BoolCallback callback, Networking::ErrorCallback errorCallback);
/** Returns the StorageInfo struct. */
virtual Networking::Request *info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback);
diff --git a/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
index da67266d12a..74cf3208e38 100644
--- a/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
+++ b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
@@ -72,8 +72,8 @@ void OneDriveCreateDirectoryRequest::start() {
url += ":/" + ConnMan.urlEncode(parent) + ":";
url += "/children";
Networking::JsonCallback innerCallback = new Common::Callback(this, &OneDriveCreateDirectoryRequest::responseCallback);
- Networking::ErrorCallback errorCallback = new Common::Callback(this, &OneDriveCreateDirectoryRequest::errorCallback);
- Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(_storage, innerCallback, errorCallback, url.c_str());
+ Networking::ErrorCallback errorResponseCallback = new Common::Callback(this, &OneDriveCreateDirectoryRequest::errorCallback);
+ Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(_storage, innerCallback, errorResponseCallback, url.c_str());
request->addHeader("Authorization: Bearer " + _storage->accessToken());
request->addHeader("Content-Type: application/json");
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index 8799f3d69f6..14e803d980e 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -67,8 +67,8 @@ void OneDriveStorage::loadKeyAndSecret() {
#endif
}
-OneDriveStorage::OneDriveStorage(Common::String accessToken, Common::String userId, Common::String refreshToken):
- _token(accessToken), _uid(userId), _refreshToken(refreshToken) {}
+OneDriveStorage::OneDriveStorage(Common::String token, Common::String uid, Common::String refreshToken):
+ _token(token), _uid(uid), _refreshToken(refreshToken) {}
OneDriveStorage::OneDriveStorage(Common::String code) {
getAccessToken(
@@ -193,35 +193,35 @@ void OneDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Netwo
return;
}
- Common::JSONObject info = json->asObject();
+ Common::JSONObject jsonInfo = json->asObject();
- Common::String uid, name, email;
+ Common::String uid, displayName, email;
uint64 quotaUsed = 0, quotaAllocated = 26843545600LL; // 25 GB, because I actually don't know any way to find out the real one
- if (Networking::CurlJsonRequest::jsonContainsObject(info, "createdBy", "OneDriveStorage::infoInnerCallback")) {
- Common::JSONObject createdBy = info.getVal("createdBy")->asObject();
+ if (Networking::CurlJsonRequest::jsonContainsObject(jsonInfo, "createdBy", "OneDriveStorage::infoInnerCallback")) {
+ Common::JSONObject createdBy = jsonInfo.getVal("createdBy")->asObject();
if (Networking::CurlJsonRequest::jsonContainsObject(createdBy, "user", "OneDriveStorage::infoInnerCallback")) {
Common::JSONObject user = createdBy.getVal("user")->asObject();
if (Networking::CurlJsonRequest::jsonContainsString(user, "id", "OneDriveStorage::infoInnerCallback"))
uid = user.getVal("id")->asString();
if (Networking::CurlJsonRequest::jsonContainsString(user, "displayName", "OneDriveStorage::infoInnerCallback"))
- name = user.getVal("displayName")->asString();
+ displayName = user.getVal("displayName")->asString();
}
}
- if (Networking::CurlJsonRequest::jsonContainsIntegerNumber(info, "size", "OneDriveStorage::infoInnerCallback")) {
- quotaUsed = info.getVal("size")->asIntegerNumber();
+ if (Networking::CurlJsonRequest::jsonContainsIntegerNumber(jsonInfo, "size", "OneDriveStorage::infoInnerCallback")) {
+ quotaUsed = jsonInfo.getVal("size")->asIntegerNumber();
}
Common::String username = email;
if (username == "")
- username = name;
+ username = displayName;
if (username == "")
username = uid;
CloudMan.setStorageUsername(kStorageOneDriveId, username);
if (outerCallback) {
- (*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, name, email, quotaUsed, quotaAllocated)));
+ (*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, displayName, email, quotaUsed, quotaAllocated)));
delete outerCallback;
}
diff --git a/backends/cloud/storagefile.cpp b/backends/cloud/storagefile.cpp
index 2428d8ff802..90ec7da1205 100644
--- a/backends/cloud/storagefile.cpp
+++ b/backends/cloud/storagefile.cpp
@@ -56,10 +56,10 @@ StorageFile::StorageFile(Common::String pth, uint32 sz, uint32 ts, bool dir) {
_isDirectory = dir;
}
-StorageFile::StorageFile(Common::String id, Common::String path, Common::String name, uint32 sz, uint32 ts, bool dir) {
- _id = id;
- _path = path;
- _name = name;
+StorageFile::StorageFile(Common::String fileId, Common::String filePath, Common::String fileName, uint32 sz, uint32 ts, bool dir) {
+ _id = fileId;
+ _path = filePath;
+ _name = fileName;
_size = sz;
_timestamp = ts;
_isDirectory = dir;
diff --git a/backends/cloud/storagefile.h b/backends/cloud/storagefile.h
index 44f16c36893..c29ac77dd11 100644
--- a/backends/cloud/storagefile.h
+++ b/backends/cloud/storagefile.h
@@ -48,7 +48,7 @@ class StorageFile {
public:
StorageFile(); //invalid empty file
StorageFile(Common::String pth, uint32 sz, uint32 ts, bool dir);
- StorageFile(Common::String id, Common::String path, Common::String name, uint32 sz, uint32 ts, bool dir);
+ StorageFile(Common::String fileId, Common::String filePath, Common::String fileName, uint32 sz, uint32 ts, bool dir);
Common::String id() const { return _id; }
Common::String path() const { return _path; }
diff --git a/backends/events/ps3sdl/ps3sdl-events.cpp b/backends/events/ps3sdl/ps3sdl-events.cpp
index 1fc10559c25..01cdc2f0f63 100644
--- a/backends/events/ps3sdl/ps3sdl-events.cpp
+++ b/backends/events/ps3sdl/ps3sdl-events.cpp
@@ -60,11 +60,11 @@ bool PS3SdlEventSource::handleJoyButtonDown(SDL_Event &ev, Common::Event &event)
switch (ev.jbutton.button) {
case BTN_CROSS: // Left mouse button
event.type = Common::EVENT_LBUTTONDOWN;
- processMouseEvent(event, _km.x, _km.y);
+ processMouseEvent(event, _km.x / MULTIPLIER, _km.y / MULTIPLIER);
break;
case BTN_CIRCLE: // Right mouse button
event.type = Common::EVENT_RBUTTONDOWN;
- processMouseEvent(event, _km.x, _km.y);
+ processMouseEvent(event, _km.x / MULTIPLIER, _km.y / MULTIPLIER);
break;
case BTN_TRIANGLE: // Game menu
event.type = Common::EVENT_KEYDOWN;
@@ -98,11 +98,11 @@ bool PS3SdlEventSource::handleJoyButtonUp(SDL_Event &ev, Common::Event &event) {
switch (ev.jbutton.button) {
case BTN_CROSS: // Left mouse button
event.type = Common::EVENT_LBUTTONUP;
- processMouseEvent(event, _km.x, _km.y);
+ processMouseEvent(event, _km.x / MULTIPLIER, _km.y / MULTIPLIER);
break;
case BTN_CIRCLE: // Right mouse button
event.type = Common::EVENT_RBUTTONUP;
- processMouseEvent(event, _km.x, _km.y);
+ processMouseEvent(event, _km.x / MULTIPLIER, _km.y / MULTIPLIER);
break;
case BTN_TRIANGLE: // Game menu
event.type = Common::EVENT_KEYUP;
diff --git a/backends/events/sdl/sdl-events.cpp b/backends/events/sdl/sdl-events.cpp
index 439689b8ccb..e710fdf9e13 100644
--- a/backends/events/sdl/sdl-events.cpp
+++ b/backends/events/sdl/sdl-events.cpp
@@ -40,6 +40,10 @@
#endif
#endif
+#ifdef JOY_ANALOG
+#include "math.h"
+#endif
+
// FIXME move joystick defines out and replace with confile file options
// we should really allow users to map any key to a joystick button
#define JOY_DEADZONE 3200
@@ -186,17 +190,18 @@ void SdlEventSource::processMouseEvent(Common::Event &event, int x, int y, int r
_graphicsManager->notifyMousePos(Common::Point(x, y));
_graphicsManager->transformMouseCoordinates(event.mouse);
}
-
- // Update the "keyboard mouse" coords
- _km.x = x;
- _km.y = y;
}
-void SdlEventSource::handleKbdMouse() {
+bool SdlEventSource::handleKbdMouse(Common::Event &event) {
+ // returns true if an event is generated
// Skip recording of these events
uint32 curTime = g_system->getMillis(true);
if (curTime >= _km.last_time + _km.delay_time) {
+
+ int16 oldKmX = _km.x;
+ int16 oldKmY = _km.y;
+
_km.last_time = curTime;
if (_km.x_down_count == 1) {
_km.x_down_time = curTime;
@@ -209,61 +214,119 @@ void SdlEventSource::handleKbdMouse() {
if (_km.x_vel || _km.y_vel) {
if (_km.x_down_count) {
- if (curTime > _km.x_down_time + _km.delay_time * 12) {
+ if (curTime > _km.x_down_time + 300) {
if (_km.x_vel > 0)
- _km.x_vel++;
+ _km.x_vel += MULTIPLIER;
else
- _km.x_vel--;
- } else if (curTime > _km.x_down_time + _km.delay_time * 8) {
+ _km.x_vel -= MULTIPLIER;
+ } else if (curTime > _km.x_down_time + 200) {
if (_km.x_vel > 0)
- _km.x_vel = 5;
+ _km.x_vel = 5 * MULTIPLIER;
else
- _km.x_vel = -5;
+ _km.x_vel = -5 * MULTIPLIER;
}
}
if (_km.y_down_count) {
- if (curTime > _km.y_down_time + _km.delay_time * 12) {
+ if (curTime > _km.y_down_time + 300) {
if (_km.y_vel > 0)
- _km.y_vel++;
+ _km.y_vel += MULTIPLIER;
else
- _km.y_vel--;
- } else if (curTime > _km.y_down_time + _km.delay_time * 8) {
+ _km.y_vel -= MULTIPLIER;
+ } else if (curTime > _km.y_down_time + 200) {
if (_km.y_vel > 0)
- _km.y_vel = 5;
+ _km.y_vel = 5 * MULTIPLIER;
else
- _km.y_vel = -5;
+ _km.y_vel = -5 * MULTIPLIER;
}
}
- _km.x += _km.x_vel;
- _km.y += _km.y_vel;
+ int16 speedFactor = 25;
+
+ if (g_system->hasFeature(OSystem::kFeatureKbdMouseSpeed)) {
+ switch (ConfMan.getInt("kbdmouse_speed")) {
+ // 0.25 keyboard pointer speed
+ case 0:
+ speedFactor = 100;
+ break;
+ // 0.5 speed
+ case 1:
+ speedFactor = 50;
+ break;
+ // 0.75 speed
+ case 2:
+ speedFactor = 33;
+ break;
+ // 1.0 speed
+ case 3:
+ speedFactor = 25;
+ break;
+ // 1.25 speed
+ case 4:
+ speedFactor = 20;
+ break;
+ // 1.5 speed
+ case 5:
+ speedFactor = 17;
+ break;
+ // 1.75 speed
+ case 6:
+ speedFactor = 14;
+ break;
+ // 2.0 speed
+ case 7:
+ speedFactor = 12;
+ break;
+ default:
+ speedFactor = 25;
+ }
+ }
+
+ // - The modifier key makes the mouse movement slower
+ // - The extra factor "delay/speedFactor" ensures velocities
+ // are independent of the kbdMouse update rate
+ // - all velocities were originally chosen
+ // at a delay of 25, so that is the reference used here
+ // - note: operator order is important to avoid overflow
+ if (_km.modifier) {
+ _km.x += ((_km.x_vel / 10) * ((int16)_km.delay_time)) / speedFactor;
+ _km.y += ((_km.y_vel / 10) * ((int16)_km.delay_time)) / speedFactor;
+ } else {
+ _km.x += (_km.x_vel * ((int16)_km.delay_time)) / speedFactor;
+ _km.y += (_km.y_vel * ((int16)_km.delay_time)) / speedFactor;
+ }
if (_km.x < 0) {
_km.x = 0;
- _km.x_vel = -1;
+ _km.x_vel = -1 * MULTIPLIER;
_km.x_down_count = 1;
- } else if (_km.x > _km.x_max) {
- _km.x = _km.x_max;
- _km.x_vel = 1;
+ } else if (_km.x > _km.x_max * MULTIPLIER) {
+ _km.x = _km.x_max * MULTIPLIER;
+ _km.x_vel = 1 * MULTIPLIER;
_km.x_down_count = 1;
}
if (_km.y < 0) {
_km.y = 0;
- _km.y_vel = -1;
+ _km.y_vel = -1 * MULTIPLIER;
_km.y_down_count = 1;
- } else if (_km.y > _km.y_max) {
- _km.y = _km.y_max;
- _km.y_vel = 1;
+ } else if (_km.y > _km.y_max * MULTIPLIER) {
+ _km.y = _km.y_max * MULTIPLIER;
+ _km.y_vel = 1 * MULTIPLIER;
_km.y_down_count = 1;
}
- // ResidualVM: disable wrap mouse for now, it's really annoying
if (_graphicsManager) {
- //_graphicsManager->getWindow()->warpMouseInWindow((Uint16)_km.x, (Uint16)_km.y);
+ _graphicsManager->getWindow()->warpMouseInWindow((Uint16)(_km.x / MULTIPLIER), (Uint16)(_km.y / MULTIPLIER));
+ }
+
+ if (_km.x != oldKmX || _km.y != oldKmY) {
+ event.type = Common::EVENT_MOUSEMOVE;
+ processMouseEvent(event, _km.x / MULTIPLIER, _km.y / MULTIPLIER);
+ return true;
}
}
}
+ return false;
}
void SdlEventSource::SDLModToOSystemKeyFlags(SDLMod mod, Common::Event &event) {
@@ -439,7 +502,6 @@ Common::KeyCode SdlEventSource::SDLToOSystemKeycode(const SDLKey key) {
}
bool SdlEventSource::pollEvent(Common::Event &event) {
- handleKbdMouse();
#if SDL_VERSION_ATLEAST(2, 0, 0)
// In case we still need to send a key up event for a key down from a
@@ -465,6 +527,12 @@ bool SdlEventSource::pollEvent(Common::Event &event) {
if (dispatchSDLEvent(ev, event))
return true;
}
+
+ // Handle mouse control via analog joystick and keyboard
+ if (handleKbdMouse(event)) {
+ return true;
+ }
+
return false;
}
@@ -490,16 +558,11 @@ bool SdlEventSource::dispatchSDLEvent(SDL_Event &ev, Common::Event &event) {
#if SDL_VERSION_ATLEAST(2, 0, 0)
case SDL_MOUSEWHEEL: {
Sint32 yDir = ev.wheel.y;
-#if SDL_VERSION_ATLEAST(2, 0, 4)
- if (ev.wheel.direction == SDL_MOUSEWHEEL_FLIPPED) {
- yDir *= -1;
- }
-#endif
// HACK: It seems we want the mouse coordinates supplied
// with a mouse wheel event. However, SDL2 does not supply
// these, thus we use whatever we got last time. It seems
// these are always stored in _km.x, _km.y.
- processMouseEvent(event, _km.x, _km.y);
+ processMouseEvent(event, _km.x / MULTIPLIER, _km.y / MULTIPLIER);
if (yDir < 0) {
event.type = Common::EVENT_WHEELDOWN;
return true;
@@ -539,7 +602,18 @@ bool SdlEventSource::dispatchSDLEvent(SDL_Event &ev, Common::Event &event) {
_graphicsManager->notifyVideoExpose();
return false;
- case SDL_WINDOWEVENT_RESIZED:
+ // SDL2 documentation indicate that SDL_WINDOWEVENT_SIZE_CHANGED is sent either as a result
+ // of the size being changed by an external event (for example the user resizing the window
+ // or going fullscreen) or a call to the SDL API (for example SDL_SetWindowSize). On the
+ // other hand SDL_WINDOWEVENT_RESIZED is only sent for resize resulting from an external event,
+ // and is always preceded by a SDL_WINDOWEVENT_SIZE_CHANGED event.
+ // We need to handle the programmatic resize as well so that the graphics manager always know
+ // the current size. See comments in SdlWindow::createOrUpdateWindow for details of one case
+ // where we need to call SDL_SetWindowSize and we need the resulting event to be processed.
+ // However if the documentation is correct we can ignore SDL_WINDOWEVENT_RESIZED since when we
+ // get one we should always get a SDL_WINDOWEVENT_SIZE_CHANGED as well.
+ case SDL_WINDOWEVENT_SIZE_CHANGED:
+ //case SDL_WINDOWEVENT_RESIZED:
return handleResizeEvent(event, ev.window.data1, ev.window.data2);
default:
@@ -680,6 +754,9 @@ bool SdlEventSource::handleKeyUp(SDL_Event &ev, Common::Event &event) {
bool SdlEventSource::handleMouseMotion(SDL_Event &ev, Common::Event &event) {
event.type = Common::EVENT_MOUSEMOVE;
processMouseEvent(event, ev.motion.x, ev.motion.y, ev.motion.xrel, ev.motion.yrel); // ResidualVM xrel,yrel
+ // update KbdMouse
+ _km.x = ev.motion.x * MULTIPLIER;
+ _km.y = ev.motion.y * MULTIPLIER;
return true;
}
@@ -703,6 +780,9 @@ bool SdlEventSource::handleMouseButtonDown(SDL_Event &ev, Common::Event &event)
return false;
processMouseEvent(event, ev.button.x, ev.button.y);
+ // update KbdMouse
+ _km.x = ev.button.x * MULTIPLIER;
+ _km.y = ev.button.y * MULTIPLIER;
return true;
}
@@ -719,6 +799,9 @@ bool SdlEventSource::handleMouseButtonUp(SDL_Event &ev, Common::Event &event) {
else
return false;
processMouseEvent(event, ev.button.x, ev.button.y);
+ // update KbdMouse
+ _km.x = ev.button.x * MULTIPLIER;
+ _km.y = ev.button.y * MULTIPLIER;
return true;
}
@@ -726,10 +809,10 @@ bool SdlEventSource::handleMouseButtonUp(SDL_Event &ev, Common::Event &event) {
bool SdlEventSource::handleJoyButtonDown(SDL_Event &ev, Common::Event &event) {
if (ev.jbutton.button == JOY_BUT_LMOUSE) {
event.type = Common::EVENT_LBUTTONDOWN;
- processMouseEvent(event, _km.x, _km.y);
+ processMouseEvent(event, _km.x / MULTIPLIER, _km.y / MULTIPLIER);
} else if (ev.jbutton.button == JOY_BUT_RMOUSE) {
event.type = Common::EVENT_RBUTTONDOWN;
- processMouseEvent(event, _km.x, _km.y);
+ processMouseEvent(event, _km.x / MULTIPLIER, _km.y / MULTIPLIER);
} else {
event.type = Common::EVENT_KEYDOWN;
switch (ev.jbutton.button) {
@@ -757,10 +840,10 @@ bool SdlEventSource::handleJoyButtonDown(SDL_Event &ev, Common::Event &event) {
bool SdlEventSource::handleJoyButtonUp(SDL_Event &ev, Common::Event &event) {
if (ev.jbutton.button == JOY_BUT_LMOUSE) {
event.type = Common::EVENT_LBUTTONUP;
- processMouseEvent(event, _km.x, _km.y);
+ processMouseEvent(event, _km.x / MULTIPLIER, _km.y / MULTIPLIER);
} else if (ev.jbutton.button == JOY_BUT_RMOUSE) {
event.type = Common::EVENT_RBUTTONUP;
- processMouseEvent(event, _km.x, _km.y);
+ processMouseEvent(event, _km.x / MULTIPLIER, _km.y / MULTIPLIER);
} else {
event.type = Common::EVENT_KEYUP;
switch (ev.jbutton.button) {
@@ -786,23 +869,26 @@ bool SdlEventSource::handleJoyButtonUp(SDL_Event &ev, Common::Event &event) {
}
bool SdlEventSource::handleJoyAxisMotion(SDL_Event &ev, Common::Event &event) {
+
int axis = ev.jaxis.value;
+#ifdef JOY_ANALOG
+ // conversion factor between keyboard mouse and joy axis value
+ int vel_to_axis = (1500 / MULTIPLIER);
+#else
if (axis > JOY_DEADZONE) {
axis -= JOY_DEADZONE;
- event.type = Common::EVENT_MOUSEMOVE;
} else if (axis < -JOY_DEADZONE) {
axis += JOY_DEADZONE;
- event.type = Common::EVENT_MOUSEMOVE;
} else
axis = 0;
+#endif
if (ev.jaxis.axis == JOY_XAXIS) {
#ifdef JOY_ANALOG
- _km.x_vel = axis / 2000;
- _km.x_down_count = 0;
+ _km.joy_x = axis;
#else
if (axis != 0) {
- _km.x_vel = (axis > 0) ? 1:-1;
+ _km.x_vel = (axis > 0) ? 1 * MULTIPLIER:-1 * MULTIPLIER;
_km.x_down_count = 1;
} else {
_km.x_vel = 0;
@@ -814,11 +900,10 @@ bool SdlEventSource::handleJoyAxisMotion(SDL_Event &ev, Common::Event &event) {
axis = -axis;
#endif
#ifdef JOY_ANALOG
- _km.y_vel = -axis / 2000;
- _km.y_down_count = 0;
+ _km.joy_y = -axis;
#else
if (axis != 0) {
- _km.y_vel = (-axis > 0) ? 1: -1;
+ _km.y_vel = (-axis > 0) ? 1 * MULTIPLIER: -1 * MULTIPLIER;
_km.y_down_count = 1;
} else {
_km.y_vel = 0;
@@ -826,10 +911,31 @@ bool SdlEventSource::handleJoyAxisMotion(SDL_Event &ev, Common::Event &event) {
}
#endif
}
+#ifdef JOY_ANALOG
+ // radial and scaled analog joystick deadzone
+ float analogX = (float)_km.joy_x;
+ float analogY = (float)_km.joy_y;
+ float deadZone = (float)JOY_DEADZONE;
+ if (g_system->hasFeature(OSystem::kFeatureJoystickDeadzone))
+ deadZone = (float)ConfMan.getInt("joystick_deadzone") * 1000.0f;
+ float scalingFactor = 1.0f;
+ float magnitude = 0.0f;
- processMouseEvent(event, _km.x, _km.y);
+ magnitude = sqrt(analogX * analogX + analogY * analogY);
- return true;
+ if (magnitude >= deadZone) {
+ _km.x_down_count = 0;
+ _km.y_down_count = 0;
+ scalingFactor = 1.0f / magnitude * (magnitude - deadZone) / (32769.0f - deadZone);
+ _km.x_vel = (int16)(analogX * scalingFactor * 32768.0f / vel_to_axis);
+ _km.y_vel = (int16)(analogY * scalingFactor * 32768.0f / vel_to_axis);
+ } else {
+ _km.x_vel = 0;
+ _km.y_vel = 0;
+ }
+#endif
+
+ return false;
}
bool SdlEventSource::remapKey(SDL_Event &ev, Common::Event &event) {
@@ -903,8 +1009,11 @@ bool SdlEventSource::remapKey(SDL_Event &ev, Common::Event &event) {
void SdlEventSource::resetKeyboardEmulation(int16 x_max, int16 y_max) {
_km.x_max = x_max;
_km.y_max = y_max;
- _km.delay_time = 25;
+ _km.delay_time = 12;
_km.last_time = 0;
+ _km.modifier = false;
+ _km.joy_x = 0;
+ _km.joy_y = 0;
}
bool SdlEventSource::handleResizeEvent(Common::Event &event, int w, int h) {
diff --git a/backends/events/sdl/sdl-events.h b/backends/events/sdl/sdl-events.h
index ab5aadaf5ed..af5138bb680 100644
--- a/backends/events/sdl/sdl-events.h
+++ b/backends/events/sdl/sdl-events.h
@@ -28,6 +28,8 @@
#include "common/events.h"
+// multiplier used to increase resolution for keyboard/joystick mouse
+#define MULTIPLIER 16
/**
* The SDL event source.
@@ -58,8 +60,9 @@ protected:
//@{
struct KbdMouse {
- int16 x, y, x_vel, y_vel, x_max, y_max, x_down_count, y_down_count;
+ int16 x, y, x_vel, y_vel, x_max, y_max, x_down_count, y_down_count, joy_x, joy_y;
uint32 last_time, delay_time, x_down_time, y_down_time;
+ bool modifier;
};
KbdMouse _km;
@@ -106,7 +109,7 @@ protected:
virtual bool handleJoyButtonDown(SDL_Event &ev, Common::Event &event);
virtual bool handleJoyButtonUp(SDL_Event &ev, Common::Event &event);
virtual bool handleJoyAxisMotion(SDL_Event &ev, Common::Event &event);
- virtual void handleKbdMouse();
+ virtual bool handleKbdMouse(Common::Event &event);
//@}
diff --git a/backends/fs/abstract-fs.h b/backends/fs/abstract-fs.h
index 24286b649db..28ea3bbecd7 100644
--- a/backends/fs/abstract-fs.h
+++ b/backends/fs/abstract-fs.h
@@ -195,11 +195,11 @@ public:
/**
* Creates a file referred by this node.
*
- * @param isDirectory true if created file must be a directory
+ * @param isDirectoryFlag true if created file must be a directory
*
* @return true if file is created successfully
*/
- virtual bool create(bool isDirectory) = 0;
+ virtual bool create(bool isDirectoryFlag) = 0;
};
diff --git a/backends/fs/amigaos4/amigaos4-fs.cpp b/backends/fs/amigaos4/amigaos4-fs.cpp
index 24a8fb98ad0..09ba3a1c836 100644
--- a/backends/fs/amigaos4/amigaos4-fs.cpp
+++ b/backends/fs/amigaos4/amigaos4-fs.cpp
@@ -443,7 +443,7 @@ Common::WriteStream *AmigaOSFilesystemNode::createWriteStream() {
return StdioStream::makeFromPath(getPath(), true);
}
-bool AmigaOSFilesystemNode::create(bool isDirectory) {
+bool AmigaOSFilesystemNode::create(bool isDirectoryFlag) {
error("Not supported");
return false;
}
diff --git a/backends/fs/amigaos4/amigaos4-fs.h b/backends/fs/amigaos4/amigaos4-fs.h
index 3ed45d3f77f..c86bb4c57a5 100644
--- a/backends/fs/amigaos4/amigaos4-fs.h
+++ b/backends/fs/amigaos4/amigaos4-fs.h
@@ -116,7 +116,7 @@ public:
virtual Common::SeekableReadStream *createReadStream();
virtual Common::WriteStream *createWriteStream();
- virtual bool create(bool isDirectory);
+ virtual bool create(bool isDirectoryFlag);
};
diff --git a/backends/fs/chroot/chroot-fs.cpp b/backends/fs/chroot/chroot-fs.cpp
index be6805bbd86..ac7cde33987 100644
--- a/backends/fs/chroot/chroot-fs.cpp
+++ b/backends/fs/chroot/chroot-fs.cpp
@@ -108,7 +108,7 @@ Common::WriteStream *ChRootFilesystemNode::createWriteStream() {
return _realNode->createWriteStream();
}
-bool ChRootFilesystemNode::create(bool /*isDir*/) {
+bool ChRootFilesystemNode::create(bool isDirectoryFlag) {
error("Not supported");
return false;
}
diff --git a/backends/fs/chroot/chroot-fs.h b/backends/fs/chroot/chroot-fs.h
index e0ecc1c47ee..76902bc92a8 100644
--- a/backends/fs/chroot/chroot-fs.h
+++ b/backends/fs/chroot/chroot-fs.h
@@ -49,7 +49,7 @@ public:
virtual Common::SeekableReadStream *createReadStream();
virtual Common::WriteStream *createWriteStream();
- virtual bool create(bool isDirectory);
+ virtual bool create(bool isDirectoryFlag);
private:
static Common::String addPathComponent(const Common::String &path, const Common::String &component);
diff --git a/backends/fs/posix/posix-fs.cpp b/backends/fs/posix/posix-fs.cpp
index ce5715210a7..3f90fc1a191 100644
--- a/backends/fs/posix/posix-fs.cpp
+++ b/backends/fs/posix/posix-fs.cpp
@@ -20,7 +20,7 @@
*
*/
-#if defined(POSIX) || defined(PLAYSTATION3)
+#if defined(POSIX) || defined(PLAYSTATION3) || defined(PSP2)
// Re-enable some forbidden symbols to avoid clashes with stat.h and unistd.h.
// Also with clock() in sys/time.h in some Mac OS X SDKs.
@@ -36,7 +36,12 @@
#include
#include
+#ifdef PSP2
+#include "backends/fs/psp2/psp2-dirent.h"
+#define mkdir sceIoMkdir
+#else
#include
+#endif
#include
#include
#include
@@ -253,10 +258,10 @@ Common::WriteStream *POSIXFilesystemNode::createWriteStream() {
return StdioStream::makeFromPath(getPath(), true);
}
-bool POSIXFilesystemNode::create(bool isDir) {
+bool POSIXFilesystemNode::create(bool isDirectoryFlag) {
bool success;
- if (isDir) {
+ if (isDirectoryFlag) {
success = mkdir(_path.c_str(), 0755) == 0;
} else {
int fd = open(_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0755);
@@ -270,12 +275,12 @@ bool POSIXFilesystemNode::create(bool isDir) {
if (success) {
setFlags();
if (_isValid) {
- if (_isDirectory != isDir) warning("failed to create %s: got %s", isDir ? "directory" : "file", _isDirectory ? "directory" : "file");
- return _isDirectory == isDir;
+ if (_isDirectory != isDirectoryFlag) warning("failed to create %s: got %s", isDirectoryFlag ? "directory" : "file", _isDirectory ? "directory" : "file");
+ return _isDirectory == isDirectoryFlag;
}
warning("POSIXFilesystemNode: %s() was a success, but stat indicates there is no such %s",
- isDir ? "mkdir" : "creat", isDir ? "directory" : "file");
+ isDirectoryFlag ? "mkdir" : "creat", isDirectoryFlag ? "directory" : "file");
return false;
}
diff --git a/backends/fs/posix/posix-fs.h b/backends/fs/posix/posix-fs.h
index 4ebce7e9d92..5a6b6bd2bd2 100644
--- a/backends/fs/posix/posix-fs.h
+++ b/backends/fs/posix/posix-fs.h
@@ -73,7 +73,7 @@ public:
virtual Common::SeekableReadStream *createReadStream();
virtual Common::WriteStream *createWriteStream();
- virtual bool create(bool isDirectory);
+ virtual bool create(bool isDirectoryFlag);
private:
/**
diff --git a/backends/fs/windows/windows-fs.cpp b/backends/fs/windows/windows-fs.cpp
index b43686f9112..caf0e31360b 100644
--- a/backends/fs/windows/windows-fs.cpp
+++ b/backends/fs/windows/windows-fs.cpp
@@ -244,10 +244,10 @@ Common::WriteStream *WindowsFilesystemNode::createWriteStream() {
return StdioStream::makeFromPath(getPath(), true);
}
-bool WindowsFilesystemNode::create(bool isDirectory) {
+bool WindowsFilesystemNode::create(bool isDirectoryFlag) {
bool success;
- if (isDirectory) {
+ if (isDirectoryFlag) {
success = CreateDirectory(toUnicode(_path.c_str()), NULL) != 0;
} else {
success = CreateFile(toUnicode(_path.c_str()), GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL) != INVALID_HANDLE_VALUE;
@@ -264,12 +264,12 @@ bool WindowsFilesystemNode::create(bool isDirectory) {
_path += '\\';
}
- if (_isDirectory != isDirectory) warning("failed to create %s: got %s", isDirectory ? "directory" : "file", _isDirectory ? "directory" : "file");
- return _isDirectory == isDirectory;
+ if (_isDirectory != isDirectoryFlag) warning("failed to create %s: got %s", isDirectoryFlag ? "directory" : "file", _isDirectory ? "directory" : "file");
+ return _isDirectory == isDirectoryFlag;
}
warning("WindowsFilesystemNode: Create%s() was a success, but GetFileAttributes() indicates there is no such %s",
- isDirectory ? "Directory" : "File", isDirectory ? "directory" : "file");
+ isDirectoryFlag ? "Directory" : "File", isDirectoryFlag ? "directory" : "file");
return false;
}
diff --git a/backends/fs/windows/windows-fs.h b/backends/fs/windows/windows-fs.h
index 7c9f2c1e1ad..3758877f05e 100644
--- a/backends/fs/windows/windows-fs.h
+++ b/backends/fs/windows/windows-fs.h
@@ -88,7 +88,7 @@ public:
virtual Common::SeekableReadStream *createReadStream();
virtual Common::WriteStream *createWriteStream();
- virtual bool create(bool isDirectory);
+ virtual bool create(bool isDirectoryFlag);
private:
/**
diff --git a/backends/graphics/graphics.h b/backends/graphics/graphics.h
index cdc91c54046..78f0aa84a63 100644
--- a/backends/graphics/graphics.h
+++ b/backends/graphics/graphics.h
@@ -46,6 +46,13 @@ public:
virtual bool setGraphicsMode(int mode) = 0;
virtual void resetGraphicsScale() = 0;
virtual int getGraphicsMode() const = 0;
+ virtual const OSystem::GraphicsMode *getSupportedShaders() const {
+ static const OSystem::GraphicsMode no_shader[2] = {{"NONE", "Normal (no shader)", 0}, {0, 0, 0}};
+ return no_shader;
+ };
+ virtual bool setShader(int id) { return false; }
+ virtual int getShader() const { return 0; }
+
#ifdef USE_RGB_COLOR
virtual Graphics::PixelFormat getScreenFormat() const = 0;
virtual Common::List getSupportedFormats() const = 0;
diff --git a/backends/midi/timidity.cpp b/backends/midi/timidity.cpp
index de68e5bd2a2..5b2bd1e64c1 100644
--- a/backends/midi/timidity.cpp
+++ b/backends/midi/timidity.cpp
@@ -52,24 +52,18 @@
#include
#include
#include
-#include /* for gethostbyname */
+#include /* for getaddrinfo */
#include
#include
#include
#include
#include
-// WORKAROUND bug #1870304: Solaris does not provide INADDR_NONE.
-#ifndef INADDR_NONE
-#define INADDR_NONE 0xffffffff
-#endif
-
// BeOS BONE uses snooze (x/1000) in place of usleep(x)
#ifdef __BEOS__
#define usleep(v) snooze(v/1000)
#endif
-
#define SEQ_MIDIPUTC 5
#define TIMIDITY_LOW_DELAY
@@ -84,7 +78,7 @@
/* default host & port */
#define DEFAULT_TIMIDITY_HOST "127.0.0.1"
-#define DEFAULT_TIMIDITY_PORT 7777
+#define DEFAULT_TIMIDITY_PORT "7777"
class MidiDriver_TIMIDITY : public MidiDriver_MPU401 {
public:
@@ -97,11 +91,8 @@ public:
void sysEx(const byte *msg, uint16 length);
private:
- /* standart routine to extract ip address from a string */
- in_addr_t host_to_addr(const char* address);
-
/* creates a tcp connection to TiMidity server, returns filedesc (like open()) */
- int connect_to_server(const char* hostname, unsigned short tcp_port);
+ int connect_to_server(const char* hostname, const char* tcp_port);
/* send command to the server; printf-like; returns reply string */
char *timidity_ctl_command(const char *fmt, ...) GCC_PRINTF(2, 3);
@@ -150,7 +141,8 @@ MidiDriver_TIMIDITY::MidiDriver_TIMIDITY() {
int MidiDriver_TIMIDITY::open() {
char *res;
char timidity_host[NI_MAXHOST];
- int timidity_port, data_port, i;
+ char timidity_port[6], data_port[6];
+ int num;
/* count ourselves open */
if (_isOpen)
@@ -166,16 +158,16 @@ int MidiDriver_TIMIDITY::open() {
/* extract control port */
if ((res = strrchr(timidity_host, ':')) != NULL) {
*res++ = '\0';
- timidity_port = atoi(res);
+ Common::strlcpy(timidity_port, res, sizeof(timidity_port));
} else {
- timidity_port = DEFAULT_TIMIDITY_PORT;
+ Common::strlcpy(timidity_port, DEFAULT_TIMIDITY_PORT, sizeof(timidity_port));
}
/*
* create control connection to the server
*/
if ((_control_fd = connect_to_server(timidity_host, timidity_port)) < 0) {
- warning("TiMidity: can't open control connection (host=%s, port=%d)", timidity_host, timidity_port);
+ warning("TiMidity: can't open control connection (host=%s, port=%s)", timidity_host, timidity_port);
return -1;
}
@@ -183,7 +175,7 @@ int MidiDriver_TIMIDITY::open() {
* "220 TiMidity++ v2.13.2 ready)" */
res = timidity_ctl_command(NULL);
if (atoi(res) != 220) {
- warning("TiMidity: bad response from server (host=%s, port=%d): %s", timidity_host, timidity_port, res);
+ warning("TiMidity: bad response from server (host=%s, port=%s): %s", timidity_host, timidity_port, res);
close_all();
return -1;
}
@@ -198,13 +190,11 @@ int MidiDriver_TIMIDITY::open() {
/* should read something like "200 63017 is ready acceptable",
* where 63017 is port for data connection */
- // FIXME: The following looks like a cheap endian test. If this is true, then
- // it should be replaced by suitable #ifdef SCUMM_LITTLE_ENDIAN.
- i = 1;
- if (*(char *)&i == 1)
- res = timidity_ctl_command("OPEN lsb");
- else
- res = timidity_ctl_command("OPEN msb");
+#ifdef SCUMM_LITTLE_ENDIAN
+ res = timidity_ctl_command("OPEN lsb");
+#else
+ res = timidity_ctl_command("OPEN msb");
+#endif
if (atoi(res) != 200) {
warning("TiMidity: bad reply for OPEN command: %s", res);
@@ -215,9 +205,15 @@ int MidiDriver_TIMIDITY::open() {
/*
* open data connection
*/
- data_port = atoi(res + 4);
+ num = atoi(res + 4);
+ if (num > 65535) {
+ warning("TiMidity: Invalid port %d given.\n", num);
+ close_all();
+ return -1;
+ }
+ snprintf(data_port, sizeof(data_port), "%d", num);
if ((_data_fd = connect_to_server(timidity_host, data_port)) < 0) {
- warning("TiMidity: can't open data connection (host=%s, port=%d)", timidity_host, data_port);
+ warning("TiMidity: can't open data connection (host=%s, port=%s)", timidity_host, data_port);
close_all();
return -1;
}
@@ -226,7 +222,7 @@ int MidiDriver_TIMIDITY::open() {
* "200 Ready data connection" */
res = timidity_ctl_command(NULL);
if (atoi(res) != 200) {
- warning("Can't connect timidity: %s\t(host=%s, port=%d)", res, timidity_host, data_port);
+ warning("Can't connect timidity: %s\t(host=%s, port=%s)", res, timidity_host, data_port);
close_all();
return -1;
}
@@ -277,46 +273,33 @@ void MidiDriver_TIMIDITY::teardown() {
close_all();
}
-in_addr_t MidiDriver_TIMIDITY::host_to_addr(const char* address) {
- in_addr_t addr;
- struct hostent *hp;
-
- /* first check if IP address is given (like 127.0.0.1)*/
- if ((addr = inet_addr(address)) != INADDR_NONE)
- return addr;
-
- /* if not, try to resolve a hostname */
- if ((hp = gethostbyname(address)) == NULL) {
- warning("TiMidity: unknown hostname: %s", address);
- return INADDR_NONE;
- }
-
- memcpy(&addr, hp->h_addr, (int)sizeof(in_addr_t) <= hp->h_length ? sizeof(in_addr_t) : hp->h_length);
-
- return addr;
-}
-
-int MidiDriver_TIMIDITY::connect_to_server(const char* hostname, unsigned short tcp_port) {
+int MidiDriver_TIMIDITY::connect_to_server(const char* hostname, const char* tcp_port) {
int fd;
- struct sockaddr_in in;
- unsigned int addr;
+ struct addrinfo hints;
+ struct addrinfo *result, *rp;
- /* create socket */
- if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
- warning("TiMidity: socket(): %s", strerror(errno));
+ /* get all address(es) matching host and port */
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
+ if (getaddrinfo(hostname, tcp_port, &hints, &result) != 0) {
+ warning("TiMidity: getaddrinfo: %s\n", strerror(errno));
return -1;
}
- /* connect */
- memset(&in, 0, sizeof(in));
- in.sin_family = AF_INET;
- in.sin_port = htons(tcp_port);
- addr = host_to_addr(hostname);
- memcpy(&in.sin_addr, &addr, 4);
-
- if (connect(fd, (struct sockaddr *)&in, sizeof(in)) < 0) {
- warning("TiMidity: connect(): %s", strerror(errno));
+ /* Try all address structures we have got previously */
+ for (rp = result; rp != NULL; rp = rp->ai_next) {
+ if ((fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) == -1)
+ continue;
+ if (connect(fd, rp->ai_addr, rp->ai_addrlen) != -1)
+ break;
::close(fd);
+ }
+
+ freeaddrinfo(result);
+
+ if (rp == NULL) {
+ warning("TiMidity: Could not connect\n");
return -1;
}
@@ -561,4 +544,4 @@ Common::Error TimidityMusicPlugin::createInstance(MidiDriver **mididriver, MidiD
REGISTER_PLUGIN_STATIC(TIMIDITY, PLUGIN_TYPE_MUSIC, TimidityMusicPlugin);
//#endif
-#endif // defined (USE_TIMIDITY)
+#endif // defined(USE_TIMIDITY)
diff --git a/backends/mixer/sdl/sdl-mixer.cpp b/backends/mixer/sdl/sdl-mixer.cpp
index 0ca3231892b..3e65fbae974 100644
--- a/backends/mixer/sdl/sdl-mixer.cpp
+++ b/backends/mixer/sdl/sdl-mixer.cpp
@@ -32,7 +32,7 @@
#if defined(GP2X)
#define SAMPLES_PER_SEC 11025
-#elif defined(PLAYSTATION3)
+#elif defined(PLAYSTATION3) || defined(PSP2)
#define SAMPLES_PER_SEC 48000
#else
#define SAMPLES_PER_SEC 44100
diff --git a/backends/modular-backend.cpp b/backends/modular-backend.cpp
index acab95e31c9..8215934f8e2 100644
--- a/backends/modular-backend.cpp
+++ b/backends/modular-backend.cpp
@@ -82,6 +82,18 @@ int ModularBackend::getGraphicsMode() const {
return _graphicsManager->getGraphicsMode();
}
+const OSystem::GraphicsMode *ModularBackend::getSupportedShaders() const {
+ return _graphicsManager->getSupportedShaders();
+}
+
+bool ModularBackend::setShader(int id) {
+ return _graphicsManager->setShader(id);
+}
+
+int ModularBackend::getShader() const {
+ return _graphicsManager->getShader();
+}
+
void ModularBackend::resetGraphicsScale() {
_graphicsManager->resetGraphicsScale();
}
diff --git a/backends/modular-backend.h b/backends/modular-backend.h
index e40ddbfb066..009a1799d56 100644
--- a/backends/modular-backend.h
+++ b/backends/modular-backend.h
@@ -66,6 +66,9 @@ public:
virtual int getDefaultGraphicsMode() const;
virtual bool setGraphicsMode(int mode);
virtual int getGraphicsMode() const;
+ virtual const GraphicsMode *getSupportedShaders() const;
+ virtual int getShader() const;
+ virtual bool setShader(int id);
virtual void resetGraphicsScale();
#ifdef USE_RGB_COLOR
virtual Graphics::PixelFormat getScreenFormat() const;
diff --git a/backends/module.mk b/backends/module.mk
index 122df256de8..58c385b86a7 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -271,6 +271,15 @@ MODULE_OBJS += \
timer/psp/timer.o
endif
+ifeq ($(BACKEND),psp2)
+MODULE_OBJS += \
+ fs/posix/posix-fs.o \
+ fs/psp2/psp2-fs-factory.o \
+ fs/psp2/psp2-dirent.o \
+ events/psp2sdl/psp2sdl-events.o \
+ graphics/psp2sdl/psp2sdl-graphics.o
+endif
+
ifeq ($(BACKEND),samsungtv)
MODULE_OBJS += \
events/samsungtvsdl/samsungtvsdl-events.o \
diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp
index e1761bddc69..d8662ab78d4 100644
--- a/backends/networking/curl/connectionmanager.cpp
+++ b/backends/networking/curl/connectionmanager.cpp
@@ -81,7 +81,11 @@ Request *ConnectionManager::addRequest(Request *request, RequestCallback callbac
Common::String ConnectionManager::urlEncode(Common::String s) const {
if (!_multi)
return "";
+#if LIBCURL_VERSION_NUM >= 0x070F04
char *output = curl_easy_escape(_multi, s.c_str(), s.size());
+#else
+ char *output = curl_escape(s.c_str(), s.size());
+#endif
if (output) {
Common::String result = output;
curl_free(output);
diff --git a/backends/networking/curl/networkreadstream.cpp b/backends/networking/curl/networkreadstream.cpp
index b4516701a75..e4fc5492b5d 100644
--- a/backends/networking/curl/networkreadstream.cpp
+++ b/backends/networking/curl/networkreadstream.cpp
@@ -67,6 +67,7 @@ void NetworkReadStream::init(const char *url, curl_slist *headersList, const byt
_sendingContentsBuffer = nullptr;
_sendingContentsSize = _sendingContentsPos = 0;
_progressDownloaded = _progressTotal = 0;
+ _bufferCopy = nullptr;
_easy = curl_easy_init();
curl_easy_setopt(_easy, CURLOPT_WRITEFUNCTION, curlDataCallback);
@@ -100,7 +101,14 @@ void NetworkReadStream::init(const char *url, curl_slist *headersList, const byt
} else {
if (post || bufferSize != 0) {
curl_easy_setopt(_easy, CURLOPT_POSTFIELDSIZE, bufferSize);
+#if LIBCURL_VERSION_NUM >= 0x071101
+ // CURLOPT_COPYPOSTFIELDS available since curl 7.17.1
curl_easy_setopt(_easy, CURLOPT_COPYPOSTFIELDS, buffer);
+#else
+ _bufferCopy = (byte*)malloc(bufferSize);
+ memcpy(_bufferCopy, buffer, bufferSize);
+ curl_easy_setopt(_easy, CURLOPT_POSTFIELDS, _bufferCopy);
+#endif
}
}
ConnMan.registerEasyHandle(_easy);
@@ -111,6 +119,7 @@ void NetworkReadStream::init(const char *url, curl_slist *headersList, Common::H
_sendingContentsBuffer = nullptr;
_sendingContentsSize = _sendingContentsPos = 0;
_progressDownloaded = _progressTotal = 0;
+ _bufferCopy = nullptr;
_easy = curl_easy_init();
curl_easy_setopt(_easy, CURLOPT_WRITEFUNCTION, curlDataCallback);
@@ -184,6 +193,7 @@ NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, c
NetworkReadStream::~NetworkReadStream() {
if (_easy)
curl_easy_cleanup(_easy);
+ free(_bufferCopy);
}
bool NetworkReadStream::eos() const {
@@ -228,19 +238,19 @@ Common::String NetworkReadStream::responseHeaders() const {
}
uint32 NetworkReadStream::fillWithSendingContents(char *bufferToFill, uint32 maxSize) {
- uint32 size = _sendingContentsSize - _sendingContentsPos;
- if (size > maxSize)
- size = maxSize;
- for (uint32 i = 0; i < size; ++i) {
+ uint32 sendSize = _sendingContentsSize - _sendingContentsPos;
+ if (sendSize > maxSize)
+ sendSize = maxSize;
+ for (uint32 i = 0; i < sendSize; ++i) {
bufferToFill[i] = _sendingContentsBuffer[_sendingContentsPos + i];
}
- _sendingContentsPos += size;
- return size;
+ _sendingContentsPos += sendSize;
+ return sendSize;
}
-uint32 NetworkReadStream::addResponseHeaders(char *buffer, uint32 size) {
- _responseHeaders += Common::String(buffer, size);
- return size;
+uint32 NetworkReadStream::addResponseHeaders(char *buffer, uint32 bufferSize) {
+ _responseHeaders += Common::String(buffer, bufferSize);
+ return bufferSize;
}
double NetworkReadStream::getProgress() const {
diff --git a/backends/networking/curl/networkreadstream.h b/backends/networking/curl/networkreadstream.h
index 62a3e41d9ba..8e59429a0ab 100644
--- a/backends/networking/curl/networkreadstream.h
+++ b/backends/networking/curl/networkreadstream.h
@@ -40,6 +40,7 @@ class NetworkReadStream: public Common::MemoryReadWriteStream {
const byte *_sendingContentsBuffer;
uint32 _sendingContentsSize;
uint32 _sendingContentsPos;
+ byte* _bufferCopy; // To use with old curl version where CURLOPT_COPYPOSTFIELDS is not available
Common::String _responseHeaders;
uint64 _progressDownloaded, _progressTotal;
void init(const char *url, curl_slist *headersList, const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post);
@@ -128,7 +129,7 @@ public:
*
* @returns how many bytes were actually read
*/
- uint32 addResponseHeaders(char *buffer, uint32 size);
+ uint32 addResponseHeaders(char *buffer, uint32 bufferSize);
/** Returns a number in range [0, 1], where 1 is "complete". */
double getProgress() const;
diff --git a/backends/networking/sdl_net/client.cpp b/backends/networking/sdl_net/client.cpp
index fbb5fadf358..0ad366d8a5c 100644
--- a/backends/networking/sdl_net/client.cpp
+++ b/backends/networking/sdl_net/client.cpp
@@ -84,7 +84,7 @@ bool Client::readMoreIfNeeded() {
return false;
}
- if (_stream->write(_buffer, bytes) != bytes) {
+ if (_stream->write(_buffer, bytes) != (uint32)bytes) {
warning("Client::readMoreIfNeeded: failed to write() into MemoryReadWriteStream");
close();
return false;
diff --git a/backends/networking/sdl_net/getclienthandler.cpp b/backends/networking/sdl_net/getclienthandler.cpp
index b22c80cebff..ef085c60820 100644
--- a/backends/networking/sdl_net/getclienthandler.cpp
+++ b/backends/networking/sdl_net/getclienthandler.cpp
@@ -145,7 +145,7 @@ void GetClientHandler::handle(Client *client) {
}
if (readBytes != 0)
- if (client->send(_buffer, readBytes) != readBytes) {
+ if (client->send(_buffer, readBytes) != (int)readBytes) {
warning("GetClientHandler: unable to send all bytes to the client");
client->close();
return;
diff --git a/backends/networking/sdl_net/handlers/filespagehandler.cpp b/backends/networking/sdl_net/handlers/filespagehandler.cpp
index 01b40c3bcb9..22afa83af8d 100644
--- a/backends/networking/sdl_net/handlers/filespagehandler.cpp
+++ b/backends/networking/sdl_net/handlers/filespagehandler.cpp
@@ -56,7 +56,7 @@ Common::String encodeHtmlEntities(Common::String s) {
result += ">";
else if (s[i] == '&')
result += "&";
- else if (s[i] > 0x7F)
+ else if (s[i] > (byte)0x7F)
result += Common::String::format("%d;", (int)s[i]);
else result += s[i];
return result;
diff --git a/backends/networking/sdl_net/handlers/indexpagehandler.cpp b/backends/networking/sdl_net/handlers/indexpagehandler.cpp
index 04a3f45eff6..17e5159768f 100644
--- a/backends/networking/sdl_net/handlers/indexpagehandler.cpp
+++ b/backends/networking/sdl_net/handlers/indexpagehandler.cpp
@@ -37,9 +37,9 @@ IndexPageHandler::~IndexPageHandler() {}
Common::String IndexPageHandler::code() const { return _code; }
void IndexPageHandler::handle(Client &client) {
- Common::String code = client.queryParameter("code");
+ Common::String queryCode = client.queryParameter("code");
- if (code == "") {
+ if (queryCode == "") {
// redirect to "/filesAJAX"
HandlerUtils::setMessageHandler(
client,
@@ -53,7 +53,7 @@ void IndexPageHandler::handle(Client &client) {
return;
}
- _code = code;
+ _code = queryCode;
sendCommand(GUI::kStorageCodePassedCmd, 0);
HandlerUtils::setMessageHandler(client, _("ScummVM got the code and already connects to your cloud storage!"));
}
diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp
index fdc89b51e4c..7ef1565ad40 100644
--- a/backends/networking/sdl_net/localwebserver.cpp
+++ b/backends/networking/sdl_net/localwebserver.cpp
@@ -33,10 +33,24 @@
#include
#ifdef POSIX
-#include
-#include
-#include
+#include
+#include
#include
+#include
+#include
+#include
+#include
+#include
+
+#ifndef SIOCGIFCONF
+#include
+#endif
+
+#ifndef _SIZEOF_ADDR_IFREQ
+#define _SIZEOF_ADDR_IFREQ sizeof
+#endif
+
+#define LSSDP_BUFFER_LEN 2048
#endif
namespace Common {
@@ -231,8 +245,8 @@ void LocalWebserver::handleClient(uint32 i) {
break;
// if no handler, answer with default BAD REQUEST
- // fallthrough
}
+ // fall through
case BAD_REQUEST:
setClientGetHandler(_client[i], "ScummVM - Bad RequestBAD REQUEST", 400);
@@ -295,56 +309,70 @@ void LocalWebserver::resolveAddress(void *ipAddress) {
// if not - try platform-specific
#ifdef POSIX
- struct ifaddrs *ifAddrStruct = NULL;
void *tmpAddrPtr = NULL;
- getifaddrs(&ifAddrStruct);
+ int fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ warning("LocalWebserver: failed to create socket: %s (%d)", strerror(errno), errno);
+ } else {
+ // get ifconfig
+ char buffer[LSSDP_BUFFER_LEN] = {};
+ struct ifconf ifc;
+ ifc.ifc_len = sizeof(buffer);
+ ifc.ifc_buf = (caddr_t) buffer;
- for (struct ifaddrs *i = ifAddrStruct; i != NULL; i = i->ifa_next) {
- if (!i->ifa_addr) {
- continue;
+ if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) {
+ warning("LocalWebserver: ioctl SIOCGIFCONF failed: %s (%d)", strerror(errno), errno);
+ } else {
+ struct ifreq *i;
+ for (size_t index = 0; index < (size_t)ifc.ifc_len; index += _SIZEOF_ADDR_IFREQ(*i)) {
+ i = (struct ifreq *)(buffer + index);
+
+ Common::String addr;
+
+ // IPv4
+ if (i->ifr_addr.sa_family == AF_INET) {
+ tmpAddrPtr = &((struct sockaddr_in *)&i->ifr_addr)->sin_addr;
+ char addressBuffer[INET_ADDRSTRLEN];
+ inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
+ debug(9, "%s IP Address %s", i->ifr_name, addressBuffer);
+ addr = addressBuffer;
+ }
+
+ // IPv6
+ /*
+ if (i->ifr_addr.sa_family == AF_INET6) {
+ tmpAddrPtr = &((struct sockaddr_in6 *)&i->ifr_addr)->sin6_addr;
+ char addressBuffer[INET6_ADDRSTRLEN];
+ inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
+ debug(9, "%s IP Address %s", i->ifr_name, addressBuffer);
+ addr = addressBuffer;
+ }
+ */
+
+ if (addr.empty())
+ continue;
+
+ // ignored IPv4 addresses
+ if (addr.equals("127.0.0.1") || addr.equals("0.0.0.0") || addr.equals("localhost"))
+ continue;
+
+ // ignored IPv6 addresses
+ /*
+ if (addr.equals("::1"))
+ continue;
+ */
+
+ // use the address found
+ _address = "http://" + addr + Common::String::format(":%u/", _serverPort);
+ }
}
- Common::String addr;
-
- // IPv4
- if (i->ifa_addr->sa_family == AF_INET) {
- tmpAddrPtr = &((struct sockaddr_in *)i->ifa_addr)->sin_addr;
- char addressBuffer[INET_ADDRSTRLEN];
- inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
- debug(9, "%s IP Address %s", i->ifa_name, addressBuffer);
- addr = addressBuffer;
+ // close socket
+ if (close(fd) != 0) {
+ warning("LocalWebserver: failed to close socket [fd %d]: %s (%d)", fd, strerror(errno), errno);
}
-
- // IPv6
- /*
- if (i->ifa_addr->sa_family == AF_INET6) {
- tmpAddrPtr = &((struct sockaddr_in6 *)i->ifa_addr)->sin6_addr;
- char addressBuffer[INET6_ADDRSTRLEN];
- inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
- debug(9, "%s IP Address %s", i->ifa_name, addressBuffer);
- addr = addressBuffer;
- }
- */
-
- if (addr.empty())
- continue;
-
- // ignored IPv4 addresses
- if (addr.equals("127.0.0.1") || addr.equals("0.0.0.0") || addr.equals("localhost"))
- continue;
-
- // ignored IPv6 addresses
- /*
- if (addr.equals("::1"))
- continue;
- */
-
- // use the address found
- _address = "http://" + addr + Common::String::format(":%u/", _serverPort);
}
-
- if (ifAddrStruct != NULL) freeifaddrs(ifAddrStruct);
#endif
}
diff --git a/backends/networking/sdl_net/localwebserver.h b/backends/networking/sdl_net/localwebserver.h
index 5c94d6d091a..243db11fb46 100644
--- a/backends/networking/sdl_net/localwebserver.h
+++ b/backends/networking/sdl_net/localwebserver.h
@@ -59,7 +59,7 @@ class LocalWebserver : public Common::Singleton {
SDLNet_SocketSet _set;
TCPsocket _serverSocket;
Client _client[MAX_CONNECTIONS];
- int _clients;
+ uint32 _clients;
bool _timerStarted, _stopOnIdle, _minimalMode;
Common::HashMap _pathHandlers;
BaseHandler *_defaultHandler;
diff --git a/backends/networking/sdl_net/reader.cpp b/backends/networking/sdl_net/reader.cpp
index aa9f4746ddd..46fb21a47a7 100644
--- a/backends/networking/sdl_net/reader.cpp
+++ b/backends/networking/sdl_net/reader.cpp
@@ -169,27 +169,27 @@ void Reader::handleFirstHeaders(Common::MemoryReadWriteStream *headersStream) {
_availableBytes = _contentLength;
}
-void Reader::parseFirstLine(const Common::String &headers) {
- uint32 headersSize = headers.size();
+void Reader::parseFirstLine(const Common::String &headersToParse) {
+ uint32 headersSize = headersToParse.size();
bool bad = false;
if (headersSize > 0) {
- const char *cstr = headers.c_str();
+ const char *cstr = headersToParse.c_str();
const char *position = strstr(cstr, "\r\n");
if (position) { //we have at least one line - and we want the first one
//" HTTP/\r\n"
- Common::String method, path, http, buf;
+ Common::String methodParsed, pathParsed, http, buf;
uint32 length = position - cstr;
if (headersSize > length)
headersSize = length;
for (uint32 i = 0; i < headersSize; ++i) {
- if (headers[i] != ' ')
- buf += headers[i];
- if (headers[i] == ' ' || i == headersSize - 1) {
- if (method == "") {
- method = buf;
- } else if (path == "") {
- path = buf;
+ if (headersToParse[i] != ' ')
+ buf += headersToParse[i];
+ if (headersToParse[i] == ' ' || i == headersSize - 1) {
+ if (methodParsed == "") {
+ methodParsed = buf;
+ } else if (pathParsed == "") {
+ pathParsed = buf;
} else if (http == "") {
http = buf;
} else {
@@ -201,44 +201,44 @@ void Reader::parseFirstLine(const Common::String &headers) {
}
//check that method is supported
- if (method != "GET" && method != "PUT" && method != "POST")
+ if (methodParsed != "GET" && methodParsed != "PUT" && methodParsed != "POST")
bad = true;
//check that HTTP/ is OK
if (!http.hasPrefix("HTTP/"))
bad = true;
- _method = method;
- parsePathQueryAndAnchor(path);
+ _method = methodParsed;
+ parsePathQueryAndAnchor(pathParsed);
}
}
if (bad) _isBadRequest = true;
}
-void Reader::parsePathQueryAndAnchor(Common::String path) {
+void Reader::parsePathQueryAndAnchor(Common::String pathToParse) {
//[?query][#anchor]
bool readingPath = true;
bool readingQuery = false;
_path = "";
_query = "";
_anchor = "";
- for (uint32 i = 0; i < path.size(); ++i) {
+ for (uint32 i = 0; i < pathToParse.size(); ++i) {
if (readingPath) {
- if (path[i] == '?') {
+ if (pathToParse[i] == '?') {
readingPath = false;
readingQuery = true;
} else {
- _path += path[i];
+ _path += pathToParse[i];
}
} else if (readingQuery) {
- if (path[i] == '#') {
+ if (pathToParse[i] == '#') {
readingQuery = false;
} else {
- _query += path[i];
+ _query += pathToParse[i];
}
} else {
- _anchor += path[i];
+ _anchor += pathToParse[i];
}
}
@@ -350,7 +350,7 @@ bool Reader::readOneByteInStream(Common::WriteStream *stream, const Common::Stri
}
byte Reader::readOne() {
- byte b;
+ byte b = 0;
_content->read(&b, 1);
--_availableBytes;
--_bytesLeft;
diff --git a/backends/networking/sdl_net/reader.h b/backends/networking/sdl_net/reader.h
index 1a60e7a58df..4955edfc39a 100644
--- a/backends/networking/sdl_net/reader.h
+++ b/backends/networking/sdl_net/reader.h
@@ -100,7 +100,7 @@ class Reader {
void handleFirstHeaders(Common::MemoryReadWriteStream *headers);
void parseFirstLine(const Common::String &headers);
- void parsePathQueryAndAnchor(Common::String path);
+ void parsePathQueryAndAnchor(Common::String pathToParse);
void parseQueryParameters();
void makeWindow(uint32 size);
diff --git a/backends/platform/sdl/macosx/macosx.cpp b/backends/platform/sdl/macosx/macosx.cpp
index 7d80f0ed353..9ce05238187 100644
--- a/backends/platform/sdl/macosx/macosx.cpp
+++ b/backends/platform/sdl/macosx/macosx.cpp
@@ -137,7 +137,7 @@ bool OSystem_MacOSX::openUrl(const Common::String &url) {
CFURLRef urlRef = CFURLCreateWithBytes (NULL, (UInt8*)url.c_str(), url.size(), kCFStringEncodingASCII, NULL);
OSStatus err = LSOpenCFURLRef(urlRef, NULL);
CFRelease(urlRef);
- return err != noErr;
+ return err == noErr;
}
Common::String OSystem_MacOSX::getSystemLanguage() const {
@@ -187,6 +187,15 @@ Common::String OSystem_MacOSX::getSystemLanguage() const {
#endif // USE_DETECTLANG
}
+Common::String OSystem_MacOSX::getScreenshotsPath() {
+ Common::String path = ConfMan.get("screenshotpath");
+ if (path.empty())
+ path = getDesktopPathMacOSX();
+ if (!path.empty() && !path.hasSuffix("/"))
+ path += "/";
+ return path;
+}
+
AudioCDManager *OSystem_MacOSX::createAudioCDManager() {
return createMacOSXAudioCDManager();
}
diff --git a/backends/platform/sdl/macosx/macosx.h b/backends/platform/sdl/macosx/macosx.h
index 72bb4a4787d..ba07364681a 100644
--- a/backends/platform/sdl/macosx/macosx.h
+++ b/backends/platform/sdl/macosx/macosx.h
@@ -44,6 +44,9 @@ public:
virtual void initBackend();
virtual void addSysArchivesToSearchSet(Common::SearchSet &s, int priority = 0);
+ //Screenshots
+ virtual Common::String getScreenshotsPath();
+
protected:
// Override createAudioCDManager() to get our Mac-specific
// version.
diff --git a/backends/platform/sdl/macosx/macosx_wrapper.h b/backends/platform/sdl/macosx/macosx_wrapper.h
index 3b346fc4865..84f0c1b2bab 100644
--- a/backends/platform/sdl/macosx/macosx_wrapper.h
+++ b/backends/platform/sdl/macosx/macosx_wrapper.h
@@ -27,5 +27,6 @@
bool hasTextInClipboardMacOSX();
Common::String getTextFromClipboardMacOSX();
+Common::String getDesktopPathMacOSX();
#endif
diff --git a/backends/platform/sdl/macosx/macosx_wrapper.mm b/backends/platform/sdl/macosx/macosx_wrapper.mm
index 8ec9eac5acb..02516e5ffee 100644
--- a/backends/platform/sdl/macosx/macosx_wrapper.mm
+++ b/backends/platform/sdl/macosx/macosx_wrapper.mm
@@ -27,6 +27,8 @@
#include
#include
+#include
+#include
bool hasTextInClipboardMacOSX() {
return [[NSPasteboard generalPasteboard] availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]] != nil;
@@ -46,3 +48,17 @@ Common::String getTextFromClipboardMacOSX() {
// we should use NSISOLatin1StringEncoding?).
return Common::String([str cStringUsingEncoding:NSASCIIStringEncoding]);
}
+
+Common::String getDesktopPathMacOSX() {
+ // The recommanded method is to use NSFileManager.
+ // NSUrl *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDesktopDirectory inDomains:NSUserDomainMask] firstObject];
+ // However it is only available in OS X 10.6+. So use NSSearchPathForDirectoriesInDomains instead (available since OS X 10.0)
+ // [NSArray firstObject] is also only available in OS X 10.6+. So we need to use [NSArray count] and [NSArray objectAtIndex:]
+ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDesktopDirectory, NSUserDomainMask, YES);
+ if ([paths count] == 0)
+ return Common::String();
+ NSString *path = [paths objectAtIndex:0];
+ if (path == nil)
+ return Common::String();
+ return Common::String([path cStringUsingEncoding:NSASCIIStringEncoding]);
+}
diff --git a/backends/platform/sdl/module.mk b/backends/platform/sdl/module.mk
index 84ce272d3cf..7fde04037f1 100644
--- a/backends/platform/sdl/module.mk
+++ b/backends/platform/sdl/module.mk
@@ -37,6 +37,13 @@ MODULE_OBJS += \
ps3/ps3.o
endif
+ifdef PSP2
+CC=arm-vita-eabi-gcc
+MODULE_OBJS += \
+ psp2/psp2-main.o \
+ psp2/psp2.o
+endif
+
# We don't use rules.mk but rather manually update OBJS and MODULE_DIRS.
MODULE_OBJS := $(addprefix $(MODULE)/, $(MODULE_OBJS))
OBJS := $(MODULE_OBJS) $(OBJS)
diff --git a/backends/platform/sdl/posix/posix-main.cpp b/backends/platform/sdl/posix/posix-main.cpp
index 5deebb0ae33..92354b273ef 100644
--- a/backends/platform/sdl/posix/posix-main.cpp
+++ b/backends/platform/sdl/posix/posix-main.cpp
@@ -22,7 +22,7 @@
#include "common/scummsys.h"
-#if defined(POSIX) && !defined(MACOSX) && !defined(SAMSUNGTV) && !defined(MAEMO) && !defined(WEBOS) && !defined(LINUXMOTO) && !defined(GPH_DEVICE) && !defined(GP2X) && !defined(DINGUX) && !defined(OPENPANDORA) && !defined(PLAYSTATION3) && !defined(ANDROIDSDL)
+#if defined(POSIX) && !defined(MACOSX) && !defined(SAMSUNGTV) && !defined(MAEMO) && !defined(WEBOS) && !defined(LINUXMOTO) && !defined(GPH_DEVICE) && !defined(GP2X) && !defined(DINGUX) && !defined(OPENPANDORA) && !defined(PLAYSTATION3) && !defined(PSP2) && !defined(ANDROIDSDL)
#include "backends/platform/sdl/posix/posix.h"
#include "backends/plugins/sdl/sdl-provider.h"
diff --git a/backends/platform/sdl/sdl.cpp b/backends/platform/sdl/sdl.cpp
index d39ba5b774e..415eb36ac82 100644
--- a/backends/platform/sdl/sdl.cpp
+++ b/backends/platform/sdl/sdl.cpp
@@ -174,6 +174,11 @@ bool OSystem_SDL::hasFeature(Feature f) {
#if SDL_VERSION_ATLEAST(2, 0, 0)
if (f == kFeatureClipboardSupport) return true;
#endif
+#ifdef JOY_ANALOG
+ if (f == kFeatureJoystickDeadzone) return true;
+#endif
+ if (f == kFeatureKbdMouseSpeed) return true;
+
// ResidualVM specific code:
if (f == kFeatureSideTextures)
return true;
@@ -240,6 +245,16 @@ void OSystem_SDL::initBackend() {
_inited = true;
+ if (!ConfMan.hasKey("kbdmouse_speed")) {
+ ConfMan.registerDefault("kbdmouse_speed", 3);
+ ConfMan.setInt("kbdmouse_speed", 3);
+ }
+#ifdef JOY_ANALOG
+ if (!ConfMan.hasKey("joystick_deadzone")) {
+ ConfMan.registerDefault("joystick_deadzone", 3);
+ ConfMan.setInt("joystick_deadzone", 3);
+ }
+#endif
ModularBackend::initBackend();
// We have to initialize the graphics manager before the event manager
@@ -474,7 +489,7 @@ Common::String OSystem_SDL::getSystemLanguage() const {
char langName[9];
char ctryName[9];
- const LCID languageIdentifier = GetThreadLocale();
+ const LCID languageIdentifier = GetUserDefaultUILanguage();
if (GetLocaleInfo(languageIdentifier, LOCALE_SISO639LANGNAME, langName, sizeof(langName)) != 0 &&
GetLocaleInfo(languageIdentifier, LOCALE_SISO3166CTRYNAME, ctryName, sizeof(ctryName)) != 0) {
@@ -612,3 +627,11 @@ Common::SaveFileManager *OSystem_SDL::getSavefileManager() {
return _savefileManager;
#endif
}
+
+//Not specified in base class
+Common::String OSystem_SDL::getScreenshotsPath() {
+ Common::String path = ConfMan.get("screenshotpath");
+ if (!path.empty() && !path.hasSuffix("/"))
+ path += "/";
+ return path;
+}
diff --git a/backends/platform/sdl/sdl.h b/backends/platform/sdl/sdl.h
index 243ec261de3..e9c3f756eee 100644
--- a/backends/platform/sdl/sdl.h
+++ b/backends/platform/sdl/sdl.h
@@ -85,6 +85,9 @@ public:
virtual Common::TimerManager *getTimerManager();
virtual Common::SaveFileManager *getSavefileManager();
+ //Screenshots
+ virtual Common::String getScreenshotsPath();
+
// ResidualVM specific code
virtual void setupScreen(uint screenW, uint screenH, bool fullscreen, bool accel3d) override;
// ResidualVM specific code
diff --git a/backends/platform/sdl/win32/win32.cpp b/backends/platform/sdl/win32/win32.cpp
index a339417560f..3e52085d8a4 100644
--- a/backends/platform/sdl/win32/win32.cpp
+++ b/backends/platform/sdl/win32/win32.cpp
@@ -29,6 +29,11 @@
#include
#undef ARRAYSIZE // winnt.h defines ARRAYSIZE, but we want our own one...
#include
+#if defined(__GNUC__) && defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
+// required for SHGFP_TYPE_CURRENT in shlobj.h
+#define _WIN32_IE 0x500
+#endif
+#include
#include "common/scummsys.h"
#include "common/config-manager.h"
@@ -145,6 +150,34 @@ bool OSystem_Win32::openUrl(const Common::String &url) {
return true;
}
+Common::String OSystem_Win32::getScreenshotsPath() {
+ Common::String screenshotsPath = ConfMan.get("screenshotpath");
+ if (!screenshotsPath.empty()) {
+ if (!screenshotsPath.hasSuffix("\\") && !screenshotsPath.hasSuffix("/"))
+ screenshotsPath += "\\";
+ return screenshotsPath;
+ }
+
+ char picturesPath[MAXPATHLEN];
+
+ // Use the My Pictures folder.
+ if (SHGetFolderPath(NULL, CSIDL_MYPICTURES, NULL, SHGFP_TYPE_CURRENT, picturesPath) != S_OK) {
+ warning("Unable to access My Pictures directory");
+ return Common::String();
+ }
+
+ screenshotsPath = Common::String(picturesPath) + "\\ResidualVM Screenshots\\";
+
+ // If the directory already exists (as it should in most cases),
+ // we don't want to fail, but we need to stop on other errors (such as ERROR_PATH_NOT_FOUND)
+ if (!CreateDirectory(screenshotsPath.c_str(), NULL)) {
+ if (GetLastError() != ERROR_ALREADY_EXISTS)
+ error("Cannot create ResidualVM Screenshots folder");
+ }
+
+ return screenshotsPath;
+}
+
Common::String OSystem_Win32::getDefaultConfigFileName() {
char configFile[MAXPATHLEN];
diff --git a/backends/platform/sdl/win32/win32.h b/backends/platform/sdl/win32/win32.h
index 636ebae88f1..6764a7fe49f 100644
--- a/backends/platform/sdl/win32/win32.h
+++ b/backends/platform/sdl/win32/win32.h
@@ -38,6 +38,8 @@ public:
virtual bool openUrl(const Common::String &url);
+ virtual Common::String getScreenshotsPath();
+
protected:
/**
* The path of the currently open log file, if any.
diff --git a/common/EventDispatcher.cpp b/common/EventDispatcher.cpp
index 2650d795b7e..5c8a9a31198 100644
--- a/common/EventDispatcher.cpp
+++ b/common/EventDispatcher.cpp
@@ -70,6 +70,15 @@ void EventDispatcher::dispatch() {
}
}
+void EventDispatcher::clearEvents() {
+ Event event;
+
+ for (List::iterator i = _sources.begin(); i != _sources.end(); ++i) {
+ while (i->source->pollEvent(event)) {}
+ }
+}
+
+
void EventDispatcher::registerMapper(EventMapper *mapper, bool autoFree) {
if (_autoFreeMapper) {
delete _mapper;
diff --git a/common/EventMapper.cpp b/common/EventMapper.cpp
index cf65946d507..f84d24b4d05 100644
--- a/common/EventMapper.cpp
+++ b/common/EventMapper.cpp
@@ -35,7 +35,6 @@ List DefaultEventMapper::mapEvent(const Event &ev, EventSource *source) {
// of middle mouse button.
const uint32 vkeybdTime = 1000;
- static bool vkeybd = false;
static uint32 vkeybdThen = 0;
if (ev.type == EVENT_MBUTTONDOWN) {
diff --git a/common/bitstream.h b/common/bitstream.h
index 325118bbee9..1fbc0065ab5 100644
--- a/common/bitstream.h
+++ b/common/bitstream.h
@@ -29,53 +29,10 @@
#include "common/textconsole.h"
#include "common/stream.h"
#include "common/types.h"
+#include "common/util.h"
namespace Common {
-/** A bit stream. */
-class BitStream {
-public:
- virtual ~BitStream() {
- }
-
- /** Return the stream position in bits. */
- virtual uint32 pos() const = 0;
-
- /** Return the stream size in bits. */
- virtual uint32 size() const = 0;
-
- /** Has the end of the stream been reached? */
- virtual bool eos() const = 0;
-
- /** Rewind the bit stream back to the start. */
- virtual void rewind() = 0;
-
- /** Skip the specified amount of bits. */
- virtual void skip(uint32 n) = 0;
-
- /** Skip the bits to closest data value border. */
- virtual void align() = 0;
-
- /** Read a bit from the bit stream. */
- virtual uint32 getBit() = 0;
-
- /** Read a multi-bit value from the bit stream. */
- virtual uint32 getBits(uint8 n) = 0;
-
- /** Read a bit from the bit stream, without changing the stream's position. */
- virtual uint32 peekBit() = 0;
-
- /** Read a multi-bit value from the bit stream, without changing the stream's position. */
- virtual uint32 peekBits(uint8 n) = 0;
-
- /** Add a bit to the value x, making it an n+1-bit value. */
- virtual void addBit(uint32 &x, uint32 n) = 0;
-
-protected:
- BitStream() {
- }
-};
-
/**
* A template implementing a bit stream for different data memory layouts.
*
@@ -86,14 +43,16 @@ protected:
* for valueBits, isLE and isMSB2LSB, reads 32bit little-endian values
* from the data stream and hands out the bits in the order of LSB to MSB.
*/
-template
-class BitStreamImpl : public BitStream {
+template
+class BitStreamImpl {
private:
- SeekableReadStream *_stream; ///< The input stream.
+ STREAM *_stream; ///< The input stream.
DisposeAfterUse::Flag _disposeAfterUse; ///< Should we delete the stream on destruction?
uint32 _value; ///< Current value.
uint8 _inValue; ///< Position within the current value.
+ uint32 _size; ///< Total bitstream size (in bits)
+ uint32 _pos; ///< Current bitstream position (in bits)
/** Read a data value. */
inline uint32 readData() {
@@ -119,7 +78,7 @@ private:
/** Read the next data value. */
inline void readValue() {
- if ((size() - pos()) < valueBits)
+ if (_size - _pos < valueBits)
error("BitStreamImpl::readValue(): End of bit stream reached");
_value = readData();
@@ -133,19 +92,23 @@ private:
public:
/** Create a bit stream using this input data stream and optionally delete it on destruction. */
- BitStreamImpl(SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::NO) :
- _stream(stream), _disposeAfterUse(disposeAfterUse), _value(0), _inValue(0) {
+ BitStreamImpl(STREAM *stream, DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::NO) :
+ _stream(stream), _disposeAfterUse(disposeAfterUse), _value(0), _inValue(0), _pos(0) {
if ((valueBits != 8) && (valueBits != 16) && (valueBits != 32))
error("BitStreamImpl: Invalid memory layout %d, %d, %d", valueBits, isLE, isMSB2LSB);
+
+ _size = (_stream->size() & ~((uint32) ((valueBits >> 3) - 1))) * 8;
}
/** Create a bit stream using this input data stream. */
- BitStreamImpl(SeekableReadStream &stream) :
- _stream(&stream), _disposeAfterUse(DisposeAfterUse::NO), _value(0), _inValue(0) {
+ BitStreamImpl(STREAM &stream) :
+ _stream(&stream), _disposeAfterUse(DisposeAfterUse::NO), _value(0), _inValue(0), _pos(0) {
if ((valueBits != 8) && (valueBits != 16) && (valueBits != 32))
error("BitStreamImpl: Invalid memory layout %d, %d, %d", valueBits, isLE, isMSB2LSB);
+
+ _size = (_stream->size() & ~((uint32) ((valueBits >> 3) - 1))) * 8;
}
~BitStreamImpl() {
@@ -153,14 +116,10 @@ public:
delete _stream;
}
- /** Read a bit from the bit stream. */
- uint32 getBit() {
- // Check if we need the next value
- if (_inValue == 0)
- readValue();
-
+private:
+ uint32 getBit_internal() {
// Get the current bit
- int b = 0;
+ uint32 b = 0;
if (isMSB2LSB)
b = ((_value & 0x80000000) == 0) ? 0 : 1;
else
@@ -172,8 +131,21 @@ public:
else
_value >>= 1;
+ return b;
+ }
+
+public:
+ /** Read a bit from the bit stream. */
+ uint32 getBit() {
+ // Check if we need the next value
+ if (_inValue == 0)
+ readValue();
+
+ uint32 b = getBit_internal();
+
// Increase the position within the current value
_inValue = (_inValue + 1) % valueBits;
+ _pos++;
return b;
}
@@ -198,16 +170,42 @@ public:
// Read the number of bits
uint32 v = 0;
- if (isMSB2LSB) {
- while (n-- > 0)
- v = (v << 1) | getBit();
- } else {
- for (uint32 i = 0; i < n; i++)
- v = (v >> 1) | (((uint32) getBit()) << 31);
+ uint8 nOrig = n;
+ if (_inValue) {
+ int count = MIN((int)n, valueBits - _inValue);
+ for (int i = 0; i < count; ++i) {
+ if (isMSB2LSB) {
+ v = (v << 1) | getBit_internal();
+ } else {
+ v = (v >> 1) | (getBit_internal() << 31);
+ }
+ }
- v >>= (32 - n);
+ n -= count;
}
+ while (n > 0) {
+ // NB: readValue doesn't care that _inValue is incorrect here
+ readValue();
+
+ int count = MIN((int)n, valueBits);
+ for (int i = 0; i < count; ++i) {
+ if (isMSB2LSB) {
+ v = (v << 1) | getBit_internal();
+ } else {
+ v = (v >> 1) | (getBit_internal() << 31);
+ }
+ }
+
+ n -= count;
+ }
+
+ _inValue = (_inValue + nOrig) % valueBits;
+ _pos += nOrig;
+
+ if (!isMSB2LSB)
+ v >>= (32 - nOrig);
+
return v;
}
@@ -215,11 +213,13 @@ public:
uint32 peekBit() {
uint32 value = _value;
uint8 inValue = _inValue;
- uint32 curPos = _stream->pos();
+ uint32 curStreamPos = _stream->pos();
+ uint32 curPos = _pos;
uint32 v = getBit();
- _stream->seek(curPos);
+ _pos = curPos;
+ _stream->seek(curStreamPos);
_inValue = inValue;
_value = value;
@@ -234,11 +234,13 @@ public:
uint32 peekBits(uint8 n) {
uint32 value = _value;
uint8 inValue = _inValue;
- uint32 curPos = _stream->pos();
+ uint32 curStreamPos = _stream->pos();
+ uint32 curPos = _pos;
uint32 v = getBits(n);
- _stream->seek(curPos);
+ _pos = curPos;
+ _stream->seek(curStreamPos);
_inValue = inValue;
_value = value;
@@ -272,6 +274,7 @@ public:
_value = 0;
_inValue = 0;
+ _pos = 0;
}
/** Skip the specified amount of bits. */
@@ -288,47 +291,215 @@ public:
/** Return the stream position in bits. */
uint32 pos() const {
- if (_stream->pos() == 0)
- return 0;
-
- uint32 p = (_inValue == 0) ? _stream->pos() : ((_stream->pos() - 1) & ~((uint32) ((valueBits >> 3) - 1)));
- return p * 8 + _inValue;
+ return _pos;
}
/** Return the stream size in bits. */
uint32 size() const {
- return (_stream->size() & ~((uint32) ((valueBits >> 3) - 1))) * 8;
+ return _size;
}
bool eos() const {
- return _stream->eos() || (pos() >= size());
+ return _stream->eos() || (_pos >= _size);
}
};
+
+
+/**
+ * A cut-down version of MemoryReadStream specifically for use with BitStream.
+ * It removes the virtual call overhead for reading bytes from a memory buffer,
+ * and allows directly inlining this access.
+ *
+ * The code duplication with MemoryReadStream is not ideal.
+ * It might be possible to avoid this by making this a final subclass of
+ * MemoryReadStream, but that is a C++11 feature.
+ */
+class BitStreamMemoryStream {
+private:
+ const byte * const _ptrOrig;
+ const byte *_ptr;
+ const uint32 _size;
+ uint32 _pos;
+ DisposeAfterUse::Flag _disposeMemory;
+ bool _eos;
+
+public:
+ BitStreamMemoryStream(const byte *dataPtr, uint32 dataSize, DisposeAfterUse::Flag disposeMemory = DisposeAfterUse::NO) :
+ _ptrOrig(dataPtr),
+ _ptr(dataPtr),
+ _size(dataSize),
+ _pos(0),
+ _disposeMemory(disposeMemory),
+ _eos(false) {}
+
+ ~BitStreamMemoryStream() {
+ if (_disposeMemory)
+ free(const_cast(_ptrOrig));
+ }
+
+ bool eos() const {
+ return _eos;
+ }
+
+ bool err() const {
+ return false;
+ }
+
+ int32 pos() const {
+ return _pos;
+ }
+
+ int32 size() const {
+ return _size;
+ }
+
+ bool seek(uint32 offset) {
+ assert(offset <= _size);
+
+ _eos = false;
+ _pos = offset;
+ _ptr = _ptrOrig + _pos;
+ return true;
+ }
+
+ byte readByte() {
+ if (_pos >= _size) {
+ _eos = true;
+ return 0;
+ }
+
+ _pos++;
+ return *_ptr++;
+ }
+
+ uint16 readUint16LE() {
+ if (_pos + 2 > _size) {
+ _eos = true;
+ if (_pos < _size) {
+ _pos++;
+ return *_ptr++;
+ } else {
+ return 0;
+ }
+ }
+
+ uint16 val = READ_LE_UINT16(_ptr);
+
+ _pos += 2;
+ _ptr += 2;
+
+ return val;
+ }
+
+ uint16 readUint16BE() {
+ if (_pos + 2 > _size) {
+ _eos = true;
+ if (_pos < _size) {
+ _pos++;
+ return (*_ptr++) << 8;
+ } else {
+ return 0;
+ }
+ }
+
+ uint16 val = READ_LE_UINT16(_ptr);
+
+ _pos += 2;
+ _ptr += 2;
+
+ return val;
+ }
+
+ uint32 readUint32LE() {
+ if (_pos + 4 > _size) {
+ uint32 val = readByte();
+ val |= (uint32)readByte() << 8;
+ val |= (uint32)readByte() << 16;
+ val |= (uint32)readByte() << 24;
+
+ return val;
+ }
+
+ uint32 val = READ_LE_UINT32(_ptr);
+
+ _pos += 4;
+ _ptr += 4;
+
+ return val;
+ }
+
+ uint32 readUint32BE() {
+ if (_pos + 4 > _size) {
+ uint32 val = (uint32)readByte() << 24;
+ val |= (uint32)readByte() << 16;
+ val |= (uint32)readByte() << 8;
+ val |= (uint32)readByte();
+
+ return val;
+ }
+
+ uint32 val = READ_BE_UINT32(_ptr);
+
+ _pos += 4;
+ _ptr += 4;
+
+ return val;
+ }
+
+};
+
+
// typedefs for various memory layouts.
/** 8-bit data, MSB to LSB. */
-typedef BitStreamImpl<8, false, true > BitStream8MSB;
+typedef BitStreamImpl BitStream8MSB;
/** 8-bit data, LSB to MSB. */
-typedef BitStreamImpl<8, false, false> BitStream8LSB;
+typedef BitStreamImpl BitStream8LSB;
/** 16-bit little-endian data, MSB to LSB. */
-typedef BitStreamImpl<16, true , true > BitStream16LEMSB;
+typedef BitStreamImpl BitStream16LEMSB;
/** 16-bit little-endian data, LSB to MSB. */
-typedef BitStreamImpl<16, true , false> BitStream16LELSB;
+typedef BitStreamImpl BitStream16LELSB;
/** 16-bit big-endian data, MSB to LSB. */
-typedef BitStreamImpl<16, false, true > BitStream16BEMSB;
+typedef BitStreamImpl BitStream16BEMSB;
/** 16-bit big-endian data, LSB to MSB. */
-typedef BitStreamImpl<16, false, false> BitStream16BELSB;
+typedef BitStreamImpl BitStream16BELSB;
/** 32-bit little-endian data, MSB to LSB. */
-typedef BitStreamImpl<32, true , true > BitStream32LEMSB;
+typedef BitStreamImpl BitStream32LEMSB;
/** 32-bit little-endian data, LSB to MSB. */
-typedef BitStreamImpl<32, true , false> BitStream32LELSB;
+typedef BitStreamImpl BitStream32LELSB;
/** 32-bit big-endian data, MSB to LSB. */
-typedef BitStreamImpl<32, false, true > BitStream32BEMSB;
+typedef BitStreamImpl BitStream32BEMSB;
/** 32-bit big-endian data, LSB to MSB. */
-typedef BitStreamImpl<32, false, false> BitStream32BELSB;
+typedef BitStreamImpl BitStream32BELSB;
+
+
+
+/** 8-bit data, MSB to LSB. */
+typedef BitStreamImpl BitStreamMemory8MSB;
+/** 8-bit data, LSB to MSB. */
+typedef BitStreamImpl BitStreamMemory8LSB;
+
+/** 16-bit little-endian data, MSB to LSB. */
+typedef BitStreamImpl BitStreamMemory16LEMSB;
+/** 16-bit little-endian data, LSB to MSB. */
+typedef BitStreamImpl BitStreamMemory16LELSB;
+/** 16-bit big-endian data, MSB to LSB. */
+typedef BitStreamImpl BitStreamMemory16BEMSB;
+/** 16-bit big-endian data, LSB to MSB. */
+typedef BitStreamImpl BitStreamMemory16BELSB;
+
+/** 32-bit little-endian data, MSB to LSB. */
+typedef BitStreamImpl BitStreamMemory32LEMSB;
+/** 32-bit little-endian data, LSB to MSB. */
+typedef BitStreamImpl BitStreamMemory32LELSB;
+/** 32-bit big-endian data, MSB to LSB. */
+typedef BitStreamImpl BitStreamMemory32BEMSB;
+/** 32-bit big-endian data, LSB to MSB. */
+typedef BitStreamImpl BitStreamMemory32BELSB;
+
} // End of namespace Common
diff --git a/common/debug-channels.h b/common/debug-channels.h
index 1414a1053a5..0fb80068038 100644
--- a/common/debug-channels.h
+++ b/common/debug-channels.h
@@ -117,7 +117,7 @@ public:
/**
* Test whether the given debug channel is enabled.
*/
- bool isDebugChannelEnabled(uint32 channel);
+ bool isDebugChannelEnabled(uint32 channel, bool enforce = false);
private:
typedef HashMap DebugChannelMap;
diff --git a/common/debug.cpp b/common/debug.cpp
index c61fc63deac..5db8990db80 100644
--- a/common/debug.cpp
+++ b/common/debug.cpp
@@ -110,9 +110,9 @@ void DebugManager::disableAllDebugChannels() {
disableDebugChannel(i->_value.name);
}
-bool DebugManager::isDebugChannelEnabled(uint32 channel) {
+bool DebugManager::isDebugChannelEnabled(uint32 channel, bool enforce) {
// Debug level 11 turns on all special debug level messages
- if (gDebugLevel == 11)
+ if (gDebugLevel == 11 && enforce == false)
return true;
else
return (gDebugChannelsEnabled & channel) != 0;
@@ -125,8 +125,8 @@ bool debugLevelSet(int level) {
}
bool debugChannelSet(int level, uint32 debugChannels) {
- if (gDebugLevel != 11)
- if (level > gDebugLevel || !(DebugMan.isDebugChannelEnabled(debugChannels)))
+ if (gDebugLevel != 11 || level == -1)
+ if ((level != -1 && level > gDebugLevel) || !(DebugMan.isDebugChannelEnabled(debugChannels, level == -1)))
return false;
return true;
diff --git a/common/debug.h b/common/debug.h
index 883a0bf29dc..5ec37f2f1ed 100644
--- a/common/debug.h
+++ b/common/debug.h
@@ -117,6 +117,7 @@ bool debugLevelSet(int level);
/**
* Returns true if the debug level and channel are active
*
+ * @param level debug level to check against. If set to -1, only channel check is active
* @see enableDebugChannel
*/
bool debugChannelSet(int level, uint32 debugChannels);
diff --git a/common/endian.h b/common/endian.h
index 295cff38393..be382c6048e 100644
--- a/common/endian.h
+++ b/common/endian.h
@@ -608,6 +608,38 @@ inline uint32 READ_BE_UINT24(const void *ptr) {
#define READ_UINT24(a) READ_BE_UINT24(a)
#endif
+inline int16 READ_LE_INT16(const void *ptr) {
+ return static_cast(READ_LE_UINT16(ptr));
+}
+
+inline void WRITE_LE_INT16(void *ptr, int16 value) {
+ WRITE_LE_UINT16(ptr, static_cast(value));
+}
+
+inline int16 READ_BE_INT16(const void *ptr) {
+ return static_cast(READ_BE_UINT16(ptr));
+}
+
+inline void WRITE_BE_INT16(void *ptr, int16 value) {
+ WRITE_BE_UINT16(ptr, static_cast(value));
+}
+
+inline int32 READ_LE_INT32(const void *ptr) {
+ return static_cast(READ_LE_UINT32(ptr));
+}
+
+inline void WRITE_LE_INT32(void *ptr, int32 value) {
+ WRITE_LE_UINT32(ptr, static_cast(value));
+}
+
+inline int32 READ_BE_INT32(const void *ptr) {
+ return static_cast(READ_BE_UINT32(ptr));
+}
+
+inline void WRITE_BE_INT32(void *ptr, int32 value) {
+ WRITE_BE_UINT32(ptr, static_cast(value));
+}
+
// ResidualVM specific:
#if defined(SCUMM_NEED_ALIGNMENT)
inline float READ_FLOAT(const char *data) {
diff --git a/common/error.cpp b/common/error.cpp
index 53cd8cec55a..bbbcb7c95e3 100644
--- a/common/error.cpp
+++ b/common/error.cpp
@@ -65,7 +65,7 @@ static String errorToString(ErrorCode errorCode) {
case kEnginePluginNotFound:
return _s("Could not find suitable engine plugin");
case kEnginePluginNotSupportSaves:
- return _s("Engine plugin does not support save states");
+ return _s("Engine plugin does not support saved games");
case kUserCanceled:
return _s("User canceled");
diff --git a/common/events.h b/common/events.h
index ccb6cd7ed20..8e9490618b1 100644
--- a/common/events.h
+++ b/common/events.h
@@ -317,6 +317,12 @@ public:
*/
void dispatch();
+ /**
+ * Clear all events currently in the event queue.
+ * The cleared events are not dispatched and are simply discarded.
+ */
+ void clearEvents();
+
/**
* Registers an event mapper with the dispatcher.
*
diff --git a/common/forbidden.h b/common/forbidden.h
index d6fd9c42fed..8f0220bb04c 100644
--- a/common/forbidden.h
+++ b/common/forbidden.h
@@ -183,7 +183,7 @@
#endif
// mingw-w64 uses [set|long]jmp in system headers
-#ifndef __MINGW64__
+#if !defined __MINGW64__ && ! defined __MINGW32__
#ifndef FORBIDDEN_SYMBOL_EXCEPTION_setjmp
#undef setjmp
#define setjmp(a) FORBIDDEN_SYMBOL_REPLACEMENT
@@ -193,7 +193,7 @@
#undef longjmp
#define longjmp(a,b) FORBIDDEN_SYMBOL_REPLACEMENT
#endif
-#endif // __MINGW64__
+#endif // __MINGW64__ __MINGW32__
#ifndef FORBIDDEN_SYMBOL_EXCEPTION_system
#undef system
diff --git a/common/gui_options.cpp b/common/gui_options.cpp
index df880f4fee8..500830a303c 100644
--- a/common/gui_options.cpp
+++ b/common/gui_options.cpp
@@ -29,6 +29,8 @@ namespace Common {
const struct GameOpt {
const char *option;
+ // Each description must be a unique identifier not containing a substring
+ // of any other description
const char *desc;
} g_gameOptions[] = {
{ GUIO_NOSUBTITLES, "sndNoSubs" },
@@ -36,6 +38,9 @@ const struct GameOpt {
{ GUIO_NOSPEECH, "sndNoSpeech" },
{ GUIO_NOSFX, "sndNoSFX" },
{ GUIO_NOMIDI, "sndNoMIDI" },
+ { GUIO_LINKSPEECHTOSFX, "sndLinkSpeechToSfx" },
+ { GUIO_LINKMUSICTOSFX, "sndLinkMusicToSfx" },
+ { GUIO_NOSPEECHVOLUME, "sndNoSpchVolume" },
{ GUIO_NOLAUNCHLOAD, "launchNoLoad" },
@@ -75,6 +80,9 @@ const struct GameOpt {
{ GUIO_GAMEOPTIONS7, "gameOption7" },
{ GUIO_GAMEOPTIONS8, "gameOption8" },
{ GUIO_GAMEOPTIONS9, "gameOption9" },
+ // Option strings must not contain substrings of any other options, so
+ // "gameOption10" would be invalid here because it contains "gameOption1"
+ { GUIO_GAMEOPTIONS10, "gameOptionA" },
{ GUIO_NONE, 0 }
};
diff --git a/common/gui_options.h b/common/gui_options.h
index d17f45cac19..6c1aa2fdcd6 100644
--- a/common/gui_options.h
+++ b/common/gui_options.h
@@ -26,6 +26,7 @@
#define GUIO_NONE "\000"
#define GUIO_NOSUBTITLES "\001"
#define GUIO_NOMUSIC "\002"
+// GUIO_NOSPEECH is a combination of GUIO_NOSPEECHVOLUME and GUIO_NOSUBTITLES
#define GUIO_NOSPEECH "\003"
#define GUIO_NOSFX "\004"
#define GUIO_NOMIDI "\005"
@@ -58,6 +59,10 @@
#define GUIO_RENDERATARIST "\042"
#define GUIO_RENDERMACINTOSH "\043"
+#define GUIO_LINKSPEECHTOSFX "\044"
+#define GUIO_LINKMUSICTOSFX "\045"
+#define GUIO_NOSPEECHVOLUME "\046"
+
// Special GUIO flags for the AdvancedDetector's caching of game specific
// options.
#define GUIO_GAMEOPTIONS1 "\050"
@@ -69,6 +74,7 @@
#define GUIO_GAMEOPTIONS7 "\056"
#define GUIO_GAMEOPTIONS8 "\057"
#define GUIO_GAMEOPTIONS9 "\060"
+#define GUIO_GAMEOPTIONS10 "\061"
#define GUIO0() (GUIO_NONE)
#define GUIO1(a) (a)
diff --git a/common/huffman.cpp b/common/huffman.cpp
index afb4fa00b6a..3268ddf2513 100644
--- a/common/huffman.cpp
+++ b/common/huffman.cpp
@@ -68,19 +68,4 @@ void Huffman::setSymbols(const uint32 *symbols) {
_symbols[i]->symbol = symbols ? *symbols++ : i;
}
-uint32 Huffman::getSymbol(BitStream &bits) const {
- uint32 code = 0;
-
- for (uint32 i = 0; i < _codes.size(); i++) {
- bits.addBit(code, i);
-
- for (CodeList::const_iterator cCode = _codes[i].begin(); cCode != _codes[i].end(); ++cCode)
- if (code == cCode->code)
- return cCode->symbol;
- }
-
- error("Unknown Huffman code");
- return 0;
-}
-
} // End of namespace Common
diff --git a/common/huffman.h b/common/huffman.h
index 5ba7deca87e..f703d078dce 100644
--- a/common/huffman.h
+++ b/common/huffman.h
@@ -31,8 +31,6 @@
namespace Common {
-class BitStream;
-
/**
* Huffman bitstream decoding
*
@@ -56,7 +54,21 @@ public:
void setSymbols(const uint32 *symbols = 0);
/** Return the next symbol in the bitstream. */
- uint32 getSymbol(BitStream &bits) const;
+ template
+ uint32 getSymbol(BITSTREAM &bits) const {
+ uint32 code = 0;
+
+ for (uint32 i = 0; i < _codes.size(); i++) {
+ bits.addBit(code, i);
+
+ for (CodeList::const_iterator cCode = _codes[i].begin(); cCode != _codes[i].end(); ++cCode)
+ if (code == cCode->code)
+ return cCode->symbol;
+ }
+
+ error("Unknown Huffman code");
+ return 0;
+ }
private:
struct Symbol {
diff --git a/common/json.cpp b/common/json.cpp
index 792d1967e9c..c8caf015199 100644
--- a/common/json.cpp
+++ b/common/json.cpp
@@ -979,17 +979,13 @@ String JSONValue::stringifyImpl(size_t const indentDepth) const {
if (isinf(_numberValue) || isnan(_numberValue))
ret_string = "null";
else {
- char str[80];
- sprintf(str, "%g", _numberValue);
- ret_string = str;
+ ret_string = String::format("%g", _numberValue);
}
break;
}
case JSONType_IntegerNumber: {
- char str[80];
- sprintf(str, "%lld", _integerValue);
- ret_string = str;
+ ret_string = String::format("%lld", _integerValue);
break;
}
diff --git a/common/language.cpp b/common/language.cpp
index 97c515b45a3..b7397bec6d7 100644
--- a/common/language.cpp
+++ b/common/language.cpp
@@ -35,6 +35,7 @@ const LanguageDescription g_languages[] = {
{ "en", "en", "English", EN_ANY }, // Generic English (when only one game version exist)
{ "gb", "en_GB", "English (GB)", EN_GRB },
{ "us", "en_US", "English (US)", EN_USA },
+ { "et", "et_EE", "Estonian", ET_EST },
{ "fr", "fr_FR", "French", FR_FRA },
{ "de", "de_DE", "German", DE_DEU },
{ "gr", "el_GR", "Greek", GR_GRE },
diff --git a/common/language.h b/common/language.h
index ac3c7267ab0..af2c252d61d 100644
--- a/common/language.h
+++ b/common/language.h
@@ -41,6 +41,7 @@ enum Language {
EN_ANY, // Generic English (when only one game version exist)
EN_GRB,
EN_USA,
+ ET_EST,
FR_FRA,
DE_DEU,
GR_GRE,
diff --git a/common/macresman.h b/common/macresman.h
index 05b2a875f42..1be825b7b46 100644
--- a/common/macresman.h
+++ b/common/macresman.h
@@ -176,6 +176,11 @@ public:
*/
MacResTagArray getResTagArray();
+ /**
+ * Load from stream in MacBinary format
+ */
+ bool loadFromMacBinary(SeekableReadStream &stream);
+
private:
SeekableReadStream *_stream;
String _baseFileName;
@@ -183,7 +188,6 @@ private:
bool load(SeekableReadStream &stream);
bool loadFromRawFork(SeekableReadStream &stream);
- bool loadFromMacBinary(SeekableReadStream &stream);
bool loadFromAppleDouble(SeekableReadStream &stream);
static String constructAppleDoubleName(String name);
diff --git a/common/memstream.h b/common/memstream.h
index 25fdde91c70..0338d35378a 100644
--- a/common/memstream.h
+++ b/common/memstream.h
@@ -212,13 +212,14 @@ public:
/**
* MemoryStream based on RingBuffer. Grows if has insufficient buffer size.
*/
-class MemoryReadWriteStream : public WriteStream {
+class MemoryReadWriteStream : public SeekableReadStream, public WriteStream {
private:
uint32 _capacity;
uint32 _size;
byte *_data;
uint32 _writePos, _readPos, _pos, _length;
DisposeAfterUse::Flag _disposeMemory;
+ bool _eos;
void ensureCapacity(uint32 new_len) {
if (new_len <= _capacity)
@@ -246,7 +247,7 @@ private:
}
}
public:
- MemoryReadWriteStream(DisposeAfterUse::Flag disposeMemory = DisposeAfterUse::NO) : _capacity(0), _size(0), _data(0), _writePos(0), _readPos(0), _pos(0), _length(0), _disposeMemory(disposeMemory) {}
+ MemoryReadWriteStream(DisposeAfterUse::Flag disposeMemory = DisposeAfterUse::NO) : _capacity(0), _size(0), _data(0), _writePos(0), _readPos(0), _pos(0), _length(0), _disposeMemory(disposeMemory), _eos(false) {}
~MemoryReadWriteStream() {
if (_disposeMemory)
@@ -271,8 +272,10 @@ public:
}
virtual uint32 read(void *dataPtr, uint32 dataSize) {
- uint32 length = _length;
- if (length < dataSize) dataSize = length;
+ if (_length < dataSize) {
+ dataSize = _length;
+ _eos = true;
+ }
if (dataSize == 0 || _capacity == 0) return 0;
if (_readPos + dataSize < _capacity) {
memcpy(dataPtr, _data + _readPos, dataSize);
@@ -287,7 +290,10 @@ public:
}
int32 pos() const { return _pos - _length; } //'read' position in the stream
- uint32 size() const { return _size; } //that's also 'write' position in the stream, as it's append-only
+ int32 size() const { return _size; } //that's also 'write' position in the stream, as it's append-only
+ bool seek(int32, int) { return false; }
+ bool eos() const { return _eos; }
+ void clearErr() { _eos = false; }
byte *getData() { return _data; }
};
diff --git a/common/scummsys.h b/common/scummsys.h
index 153db3c623e..785978764dd 100644
--- a/common/scummsys.h
+++ b/common/scummsys.h
@@ -132,6 +132,7 @@
#include
#include
#include
+ #include
#include
#include
// MSVC does not define M_PI, M_SQRT2 and other math defines by default.
@@ -152,6 +153,19 @@
#endif
#endif
+#ifndef STATIC_ASSERT
+ /**
+ * Generates a compile-time assertion.
+ *
+ * @param expression An expression that can be evaluated at compile time.
+ * @param message An underscore-delimited message to be presented at compile
+ * time if the expression evaluates to false.
+ */
+ #define STATIC_ASSERT(expression, message) \
+ extern int STATIC_ASSERT_##message[(expression) ? 1 : -1]; \
+ (void)(STATIC_ASSERT_##message);
+#endif
+
// The following math constants are usually defined by the system math.h header, but
// they are not part of the ANSI C++ standards and so can NOT be relied upon to be
// present i.e. when -std=c++11 is passed to GCC, enabling strict ANSI compliance.
@@ -295,6 +309,25 @@
#endif
#endif
+//
+// Determine 64 bitness
+// Reference: http://nadeausoftware.com/articles/2012/02/c_c_tip_how_detect_processor_type_using_compiler_predefined_macros
+//
+#if !defined(HAVE_CONFIG_H)
+
+ #if defined(__x86_64__) || \
+ defined(_M_X64) || \
+ defined(__ppc64__) || \
+ defined(__powerpc64__) || \
+ defined(__LP64__)
+
+ #if !defined(SCUMM_64BITS)
+ #define SCUMM_64BITS
+ #endif
+
+ #endif
+
+#endif
//
// Some more system specific settings.
diff --git a/common/serializer.h b/common/serializer.h
index ef8fca9906b..2def17cfbd2 100644
--- a/common/serializer.h
+++ b/common/serializer.h
@@ -144,6 +144,10 @@ public:
*/
Version getVersion() const { return _version; }
+ /**
+ * Manually set the version
+ */
+ void setVersion(Version version) { _version = version; }
/**
* Return the total number of bytes synced so far.
diff --git a/common/str.cpp b/common/str.cpp
index 90bd5397908..3a0fd6a08e8 100644
--- a/common/str.cpp
+++ b/common/str.cpp
@@ -942,6 +942,13 @@ size_t strlcat(char *dst, const char *src, size_t size) {
return dstLength + (src - srcStart);
}
+size_t strnlen(const char *src, size_t maxSize) {
+ size_t counter = 0;
+ while (counter != maxSize && *src++)
+ ++counter;
+ return counter;
+}
+
} // End of namespace Common
// Portable implementation of stricmp / strcasecmp / strcmpi.
diff --git a/common/str.h b/common/str.h
index d55ba072a9f..ba1e0b83419 100644
--- a/common/str.h
+++ b/common/str.h
@@ -444,6 +444,17 @@ size_t strlcpy(char *dst, const char *src, size_t size);
*/
size_t strlcat(char *dst, const char *src, size_t size);
+/**
+ * Determine the length of a string up to a maximum of `maxSize` characters.
+ * This should be used instead of `strlen` when reading the length of a C string
+ * from potentially unsafe or corrupt sources, like game assets.
+ *
+ * @param src The source string.
+ * @param maxSize The maximum size of the string.
+ * @return The length of the string.
+ */
+size_t strnlen(const char *src, size_t maxSize);
+
/**
* Convenience wrapper for tag2string which "returns" a C string.
* Note: It is *NOT* safe to do anything with the return value other than directly
diff --git a/common/stream.cpp b/common/stream.cpp
index 08774312fdf..8d938880208 100644
--- a/common/stream.cpp
+++ b/common/stream.cpp
@@ -39,6 +39,27 @@ SeekableReadStream *ReadStream::readStream(uint32 dataSize) {
return new MemoryReadStream((byte *)buf, dataSize, DisposeAfterUse::YES);
}
+Common::String ReadStream::readPascalString(bool transformCR) {
+ Common::String s;
+ char *buf;
+ int len;
+ int i;
+
+ len = readByte();
+ buf = (char *)malloc(len + 1);
+ for (i = 0; i < len; i++) {
+ buf[i] = readByte();
+ if (transformCR && buf[i] == 0x0d)
+ buf[i] = '\n';
+ }
+
+ buf[i] = 0;
+
+ s = buf;
+ free(buf);
+
+ return s;
+}
uint32 MemoryReadStream::read(void *dataPtr, uint32 dataSize) {
// Read at most as many bytes as are still available...
diff --git a/common/stream.h b/common/stream.h
index 30107720dc7..6932c7d2d0b 100644
--- a/common/stream.h
+++ b/common/stream.h
@@ -185,6 +185,32 @@ public:
}
#endif
+
+ /**
+ * Write the given 32-bit floating point value stored
+ * in little endian(LSB first) order into the stream.
+ */
+ FORCEINLINE void writeFloatLE(float value) {
+ uint32 n;
+
+ memcpy(&n, &value, 4);
+
+ writeUint32LE(n);
+ }
+
+
+ /**
+ * Write the given 32-bit floating point value stored
+ * in big endian order into the stream.
+ */
+ FORCEINLINE void writeFloatBE(float value) {
+ uint32 n;
+
+ memcpy(&n, &value, 4);
+
+ writeUint32BE(n);
+ }
+
/**
* Write the given string to the stream.
* This writes str.size() characters, but no terminating zero byte.
@@ -417,6 +443,22 @@ public:
return f;
}
+ /**
+ * Read a 32-bit floating point value stored in big endian
+ * order from the stream and return it.
+ * Performs no error checking. The return value is undefined
+ * if a read error occurred (for which client code can check by
+ * calling err() and eos() ).
+ */
+ FORCEINLINE float readFloatBE() {
+ uint32 n = readUint32BE();
+ float f;
+
+ memcpy(&f, &n, 4);
+
+ return f;
+ }
+
/**
* Read the specified amount of data into a malloc'ed buffer
* which then is wrapped into a MemoryReadStream.
@@ -427,6 +469,14 @@ public:
*/
SeekableReadStream *readStream(uint32 dataSize);
+ /**
+ * Read stream in Pascal format, that is, one byte is
+ * string length, followed by string data
+ *
+ * @param transformCR if set (default), then transform \r into \n
+ */
+ Common::String readPascalString(bool transformCR = true);
+
};
diff --git a/common/system.h b/common/system.h
index e25feee3018..fb086a6ece8 100644
--- a/common/system.h
+++ b/common/system.h
@@ -256,7 +256,7 @@ public:
* particular, interpolation, and works in-place.
*/
kFeatureAspectRatioCorrection,
-
+
/**
* If supported this flag can be used to switch between unfiltered and
* filtered graphics modes.
@@ -366,7 +366,38 @@ public:
*
* This feature has no associated state.
*/
- kFeatureOpenUrl
+ kFeatureOpenUrl ,
+
+ /**
+ * show on-screen control
+ */
+ kFeatureOnScreenControl,
+
+ /**
+ * mouse emulation mode
+ */
+ kFeatureTouchpadMode,
+
+ /**
+ * swap menu and back buttons
+ */
+ kFeatureSwapMenuAndBackButtons,
+
+ /**
+ * keyboard mouse and joystick mouse speed
+ */
+ kFeatureKbdMouseSpeed,
+
+ /**
+ * change analog joystick deadzone
+ */
+ kFeatureJoystickDeadzone,
+
+ /**
+ * shaders
+ */
+ kFeatureShader
+
};
/**
@@ -598,6 +629,38 @@ public:
/**
* !!! Not used in ResidualVM !!!
*
+ * Retrieve a list of all hardware shaders supported by this backend.
+ * This can be only hardware shaders.
+ * it is completely up to the backend maintainer to decide what is
+ * appropriate here and what not.
+ * The list is terminated by an all-zero entry.
+ * @return a list of supported shaders
+ */
+ virtual const GraphicsMode *getSupportedShaders() const {
+ static const OSystem::GraphicsMode no_shader[2] = {{"NONE", "Normal (no shader)", 0}, {0, 0, 0}};
+ return no_shader;
+ }
+
+ /**
+ * !!! Not used in ResidualVM !!!
+ *
+ * Switch to the specified shader mode. If switching to the new mode
+ * failed, this method returns false.
+ *
+ * @param mode the ID of the new shader mode
+ * @return true if the switch was successful, false otherwise
+ */
+ virtual bool setShader(int id) { return false; }
+
+ /**
+ * !!! Not used in ResidualVM !!!
+ *
+ * Determine which shader is currently active.
+ * @return the ID of the active shader
+ */
+ virtual int getShader() const { return 0; }
+
+ /**
* Set the size and color format of the virtual screen. Typical sizes include:
* - 320x200 (e.g. for most SCUMM games, and Simon)
* - 320x240 (e.g. for FM-TOWN SCUMM games)
diff --git a/common/zlib.h b/common/zlib.h
index 5adba640761..c8877e98a12 100644
--- a/common/zlib.h
+++ b/common/zlib.h
@@ -111,6 +111,7 @@ bool inflateZlibInstallShield(byte *dst, uint dstLen, const byte *src, uint srcL
* still need the length carried along with the stream, and you know
* the decompressed length at wrap-time, then it can be supplied as knownSize
* here. knownSize will be ignored if the GZip-stream DOES include a length.
+ * The created stream also becomes responsible for freeing the passed stream.
*
* It is safe to call this with a NULL parameter (in this case, NULL is
* returned).
@@ -125,6 +126,7 @@ SeekableReadStream *wrapCompressedReadStream(SeekableReadStream *toBeWrapped, ui
* 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).
+ * The created stream also becomes responsible for freeing the passed stream.
*
* It is safe to call this with a NULL parameter (in this case, NULL is
* returned).
diff --git a/configure b/configure
index 164ad7a0794..bb4e08a470a 100755
--- a/configure
+++ b/configure
@@ -1557,6 +1557,22 @@ ps3)
datadir='${datarootdir}'
docdir='${prefix}/doc'
;;
+psp2)
+ _host_os=psp2
+ _host_cpu=arm
+ _host_alias=arm-vita-eabi
+
+ # The prefix is always the same on PSP2 so we hardcode the default
+ # here. It is still possible to define a custom prefix which is
+ # needed when packaging the app with a user-specific app ID.
+ test "x$prefix" = xNONE && prefix=app0:
+ # PSP2 apps are installed into app-specific directories. The
+ # default directory structure of ScummVM makes no sense here so we
+ # hardcode PSP2 specific directories here.
+ datarootdir='${prefix}/data'
+ datadir='${datarootdir}'
+ docdir='${prefix}/doc'
+ ;;
psp)
_host_os=psp
_host_cpu=mipsallegrexel
@@ -1623,7 +1639,7 @@ fi
#
case $_host in
-caanoo | gp2x | gp2xwiz | openpandora | ps2)
+caanoo | gp2x | gp2xwiz | openpandora | ps2 | psp2)
if test "$_debug_build" = auto; then
# If you want to debug one of these platforms, use '--disable-optimizations --enable-debug'
_debug_build=no
@@ -1717,6 +1733,12 @@ ps3)
exit 1
fi
;;
+psp2)
+ if test -z "$VITASDK"; then
+ echo "Please set VITASDK in your environment. export VITASDK="
+ exit 1
+ fi
+ ;;
psp)
if test -z "$PSPDEV"; then
PSPDEV=`psp-config --pspdev-path`
@@ -1945,7 +1967,7 @@ if test "$have_gcc" = yes ; then
case $_host_os in
# newlib-based system include files suppress non-C89 function
# declarations under __STRICT_ANSI__
- 3ds | amigaos* | android | androidsdl | dreamcast | ds | gamecube | mingw* | n64 | psp | ps2 | ps3 | tizen | wii | wince )
+ 3ds | amigaos* | android | androidsdl | dreamcast | ds | gamecube | mingw* | n64 | psp | ps2 | ps3 | psp2 | tizen | wii | wince )
;;
*)
append_var CXXFLAGS "-ansi"
@@ -1998,13 +2020,30 @@ echocheck "whether -Wglobal-constructors work"
cat > $TMPC << EOF
int main() { return 0; }
EOF
-cc_check -Wglobal-constructors && _global_constructors=yes
+cc_check -Wglobal-constructors -Werror && _global_constructors=yes
if test "$_global_constructors" = yes; then
append_var CXXFLAGS "-Wglobal-constructors"
fi
echo $_global_constructors
+# If the compiler supports the -Wundefined-var-template flag, silence that warning.
+# We get this warning a lot with regard to the Singleton class as we explicitly
+# instantiate each specialisation. An alternate way to deal with it would be to
+# change the way we instantiate the singleton classes as done in PR #967.
+# Note: we check the -Wundefined-var-template as gcc does not error out on unknown
+# -Wno-xxx flags.
+echocheck "whether -Wno-undefined-var-template work"
+cat > $TMPC << EOF
+int main() { return 0; }
+EOF
+cc_check -Wundefined-var-template -Werror && _no_undefined_var_template=yes
+
+if test "$_no_undefined_var_template" = yes; then
+ append_var CXXFLAGS "-Wno-undefined-var-template"
+fi
+echo $_no_undefined_var_template
+
echo_n "Checking for $_host_alias-strings... " >> "$TMPLOG"
if `which $_host_alias-strings >/dev/null 2>&1`; then
_strings=$_host_alias-strings
@@ -2165,7 +2204,7 @@ cc_check_clean tmp_find_type_with_size.cpp
# for the smaller sizes.
echo_n "Alignment required... "
case $_host_cpu in
- i[3-6]86 | amd64 | x86_64 | powerpc*)
+ i[3-6]86 | amd64 | x86_64 | powerpc* | ppc*)
# Unaligned access should work
_need_memalign=no
;;
@@ -2198,6 +2237,9 @@ case $_host_cpu in
;;
arm-apple-darwin11)
;;
+ # psvita does not like the asm code...
+ arm-vita-eabi)
+ ;;
*)
define_in_config_if_yes yes 'USE_ARM_SCALER_ASM'
@@ -2226,7 +2268,7 @@ case $_host_cpu in
echo "MIPS"
append_var DEFINES "-DMIPS_TARGET"
;;
- powerpc*)
+ powerpc* | ppc*)
echo "PowerPC"
append_var DEFINES "-DPPC_TARGET"
;;
@@ -2294,7 +2336,6 @@ case $_host_os in
append_var CXXFLAGS "-mfpu=vfp"
append_var LDFLAGS "-Wl,--fix-cortex-a8"
ABI="armeabi-v7a"
- # Platform version 9 is used by ResidualVM
ANDROID_PLATFORM=9
ANDROID_PLATFORM_ARCH="arm"
;;
@@ -2601,6 +2642,7 @@ case $_host_os in
append_var DEFINES "-DDISABLE_NES_APU"
append_var DEFINES "-DDISABLE_SID"
append_var DEFINES "-DREDUCE_MEMORY_USAGE"
+ add_line_to_config_mk 'N64 = 1'
;;
ps2)
append_var CXXFLAGS "-G2"
@@ -2624,6 +2666,35 @@ case $_host_os in
add_line_to_config_mk 'PLAYSTATION3 = 1'
add_line_to_config_h "#define PREFIX \"${prefix}\""
;;
+ psp2)
+ _freetypepath="$VITASDK/arm-vita-eabi/bin"
+ _freetype2=yes
+ _libcurlpath="$VITASDK/arm-vita-eabi/bin"
+ append_var CXXFLAGS "--sysroot=$VITASDK/arm-vita-eabi"
+ append_var LDFLAGS "--sysroot=$VITASDK/arm-vita-eabi"
+ append_var DEFINES "-DPSP2 -DSYSTEM_NOT_SUPPORTING_D_TYPE"
+ append_var CXXFLAGS "-Wl,-q -I$VITASDK/arm-vita-eabi/include"
+ append_var CXXFLAGS "-march=armv7-a -mtune=cortex-a9 -mfpu=neon -mfloat-abi=hard"
+ append_var LDFLAGS "-Wl,-q -L$VITASDK/arm-vita-eabi/lib"
+ #the next line fixes a crash when starting scumm-7-8 games (The Dig etc.)
+ #when either -O2, -O3 or -Os optimizations are enabled
+ append_var CXXFLAGS "-fno-optimize-sibling-calls"
+ #the next line fixes "branch out of range" error in gob engine when -Os is used
+ append_var CXXFLAGS "-mlong-calls"
+ if test "$_debug_build" = no; then
+ #optimize for smallest file size. This is necessary to prevent a crash on startup
+ #due to the large executable file size when many engines are enabled
+ #for example when --enable-all-engines is used to enable all the unstable engines
+ _optimization_level=-Os
+ fi
+ if test "$_debug_build" = yes; then
+ _optimization_level=-O0
+ append_var DEFINES "-D__PSP2_DEBUG__"
+ append_var LIBS "-lpsp2shell"
+ fi
+ add_line_to_config_mk 'PSP2 = 1'
+ add_line_to_config_h "#define PREFIX \"${prefix}\""
+ ;;
psp)
if test -d "$PSPDEV/psp/lib"; then
append_var LDFLAGS "-L$PSPDEV/psp/lib"
@@ -2634,6 +2705,7 @@ case $_host_os in
append_var CXXFLAGS "-I$PSPSDK/include"
# FIXME: Why is the following in CXXFLAGS and not in DEFINES? Change or document this.
append_var CXXFLAGS "-D_PSP_FW_VERSION=150"
+ add_line_to_config_mk 'PSP = 1'
;;
solaris*)
append_var DEFINES "-DSOLARIS"
@@ -2732,7 +2804,7 @@ if test -n "$_host"; then
_mt32emu=no
_timidity=no
;;
- androidsdl | androidsdl-armeabi | androidsdl-armeabi-v7a | androidsdl-mips | androidsdl-x86 | androidsdl-arm64-v8a)
+ androidsdl | androidsdl-armeabi | androidsdl-armeabi-v7a | androidsdl-mips | androidsdl-x86 | androidsdl-arm64-v8a | androidsdl-x86_64)
DEFINES="$DEFINES -DANDROIDSDL"
_unix=yes
_seq_midi=no
@@ -3117,6 +3189,16 @@ if test -n "$_host"; then
_eventrec=no
_port_mk="backends/platform/sdl/ps3/ps3.mk"
;;
+ psp2)
+ _backend="psp2"
+ _vkeybd=yes
+ _build_scalers=yes
+ _build_hq_scalers=no
+ _mt32emu=no
+ _timidity=no
+ _eventrec=no
+ _port_mk="backends/platform/sdl/psp2/psp2.mk"
+ ;;
psp)
_backend="psp"
_build_scalers=no
@@ -3195,7 +3277,7 @@ case $_backend in
append_var DEFINES "-DREDUCE_MEMORY_USAGE"
append_var CXXFLAGS "-Wa,--noexecstack"
append_var LDFLAGS "-Wl,-z,noexecstack"
- append_var INCLUDES "-I$ANDROID_NDK/sources/cxx-stl/system/include"
+ append_var INCLUDES "-isystem $ANDROID_NDK/sources/cxx-stl/system/include"
;;
androidsdl)
;;
@@ -3290,6 +3372,17 @@ case $_backend in
append_var LIBS "-lpng"
append_var LIBS "-Wl,-Map,mapfile.txt"
;;
+ psp2)
+ append_var LIBS "-lvitashaders -lSDL2 -lvita2d_fbo -lSceCommonDialog_stub"
+ append_var LIBS "-lSceSysmodule_stub -lSceDisplay_stub -lSceGxm_stub"
+ append_var LIBS "-lSceAudio_stub -lSceCtrl_stub -lScePower_stub"
+ append_var LIBS "-lSceNet_stub -lSceNetCtl_stub -lSceAppMgr_stub -lScePgf_stub"
+ append_var DEFINES "-DSDL_BACKEND"
+ add_line_to_config_mk "SDL_BACKEND = 1"
+ add_line_to_config_mk "USE_SDL2 = 1"
+ append_var MODULES "backends/platform/sdl"
+ append_var INCLUDES "-I$VITASDK/arm-vita-eabi/include/SDL2"
+ ;;
samsungtv)
append_var DEFINES "-DSAMSUNGTV"
append_var LDFLAGS "-shared"
@@ -3400,7 +3493,7 @@ esac
# Enable 16bit support only for backends which support it
#
case $_backend in
- 3ds | android | androidsdl | dingux | dc | gph | iphone | ios7 | maemo | openpandora | psp | samsungtv | sdl | tizen | webos | wii)
+ 3ds | android | androidsdl | dingux | dc | gph | iphone | ios7 | maemo | openpandora | psp | psp2 | samsungtv | sdl | tizen | webos | wii)
if test "$_16bit" = auto ; then
_16bit=yes
else
@@ -3476,7 +3569,7 @@ esac
#
echo_n "Checking if host is POSIX compliant... "
case $_host_os in
- amigaos* | cygwin* | dreamcast | ds | gamecube | mingw* | n64 | ps2 | ps3 | psp | wii | wince)
+ amigaos* | cygwin* | dreamcast | ds | gamecube | mingw* | n64 | ps2 | ps3 | psp2 | psp | wii | wince)
_posix=no
;;
3ds | android | androidsdl | beos* | bsd* | darwin* | freebsd* | gnu* | gph-linux | haiku* | hpux* | iphone | ios7 | irix*| k*bsd*-gnu* | linux* | maemo | mint* | netbsd* | openbsd* | solaris* | sunos* | uclinux* | webos)
@@ -3647,7 +3740,7 @@ PRE_OBJS_FLAGS := -Wl,-export-dynamic -Wl,-whole-archive
POST_OBJS_FLAGS := -Wl,-no-whole-archive
'
;;
- *mingw32*)
+ *mingw32* | mingw64)
_plugin_prefix=""
_plugin_suffix=".dll"
_mak_plugins='
@@ -4052,7 +4145,8 @@ EOF
cc_check $ZLIB_CFLAGS $ZLIB_LIBS -lz && _zlib=yes
fi
if test "$_zlib" = yes ; then
- append_var LIBS "$ZLIB_LIBS -lz"
+ append_var ZLIB_LIBS "-lz"
+ append_var LIBS "$ZLIB_LIBS"
append_var INCLUDES "$ZLIB_CFLAGS"
fi
define_in_config_if_yes "$_zlib" 'USE_ZLIB'
@@ -4185,6 +4279,9 @@ if test "$_libcurl" != "no"; then
amigaos*)
append_var LIBCURL_LIBS "-lpthread"
;;
+ psp2*)
+ append_var LIBCURL_LIBS "-lssl -lcrypto"
+ ;;
esac
if test "$_libcurl" = "auto"; then
@@ -5310,6 +5407,8 @@ EXEPRE := $HOSTEXEPRE
EXEEXT := $HOSTEXEEXT
NASM := $NASM
NASMFLAGS := $NASMFLAGS
+ZLIB_LIBS := $ZLIB_LIBS
+ZLIB_CFLAGS := $ZLIB_CFLAGS
prefix = $prefix
exec_prefix = $exec_prefix
diff --git a/devtools/create_project/cmake.cpp b/devtools/create_project/cmake.cpp
index 44fc95dd77c..d52e907a9ab 100644
--- a/devtools/create_project/cmake.cpp
+++ b/devtools/create_project/cmake.cpp
@@ -83,8 +83,8 @@ void CMakeProvider::createWorkspace(const BuildSetup &setup) {
workspace << "# Generate options for the engines\n";
writeEngineOptions(workspace);
- workspace << "include_directories(${" << setup.projectDescription << "_SOURCE_DIR} ${" << setup.projectDescription << "_SOURCE_DIR}/engines\n"
- "$ENV{"<enable = false;
}
+
+ // Disable engines for which we are missing dependencies
+ for (EngineDescList::const_iterator i = setup.engines.begin(); i != setup.engines.end(); ++i) {
+ if (i->enable) {
+ for (StringList::const_iterator ef = i->requiredFeatures.begin(); ef != i->requiredFeatures.end(); ++ef) {
+ FeatureList::iterator feature = std::find(setup.features.begin(), setup.features.end(), *ef);
+ if (feature != setup.features.end() && !feature->enable) {
+ setEngineBuildState(i->name, setup.engines, false);
+ break;
+ }
+ }
+ }
+ }
// Print status
cout << "Enabled engines:\n\n";
@@ -566,7 +579,7 @@ int main(int argc, char *argv[]) {
globalWarnings.push_back("6385");
globalWarnings.push_back("6386");
- if (msvcVersion == 14) {
+ if (msvcVersion == 14 || msvcVersion == 15) {
globalWarnings.push_back("4267");
globalWarnings.push_back("4577");
}
@@ -688,7 +701,8 @@ void displayHelp(const char *exe) {
" 11 stands for \"Visual Studio 2012\"\n"
" 12 stands for \"Visual Studio 2013\"\n"
" 14 stands for \"Visual Studio 2015\"\n"
- " The default is \"9\", thus \"Visual Studio 2008\"\n"
+ " 15 stands for \"Visual Studio 2017\"\n"
+ " The default is \"12\", thus \"Visual Studio 2013\"\n"
" --build-events Run custom build events as part of the build\n"
" (default: false)\n"
" --installer Create NSIS installer after the build (implies --build-events)\n"
@@ -906,7 +920,7 @@ namespace {
*/
bool parseEngine(const std::string &line, EngineDesc &engine) {
// Format:
- // add_engine engine_name "Readable Description" enable_default ["SubEngineList"]
+ // add_engine engine_name "Readable Description" enable_default ["SubEngineList"] ["base games"] ["dependencies"]
TokenList tokens = tokenize(line);
if (tokens.size() < 4)
@@ -921,8 +935,14 @@ bool parseEngine(const std::string &line, EngineDesc &engine) {
engine.name = *token; ++token;
engine.desc = *token; ++token;
engine.enable = (*token == "yes"); ++token;
- if (token != tokens.end())
+ if (token != tokens.end()) {
engine.subEngines = tokenize(*token);
+ ++token;
+ if (token != tokens.end())
+ ++token;
+ if (token != tokens.end())
+ engine.requiredFeatures = tokenize(*token);
+ }
return true;
}
@@ -1427,7 +1447,7 @@ void ProjectProvider::createProject(BuildSetup &setup) {
in.push_back(setup.srcDir + "/COPYING.FREEFONT");
in.push_back(setup.srcDir + "/COPYRIGHT");
in.push_back(setup.srcDir + "/NEWS");
- in.push_back(setup.srcDir + "/README.md"); // ResidualVM change
+ in.push_back(setup.srcDir + "/README.md");
in.push_back(setup.srcDir + "/TODO");
// Create the main project file.
@@ -1522,7 +1542,8 @@ void ProjectProvider::addFilesToProject(const std::string &dir, std::ofstream &p
StringList duplicate;
for (StringList::const_iterator i = includeList.begin(); i != includeList.end(); ++i) {
- const std::string fileName = getLastPathComponent(*i);
+ std::string fileName = getLastPathComponent(*i);
+ std::transform(fileName.begin(), fileName.end(), fileName.begin(), tolower);
// Leave out non object file names.
if (fileName.size() < 2 || fileName.compare(fileName.size() - 2, 2, ".o"))
@@ -1535,7 +1556,9 @@ void ProjectProvider::addFilesToProject(const std::string &dir, std::ofstream &p
// Search for duplicates
StringList::const_iterator j = i; ++j;
for (; j != includeList.end(); ++j) {
- if (fileName == getLastPathComponent(*j)) {
+ std::string candidateFileName = getLastPathComponent(*j);
+ std::transform(candidateFileName.begin(), candidateFileName.end(), candidateFileName.begin(), tolower);
+ if (fileName == candidateFileName) {
duplicate.push_back(fileName);
break;
}
@@ -1741,18 +1764,20 @@ void ProjectProvider::createModuleList(const std::string &moduleDir, const Strin
if (std::find(defines.begin(), defines.end(), *i) == defines.end())
shouldInclude.push(false);
else
- shouldInclude.push(true);
+ shouldInclude.push(true && shouldInclude.top());
} else if (*i == "ifndef") {
if (tokens.size() < 2)
error("Malformed ifndef in " + moduleMkFile);
++i;
if (std::find(defines.begin(), defines.end(), *i) == defines.end())
- shouldInclude.push(true);
+ shouldInclude.push(true && shouldInclude.top());
else
shouldInclude.push(false);
} else if (*i == "else") {
- shouldInclude.top() = !shouldInclude.top();
+ bool last = shouldInclude.top();
+ shouldInclude.pop();
+ shouldInclude.push(!last && shouldInclude.top());
} else if (*i == "endif") {
if (shouldInclude.size() <= 1)
error("endif without ifdef found in " + moduleMkFile);
diff --git a/devtools/create_project/create_project.h b/devtools/create_project/create_project.h
index bd0dcf6be9e..9f662ae8060 100644
--- a/devtools/create_project/create_project.h
+++ b/devtools/create_project/create_project.h
@@ -86,6 +86,11 @@ struct EngineDesc {
* Whether the engine should be included in the build or not.
*/
bool enable;
+
+ /**
+ * Features required for this engine.
+ */
+ StringList requiredFeatures;
/**
* A list of all available sub engine names. Sub engines are engines
diff --git a/devtools/create_project/msbuild.cpp b/devtools/create_project/msbuild.cpp
index dfbc6ca45ce..052ab4f19f8 100644
--- a/devtools/create_project/msbuild.cpp
+++ b/devtools/create_project/msbuild.cpp
@@ -58,11 +58,17 @@ int MSBuildProvider::getVisualStudioVersion() {
if (_version == 14)
return 14;
+ if (_version == 15)
+ return 15;
+
error("Unsupported version passed to getVisualStudioVersion");
}
int MSBuildProvider::getSolutionVersion() {
- return (_version < 14) ? _version + 1 : _version;
+ if (_version == 14 || _version == 15)
+ return 14;
+
+ return _version + 1;
}
namespace {
@@ -123,7 +129,7 @@ void MSBuildProvider::createProjectFile(const std::string &name, const std::stri
// Shared configuration
project << "\t\n";
- std::string version = "v" + toString(_version) + "0";
+ std::string version = _version == 15 ? "v141" : "v" + toString(_version) + "0";
std::string llvm = "LLVM-vs" + toString(getVisualStudioVersion());
outputConfigurationType(setup, project, name, "Release|Win32", version);
@@ -514,7 +520,9 @@ void MSBuildProvider::writeFileListToProject(const FileNode &dir, std::ofstream
if (!_compileFiles.empty()) {
projectFile << "\t\n";
for (std::list::const_iterator entry = _compileFiles.begin(); entry != _compileFiles.end(); ++entry) {
- const bool isDuplicate = (std::find(duplicate.begin(), duplicate.end(), (*entry).name + ".o") != duplicate.end());
+ std::string fileName = (*entry).name + ".o";
+ std::transform(fileName.begin(), fileName.end(), fileName.begin(), tolower);
+ const bool isDuplicate = (std::find(duplicate.begin(), duplicate.end(), fileName) != duplicate.end());
// Deal with duplicated file names
if (isDuplicate) {
diff --git a/devtools/create_project/msvc10/create_project.vcxproj b/devtools/create_project/msvc10/create_project.vcxproj
index 700c4bb2833..0386c3e5ad8 100644
--- a/devtools/create_project/msvc10/create_project.vcxproj
+++ b/devtools/create_project/msvc10/create_project.vcxproj
@@ -50,7 +50,7 @@
Level4
EditAndContinue
false
- 4003;4512;4127
+ 4003;4512;4127;4100
Rpcrt4.lib;%(AdditionalDependencies)
diff --git a/devtools/create_project/msvc11/create_project.vcxproj b/devtools/create_project/msvc11/create_project.vcxproj
index 09392a43e3a..9168163e3ea 100644
--- a/devtools/create_project/msvc11/create_project.vcxproj
+++ b/devtools/create_project/msvc11/create_project.vcxproj
@@ -53,7 +53,7 @@
Level4
EditAndContinue
false
- 4003;4512;4127
+ 4003;4512;4127;4100
Rpcrt4.lib;%(AdditionalDependencies)
diff --git a/devtools/create_project/msvc12/create_project.vcxproj b/devtools/create_project/msvc12/create_project.vcxproj
index 3b38972e51e..0623b24a40d 100644
--- a/devtools/create_project/msvc12/create_project.vcxproj
+++ b/devtools/create_project/msvc12/create_project.vcxproj
@@ -52,7 +52,7 @@
Level4
EditAndContinue
false
- 4003;4512;4127
+ 4003;4512;4127;4100
Rpcrt4.lib;%(AdditionalDependencies)
diff --git a/devtools/create_project/msvc9/create_project.vcproj b/devtools/create_project/msvc9/create_project.vcproj
index eaa72099cc4..f348226337a 100644
--- a/devtools/create_project/msvc9/create_project.vcproj
+++ b/devtools/create_project/msvc9/create_project.vcproj
@@ -45,7 +45,7 @@
RuntimeLibrary="3"
WarningLevel="4"
DebugInformationFormat="4"
- DisableSpecificWarnings="4003;4512;4127"
+ DisableSpecificWarnings="4003;4512;4127;4100"
/>
name)) {
std::string name, ext;
splitFilename(node->name, name, ext);
- const bool isDuplicate = (std::find(duplicate.begin(), duplicate.end(), name + ".o") != duplicate.end());
+ name += ".o";
+ std::transform(name.begin(), name.end(), name.begin(), tolower);
+ const bool isDuplicate = (std::find(duplicate.begin(), duplicate.end(), name) != duplicate.end());
if (ext == "asm") {
std::string objFileName = "$(IntDir)\\";
diff --git a/devtools/create_project/xcode.cpp b/devtools/create_project/xcode.cpp
index 21fc25643f0..d60934bcb8a 100644
--- a/devtools/create_project/xcode.cpp
+++ b/devtools/create_project/xcode.cpp
@@ -665,6 +665,8 @@ XcodeProvider::ValueList& XcodeProvider::getResourceFiles() const {
files.push_back("gui/themes/scummclassic.zip");
files.push_back("gui/themes/scummmodern.zip");
files.push_back("gui/themes/translations.dat");
+ files.push_back("dists/engine-data/access.dat");
+ files.push_back("dists/engine-data/cryo.dat");
files.push_back("dists/engine-data/drascula.dat");
files.push_back("dists/engine-data/hugo.dat");
files.push_back("dists/engine-data/kyra.dat");
@@ -674,9 +676,11 @@ XcodeProvider::ValueList& XcodeProvider::getResourceFiles() const {
files.push_back("dists/engine-data/queen.tbl");
files.push_back("dists/engine-data/sky.cpt");
files.push_back("dists/engine-data/teenagent.dat");
+ files.push_back("dists/engine-data/titanic.dat");
files.push_back("dists/engine-data/tony.dat");
files.push_back("dists/engine-data/toon.dat");
files.push_back("dists/engine-data/wintermute.zip");
+ files.push_back("dists/engine-data/macventure.dat");
files.push_back("dists/pred.dic");
files.push_back("icons/scummvm.icns");
}
@@ -950,6 +954,7 @@ void XcodeProvider::setupBuildConfiguration(const BuildSetup &setup) {
scummvmOSX_LdFlags.push_back("-lvorbis");
scummvmOSX_LdFlags.push_back("-lmad");
scummvmOSX_LdFlags.push_back("-lFLAC");
+ scummvmOSX_LdFlags.push_back("-lcurl");
if (setup.useSDL2) {
scummvmOSX_LdFlags.push_back("-lSDL2main");
scummvmOSX_LdFlags.push_back("-lSDL2");
diff --git a/devtools/create_translations/cp_parser.cpp b/devtools/create_translations/cp_parser.cpp
index f5e4c9d6bb1..c7c7597837e 100644
--- a/devtools/create_translations/cp_parser.cpp
+++ b/devtools/create_translations/cp_parser.cpp
@@ -50,7 +50,7 @@ Codepage *parseCodepageMapping(const std::string &filename) {
std::ifstream in(filename.c_str());
if (!in) {
- fprintf(stderr, "ERROR: Could not open file \"%s\"!", filename.c_str());
+ fprintf(stderr, "ERROR: Could not open file \"%s\"!\n", filename.c_str());
return 0;
}
@@ -63,7 +63,7 @@ Codepage *parseCodepageMapping(const std::string &filename) {
if (in.eof())
break;
if (in.fail()) {
- fprintf(stderr, "ERROR: Reading from file \"%s\" failed!", filename.c_str());
+ fprintf(stderr, "ERROR: Reading from file \"%s\" failed!\n", filename.c_str());
return 0;
}
@@ -76,7 +76,7 @@ Codepage *parseCodepageMapping(const std::string &filename) {
int unicode, required;
const int parsed = sscanf(line.c_str(), "%d %d", &unicode, &required);
if (parsed < 1 || parsed > 2) {
- fprintf(stderr, "ERROR: Line \"%s\" is malformed!", filename.c_str());
+ fprintf(stderr, "ERROR: Line \"%s\" is malformed!\n", filename.c_str());
return 0;
}
// In case no required integer was given, we assume the character is
@@ -97,7 +97,7 @@ Codepage *parseCodepageMapping(const std::string &filename) {
// Check whether all character encodings are mapped, if not error out
if (processedMappings != 256) {
- fprintf(stderr, "ERROR: File \"%s\" does not contain mappings for exactly 256 characters!", filename.c_str());
+ fprintf(stderr, "ERROR: File \"%s\" does not contain mappings for exactly 256 characters!\n", filename.c_str());
return 0;
}
diff --git a/devtools/encode-macbinary.sh b/devtools/encode-macbinary.sh
index 6635f7fcda8..235dd860a44 100644
--- a/devtools/encode-macbinary.sh
+++ b/devtools/encode-macbinary.sh
@@ -5,7 +5,19 @@
for i in *
do
- macbinary encode "$i"
- touch -r "$i" "$i.bin"
- mv "$i.bin" "$i"
+ if test -d "$i" ; then
+ cd "$i"
+ bash $0 "$1/$i"
+ cd ..
+ else
+ echo -n $1/$i ; echo -ne "... \r"
+ macbinary encode "$i"
+ touch -r "$i" "$i.bin"
+ mv "$i.bin" "$i"
+ fi
done
+
+# on the top level we want to print a new line
+if test -z $1 ; then
+ echo
+fi
diff --git a/engines/advancedDetector.cpp b/engines/advancedDetector.cpp
index 0c8a20d1d60..02eab48f29c 100644
--- a/engines/advancedDetector.cpp
+++ b/engines/advancedDetector.cpp
@@ -331,10 +331,10 @@ void AdvancedMetaEngine::reportUnknown(const Common::FSNode &path, const ADFileP
//
// Might also be helpful to display the full path (for when this is used
// from the mass detector).
- Common::String report = Common::String::format(_("The game in '%s' seems to be unknown."), path.getPath().c_str()) + "\n";
- report += _("Please, report the following data to the ResidualVM team along with name");
- report += "\n";
- report += _("of the game you tried to add and its version/language/etc.:");
+ Common::String report = Common::String::format(
+ _("The game in '%s' seems to be unknown.\n"
+ "Please, report the following data to the ResidualVM team along with name\n"
+ "of the game you tried to add and its version, language, etc.:"), path.getPath().c_str()) + "\n";
report += "\n";
for (ADFilePropertiesMap::const_iterator file = filesProps.begin(); file != filesProps.end(); ++file)
@@ -345,7 +345,7 @@ void AdvancedMetaEngine::reportUnknown(const Common::FSNode &path, const ADFileP
g_system->logMessage(LogMessageType::kInfo, report.c_str());
}
-void AdvancedMetaEngine::composeFileHashMap(FileMap &allFiles, const Common::FSList &fslist, int depth) const {
+void AdvancedMetaEngine::composeFileHashMap(FileMap &allFiles, const Common::FSList &fslist, int depth, const Common::String &parentName) const {
if (depth <= 0)
return;
@@ -353,6 +353,8 @@ void AdvancedMetaEngine::composeFileHashMap(FileMap &allFiles, const Common::FSL
return;
for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
+ Common::String tstr = (_matchFullPaths && !parentName.empty() ? parentName + "/" : "") + file->getName();
+
if (file->isDirectory()) {
Common::FSList files;
@@ -372,11 +374,9 @@ void AdvancedMetaEngine::composeFileHashMap(FileMap &allFiles, const Common::FSL
if (!file->getChildren(files, Common::FSNode::kListAll))
continue;
- composeFileHashMap(allFiles, files, depth - 1);
+ composeFileHashMap(allFiles, files, depth - 1, tstr);
}
- Common::String tstr = file->getName();
-
// Strip any trailing dot
if (tstr.lastChar() == '.')
tstr.deleteLastChar();
@@ -397,7 +397,9 @@ bool AdvancedMetaEngine::getFileProperties(const Common::FSNode &parent, const F
fileProps.md5 = macResMan.computeResForkMD5AsString(_md5Bytes);
fileProps.size = macResMan.getResForkDataSize();
- return true;
+
+ if (fileProps.size != 0)
+ return true;
}
if (!allFiles.contains(fname))
@@ -623,6 +625,7 @@ AdvancedMetaEngine::AdvancedMetaEngine(const void *descs, uint descItemSize, con
_guiOptions = GUIO_NONE;
_maxScanDepth = 1;
_directoryGlobs = NULL;
+ _matchFullPaths = false;
}
void AdvancedMetaEngine::initSubSystems(const ADGameDescription *gameDesc) const {
diff --git a/engines/advancedDetector.h b/engines/advancedDetector.h
index ab3ec22bdcb..e218c41b52d 100644
--- a/engines/advancedDetector.h
+++ b/engines/advancedDetector.h
@@ -251,6 +251,17 @@ protected:
*/
const char * const *_directoryGlobs;
+ /**
+ * If true, filenames will be matched against the entire path, relative to
+ * the root detection directory (e.g. "foo/bar.000" for a file at
+ * "/foo/bar.000"). Otherwise, filenames only match the basename
+ * (e.g. "bar.000" for the same file).
+ *
+ * @note _maxScanDepth and _directoryGlobs must still be configured to allow
+ * the detector to find files inside subdirectories.
+ */
+ bool _matchFullPaths;
+
public:
AdvancedMetaEngine(const void *descs, uint descItemSize, const PlainGameDescriptor *gameIds, const ADExtraGuiOptionsMap *extraGuiOptions = 0);
@@ -326,7 +337,7 @@ protected:
* Compose a hashmap of all files in fslist.
* Includes nifty stuff like removing trailing dots and ignoring case.
*/
- void composeFileHashMap(FileMap &allFiles, const Common::FSList &fslist, int depth) const;
+ void composeFileHashMap(FileMap &allFiles, const Common::FSList &fslist, int depth, const Common::String &parentName = Common::String()) const;
/** Get the properties (size and MD5) of this file. */
bool getFileProperties(const Common::FSNode &parent, const FileMap &allFiles, const ADGameDescription &game, const Common::String fname, ADFileProperties &fileProps) const;
diff --git a/engines/dialogs.cpp b/engines/dialogs.cpp
index bc0dd80931a..fab790fb374 100644
--- a/engines/dialogs.cpp
+++ b/engines/dialogs.cpp
@@ -85,11 +85,11 @@ MainMenuDialog::MainMenuDialog(Engine *engine)
new GUI::ButtonWidget(this, "GlobalMenu.Resume", _("~R~esume"), 0, kPlayCmd, 'P');
_loadButton = new GUI::ButtonWidget(this, "GlobalMenu.Load", _("~L~oad"), 0, kLoadCmd);
- // TODO: setEnabled -> setVisible
+ _loadButton->setVisible(_engine->hasFeature(Engine::kSupportsLoadingDuringRuntime));
_loadButton->setEnabled(_engine->hasFeature(Engine::kSupportsLoadingDuringRuntime));
_saveButton = new GUI::ButtonWidget(this, "GlobalMenu.Save", _("~S~ave"), 0, kSaveCmd);
- // TODO: setEnabled -> setVisible
+ _saveButton->setVisible(_engine->hasFeature(Engine::kSupportsSavingDuringRuntime));
_saveButton->setEnabled(_engine->hasFeature(Engine::kSupportsSavingDuringRuntime));
new GUI::ButtonWidget(this, "GlobalMenu.Options", _("~O~ptions"), 0, kOptionsCmd);
@@ -231,7 +231,7 @@ void MainMenuDialog::save() {
Common::Error status = _engine->saveGameState(slot, result);
if (status.getCode() != Common::kNoError) {
- Common::String failMessage = Common::String::format(_("Gamestate save failed (%s)! "
+ Common::String failMessage = Common::String::format(_("Failed to save game (%s)! "
"Please consult the README for basic information, and for "
"instructions on how to obtain further assistance."), status.getDesc().c_str());
GUI::MessageDialog dialog(failMessage);
diff --git a/engines/engine.cpp b/engines/engine.cpp
index b88d724d8e4..834d1742155 100644
--- a/engines/engine.cpp
+++ b/engines/engine.cpp
@@ -540,7 +540,7 @@ void Engine::openMainMenuDialog() {
if (_saveSlotToLoad >= 0) {
Common::Error status = loadGameState(_saveSlotToLoad);
if (status.getCode() != Common::kNoError) {
- Common::String failMessage = Common::String::format(_("Gamestate load failed (%s)! "
+ Common::String failMessage = Common::String::format(_("Failed to load saved game (%s)! "
"Please consult the README for basic information, and for "
"instructions on how to obtain further assistance."), status.getDesc().c_str());
GUI::MessageDialog dialog(failMessage);
diff --git a/engines/grim/movie/codecs/smush_decoder.cpp b/engines/grim/movie/codecs/smush_decoder.cpp
index 49908807a5f..62769a4a1c5 100644
--- a/engines/grim/movie/codecs/smush_decoder.cpp
+++ b/engines/grim/movie/codecs/smush_decoder.cpp
@@ -184,7 +184,7 @@ bool SmushDecoder::readHeader() {
_file->readUint32BE();
_file->readUint32BE();
- _audioTrack = new SmushAudioTrack(false, audioRate, 2);
+ _audioTrack = new SmushAudioTrack(getSoundType(), false, audioRate, 2);
addTrack(_audioTrack);
return true;
@@ -245,7 +245,7 @@ bool SmushDecoder::handleFramesHeader() {
} while (pos < size);
delete[] f_header;
- _audioTrack = new SmushAudioTrack(true, freq, channels);
+ _audioTrack = new SmushAudioTrack(getSoundType(), true, freq, channels);
addTrack(_audioTrack);
return true;
}
@@ -592,7 +592,8 @@ Graphics::Surface *SmushDecoder::SmushVideoTrack::decodeNextFrame() {
void SmushDecoder::SmushVideoTrack::setMsPerFrame(int ms) {
_frameRate = Common::Rational(1000000, ms);
}
-SmushDecoder::SmushAudioTrack::SmushAudioTrack(bool isVima, int freq, int channels) {
+SmushDecoder::SmushAudioTrack::SmushAudioTrack(Audio::Mixer::SoundType soundType, bool isVima, int freq, int channels) :
+ AudioTrack(soundType) {
_isVima = isVima;
_channels = channels;
_freq = freq;
diff --git a/engines/grim/movie/codecs/smush_decoder.h b/engines/grim/movie/codecs/smush_decoder.h
index 8f1619c571d..a69658a51c3 100644
--- a/engines/grim/movie/codecs/smush_decoder.h
+++ b/engines/grim/movie/codecs/smush_decoder.h
@@ -108,7 +108,7 @@ protected:
class SmushAudioTrack : public AudioTrack {
public:
- SmushAudioTrack(bool isVima, int freq = 22050, int channels = -1);
+ SmushAudioTrack(Audio::Mixer::SoundType soundType, bool isVima, int freq = 22050, int channels = -1);
~SmushAudioTrack();
Audio::AudioStream *getAudioStream() const override { return _queueStream; }
diff --git a/engines/grim/stuffit.cpp b/engines/grim/stuffit.cpp
index c26dabfe2c4..cb99c92fcb0 100644
--- a/engines/grim/stuffit.cpp
+++ b/engines/grim/stuffit.cpp
@@ -25,7 +25,6 @@
#include "engines/grim/stuffit.h"
-#include "common/bitstream.h"
#include "common/debug.h"
#include "common/memstream.h"
#include "common/substream.h"
@@ -258,7 +257,7 @@ struct SIT14Data {
if (b->pos() & 7) \
b->skip(8 - (b->pos() & 7))
-void StuffItArchive::readTree14(Common::BitStream *bits, SIT14Data *dat, uint16 codesize, uint16 *result) const {
+void StuffItArchive::readTree14(Common::BitStream8LSB *bits, SIT14Data *dat, uint16 codesize, uint16 *result) const {
uint32 i, l, n;
uint32 k = bits->getBit();
uint32 j = bits->getBits(2) + 2;
@@ -386,7 +385,7 @@ Common::SeekableReadStream *StuffItArchive::decompress14(Common::SeekableReadStr
byte *dst = new byte[uncompressedSize];
Common::MemoryWriteStream out(dst, uncompressedSize);
- Common::BitStream *bits = new Common::BitStream8LSB(src);
+ Common::BitStream8LSB *bits = new Common::BitStream8LSB(src);
uint32 i, j, k, l, m, n;
diff --git a/engines/grim/stuffit.h b/engines/grim/stuffit.h
index dc9a7d81f4b..d46cf3a6e6d 100644
--- a/engines/grim/stuffit.h
+++ b/engines/grim/stuffit.h
@@ -24,6 +24,7 @@
#define GRIM_STUFFIT_H
#include "common/archive.h"
+#include "common/bitstream.h"
#include "common/scummsys.h"
#include "common/endian.h"
#include "common/file.h"
@@ -31,10 +32,6 @@
#include "common/hashmap.h"
#include "common/str.h"
-namespace Common {
-class BitStream;
-}
-
namespace Grim {
struct SIT14Data;
@@ -72,7 +69,7 @@ private:
// Decompression Helpers
void update14(uint16 first, uint16 last, byte *code, uint16 *freq) const;
- void readTree14(Common::BitStream *bits, SIT14Data *dat, uint16 codesize, uint16 *result) const;
+ void readTree14(Common::BitStream8LSB *bits, SIT14Data *dat, uint16 codesize, uint16 *result) const;
};
} // End of namespace Grim
diff --git a/engines/logo_data.h b/engines/logo_data.h
index f8a5cdbbb77..384c2130d08 100644
--- a/engines/logo_data.h
+++ b/engines/logo_data.h
@@ -1,4 +1,4 @@
-/* ResidualVM - Graphic Adventure Engine
+/* ResidualVM - A 3D game interpreter
*
* ResidualVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
diff --git a/engines/savestate.cpp b/engines/savestate.cpp
index 7366aa6a619..20a37581dc8 100644
--- a/engines/savestate.cpp
+++ b/engines/savestate.cpp
@@ -43,7 +43,7 @@ void SaveStateDescriptor::setThumbnail(Graphics::Surface *t) {
}
void SaveStateDescriptor::setSaveDate(int year, int month, int day) {
- _saveDate = Common::String::format("%.2d.%.2d.%.4d", day, month, year);
+ _saveDate = Common::String::format("%.4d-%.2d-%.2d", year, month, day);
}
void SaveStateDescriptor::setSaveTime(int hour, int min) {
diff --git a/graphics/VectorRendererSpec.cpp b/graphics/VectorRendererSpec.cpp
index 3dd9c86f455..ef7d672fd08 100644
--- a/graphics/VectorRendererSpec.cpp
+++ b/graphics/VectorRendererSpec.cpp
@@ -449,13 +449,13 @@ void colorFill(PixelType *first, PixelType *last, PixelType color) {
register int n = (count + 7) >> 3;
switch (count % 8) {
case 0: do {
- *first++ = color;
- case 7: *first++ = color;
- case 6: *first++ = color;
- case 5: *first++ = color;
- case 4: *first++ = color;
- case 3: *first++ = color;
- case 2: *first++ = color;
+ *first++ = color; // fall through
+ case 7: *first++ = color; // fall through
+ case 6: *first++ = color; // fall through
+ case 5: *first++ = color; // fall through
+ case 4: *first++ = color; // fall through
+ case 3: *first++ = color; // fall through
+ case 2: *first++ = color; // fall through
case 1: *first++ = color;
} while (--n > 0);
}
@@ -488,13 +488,13 @@ void colorFillClip(PixelType *first, PixelType *last, PixelType color, int realX
register int n = (count + 7) >> 3;
switch (count % 8) {
case 0: do {
- *first++ = color;
- case 7: *first++ = color;
- case 6: *first++ = color;
- case 5: *first++ = color;
- case 4: *first++ = color;
- case 3: *first++ = color;
- case 2: *first++ = color;
+ *first++ = color; // fall through
+ case 7: *first++ = color; // fall through
+ case 6: *first++ = color; // fall through
+ case 5: *first++ = color; // fall through
+ case 4: *first++ = color; // fall through
+ case 3: *first++ = color; // fall through
+ case 2: *first++ = color; // fall through
case 1: *first++ = color;
} while (--n > 0);
}
diff --git a/graphics/font.cpp b/graphics/font.cpp
index 7768b7362d1..3446619299f 100644
--- a/graphics/font.cpp
+++ b/graphics/font.cpp
@@ -142,11 +142,11 @@ struct WordWrapper {
};
template
-int wordWrapTextImpl(const Font &font, const StringType &str, int maxWidth, Common::Array &lines) {
+int wordWrapTextImpl(const Font &font, const StringType &str, int maxWidth, Common::Array &lines, int initWidth) {
WordWrapper wrapper(lines);
StringType line;
StringType tmpStr;
- int lineWidth = 0;
+ int lineWidth = initWidth;
int tmpWidth = 0;
// The rough idea behind this algorithm is as follows:
@@ -305,12 +305,12 @@ void Font::drawString(ManagedSurface *dst, const Common::U32String &str, int x,
}
}
-int Font::wordWrapText(const Common::String &str, int maxWidth, Common::Array &lines) const {
- return wordWrapTextImpl(*this, str, maxWidth, lines);
+int Font::wordWrapText(const Common::String &str, int maxWidth, Common::Array &lines, int initWidth) const {
+ return wordWrapTextImpl(*this, str, maxWidth, lines, initWidth);
}
-int Font::wordWrapText(const Common::U32String &str, int maxWidth, Common::Array &lines) const {
- return wordWrapTextImpl(*this, str, maxWidth, lines);
+int Font::wordWrapText(const Common::U32String &str, int maxWidth, Common::Array &lines, int initWidth) const {
+ return wordWrapTextImpl(*this, str, maxWidth, lines, initWidth);
}
Common::String Font::handleEllipsis(const Common::String &input, int w) const {
diff --git a/graphics/font.h b/graphics/font.h
index 0478608708e..6682ce015b5 100644
--- a/graphics/font.h
+++ b/graphics/font.h
@@ -169,13 +169,14 @@ public:
* It returns the maximal width of any of the new lines (i.e. a value which is less
* or equal to maxWidth).
*
- * @param str the string to word wrap
- * @param maxWidth the maximum width a line may have
- * @param lines the string list to which the text lines from str are appended
+ * @param str the string to word wrap
+ * @param maxWidth the maximum width a line may have
+ * @param lines the string list to which the text lines from str are appended
+ * @param initWidth the starting width of the first line, for partially filled lines (optional)
* @return the maximal width of any of the lines added to lines
*/
- int wordWrapText(const Common::String &str, int maxWidth, Common::Array &lines) const;
- int wordWrapText(const Common::U32String &str, int maxWidth, Common::Array &lines) const;
+ int wordWrapText(const Common::String &str, int maxWidth, Common::Array &lines, int initWidth = 0) const;
+ int wordWrapText(const Common::U32String &str, int maxWidth, Common::Array &lines, int initWidth = 0) const;
private:
Common::String handleEllipsis(const Common::String &str, int w) const;
diff --git a/graphics/managed_surface.cpp b/graphics/managed_surface.cpp
index 0b9fa111819..60b2941efa5 100644
--- a/graphics/managed_surface.cpp
+++ b/graphics/managed_surface.cpp
@@ -166,15 +166,60 @@ void ManagedSurface::blitFrom(const Surface &src, const Common::Rect &srcRect,
Common::Rect srcBounds = srcRect;
Common::Rect destBounds(destPos.x, destPos.y, destPos.x + srcRect.width(),
destPos.y + srcRect.height());
- assert(src.format.bytesPerPixel == format.bytesPerPixel);
+ uint destPixel;
+ byte rSrc, gSrc, bSrc, aSrc;
+ byte rDest, gDest, bDest;
+ double alpha;
if (!srcRect.isValidRect() || !clip(srcBounds, destBounds))
return;
+ if (format != src.format) {
+ // When the pixel format differs, both source an dest must be
+ // 2 or 4 bytes per pixel
+ assert(format.bytesPerPixel == 2 || format.bytesPerPixel == 4);
+ assert(src.format.bytesPerPixel == 2 || src.format.bytesPerPixel == 4);
+ }
+
for (int y = 0; y < srcBounds.height(); ++y) {
const byte *srcP = (const byte *)src.getBasePtr(srcBounds.left, srcBounds.top + y);
byte *destP = (byte *)getBasePtr(destBounds.left, destBounds.top + y);
- Common::copy(srcP, srcP + srcBounds.width() * format.bytesPerPixel, destP);
+
+ if (src.format == format) {
+ // Matching surface formats, so we can do a straight copy
+ Common::copy(srcP, srcP + srcBounds.width() * format.bytesPerPixel, destP);
+ } else {
+ for (int x = 0; x < srcBounds.width(); ++x,
+ srcP += src.format.bytesPerPixel,
+ destP += format.bytesPerPixel) {
+ src.format.colorToARGB(src.format.bytesPerPixel == 2 ? *(const uint16 *)srcP : *(const uint32 *)srcP,
+ aSrc, rSrc, gSrc, bSrc);
+ format.colorToRGB(format.bytesPerPixel == 2 ? *(const uint16 *)destP : *(const uint32 *)destP,
+ rDest, gDest, bDest);
+
+ if (aSrc == 0) {
+ // Completely transparent, so skip
+ continue;
+ } else if (aSrc == 0xff) {
+ // Completely opaque, so copy RGB values over
+ rDest = rSrc;
+ gDest = gSrc;
+ bDest = bSrc;
+ } else {
+ // Partially transparent, so calculate new pixel colors
+ alpha = (double)aSrc / 255.0;
+ rDest = static_cast((rSrc * alpha) + (rDest * (1.0 - alpha)));
+ gDest = static_cast((gSrc * alpha) + (gDest * (1.0 - alpha)));
+ bDest = static_cast((bSrc * alpha) + (bDest * (1.0 - alpha)));
+ }
+
+ destPixel = format.ARGBToColor(0xff, rDest, gDest, bDest);
+ if (format.bytesPerPixel == 2)
+ *(uint16 *)destP = destPixel;
+ else
+ *(uint32 *)destP = destPixel;
+ }
+ }
}
addDirtyRect(Common::Rect(0, 0, this->w, this->h));
@@ -197,49 +242,85 @@ void ManagedSurface::transBlitFrom(const Surface &src, const Common::Rect &srcRe
destPos.x + src.w, destPos.y + src.h), transColor, false, overrideColor);
}
-template
-void transBlit(const Surface &src, const Common::Rect &srcRect, Surface *dest, const Common::Rect &destRect, uint transColor, bool flipped, uint overrideColor) {
+template
+void transBlit(const Surface &src, const Common::Rect &srcRect, Surface &dest, const Common::Rect &destRect, TSRC transColor, bool flipped, uint overrideColor) {
int scaleX = SCALE_THRESHOLD * srcRect.width() / destRect.width();
int scaleY = SCALE_THRESHOLD * srcRect.height() / destRect.height();
+ const Graphics::PixelFormat &srcFormat = src.format;
+ const Graphics::PixelFormat &destFormat = dest.format;
+ byte aSrc, rSrc, gSrc, bSrc;
+ byte rDest, gDest, bDest;
+ double alpha;
// Loop through drawing output lines
for (int destY = destRect.top, scaleYCtr = 0; destY < destRect.bottom; ++destY, scaleYCtr += scaleY) {
- if (destY < 0 || destY >= dest->h)
+ if (destY < 0 || destY >= dest.h)
continue;
- const T *srcLine = (const T *)src.getBasePtr(0, scaleYCtr / SCALE_THRESHOLD);
- T *destLine = (T *)dest->getBasePtr(destRect.left, destY);
+ const TSRC *srcLine = (const TSRC *)src.getBasePtr(srcRect.left, scaleYCtr / SCALE_THRESHOLD + srcRect.top);
+ TDEST *destLine = (TDEST *)dest.getBasePtr(destRect.left, destY);
// Loop through drawing the pixels of the row
for (int destX = destRect.left, xCtr = 0, scaleXCtr = 0; destX < destRect.right; ++destX, ++xCtr, scaleXCtr += scaleX) {
- if (destX < 0 || destX >= dest->w)
+ if (destX < 0 || destX >= dest.w)
continue;
- T srcVal = srcLine[flipped ? src.w - scaleXCtr / SCALE_THRESHOLD - 1 : scaleXCtr / SCALE_THRESHOLD];
- if (srcVal != transColor) {
+ TSRC srcVal = srcLine[flipped ? src.w - scaleXCtr / SCALE_THRESHOLD - 1 : scaleXCtr / SCALE_THRESHOLD];
+ if (srcVal == transColor)
+ continue;
+
+ if (srcFormat == destFormat) {
+ // Matching formats, so we can do a straight copy
destLine[xCtr] = overrideColor ? overrideColor : srcVal;
+ } else {
+ // Otherwise we have to manually decode and re-encode each pixel
+ srcFormat.colorToARGB(*srcLine, aSrc, rSrc, gSrc, bSrc);
+ destFormat.colorToRGB(destLine[xCtr], rDest, gDest, bDest);
+
+ if (aSrc == 0) {
+ // Completely transparent, so skip
+ continue;
+ } else if (aSrc == 0xff) {
+ // Completely opaque, so copy RGB values over
+ rDest = rSrc;
+ gDest = gSrc;
+ bDest = bSrc;
+ } else {
+ // Partially transparent, so calculate new pixel colors
+ alpha = (double)aSrc / 255.0;
+ rDest = static_cast((rSrc * alpha) + (rDest * (1.0 - alpha)));
+ gDest = static_cast((gSrc * alpha) + (gDest * (1.0 - alpha)));
+ bDest = static_cast((bSrc * alpha) + (bDest * (1.0 - alpha)));
+ }
+
+ destLine[xCtr] = destFormat.ARGBToColor(0xff, rDest, gDest, bDest);
}
}
}
}
+#define HANDLE_BLIT(SRC_BYTES, DEST_BYTES, SRC_TYPE, DEST_TYPE) \
+ if (src.format.bytesPerPixel == SRC_BYTES && format.bytesPerPixel == DEST_BYTES) \
+ transBlit(src, srcRect, _innerSurface, destRect, transColor, flipped, overrideColor); \
+ else
+
void ManagedSurface::transBlitFrom(const Surface &src, const Common::Rect &srcRect,
const Common::Rect &destRect, uint transColor, bool flipped, uint overrideColor) {
if (src.w == 0 || src.h == 0 || destRect.width() == 0 || destRect.height() == 0)
return;
- if (format.bytesPerPixel == 1)
- transBlit(src, srcRect, &_innerSurface, destRect, transColor, flipped, overrideColor);
- else if (format.bytesPerPixel == 2)
- transBlit(src, srcRect, &_innerSurface, destRect, transColor, flipped, overrideColor);
- else if (format.bytesPerPixel == 4)
- transBlit(src, srcRect, &_innerSurface, destRect, transColor, flipped, overrideColor);
- else
+ HANDLE_BLIT(1, 1, byte, byte)
+ HANDLE_BLIT(2, 2, uint16, uint16)
+ HANDLE_BLIT(4, 4, uint32, uint32)
+ HANDLE_BLIT(2, 4, uint16, uint32)
+ HANDLE_BLIT(4, 2, uint32, uint16)
error("Surface::transBlitFrom: bytesPerPixel must be 1, 2, or 4");
// Mark the affected area
addDirtyRect(destRect);
}
+#undef HANDLE_BLIT
+
void ManagedSurface::markAllDirty() {
addDirtyRect(Common::Rect(0, 0, this->w, this->h));
}
diff --git a/graphics/pixelformat.cpp b/graphics/pixelformat.cpp
index 0a464112549..3134600c526 100644
--- a/graphics/pixelformat.cpp
+++ b/graphics/pixelformat.cpp
@@ -58,7 +58,7 @@ Common::String PixelFormat::toString() const {
digits += '0' + 8 - componentLoss;
}
- return letters + digits;
+ return letters + digits + '@' + ('0' + bytesPerPixel);
}
} // End of namespace Graphics
diff --git a/graphics/transform_struct.h b/graphics/transform_struct.h
index 1d37492de40..c935f314201 100644
--- a/graphics/transform_struct.h
+++ b/graphics/transform_struct.h
@@ -32,6 +32,7 @@ enum TSpriteBlendMode {
BLEND_NORMAL = 0,
BLEND_ADDITIVE = 1,
BLEND_SUBTRACTIVE = 2,
+ BLEND_MULTIPLY = 3,
NUM_BLEND_MODES
};
diff --git a/graphics/transparent_surface.cpp b/graphics/transparent_surface.cpp
index 7ef7d002863..10f69b52816 100644
--- a/graphics/transparent_surface.cpp
+++ b/graphics/transparent_surface.cpp
@@ -37,8 +37,6 @@
#include "graphics/transparent_surface.h"
#include "graphics/transform_tools.h"
-//#define ENABLE_BILINEAR
-
namespace Graphics {
static const int kBModShift = 0;//img->format.bShift;
@@ -64,6 +62,7 @@ void doBlitBinaryFast(byte *ino, byte *outo, uint32 width, uint32 height, uint32
void doBlitAlphaBlend(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep, uint32 color);
void doBlitAdditiveBlend(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep, uint32 color);
void doBlitSubtractiveBlend(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep, uint32 color);
+void doBlitMultiplyBlend(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep, uint32 color);
TransparentSurface::TransparentSurface() : Surface(), _alphaMode(ALPHA_FULL) {}
@@ -328,6 +327,72 @@ void doBlitSubtractiveBlend(byte *ino, byte *outo, uint32 width, uint32 height,
}
}
+/**
+ * Optimized version of doBlit to be used with multiply blended blitting
+ */
+void doBlitMultiplyBlend(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep, uint32 color) {
+ byte *in;
+ byte *out;
+
+ if (color == 0xffffffff) {
+ for (uint32 i = 0; i < height; i++) {
+ out = outo;
+ in = ino;
+ for (uint32 j = 0; j < width; j++) {
+
+ if (in[kAIndex] != 0) {
+ out[kRIndex] = MIN((in[kRIndex] * in[kAIndex] >> 8) * out[kRIndex] >> 8, 255);
+ out[kGIndex] = MIN((in[kGIndex] * in[kAIndex] >> 8) * out[kGIndex] >> 8, 255);
+ out[kBIndex] = MIN((in[kBIndex] * in[kAIndex] >> 8) * out[kBIndex] >> 8, 255);
+ }
+
+ in += inStep;
+ out += 4;
+ }
+ outo += pitch;
+ ino += inoStep;
+ }
+ } else {
+ byte ca = (color >> kAModShift) & 0xFF;
+ byte cr = (color >> kRModShift) & 0xFF;
+ byte cg = (color >> kGModShift) & 0xFF;
+ byte cb = (color >> kBModShift) & 0xFF;
+
+ for (uint32 i = 0; i < height; i++) {
+ out = outo;
+ in = ino;
+ for (uint32 j = 0; j < width; j++) {
+
+ uint32 ina = in[kAIndex] * ca >> 8;
+
+ if (cb != 255) {
+ out[kBIndex] = MIN(out[kBIndex] * ((in[kBIndex] * cb * ina) >> 16) >> 8, 255u);
+ } else {
+ out[kBIndex] = MIN(out[kBIndex] * (in[kBIndex] * ina >> 8) >> 8, 255u);
+ }
+
+ if (cg != 255) {
+ out[kGIndex] = MIN(out[kGIndex] * ((in[kGIndex] * cg * ina) >> 16) >> 8, 255u);
+ } else {
+ out[kGIndex] = MIN(out[kGIndex] * (in[kGIndex] * ina >> 8) >> 8, 255u);
+ }
+
+ if (cr != 255) {
+ out[kRIndex] = MIN(out[kRIndex] * ((in[kRIndex] * cr * ina) >> 16) >> 8, 255u);
+ } else {
+ out[kRIndex] = MIN(out[kRIndex] * (in[kRIndex] * ina >> 8) >> 8, 255u);
+ }
+
+ in += inStep;
+ out += 4;
+ }
+ outo += pitch;
+ ino += inoStep;
+ }
+ }
+
+}
+
Common::Rect TransparentSurface::blit(Graphics::Surface &target, int posX, int posY, int flipping, Common::Rect *pPartRect, uint color, int width, int height, TSpriteBlendMode blendMode) {
Common::Rect retSize;
@@ -402,19 +467,31 @@ Common::Rect TransparentSurface::blit(Graphics::Surface &target, int posX, int p
// Handle off-screen clipping
if (posY < 0) {
img->h = MAX(0, (int)img->h - -posY);
- img->setPixels((byte *)img->getBasePtr(0, -posY));
+ if (!(flipping & FLIP_V))
+ img->setPixels((byte *)img->getBasePtr(0, -posY));
posY = 0;
}
if (posX < 0) {
img->w = MAX(0, (int)img->w - -posX);
- img->setPixels((byte *)img->getBasePtr(-posX, 0));
+ if (!(flipping & FLIP_H))
+ img->setPixels((byte *)img->getBasePtr(-posX, 0));
posX = 0;
}
- img->w = CLIP((int)img->w, 0, (int)MAX((int)target.w - posX, 0));
- img->h = CLIP((int)img->h, 0, (int)MAX((int)target.h - posY, 0));
+ if (img->w > target.w - posX) {
+ if (flipping & FLIP_H)
+ img->setPixels((byte *)img->getBasePtr(img->w - target.w + posX, 0));
+ img->w = CLIP((int)img->w, 0, (int)MAX((int)target.w - posX, 0));
+ }
+ if (img->h > target.h - posY) {
+ if (flipping & FLIP_V)
+ img->setPixels((byte *)img->getBasePtr(0, img->h - target.h + posY));
+ img->h = CLIP((int)img->h, 0, (int)MAX((int)target.h - posY, 0));
+ }
+
+ // Flip surface
if ((img->w > 0) && (img->h > 0)) {
int xp = 0, yp = 0;
@@ -442,6 +519,8 @@ Common::Rect TransparentSurface::blit(Graphics::Surface &target, int posX, int p
doBlitAdditiveBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color);
} else if (blendMode == BLEND_SUBTRACTIVE) {
doBlitSubtractiveBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color);
+ } else if (blendMode == BLEND_MULTIPLY) {
+ doBlitMultiplyBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color);
} else {
assert(blendMode == BLEND_NORMAL);
doBlitAlphaBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color);
@@ -535,19 +614,31 @@ Common::Rect TransparentSurface::blitClip(Graphics::Surface &target, Common::Rec
// Handle off-screen clipping
if (posY < clippingArea.top) {
img->h = MAX(0, (int)img->h - (clippingArea.top - posY));
- img->setPixels((byte *)img->getBasePtr(0, clippingArea.top - posY));
+ if (!(flipping & FLIP_V))
+ img->setPixels((byte *)img->getBasePtr(0, clippingArea.top - posY));
posY = clippingArea.top;
}
if (posX < clippingArea.left) {
img->w = MAX(0, (int)img->w - (clippingArea.left - posX));
- img->setPixels((byte *)img->getBasePtr(clippingArea.left - posX, 0));
+ if (!(flipping & FLIP_H))
+ img->setPixels((byte *)img->getBasePtr(clippingArea.left - posX, 0));
posX = clippingArea.left;
}
- img->w = CLIP((int)img->w, 0, (int)MAX((int)clippingArea.right - posX, 0));
- img->h = CLIP((int)img->h, 0, (int)MAX((int)clippingArea.bottom - posY, 0));
+ if (img->w > clippingArea.right - posX) {
+ if (flipping & FLIP_H)
+ img->setPixels((byte *)img->getBasePtr(img->w - clippingArea.right + posX, 0));
+ img->w = CLIP((int)img->w, 0, (int)MAX((int)clippingArea.right - posX, 0));
+ }
+ if (img->h > clippingArea.bottom - posY) {
+ if (flipping & FLIP_V)
+ img->setPixels((byte *)img->getBasePtr(0, img->h - clippingArea.bottom + posY));
+ img->h = CLIP((int)img->h, 0, (int)MAX((int)clippingArea.bottom - posY, 0));
+ }
+
+ // Flip surface
if ((img->w > 0) && (img->h > 0)) {
int xp = 0, yp = 0;
@@ -575,6 +666,8 @@ Common::Rect TransparentSurface::blitClip(Graphics::Surface &target, Common::Rec
doBlitAdditiveBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color);
} else if (blendMode == BLEND_SUBTRACTIVE) {
doBlitSubtractiveBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color);
+ } else if (blendMode == BLEND_MULTIPLY) {
+ doBlitMultiplyBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color);
} else {
assert(blendMode == BLEND_NORMAL);
doBlitAlphaBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color);
@@ -676,8 +769,10 @@ systems.
+struct tColorRGBA { byte r; byte g; byte b; byte a; };
-TransparentSurface *TransparentSurface::rotoscale(const TransformStruct &transform) const {
+template
+TransparentSurface *TransparentSurface::rotoscaleT(const TransformStruct &transform) const {
assert(transform._angle != 0); // This would not be ideal; rotoscale() should never be called in conditional branches where angle = 0 anyway.
@@ -704,7 +799,6 @@ TransparentSurface *TransparentSurface::rotoscale(const TransformStruct &transfo
float invCos = cos(invAngle * M_PI / 180.0);
float invSin = sin(invAngle * M_PI / 180.0);
- struct tColorRGBA { byte r; byte g; byte b; byte a; };
int icosx = (int)(invCos * (65536.0f * kDefaultZoomX / transform._zoom.x));
int isinx = (int)(invSin * (65536.0f * kDefaultZoomX / transform._zoom.x));
int icosy = (int)(invCos * (65536.0f * kDefaultZoomY / transform._zoom.y));
@@ -739,50 +833,50 @@ TransparentSurface *TransparentSurface::rotoscale(const TransformStruct &transfo
dy = sh - dy;
}
-#ifdef ENABLE_BILINEAR
- if ((dx > -1) && (dy > -1) && (dx < sw) && (dy < sh)) {
- const tColorRGBA *sp = (const tColorRGBA *)getBasePtr(dx, dy);
- tColorRGBA c00, c01, c10, c11, cswap;
- c00 = *sp;
- sp += 1;
- c01 = *sp;
- sp += (this->pitch / 4);
- c11 = *sp;
- sp -= 1;
- c10 = *sp;
- if (flipx) {
- cswap = c00; c00=c01; c01=cswap;
- cswap = c10; c10=c11; c11=cswap;
+ if (filteringMode == FILTER_BILINEAR) {
+ if ((dx > -1) && (dy > -1) && (dx < sw) && (dy < sh)) {
+ const tColorRGBA *sp = (const tColorRGBA *)getBasePtr(dx, dy);
+ tColorRGBA c00, c01, c10, c11, cswap;
+ c00 = *sp;
+ sp += 1;
+ c01 = *sp;
+ sp += (this->pitch / 4);
+ c11 = *sp;
+ sp -= 1;
+ c10 = *sp;
+ if (flipx) {
+ cswap = c00; c00=c01; c01=cswap;
+ cswap = c10; c10=c11; c11=cswap;
+ }
+ if (flipy) {
+ cswap = c00; c00=c10; c10=cswap;
+ cswap = c01; c01=c11; c11=cswap;
+ }
+ /*
+ * Interpolate colors
+ */
+ int ex = (sdx & 0xffff);
+ int ey = (sdy & 0xffff);
+ int t1, t2;
+ t1 = ((((c01.r - c00.r) * ex) >> 16) + c00.r) & 0xff;
+ t2 = ((((c11.r - c10.r) * ex) >> 16) + c10.r) & 0xff;
+ pc->r = (((t2 - t1) * ey) >> 16) + t1;
+ t1 = ((((c01.g - c00.g) * ex) >> 16) + c00.g) & 0xff;
+ t2 = ((((c11.g - c10.g) * ex) >> 16) + c10.g) & 0xff;
+ pc->g = (((t2 - t1) * ey) >> 16) + t1;
+ t1 = ((((c01.b - c00.b) * ex) >> 16) + c00.b) & 0xff;
+ t2 = ((((c11.b - c10.b) * ex) >> 16) + c10.b) & 0xff;
+ pc->b = (((t2 - t1) * ey) >> 16) + t1;
+ t1 = ((((c01.a - c00.a) * ex) >> 16) + c00.a) & 0xff;
+ t2 = ((((c11.a - c10.a) * ex) >> 16) + c10.a) & 0xff;
+ pc->a = (((t2 - t1) * ey) >> 16) + t1;
}
- if (flipy) {
- cswap = c00; c00=c10; c10=cswap;
- cswap = c01; c01=c11; c11=cswap;
+ } else {
+ if ((dx >= 0) && (dy >= 0) && (dx < srcW) && (dy < srcH)) {
+ const tColorRGBA *sp = (const tColorRGBA *)getBasePtr(dx, dy);
+ *pc = *sp;
}
- /*
- * Interpolate colors
- */
- int ex = (sdx & 0xffff);
- int ey = (sdy & 0xffff);
- int t1, t2;
- t1 = ((((c01.r - c00.r) * ex) >> 16) + c00.r) & 0xff;
- t2 = ((((c11.r - c10.r) * ex) >> 16) + c10.r) & 0xff;
- pc->r = (((t2 - t1) * ey) >> 16) + t1;
- t1 = ((((c01.g - c00.g) * ex) >> 16) + c00.g) & 0xff;
- t2 = ((((c11.g - c10.g) * ex) >> 16) + c10.g) & 0xff;
- pc->g = (((t2 - t1) * ey) >> 16) + t1;
- t1 = ((((c01.b - c00.b) * ex) >> 16) + c00.b) & 0xff;
- t2 = ((((c11.b - c10.b) * ex) >> 16) + c10.b) & 0xff;
- pc->b = (((t2 - t1) * ey) >> 16) + t1;
- t1 = ((((c01.a - c00.a) * ex) >> 16) + c00.a) & 0xff;
- t2 = ((((c11.a - c10.a) * ex) >> 16) + c10.a) & 0xff;
- pc->a = (((t2 - t1) * ey) >> 16) + t1;
}
-#else
- if ((dx >= 0) && (dy >= 0) && (dx < srcW) && (dy < srcH)) {
- const tColorRGBA *sp = (const tColorRGBA *)getBasePtr(dx, dy);
- *pc = *sp;
- }
-#endif
sdx += icosx;
sdy += isiny;
pc++;
@@ -791,192 +885,189 @@ TransparentSurface *TransparentSurface::rotoscale(const TransformStruct &transfo
return target;
}
-TransparentSurface *TransparentSurface::scale(uint16 newWidth, uint16 newHeight) const {
-
- Common::Rect srcRect(0, 0, (int16)w, (int16)h);
- Common::Rect dstRect(0, 0, (int16)newWidth, (int16)newHeight);
+template
+TransparentSurface *TransparentSurface::scaleT(uint16 newWidth, uint16 newHeight) const {
TransparentSurface *target = new TransparentSurface();
- assert(format.bytesPerPixel == 4);
+ int srcW = w;
+ int srcH = h;
+ int dstW = newWidth;
+ int dstH = newHeight;
- int srcW = srcRect.width();
- int srcH = srcRect.height();
- int dstW = dstRect.width();
- int dstH = dstRect.height();
+ target->create((uint16)dstW, (uint16)dstH, format);
- target->create((uint16)dstW, (uint16)dstH, this->format);
+ if (filteringMode == FILTER_BILINEAR) {
+ assert(format.bytesPerPixel == 4);
-#ifdef ENABLE_BILINEAR
-
- // NB: The actual order of these bytes may not be correct, but
- // since all values are treated equal, that does not matter.
- struct tColorRGBA { byte r; byte g; byte b; byte a; };
-
- bool flipx = false, flipy = false; // TODO: See mirroring comment in RenderTicket ctor
+ bool flipx = false, flipy = false; // TODO: See mirroring comment in RenderTicket ctor
- int *sax = new int[dstW + 1];
- int *say = new int[dstH + 1];
- assert(sax && say);
+ int *sax = new int[dstW + 1];
+ int *say = new int[dstH + 1];
+ assert(sax && say);
- /*
- * Precalculate row increments
- */
- int spixelw = (srcW - 1);
- int spixelh = (srcH - 1);
- int sx = (int) (65536.0f * (float) spixelw / (float) (dstW - 1));
- int sy = (int) (65536.0f * (float) spixelh / (float) (dstH - 1));
-
- /* Maximum scaled source size */
- int ssx = (srcW << 16) - 1;
- int ssy = (srcH << 16) - 1;
-
- /* Precalculate horizontal row increments */
- int csx = 0;
- int *csax = sax;
- for (int x = 0; x <= dstW; x++) {
- *csax = csx;
- csax++;
- csx += sx;
-
- /* Guard from overflows */
- if (csx > ssx) {
- csx = ssx;
- }
- }
-
- /* Precalculate vertical row increments */
- int csy = 0;
- int *csay = say;
- for (int y = 0; y <= dstH; y++) {
- *csay = csy;
- csay++;
- csy += sy;
-
- /* Guard from overflows */
- if (csy > ssy) {
- csy = ssy;
- }
- }
-
- const tColorRGBA *sp = (const tColorRGBA *) getBasePtr(0, 0);
- tColorRGBA *dp = (tColorRGBA *) target->getBasePtr(0, 0);
- int spixelgap = srcW;
-
- if (flipx) {
- sp += spixelw;
- }
- if (flipy) {
- sp += spixelgap * spixelh;
- }
-
- csay = say;
- for (int y = 0; y < dstH; y++) {
- const tColorRGBA *csp = sp;
- csax = sax;
- for (int x = 0; x < dstW; x++) {
- /*
- * Setup color source pointers
- */
- int ex = (*csax & 0xffff);
- int ey = (*csay & 0xffff);
- int cx = (*csax >> 16);
- int cy = (*csay >> 16);
-
- const tColorRGBA *c00, *c01, *c10, *c11;
- c00 = sp;
- c01 = sp;
- c10 = sp;
- if (cy < spixelh) {
- if (flipy) {
- c10 -= spixelgap;
- } else {
- c10 += spixelgap;
- }
- }
- c11 = c10;
- if (cx < spixelw) {
- if (flipx) {
- c01--;
- c11--;
- } else {
- c01++;
- c11++;
- }
- }
-
- /*
- * Draw and interpolate colors
- */
- int t1, t2;
- t1 = ((((c01->r - c00->r) * ex) >> 16) + c00->r) & 0xff;
- t2 = ((((c11->r - c10->r) * ex) >> 16) + c10->r) & 0xff;
- dp->r = (((t2 - t1) * ey) >> 16) + t1;
- t1 = ((((c01->g - c00->g) * ex) >> 16) + c00->g) & 0xff;
- t2 = ((((c11->g - c10->g) * ex) >> 16) + c10->g) & 0xff;
- dp->g = (((t2 - t1) * ey) >> 16) + t1;
- t1 = ((((c01->b - c00->b) * ex) >> 16) + c00->b) & 0xff;
- t2 = ((((c11->b - c10->b) * ex) >> 16) + c10->b) & 0xff;
- dp->b = (((t2 - t1) * ey) >> 16) + t1;
- t1 = ((((c01->a - c00->a) * ex) >> 16) + c00->a) & 0xff;
- t2 = ((((c11->a - c10->a) * ex) >> 16) + c10->a) & 0xff;
- dp->a = (((t2 - t1) * ey) >> 16) + t1;
-
- /*
- * Advance source pointer x
- */
- int *salastx = csax;
- csax++;
- int sstepx = (*csax >> 16) - (*salastx >> 16);
- if (flipx) {
- sp -= sstepx;
- } else {
- sp += sstepx;
- }
-
- /*
- * Advance destination pointer x
- */
- dp++;
- }
/*
- * Advance source pointer y
+ * Precalculate row increments
*/
- int *salasty = csay;
- csay++;
- int sstepy = (*csay >> 16) - (*salasty >> 16);
- sstepy *= spixelgap;
+ int spixelw = (srcW - 1);
+ int spixelh = (srcH - 1);
+ int sx = (int) (65536.0f * (float) spixelw / (float) (dstW - 1));
+ int sy = (int) (65536.0f * (float) spixelh / (float) (dstH - 1));
+
+ /* Maximum scaled source size */
+ int ssx = (srcW << 16) - 1;
+ int ssy = (srcH << 16) - 1;
+
+ /* Precalculate horizontal row increments */
+ int csx = 0;
+ int *csax = sax;
+ for (int x = 0; x <= dstW; x++) {
+ *csax = csx;
+ csax++;
+ csx += sx;
+
+ /* Guard from overflows */
+ if (csx > ssx) {
+ csx = ssx;
+ }
+ }
+
+ /* Precalculate vertical row increments */
+ int csy = 0;
+ int *csay = say;
+ for (int y = 0; y <= dstH; y++) {
+ *csay = csy;
+ csay++;
+ csy += sy;
+
+ /* Guard from overflows */
+ if (csy > ssy) {
+ csy = ssy;
+ }
+ }
+
+ const tColorRGBA *sp = (const tColorRGBA *) getBasePtr(0, 0);
+ tColorRGBA *dp = (tColorRGBA *) target->getBasePtr(0, 0);
+ int spixelgap = srcW;
+
+ if (flipx) {
+ sp += spixelw;
+ }
if (flipy) {
- sp = csp - sstepy;
- } else {
- sp = csp + sstepy;
+ sp += spixelgap * spixelh;
}
- }
- delete[] sax;
- delete[] say;
+ csay = say;
+ for (int y = 0; y < dstH; y++) {
+ const tColorRGBA *csp = sp;
+ csax = sax;
+ for (int x = 0; x < dstW; x++) {
+ /*
+ * Setup color source pointers
+ */
+ int ex = (*csax & 0xffff);
+ int ey = (*csay & 0xffff);
+ int cx = (*csax >> 16);
+ int cy = (*csay >> 16);
-#else
+ const tColorRGBA *c00, *c01, *c10, *c11;
+ c00 = sp;
+ c01 = sp;
+ c10 = sp;
+ if (cy < spixelh) {
+ if (flipy) {
+ c10 -= spixelgap;
+ } else {
+ c10 += spixelgap;
+ }
+ }
+ c11 = c10;
+ if (cx < spixelw) {
+ if (flipx) {
+ c01--;
+ c11--;
+ } else {
+ c01++;
+ c11++;
+ }
+ }
- int *scaleCacheX = new int[dstW];
- for (int x = 0; x < dstW; x++) {
- scaleCacheX[x] = (x * srcW) / dstW;
- }
+ /*
+ * Draw and interpolate colors
+ */
+ int t1, t2;
+ t1 = ((((c01->r - c00->r) * ex) >> 16) + c00->r) & 0xff;
+ t2 = ((((c11->r - c10->r) * ex) >> 16) + c10->r) & 0xff;
+ dp->r = (((t2 - t1) * ey) >> 16) + t1;
+ t1 = ((((c01->g - c00->g) * ex) >> 16) + c00->g) & 0xff;
+ t2 = ((((c11->g - c10->g) * ex) >> 16) + c10->g) & 0xff;
+ dp->g = (((t2 - t1) * ey) >> 16) + t1;
+ t1 = ((((c01->b - c00->b) * ex) >> 16) + c00->b) & 0xff;
+ t2 = ((((c11->b - c10->b) * ex) >> 16) + c10->b) & 0xff;
+ dp->b = (((t2 - t1) * ey) >> 16) + t1;
+ t1 = ((((c01->a - c00->a) * ex) >> 16) + c00->a) & 0xff;
+ t2 = ((((c11->a - c10->a) * ex) >> 16) + c10->a) & 0xff;
+ dp->a = (((t2 - t1) * ey) >> 16) + t1;
- for (int y = 0; y < dstH; y++) {
- uint32 *destP = (uint32 *)target->getBasePtr(0, y);
- const uint32 *srcP = (const uint32 *)getBasePtr(0, (y * srcH) / dstH);
+ /*
+ * Advance source pointer x
+ */
+ int *salastx = csax;
+ csax++;
+ int sstepx = (*csax >> 16) - (*salastx >> 16);
+ if (flipx) {
+ sp -= sstepx;
+ } else {
+ sp += sstepx;
+ }
+
+ /*
+ * Advance destination pointer x
+ */
+ dp++;
+ }
+ /*
+ * Advance source pointer y
+ */
+ int *salasty = csay;
+ csay++;
+ int sstepy = (*csay >> 16) - (*salasty >> 16);
+ sstepy *= spixelgap;
+ if (flipy) {
+ sp = csp - sstepy;
+ } else {
+ sp = csp + sstepy;
+ }
+ }
+
+ delete[] sax;
+ delete[] say;
+
+ } else {
+ int *scaleCacheX = new int[dstW];
for (int x = 0; x < dstW; x++) {
- *destP++ = srcP[scaleCacheX[x]];
+ scaleCacheX[x] = (x * srcW) / dstW;
}
- }
- delete[] scaleCacheX;
-#endif
+ switch (format.bytesPerPixel) {
+ case 1:
+ scaleNN(scaleCacheX, target);
+ break;
+ case 2:
+ scaleNN(scaleCacheX, target);
+ break;
+ case 4:
+ scaleNN(scaleCacheX, target);
+ break;
+ default:
+ error("Can only scale 8bpp, 16bpp, and 32bpp");
+ }
+
+ delete[] scaleCacheX;
+ }
return target;
-
}
TransparentSurface *TransparentSurface::convertTo(const PixelFormat &dstFormat, const byte *palette) const {
@@ -1057,4 +1148,32 @@ TransparentSurface *TransparentSurface::convertTo(const PixelFormat &dstFormat,
return surface;
}
+template
+void TransparentSurface::scaleNN(int *scaleCacheX, TransparentSurface *target) const {
+ for (int y = 0; y < target->h; y++) {
+ Size *destP = (Size *)target->getBasePtr(0, y);
+ const Size *srcP = (const Size *)getBasePtr(0, (y * h) / target->h);
+ for (int x = 0; x < target->w; x++) {
+ *destP++ = srcP[scaleCacheX[x]];
+ }
+ }
+}
+
+template TransparentSurface *TransparentSurface::rotoscaleT(const TransformStruct &transform) const;
+template TransparentSurface *TransparentSurface::rotoscaleT(const TransformStruct &transform) const;
+template TransparentSurface *TransparentSurface::scaleT(uint16 newWidth, uint16 newHeight) const;
+template TransparentSurface *TransparentSurface::scaleT(uint16 newWidth, uint16 newHeight) const;
+
+template void TransparentSurface::scaleNN(int *scaleCacheX, TransparentSurface *target) const;
+template void TransparentSurface::scaleNN(int *scaleCacheX, TransparentSurface *target) const;
+template void TransparentSurface::scaleNN(int *scaleCacheX, TransparentSurface *target) const;
+
+TransparentSurface *TransparentSurface::rotoscale(const TransformStruct &transform) const {
+ return rotoscaleT(transform);
+}
+
+TransparentSurface *TransparentSurface::scale(uint16 newWidth, uint16 newHeight) const {
+ return scaleT(newWidth, newHeight);
+}
+
} // End of namespace Graphics
diff --git a/graphics/transparent_surface.h b/graphics/transparent_surface.h
index 86541835483..22fe04551ea 100644
--- a/graphics/transparent_surface.h
+++ b/graphics/transparent_surface.h
@@ -68,6 +68,11 @@ enum AlphaType {
ALPHA_FULL = 2
};
+enum TFilteringMode {
+ FILTER_NEAREST = 0,
+ FILTER_BILINEAR = 1
+};
+
/**
* A transparent graphics surface, which implements alpha blitting.
*/
@@ -141,6 +146,9 @@ struct TransparentSurface : public Graphics::Surface {
* @param newHeight the resulting height.
* @see TransformStruct
*/
+ template
+ TransparentSurface *scaleT(uint16 newWidth, uint16 newHeight) const;
+
TransparentSurface *scale(uint16 newWidth, uint16 newHeight) const;
/**
@@ -150,6 +158,9 @@ struct TransparentSurface : public Graphics::Surface {
* @param transform a TransformStruct wrapping the required info. @see TransformStruct
*
*/
+ template
+ TransparentSurface *rotoscaleT(const TransformStruct &transform) const;
+
TransparentSurface *rotoscale(const TransformStruct &transform) const;
TransparentSurface *convertTo(const PixelFormat &dstFormat, const byte *palette = 0) const;
@@ -166,6 +177,8 @@ struct TransparentSurface : public Graphics::Surface {
private:
AlphaType _alphaMode;
+ template
+ void scaleNN(int *scaleCacheX, TransparentSurface *target) const;
};
/**
diff --git a/gui/KeysDialog.cpp b/gui/KeysDialog.cpp
index 7adb20a3796..fcf9201877e 100644
--- a/gui/KeysDialog.cpp
+++ b/gui/KeysDialog.cpp
@@ -68,7 +68,7 @@ void KeysDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
case kListSelectionChangedCmd:
if (_actionsList->getSelected() >= 0) {
- char selection[100];
+ Common::String selection;
uint16 key = Actions::Instance()->getMapping(_actionsList->getSelected());
#ifdef __SYMBIAN32__
@@ -77,9 +77,9 @@ void KeysDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
key = key - Common::ASCII_F1 + SDLK_F1;
#endif
if (key != 0)
- sprintf(selection, _("Associated key : %s"), SDL_GetKeyName((SDLKey)key));
+ selection = Common::String::format(_("Associated key : %s"), SDL_GetKeyName((SDLKey)key));
else
- sprintf(selection, _("Associated key : none"));
+ selection = Common::String::format(_("Associated key : none"));
_keyMapping->setLabel(selection);
_keyMapping->draw();
@@ -89,7 +89,7 @@ void KeysDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
if (_actionsList->getSelected() < 0) {
_actionTitle->setLabel(_("Please select an action"));
} else {
- char selection[100];
+ Common::String selection;
_actionSelected = _actionsList->getSelected();
uint16 key = Actions::Instance()->getMapping(_actionSelected);
@@ -99,9 +99,9 @@ void KeysDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
key = key - Common::ASCII_F1 + SDLK_F1;
#endif
if (key != 0)
- sprintf(selection, _("Associated key : %s"), SDL_GetKeyName((SDLKey)key));
+ selection = Common::String::format(_("Associated key : %s"), SDL_GetKeyName((SDLKey)key));
else
- sprintf(selection, _("Associated key : none"));
+ selection = Common::String::format(_("Associated key : none"));
_actionTitle->setLabel(_("Press the key to associate"));
_keyMapping->setLabel(selection);
@@ -133,14 +133,14 @@ void KeysDialog::handleKeyUp(Common::KeyState state) {
#else
if (state.flags == 0xff && Actions::Instance()->mappingActive()) { // GAPI key was selected
#endif
- char selection[100];
+ Common::String selection;
Actions::Instance()->setMapping((ActionType)_actionSelected, state.ascii);
if (state.ascii != 0)
- sprintf(selection, _("Associated key : %s"), SDL_GetKeyName((SDLKey) state.keycode));
+ selection = Common::String::format(_("Associated key : %s"), SDL_GetKeyName((SDLKey) state.keycode));
else
- sprintf(selection, _("Associated key : none"));
+ selection = Common::String::format(_("Associated key : none"));
_actionTitle->setLabel(_("Choose an action to map"));
_keyMapping->setLabel(selection);
diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp
index 8763b3c4529..1a6d8e71a56 100644
--- a/gui/ThemeEngine.cpp
+++ b/gui/ThemeEngine.cpp
@@ -87,6 +87,7 @@ struct WidgetDrawData {
E.g. when taking into account rounded corners, drop shadows, etc
Used when restoring the widget background */
uint16 _backgroundOffset;
+ uint16 _shadowOffset;
bool _buffer;
@@ -271,6 +272,10 @@ void ThemeItemDrawData::drawSelf(bool draw, bool restore) {
Common::Rect extendedRect = _area;
extendedRect.grow(_engine->kDirtyRectangleThreshold + _data->_backgroundOffset);
+ if (_data->_shadowOffset > _data->_backgroundOffset) {
+ extendedRect.right += _data->_shadowOffset - _data->_backgroundOffset;
+ extendedRect.bottom += _data->_shadowOffset - _data->_backgroundOffset;
+ }
if (restore)
_engine->restoreBackground(extendedRect);
@@ -288,6 +293,10 @@ void ThemeItemDrawDataClip::drawSelf(bool draw, bool restore) {
Common::Rect extendedRect = _area;
extendedRect.grow(_engine->kDirtyRectangleThreshold + _data->_backgroundOffset);
+ if (_data->_shadowOffset > _data->_backgroundOffset) {
+ extendedRect.right += _data->_shadowOffset - _data->_backgroundOffset;
+ extendedRect.bottom += _data->_shadowOffset - _data->_backgroundOffset;
+ }
if (restore)
_engine->restoreBackground(extendedRect);
@@ -621,6 +630,7 @@ void ThemeEngine::setGraphicsMode(GraphicsMode mode) {
_bytesPerPixel = sizeof(uint16);
break;
}
+ // fall through
default:
error("Invalid graphics mode");
}
@@ -646,17 +656,18 @@ void ThemeEngine::setGraphicsMode(GraphicsMode mode) {
}
void WidgetDrawData::calcBackgroundOffset() {
- uint maxShadow = 0;
+ uint maxShadow = 0, maxBevel = 0;
for (Common::List::const_iterator step = _steps.begin();
step != _steps.end(); ++step) {
if ((step->autoWidth || step->autoHeight) && step->shadow > maxShadow)
maxShadow = step->shadow;
- if (step->drawingCall == &Graphics::VectorRenderer::drawCallback_BEVELSQ && step->bevel > maxShadow)
- maxShadow = step->bevel;
+ if (step->drawingCall == &Graphics::VectorRenderer::drawCallback_BEVELSQ && step->bevel > maxBevel)
+ maxBevel = step->bevel;
}
- _backgroundOffset = maxShadow;
+ _backgroundOffset = maxBevel;
+ _shadowOffset = maxShadow;
}
void ThemeEngine::restoreBackground(Common::Rect r) {
@@ -722,7 +733,9 @@ bool ThemeEngine::addFont(TextData textId, const Common::String &file, const Com
#ifdef USE_TRANSLATION
TransMan.setLanguage("C");
#endif
- warning("Failed to load localized font '%s'. Using non-localized font and default GUI language instead", localized.c_str());
+ warning("Failed to load localized font '%s'.", localized.c_str());
+
+ return false;
}
}
@@ -810,7 +823,9 @@ bool ThemeEngine::addAlphaBitmap(const Common::String &filename) {
if (surf)
return true;
+#ifdef USE_PNG
const Graphics::TransparentSurface *srcSurface = 0;
+#endif
if (filename.hasSuffix(".png")) {
// Maybe it is PNG?
@@ -1306,7 +1321,7 @@ void ThemeEngine::drawRadiobuttonClip(const Common::Rect &r, const Common::Rect
queueDDClip(dd, r2, clippingRect);
r2.left = r2.right + checkBoxSize;
- r2.right = r.right;
+ r2.right = MAX(r2.left, r.right);
queueDDTextClip(getTextData(dd), getTextColor(dd), r2, clippingRect, str, true, false, _widgets[kDDRadiobuttonDefault]->_textAlignH, _widgets[dd]->_textAlignV);
}
@@ -1517,7 +1532,7 @@ void ThemeEngine::drawPopUpWidgetClip(const Common::Rect &r, const Common::Rect
queueDDClip(dd, r, clip);
- if (!sel.empty()) {
+ if (!sel.empty() && r.width() >= 13 && r.height() >= 1) {
Common::Rect text(r.left + 3, r.top + 1, r.right - 10, r.bottom);
queueDDTextClip(getTextData(dd), getTextColor(dd), text, clip, sel, true, false, _widgets[dd]->_textAlignH, _widgets[dd]->_textAlignV, deltax);
}
@@ -1618,28 +1633,34 @@ void ThemeEngine::drawTab(const Common::Rect &r, int tabHeight, int tabWidth, co
}
}
-void ThemeEngine::drawTabClip(const Common::Rect &r, const Common::Rect &clip, int tabHeight, int tabWidth, const Common::Array &tabs, int active, uint16 hints, int titleVPad, WidgetStateInfo state) {
+void ThemeEngine::drawTabClip(const Common::Rect &r, const Common::Rect &clip, int tabHeight, const Common::Array &tabWidths, const Common::Array &tabs, int active, uint16 hints, int titleVPad, WidgetStateInfo state) {
if (!ready())
return;
+ assert(tabs.size() == tabWidths.size());
+
queueDDClip(kDDTabBackground, Common::Rect(r.left, r.top, r.right, r.top + tabHeight), clip);
- for (int i = 0; i < (int)tabs.size(); ++i) {
- if (i == active)
+ int width = 0;
+ int activePos = -1;
+ for (int i = 0; i < (int)tabs.size(); width += tabWidths[i++]) {
+ if (r.left + width > r.right || r.left + width + tabWidths[i] > r.right)
continue;
- if (r.left + i * tabWidth > r.right || r.left + (i + 1) * tabWidth > r.right)
+ if (i == active) {
+ activePos = width;
continue;
+ }
- Common::Rect tabRect(r.left + i * tabWidth, r.top, r.left + (i + 1) * tabWidth, r.top + tabHeight);
+
+ Common::Rect tabRect(r.left + width, r.top, r.left + width + tabWidths[i], r.top + tabHeight);
queueDDClip(kDDTabInactive, tabRect, clip);
queueDDTextClip(getTextData(kDDTabInactive), getTextColor(kDDTabInactive), tabRect, clip, tabs[i], false, false, _widgets[kDDTabInactive]->_textAlignH, _widgets[kDDTabInactive]->_textAlignV);
}
- if (active >= 0 &&
- (r.left + active * tabWidth < r.right) && (r.left + (active + 1) * tabWidth < r.right)) {
- Common::Rect tabRect(r.left + active * tabWidth, r.top, r.left + (active + 1) * tabWidth, r.top + tabHeight);
- const uint16 tabLeft = active * tabWidth;
+ if (activePos >= 0) {
+ Common::Rect tabRect(r.left + activePos, r.top, r.left + activePos + tabWidths[active], r.top + tabHeight);
+ const uint16 tabLeft = activePos;
const uint16 tabRight = MAX(r.right - tabRect.right, 0);
queueDDClip(kDDTabActive, tabRect, clip, (tabLeft << 16) | (tabRight & 0xFFFF));
queueDDTextClip(getTextData(kDDTabActive), getTextColor(kDDTabActive), tabRect, clip, tabs[active], false, false, _widgets[kDDTabActive]->_textAlignH, _widgets[kDDTabActive]->_textAlignV);
@@ -2412,19 +2433,32 @@ Common::String ThemeEngine::getThemeId(const Common::String &filename) {
return "builtin";
Common::FSNode node(filename);
- if (!node.exists())
- return "builtin";
+ if (node.exists()) {
+ if (node.getName().matchString("*.zip", true)) {
+ Common::String id = node.getName();
- if (node.getName().matchString("*.zip", true)) {
- Common::String id = node.getName();
+ for (int i = 0; i < 4; ++i)
+ id.deleteLastChar();
- for (int i = 0; i < 4; ++i)
- id.deleteLastChar();
-
- return id;
- } else {
- return node.getName();
+ return id;
+ } else {
+ return node.getName();
+ }
}
+
+ // FIXME:
+ // A very ugly hack to map a id to a filename, this will generate
+ // a complete theme list, thus it is slower than it could be.
+ // But it is the easiest solution for now.
+ Common::List list;
+ listUsableThemes(list);
+
+ for (Common::List::const_iterator i = list.begin(); i != list.end(); ++i) {
+ if (filename.equalsIgnoreCase(i->filename))
+ return i->id;
+ }
+
+ return "builtin";
}
void ThemeEngine::showCursor() {
diff --git a/gui/ThemeEngine.h b/gui/ThemeEngine.h
index 8a6db17dfd2..9d8ed417380 100644
--- a/gui/ThemeEngine.h
+++ b/gui/ThemeEngine.h
@@ -388,7 +388,7 @@ public:
void drawTab(const Common::Rect &r, int tabHeight, int tabWidth,
const Common::Array &tabs, int active, uint16 hints,
int titleVPad, WidgetStateInfo state = kStateEnabled);
- void drawTabClip(const Common::Rect &r, const Common::Rect &clippingRect, int tabHeight, int tabWidth,
+ void drawTabClip(const Common::Rect &r, const Common::Rect &clippingRect, int tabHeight, const Common::Array &tabWidths,
const Common::Array &tabs, int active, uint16 hints,
int titleVPad, WidgetStateInfo state = kStateEnabled);
diff --git a/gui/browser.h b/gui/browser.h
index b77907bad28..10663f547e9 100644
--- a/gui/browser.h
+++ b/gui/browser.h
@@ -52,7 +52,6 @@ protected:
#ifdef MACOSX
const void *_titleRef;
const void *_chooseRef;
- const void *_hiddenFilesRef;
#else
ListWidget *_fileList;
StaticTextWidget *_currentPath;
diff --git a/gui/browser_osx.mm b/gui/browser_osx.mm
index cfb70a3f650..ffb64ee96a5 100644
--- a/gui/browser_osx.mm
+++ b/gui/browser_osx.mm
@@ -27,45 +27,93 @@
#include "common/config-manager.h"
#include "common/system.h"
+#include "common/events.h"
#include "common/algorithm.h"
#include "common/translation.h"
#include
#include
+#include
#include
#include
#include
+#include
-@interface ShowHiddenFilesController : NSObject {
- NSOpenPanel* _panel;
+
+@interface BrowserDialogPresenter : NSObject {
+@public
+ NSURL *_url;
+@private
+ NSOpenPanel *_panel;
}
-
- (id) init;
- (void) dealloc;
-- (void) setOpenPanel : (NSOpenPanel*) panel;
+- (void) showOpenPanel: (NSOpenPanel*) panel;
- (IBAction) showHiddenFiles : (id) sender;
-
@end
-@implementation ShowHiddenFilesController
+@implementation BrowserDialogPresenter
- (id) init {
self = [super init];
+ _url = 0;
_panel = 0;
-
return self;
}
- (void) dealloc {
- [_panel release];
+ [_url release];
[super dealloc];
}
-- (void) setOpenPanel : (NSOpenPanel*) panel {
+- (void) showOpenPanel: (NSOpenPanel*) panel {
_panel = panel;
- [_panel retain];
-}
+ NSButton *showHiddenFilesButton = 0;
+ if ([panel respondsToSelector:@selector(setShowsHiddenFiles:)]) {
+ showHiddenFilesButton = [[NSButton alloc] init];
+ [showHiddenFilesButton setButtonType:NSSwitchButton];
+
+#ifdef USE_TRANSLATION
+ CFStringRef encStr = CFStringCreateWithCString(NULL, TransMan.getCurrentCharset().c_str(), kCFStringEncodingASCII);
+ CFStringEncoding stringEncoding = CFStringConvertIANACharSetNameToEncoding(encStr);
+ CFRelease(encStr);
+#else
+ CFStringEncoding stringEncoding = kCFStringEncodingASCII;
+#endif
+ CFStringRef hiddenFilesString = CFStringCreateWithCString(0, _("Show hidden files"), stringEncoding);
+ [showHiddenFilesButton setTitle:(NSString*)hiddenFilesString];
+ CFRelease(hiddenFilesString);
+
+ [showHiddenFilesButton sizeToFit];
+ if (ConfMan.getBool("gui_browser_show_hidden", Common::ConfigManager::kApplicationDomain)) {
+ [showHiddenFilesButton setState:NSOnState];
+ [panel setShowsHiddenFiles: YES];
+ } else {
+ [showHiddenFilesButton setState:NSOffState];
+ [panel setShowsHiddenFiles: NO];
+ }
+ [panel setAccessoryView:showHiddenFilesButton];
+
+ [showHiddenFilesButton setTarget:self];
+ [showHiddenFilesButton setAction:@selector(showHiddenFiles:)];
+ }
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED <= 1090
+ if ([panel runModal] == NSOKButton) {
+#else
+ if ([panel runModal] == NSModalResponseOK) {
+#endif
+ NSURL *url = [panel URL];
+ if ([url isFileURL]) {
+ _url = url;
+ [_url retain];
+ }
+ }
+
+ [showHiddenFilesButton release];
+ _panel = 0;
+}
- (IBAction) showHiddenFiles : (id) sender {
if ([sender state] == NSOnState) {
@@ -101,13 +149,11 @@ BrowserDialog::BrowserDialog(const char *title, bool dirBrowser)
// Convert button text to NSString
_chooseRef = CFStringCreateWithCString(0, _("Choose"), stringEncoding);
- _hiddenFilesRef = CFStringCreateWithCString(0, _("Show hidden files"), stringEncoding);
}
BrowserDialog::~BrowserDialog() {
CFRelease(_titleRef);
CFRelease(_chooseRef);
- CFRelease(_hiddenFilesRef);
}
int BrowserDialog::runModal() {
@@ -124,6 +170,10 @@ int BrowserDialog::runModal() {
// Temporarily show the real mouse
CGDisplayShowCursor(kCGDirectMainDisplay);
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ NSWindow *keyWindow = [[NSApplication sharedApplication] keyWindow];
+
+
NSOpenPanel *panel = [NSOpenPanel openPanel];
[panel setCanChooseFiles:!_isDirBrowser];
[panel setCanChooseDirectories:_isDirBrowser];
@@ -132,43 +182,25 @@ int BrowserDialog::runModal() {
[panel setTitle:(NSString *)_titleRef];
[panel setPrompt:(NSString *)_chooseRef];
- NSButton *showHiddenFilesButton = 0;
- ShowHiddenFilesController *showHiddenFilesController = 0;
- if ([panel respondsToSelector:@selector(setShowsHiddenFiles:)]) {
- showHiddenFilesButton = [[NSButton alloc] init];
- [showHiddenFilesButton setButtonType:NSSwitchButton];
- [showHiddenFilesButton setTitle:(NSString *)_hiddenFilesRef];
- [showHiddenFilesButton sizeToFit];
- if (ConfMan.getBool("gui_browser_show_hidden", Common::ConfigManager::kApplicationDomain)) {
- [showHiddenFilesButton setState:NSOnState];
- [panel setShowsHiddenFiles: YES];
- } else {
- [showHiddenFilesButton setState:NSOffState];
- [panel setShowsHiddenFiles: NO];
- }
- [panel setAccessoryView:showHiddenFilesButton];
-
- showHiddenFilesController = [[ShowHiddenFilesController alloc] init];
- [showHiddenFilesController setOpenPanel:panel];
- [showHiddenFilesButton setTarget:showHiddenFilesController];
- [showHiddenFilesButton setAction:@selector(showHiddenFiles:)];
+ BrowserDialogPresenter* presenter = [[BrowserDialogPresenter alloc] init];
+ [presenter performSelectorOnMainThread:@selector(showOpenPanel:) withObject:panel waitUntilDone:YES];
+ if (presenter->_url) {
+ Common::String filename = [[presenter->_url path] UTF8String];
+ _choice = Common::FSNode(filename);
+ choiceMade = true;
}
+ [presenter release];
-#if MAC_OS_X_VERSION_MAX_ALLOWED <= 1090
- if ([panel runModal] == NSOKButton) {
-#else
- if ([panel runModal] == NSModalResponseOK) {
-#endif
- NSURL *url = [panel URL];
- if ([url isFileURL]) {
- const char *filename = [[url path] UTF8String];
- _choice = Common::FSNode(filename);
- choiceMade = true;
- }
- }
+ [pool release];
+ [keyWindow makeKeyAndOrderFront:nil];
- [showHiddenFilesButton release];
- [showHiddenFilesController release];
+ // While the native macOS file browser is open, any input events (e.g. keypresses) are
+ // still received by the NSApplication. With SDL backend for example this results in the
+ // events beeing queued and processed after we return, thus dispatching events that were
+ // intended for the native file browser. For example: pressing Esc to cancel the native
+ // macOS file browser would cause the application to quit in addition to closing the
+ // file browser. To avoid this happening clear all pending events.
+ g_system->getEventManager()->getEventDispatcher()->clearEvents();
// If we were in fullscreen mode, switch back
if (wasFullscreen) {
diff --git a/gui/console.cpp b/gui/console.cpp
index 4cdad41f5f1..27b38ac78b5 100644
--- a/gui/console.cpp
+++ b/gui/console.cpp
@@ -353,6 +353,7 @@ void ConsoleDialog::handleKeyDown(Common::KeyState state) {
defaultKeyDownHandler(state);
break;
}
+ // fall through
case Common::KEYCODE_DELETE:
if (_currentPos < _promptEndPos) {
killChar();
@@ -365,6 +366,7 @@ void ConsoleDialog::handleKeyDown(Common::KeyState state) {
defaultKeyDownHandler(state);
break;
}
+ // fall through
case Common::KEYCODE_END:
if (state.hasFlags(Common::KBD_SHIFT)) {
_scrollLine = _promptEndPos / kCharsPerLine;
@@ -382,6 +384,7 @@ void ConsoleDialog::handleKeyDown(Common::KeyState state) {
defaultKeyDownHandler(state);
break;
}
+ // fall through
case Common::KEYCODE_DOWN:
historyScroll(-1);
break;
@@ -391,6 +394,7 @@ void ConsoleDialog::handleKeyDown(Common::KeyState state) {
defaultKeyDownHandler(state);
break;
}
+ // fall through
case Common::KEYCODE_PAGEDOWN:
if (state.hasFlags(Common::KBD_SHIFT)) {
_scrollLine += _linesPerPage - 1;
@@ -409,6 +413,7 @@ void ConsoleDialog::handleKeyDown(Common::KeyState state) {
defaultKeyDownHandler(state);
break;
}
+ // fall through
case Common::KEYCODE_LEFT:
if (_currentPos > _promptStartPos)
_currentPos--;
@@ -420,6 +425,7 @@ void ConsoleDialog::handleKeyDown(Common::KeyState state) {
defaultKeyDownHandler(state);
break;
}
+ // fall through
case Common::KEYCODE_RIGHT:
if (_currentPos < _promptEndPos)
_currentPos++;
@@ -431,6 +437,7 @@ void ConsoleDialog::handleKeyDown(Common::KeyState state) {
defaultKeyDownHandler(state);
break;
}
+ // fall through
case Common::KEYCODE_HOME:
if (state.hasFlags(Common::KBD_SHIFT)) {
_scrollLine = _firstLineInBuffer + _linesPerPage - 1;
@@ -446,6 +453,7 @@ void ConsoleDialog::handleKeyDown(Common::KeyState state) {
defaultKeyDownHandler(state);
break;
}
+ // fall through
case Common::KEYCODE_UP:
historyScroll(+1);
break;
@@ -455,6 +463,7 @@ void ConsoleDialog::handleKeyDown(Common::KeyState state) {
defaultKeyDownHandler(state);
break;
}
+ // fall through
case Common::KEYCODE_PAGEUP:
if (state.hasFlags(Common::KBD_SHIFT)) {
_scrollLine -= _linesPerPage - 1;
diff --git a/gui/dialog.cpp b/gui/dialog.cpp
index 523227a2374..24b3db4d6d6 100644
--- a/gui/dialog.cpp
+++ b/gui/dialog.cpp
@@ -88,13 +88,7 @@ void Dialog::open() {
_visible = true;
g_gui.openDialog(this);
- Widget *w = _firstWidget;
- // Search for the first objects that wantsFocus() (if any) and give it the focus
- while (w && !w->wantsFocus()) {
- w = w->_next;
- }
-
- setFocusWidget(w);
+ setDefaultFocusedWidget();
}
void Dialog::close() {
@@ -142,6 +136,16 @@ void Dialog::setFocusWidget(Widget *widget) {
_focusedWidget = widget;
}
+void Dialog::setDefaultFocusedWidget() {
+ Widget *w = _firstWidget;
+ // Search for the first objects that wantsFocus() (if any) and give it the focus
+ while (w && !w->wantsFocus()) {
+ w = w->_next;
+ }
+
+ setFocusWidget(w);
+}
+
void Dialog::releaseFocus() {
if (_focusedWidget) {
_focusedWidget->lostFocus();
@@ -361,11 +365,11 @@ Widget *Dialog::findWidget(const char *name) {
}
void Dialog::removeWidget(Widget *del) {
- if (del == _mouseWidget)
+ if (del == _mouseWidget || del->containsWidget(_mouseWidget))
_mouseWidget = NULL;
- if (del == _focusedWidget)
+ if (del == _focusedWidget || del->containsWidget(_focusedWidget))
_focusedWidget = NULL;
- if (del == _dragWidget)
+ if (del == _dragWidget || del->containsWidget(_dragWidget))
_dragWidget = NULL;
GuiObject::removeWidget(del);
diff --git a/gui/dialog.h b/gui/dialog.h
index 0e06effabd2..cf734a83f8b 100644
--- a/gui/dialog.h
+++ b/gui/dialog.h
@@ -107,6 +107,8 @@ protected:
Widget *findWidget(const char *name);
void removeWidget(Widget *widget);
+ void setDefaultFocusedWidget();
+
void setResult(int result) { _result = result; }
int getResult() const { return _result; }
};
diff --git a/gui/editgamedialog.cpp b/gui/editgamedialog.cpp
index 85b37929dc1..94c74b40c09 100644
--- a/gui/editgamedialog.cpp
+++ b/gui/editgamedialog.cpp
@@ -539,7 +539,7 @@ void EditGameDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
_domain = newDomain;
}
}
- // FALL THROUGH to default case
+ // fall through
default:
OptionsDialog::handleCommand(sender, cmd, data);
}
diff --git a/gui/gui-manager.cpp b/gui/gui-manager.cpp
index 421a8f4cac6..e11afa96d90 100644
--- a/gui/gui-manager.cpp
+++ b/gui/gui-manager.cpp
@@ -254,6 +254,23 @@ Dialog *GuiManager::getTopDialog() const {
return _dialogStack.top();
}
+void GuiManager::addToTrash(GuiObject* object, Dialog* parent) {
+ debug(7, "Adding Gui Object %p to trash", (void *)object);
+ GuiObjectTrashItem t;
+ t.object = object;
+ t.parent = 0;
+ // If a dialog was provided, check it is in the dialog stack
+ if (parent != 0) {
+ for (uint i = 0 ; i < _dialogStack.size() ; ++i) {
+ if (_dialogStack[i] == parent) {
+ t.parent = parent;
+ break;
+ }
+ }
+ }
+ _guiObjectTrash.push_back(t);
+}
+
void GuiManager::runLoop() {
Dialog * const activeDialog = getTopDialog();
bool didSaveState = false;
@@ -326,11 +343,14 @@ void GuiManager::runLoop() {
//
// This hopefully fixes strange behavior/crashes with pop-up widgets. (Most easily
// triggered in 3x mode or when running ScummVM under Valgrind.)
- if (activeDialog != getTopDialog() && event.type != Common::EVENT_SCREEN_CHANGED)
+ if (activeDialog != getTopDialog() && event.type != Common::EVENT_SCREEN_CHANGED) {
+ processEvent(event, getTopDialog());
continue;
+ }
processEvent(event, activeDialog);
+
if (lastRedraw + waitTime < _system->getMillis(true)) {
lastRedraw = _system->getMillis(true);
_theme->updateScreen();
@@ -338,6 +358,17 @@ void GuiManager::runLoop() {
}
}
+ // Delete GuiObject that have been added to the trash for a delayed deletion
+ Common::List::iterator it = _guiObjectTrash.begin();
+ while (it != _guiObjectTrash.end()) {
+ if ((*it).parent == 0 || (*it).parent == activeDialog) {
+ debug(7, "Delayed deletion of Gui Object %p", (void *)(*it).object);
+ delete (*it).object;
+ it = _guiObjectTrash.erase(it);
+ } else
+ ++it;
+ }
+
if (_lastMousePosition.time + kTooltipDelay < _system->getMillis(true)) {
Widget *wdg = activeDialog->findWidget(_lastMousePosition.x, _lastMousePosition.y);
if (wdg && wdg->hasTooltip() && !(wdg->getFlags() & WIDGET_PRESSED)) {
@@ -504,6 +535,8 @@ void GuiManager::screenChange() {
}
void GuiManager::processEvent(const Common::Event &event, Dialog *const activeDialog) {
+ if (activeDialog == 0)
+ return;
int button;
uint32 time;
Common::Point mouse(event.mouse.x - activeDialog->_x, event.mouse.y - activeDialog->_y);
diff --git a/gui/gui-manager.h b/gui/gui-manager.h
index 4dc9af95fba..82a8aa9cfd0 100644
--- a/gui/gui-manager.h
+++ b/gui/gui-manager.h
@@ -27,6 +27,7 @@
#include "common/singleton.h"
#include "common/stack.h"
#include "common/str.h"
+#include "common/list.h"
#include "gui/ThemeEngine.h"
@@ -44,6 +45,7 @@ namespace GUI {
class Dialog;
class ThemeEval;
+class GuiObject;
#define g_gui (GUI::GuiManager::instance())
@@ -99,6 +101,13 @@ public:
*/
bool checkScreenChange();
+ /**
+ * Tell the GuiManager to delete the given GuiObject later. If a parent
+ * dialog is provided and is present in the DialogStack, the object will
+ * only be deleted when that dialog is the top level dialog.
+ */
+ void addToTrash(GuiObject*, Dialog* parent = 0);
+
bool _launched;
protected:
@@ -137,6 +146,13 @@ protected:
int _cursorAnimateTimer;
byte _cursor[2048];
+ // delayed deletion of GuiObject
+ struct GuiObjectTrashItem {
+ GuiObject* object;
+ Dialog* parent;
+ };
+ Common::List _guiObjectTrash;
+
void initKeymap();
void pushKeymap();
void popKeymap();
diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index 750e4069773..303403abd0d 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -91,7 +91,7 @@ enum {
#pragma mark -
LauncherDialog::LauncherDialog()
- : Dialog(0, 0, 640, 400) { // ResidualVM specific
+ : Dialog(0, 0, 640, 480) { // ResidualVM specific
_backgroundType = GUI::ThemeEngine::kDialogBackgroundMain;
const int screenW = g_system->getOverlayWidth();
const int screenH = g_system->getOverlayHeight();
@@ -204,7 +204,10 @@ void LauncherDialog::clean() {
while (_firstWidget) {
Widget* w = _firstWidget;
removeWidget(w);
- delete w;
+ // This is called from rebuild() which may result from handleCommand being called by
+ // a child widget sendCommand call. In such a case sendCommand is still being executed
+ // so we should not delete yet the child widget. Thus delay the deletion.
+ g_gui.addToTrash(w, this);
}
delete _browser;
delete _loadDialog;
@@ -214,7 +217,7 @@ void LauncherDialog::rebuild() {
clean();
build();
reflowLayout();
- setFocusWidget(_firstWidget);
+ setDefaultFocusedWidget();
}
void LauncherDialog::open() {
diff --git a/gui/options.cpp b/gui/options.cpp
index 9e2e29ef5fe..d8fd862f35c 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -81,6 +81,8 @@ enum {
kChoosePluginsDirCmd = 'chpl',
kChooseThemeCmd = 'chtf',
kUpdatesCheckCmd = 'updc',
+ kKbdMouseSpeedChanged = 'kmsc',
+ kJoystickDeadzoneChanged= 'jodc',
kFullscreenToggled = 'oful' // ResidualVM specific
};
@@ -114,7 +116,7 @@ enum {
kRootPathClearCmd = 'clrp'
};
#endif
-
+
enum {
kApplyCmd = 'appl'
};
@@ -123,6 +125,9 @@ static const char *savePeriodLabels[] = { _s("Never"), _s("every 5 mins"), _s("e
static const int savePeriodValues[] = { 0, 5 * 60, 10 * 60, 15 * 60, 30 * 60, -1 };
static const char *outputRateLabels[] = { _s(""), _s("8 kHz"), _s("11 kHz"), _s("22 kHz"), _s("44 kHz"), _s("48 kHz"), 0 };
static const int outputRateValues[] = { 0, 8000, 11025, 22050, 44100, 48000, -1 };
+// The keyboard mouse speed values range from 0 to 7 and correspond to speeds shown in the label
+// "10" (value 3) is the default speed corresponding to the speed before introduction of this control
+static const char *kbdMouseSpeedLabels[] = { "3", "5", "8", "10", "13", "15", "18", "20", 0 };
OptionsDialog::OptionsDialog(const Common::String &domain, int x, int y, int w, int h)
: Dialog(x, y, w, h), _domain(domain), _graphicsTabId(-1), _midiTabId(-1), _pathsTabId(-1), _tabWidget(0) {
@@ -139,6 +144,16 @@ OptionsDialog::~OptionsDialog() {
}
void OptionsDialog::init() {
+ _enableControlSettings = false;
+ _onscreenCheckbox = 0;
+ _touchpadCheckbox = 0;
+ _swapMenuAndBackBtnsCheckbox = 0;
+ _kbdMouseSpeedDesc = 0;
+ _kbdMouseSpeedSlider = 0;
+ _kbdMouseSpeedLabel = 0;
+ _joystickDeadzoneDesc = 0;
+ _joystickDeadzoneSlider = 0;
+ _joystickDeadzoneLabel = 0;
_enableGraphicSettings = false;
_gfxPopUp = 0;
_gfxPopUpDesc = 0;
@@ -147,6 +162,9 @@ void OptionsDialog::init() {
_fullscreenCheckbox = 0;
_filteringCheckbox = 0;
_aspectCheckbox = 0;
+ _enableShaderSettings = false;
+ _shaderPopUpDesc = 0;
+ _shaderPopUp = 0;
_vsyncCheckbox = 0; // ResidualVM specific
_rendererTypePopUpDesc = 0; // ResidualVM specific
_rendererTypePopUp = 0; // ResidualVM specific
@@ -200,7 +218,7 @@ void OptionsDialog::init() {
_guioptions = parseGameGUIOptions(_guioptionsString);
}
}
-
+
void OptionsDialog::build() {
// Retrieve game GUI options
_guioptions.clear();
@@ -209,6 +227,47 @@ void OptionsDialog::build() {
_guioptions = parseGameGUIOptions(_guioptionsString);
}
+ // Control options
+ if (g_system->hasFeature(OSystem::kFeatureOnScreenControl)) {
+ if (ConfMan.hasKey("onscreen_control", _domain)) {
+ bool onscreenState = g_system->getFeatureState(OSystem::kFeatureOnScreenControl);
+ if (_onscreenCheckbox != 0)
+ _onscreenCheckbox->setState(onscreenState);
+ }
+ }
+ if (g_system->hasFeature(OSystem::kFeatureTouchpadMode)) {
+ if (ConfMan.hasKey("touchpad_mouse_mode", _domain)) {
+ bool touchpadState = g_system->getFeatureState(OSystem::kFeatureTouchpadMode);
+ if (_touchpadCheckbox != 0)
+ _touchpadCheckbox->setState(touchpadState);
+ }
+ }
+ if (g_system->hasFeature(OSystem::kFeatureSwapMenuAndBackButtons)) {
+ if (ConfMan.hasKey("swap_menu_and_back_buttons", _domain)) {
+ bool state = g_system->getFeatureState(OSystem::kFeatureSwapMenuAndBackButtons);
+ if (_swapMenuAndBackBtnsCheckbox != 0)
+ _swapMenuAndBackBtnsCheckbox->setState(state);
+ }
+ }
+ if (g_system->hasFeature(OSystem::kFeatureKbdMouseSpeed)) {
+ if (ConfMan.hasKey("kbdmouse_speed", _domain)) {
+ int value = ConfMan.getInt("kbdmouse_speed", _domain);
+ if (_kbdMouseSpeedSlider && value < ARRAYSIZE(kbdMouseSpeedLabels) - 1 && value >= 0) {
+ _kbdMouseSpeedSlider->setValue(value);
+ _kbdMouseSpeedLabel->setLabel(_(kbdMouseSpeedLabels[value]));
+ }
+ }
+ }
+ if (g_system->hasFeature(OSystem::kFeatureJoystickDeadzone)) {
+ if (ConfMan.hasKey("joystick_deadzone", _domain)) {
+ int value = ConfMan.getInt("joystick_deadzone", _domain);
+ if (_joystickDeadzoneSlider != 0) {
+ _joystickDeadzoneSlider->setValue(value);
+ _joystickDeadzoneLabel->setValue(value);
+ }
+ }
+ }
+
// Graphic options
if (_fullscreenCheckbox) {
#if 0 // ResidualVM specific
@@ -270,6 +329,14 @@ void OptionsDialog::build() {
_rendererTypePopUp->setSelectedTag(Graphics::parseRendererTypeCode(ConfMan.get("renderer", _domain))); // ResidualVM specific
}
+ // Shader options
+ if (g_system->hasFeature(OSystem::kFeatureShader)) {
+ if (_shaderPopUp) {
+ int value = ConfMan.getInt("shader", _domain);
+ _shaderPopUp->setSelected(value);
+ }
+ }
+
// Audio options
if (!loadMusicDeviceSetting(_midiPopUp, "music_driver"))
_midiPopUp->setSelected(0);
@@ -361,24 +428,27 @@ void OptionsDialog::build() {
_subSpeedLabel->setValue(speed);
}
}
-
+
void OptionsDialog::clean() {
delete _subToggleGroup;
while (_firstWidget) {
Widget* w = _firstWidget;
removeWidget(w);
- delete w;
+ // This is called from rebuild() which may result from handleCommand being called by
+ // a child widget sendCommand call. In such a case sendCommand is still being executed
+ // so we should not delete yet the child widget. Thus delay the deletion.
+ g_gui.addToTrash(w, this);
}
init();
}
-
+
void OptionsDialog::rebuild() {
int currentTab = _tabWidget->getActiveTab();
clean();
build();
reflowLayout();
_tabWidget->setActiveTab(currentTab);
- setFocusWidget(_firstWidget);
+ setDefaultFocusedWidget();
}
void OptionsDialog::open() {
@@ -391,8 +461,9 @@ void OptionsDialog::open() {
}
void OptionsDialog::apply() {
- // Graphic options
bool graphicsModeChanged = false;
+
+ // Graphic options
if (_fullscreenCheckbox) {
if (_enableGraphicSettings) {
if (ConfMan.getBool("filtering", _domain) != _filteringCheckbox->getState())
@@ -480,11 +551,11 @@ void OptionsDialog::apply() {
// Dialog::close) is called, to prevent crashes caused by invalid
// widgets being referenced or similar errors.
g_gui.checkScreenChange();
-
+
if (gfxError != OSystem::kTransactionSuccess) {
// Revert ConfMan to what OSystem is using.
Common::String message = _("Failed to apply some of the graphic options changes:");
-
+
if (gfxError & OSystem::kTransactionModeSwitchFailed) {
const OSystem::GraphicsMode *gm = g_system->getSupportedGraphicsModes();
while (gm->name) {
@@ -497,31 +568,72 @@ void OptionsDialog::apply() {
message += "\n";
message += _("the video mode could not be changed.");
}
-
+
if (gfxError & OSystem::kTransactionAspectRatioFailed) {
ConfMan.setBool("aspect_ratio", g_system->getFeatureState(OSystem::kFeatureAspectRatioCorrection), _domain);
message += "\n";
message += _("the aspect ratio setting could not be changed");
}
-
+
if (gfxError & OSystem::kTransactionFullscreenFailed) {
ConfMan.setBool("fullscreen", g_system->getFeatureState(OSystem::kFeatureFullscreenMode), _domain);
message += "\n";
message += _("the fullscreen setting could not be changed");
}
-
+
if (gfxError & OSystem::kTransactionFilteringFailed) {
ConfMan.setBool("filtering", g_system->getFeatureState(OSystem::kFeatureFilteringMode), _domain);
message += "\n";
message += _("the filtering setting could not be changed");
}
-
+
// And display the error
GUI::MessageDialog dialog(message);
dialog.runModal();
}
}
-
+
+ // Shader options
+ if (_enableShaderSettings) {
+ if (g_system->hasFeature(OSystem::kFeatureShader)) {
+ if (_shaderPopUp) {
+ if (ConfMan.getInt("shader", _domain) != (int32)_shaderPopUp->getSelectedTag()) {
+ ConfMan.setInt("shader", _shaderPopUp->getSelectedTag(), _domain);
+ g_system->setShader(_shaderPopUp->getSelectedTag());
+ }
+ }
+ }
+ }
+
+ // Control options
+ if (_enableControlSettings) {
+ if (g_system->hasFeature(OSystem::kFeatureOnScreenControl)) {
+ if (ConfMan.getBool("onscreen_control", _domain) != _onscreenCheckbox->getState()) {
+ g_system->setFeatureState(OSystem::kFeatureOnScreenControl, _onscreenCheckbox->getState());
+ }
+ }
+ if (g_system->hasFeature(OSystem::kFeatureTouchpadMode)) {
+ if (ConfMan.getBool("touchpad_mouse_mode", _domain) != _touchpadCheckbox->getState()) {
+ g_system->setFeatureState(OSystem::kFeatureTouchpadMode, _touchpadCheckbox->getState());
+ }
+ }
+ if (g_system->hasFeature(OSystem::kFeatureSwapMenuAndBackButtons)) {
+ if (ConfMan.getBool("swap_menu_and_back_buttons", _domain) != _swapMenuAndBackBtnsCheckbox->getState()) {
+ g_system->setFeatureState(OSystem::kFeatureSwapMenuAndBackButtons, _swapMenuAndBackBtnsCheckbox->getState());
+ }
+ }
+ if (g_system->hasFeature(OSystem::kFeatureKbdMouseSpeed)) {
+ if (ConfMan.getInt("kbdmouse_speed", _domain) != _kbdMouseSpeedSlider->getValue()) {
+ ConfMan.setInt("kbdmouse_speed", _kbdMouseSpeedSlider->getValue(), _domain);
+ }
+ }
+ if (g_system->hasFeature(OSystem::kFeatureJoystickDeadzone)) {
+ if (ConfMan.getInt("joystick_deadzone", _domain) != _joystickDeadzoneSlider->getValue()) {
+ ConfMan.setInt("joystick_deadzone", _joystickDeadzoneSlider->getValue(), _domain);
+ }
+ }
+ }
+
// Volume options
if (_musicVolumeSlider) {
if (_enableVolumeSettings) {
@@ -536,7 +648,7 @@ void OptionsDialog::apply() {
ConfMan.removeKey("mute", _domain);
}
}
-
+
// Audio options
if (_midiPopUp) {
if (_enableAudioSettings) {
@@ -545,11 +657,11 @@ void OptionsDialog::apply() {
ConfMan.removeKey("music_driver", _domain);
}
}
-
+
if (_oplPopUp) {
if (_enableAudioSettings) {
const OPL::Config::EmulatorDescription *ed = OPL::Config::findDriver(_oplPopUp->getSelectedTag());
-
+
if (ed)
ConfMan.set("opl_driver", ed->name, _domain);
else
@@ -558,7 +670,7 @@ void OptionsDialog::apply() {
ConfMan.removeKey("opl_driver", _domain);
}
}
-
+
if (_outputRatePopUp) {
if (_enableAudioSettings) {
if (_outputRatePopUp->getSelectedTag() != 0)
@@ -569,15 +681,15 @@ void OptionsDialog::apply() {
ConfMan.removeKey("output_rate", _domain);
}
}
-
+
// MIDI options
if (_multiMidiCheckbox) {
if (_enableMIDISettings) {
saveMusicDeviceSetting(_gmDevicePopUp, "gm_device");
-
+
ConfMan.setBool("multi_midi", _multiMidiCheckbox->getState(), _domain);
ConfMan.setInt("midi_gain", _midiGainSlider->getValue(), _domain);
-
+
Common::String soundFont(_soundFont->getLabel());
if (!soundFont.empty() && (soundFont != _c("None", "soundfont")))
ConfMan.set("soundfont", soundFont, _domain);
@@ -590,7 +702,7 @@ void OptionsDialog::apply() {
ConfMan.removeKey("soundfont", _domain);
}
}
-
+
// MT-32 options
if (_mt32DevicePopUp) {
if (_enableMT32Settings) {
@@ -603,14 +715,14 @@ void OptionsDialog::apply() {
ConfMan.removeKey("enable_gs", _domain);
}
}
-
+
// Subtitle options
if (_subToggleGroup) {
if (_enableSubtitleSettings) {
bool subtitles, speech_mute;
int talkspeed;
int sliderMaxValue = _subSpeedSlider->getMaxValue();
-
+
switch (_subToggleGroup->getValue()) {
case kSubtitlesSpeech:
subtitles = speech_mute = false;
@@ -624,22 +736,22 @@ void OptionsDialog::apply() {
subtitles = speech_mute = true;
break;
}
-
+
ConfMan.setBool("subtitles", subtitles, _domain);
ConfMan.setBool("speech_mute", speech_mute, _domain);
-
+
// Engines that reuse the subtitle speed widget set their own max value.
// Scale the config value accordingly (see addSubtitleControls)
talkspeed = (_subSpeedSlider->getValue() * 255 + sliderMaxValue / 2) / sliderMaxValue;
ConfMan.setInt("talkspeed", talkspeed, _domain);
-
+
} else {
ConfMan.removeKey("subtitles", _domain);
ConfMan.removeKey("talkspeed", _domain);
ConfMan.removeKey("speech_mute", _domain);
}
}
-
+
// Save config file
ConfMan.flushToDisk();
}
@@ -657,18 +769,51 @@ void OptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data
_midiGainLabel->setLabel(Common::String::format("%.2f", (double)_midiGainSlider->getValue() / 100.0));
_midiGainLabel->draw();
break;
- case kMusicVolumeChanged:
- _musicVolumeLabel->setValue(_musicVolumeSlider->getValue());
+ case kMusicVolumeChanged: {
+ const int newValue = _musicVolumeSlider->getValue();
+ _musicVolumeLabel->setValue(newValue);
_musicVolumeLabel->draw();
+
+ if (_guioptions.contains(GUIO_LINKMUSICTOSFX)) {
+ updateSfxVolume(newValue);
+
+ if (_guioptions.contains(GUIO_LINKSPEECHTOSFX)) {
+ updateSpeechVolume(newValue);
+ }
+ }
+
break;
- case kSfxVolumeChanged:
+ }
+ case kSfxVolumeChanged: {
+ const int newValue = _sfxVolumeSlider->getValue();
_sfxVolumeLabel->setValue(_sfxVolumeSlider->getValue());
_sfxVolumeLabel->draw();
+
+ if (_guioptions.contains(GUIO_LINKMUSICTOSFX)) {
+ updateMusicVolume(newValue);
+ }
+
+ if (_guioptions.contains(GUIO_LINKSPEECHTOSFX)) {
+ updateSpeechVolume(newValue);
+ }
+
break;
- case kSpeechVolumeChanged:
- _speechVolumeLabel->setValue(_speechVolumeSlider->getValue());
+ }
+ case kSpeechVolumeChanged: {
+ const int newValue = _speechVolumeSlider->getValue();
+ _speechVolumeLabel->setValue(newValue);
_speechVolumeLabel->draw();
+
+ if (_guioptions.contains(GUIO_LINKSPEECHTOSFX)) {
+ updateSfxVolume(newValue);
+
+ if (_guioptions.contains(GUIO_LINKMUSICTOSFX)) {
+ updateMusicVolume(newValue);
+ }
+ }
+
break;
+ }
case kMuteAllChanged:
// 'true' because if control is disabled then event do not pass
setVolumeSettingsState(true);
@@ -688,6 +833,14 @@ void OptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data
_soundFontClearButton->setEnabled(false);
draw();
break;
+ case kKbdMouseSpeedChanged:
+ _kbdMouseSpeedLabel->setLabel(_(kbdMouseSpeedLabels[_kbdMouseSpeedSlider->getValue()]));
+ _kbdMouseSpeedLabel->draw();
+ break;
+ case kJoystickDeadzoneChanged:
+ _joystickDeadzoneLabel->setValue(_joystickDeadzoneSlider->getValue());
+ _joystickDeadzoneLabel->draw();
+ break;
// ResidualVM specific
case kFullscreenToggled:
_aspectCheckbox->setEnabled(_fullscreenCheckbox->getState());
@@ -716,6 +869,7 @@ void OptionsDialog::setGraphicSettingsState(bool enabled) {
_gfxPopUp->setEnabled(enabled);
_renderModePopUpDesc->setEnabled(enabled);
_renderModePopUp->setEnabled(enabled);
+ _filteringCheckbox->setEnabled(enabled);
#endif
#ifndef GUI_ENABLE_KEYSDIALOG
_fullscreenCheckbox->setEnabled(enabled);
@@ -809,7 +963,7 @@ void OptionsDialog::setVolumeSettingsState(bool enabled) {
// Disable speech volume slider, when we are in subtitle only mode.
if (_subToggleGroup)
ena = ena && _subToggleGroup->getValue() != kSubtitlesSubs;
- if (_guioptions.contains(GUIO_NOSPEECH))
+ if (_guioptions.contains(GUIO_NOSPEECH) || _guioptions.contains(GUIO_NOSPEECHVOLUME))
ena = false;
_speechVolumeDesc->setEnabled(ena);
@@ -839,6 +993,64 @@ void OptionsDialog::setSubtitleSettingsState(bool enabled) {
_subSpeedLabel->setEnabled(ena);
}
+void OptionsDialog::addControlControls(GuiObject *boss, const Common::String &prefix) {
+ // Show On-Screen control
+ if (g_system->hasFeature(OSystem::kFeatureOnScreenControl))
+ _onscreenCheckbox = new CheckboxWidget(boss, prefix + "grOnScreenCheckbox", _("Show On-screen control"));
+
+ // Touchpad Mouse mode
+ if (g_system->hasFeature(OSystem::kFeatureTouchpadMode))
+ _touchpadCheckbox = new CheckboxWidget(boss, prefix + "grTouchpadCheckbox", _("Touchpad mouse mode"));
+
+ // Swap menu and back buttons
+ if (g_system->hasFeature(OSystem::kFeatureSwapMenuAndBackButtons))
+ _swapMenuAndBackBtnsCheckbox = new CheckboxWidget(boss, prefix + "grSwapMenuAndBackBtnsCheckbox", _("Swap Menu and Back buttons"));
+
+ // Keyboard and joystick mouse speed
+ if (g_system->hasFeature(OSystem::kFeatureKbdMouseSpeed)) {
+ if (g_system->getOverlayWidth() > 320)
+ _kbdMouseSpeedDesc = new StaticTextWidget(boss, prefix + "grKbdMouseSpeedDesc", _("Pointer Speed:"), _("Speed for keyboard/joystick mouse pointer control"));
+ else
+ _kbdMouseSpeedDesc = new StaticTextWidget(boss, prefix + "grKbdMouseSpeedDesc", _c("Pointer Speed:", "lowres"), _("Speed for keyboard/joystick mouse pointer control"));
+ _kbdMouseSpeedSlider = new SliderWidget(boss, prefix + "grKbdMouseSpeedSlider", _("Speed for keyboard/joystick mouse pointer control"), kKbdMouseSpeedChanged);
+ _kbdMouseSpeedLabel = new StaticTextWidget(boss, prefix + "grKbdMouseSpeedLabel", " ");
+ _kbdMouseSpeedSlider->setMinValue(0);
+ _kbdMouseSpeedSlider->setMaxValue(7);
+ _kbdMouseSpeedLabel->setFlags(WIDGET_CLEARBG);
+ }
+
+ // Joystick deadzone
+ if (g_system->hasFeature(OSystem::kFeatureJoystickDeadzone)) {
+ if (g_system->getOverlayWidth() > 320)
+ _joystickDeadzoneDesc = new StaticTextWidget(boss, prefix + "grJoystickDeadzoneDesc", _("Joy Deadzone:"), _("Analog joystick Deadzone"));
+ else
+ _joystickDeadzoneDesc = new StaticTextWidget(boss, prefix + "grJoystickDeadzoneDesc", _c("Joy Deadzone:", "lowres"), _("Analog joystick Deadzone"));
+ _joystickDeadzoneSlider = new SliderWidget(boss, prefix + "grJoystickDeadzoneSlider", _("Analog joystick Deadzone"), kJoystickDeadzoneChanged);
+ _joystickDeadzoneLabel = new StaticTextWidget(boss, prefix + "grJoystickDeadzoneLabel", " ");
+ _joystickDeadzoneSlider->setMinValue(1);
+ _joystickDeadzoneSlider->setMaxValue(10);
+ _joystickDeadzoneLabel->setFlags(WIDGET_CLEARBG);
+ }
+ _enableControlSettings = true;
+}
+
+void OptionsDialog::addShaderControls(GuiObject *boss, const Common::String &prefix) {
+ // Shader selector
+ if (g_system->hasFeature(OSystem::kFeatureShader)) {
+ if (g_system->getOverlayWidth() > 320)
+ _shaderPopUpDesc = new StaticTextWidget(boss, prefix + "grShaderPopUpDesc", _("HW Shader:"), _("Different hardware shaders give different visual effects"));
+ else
+ _shaderPopUpDesc = new StaticTextWidget(boss, prefix + "grShaderPopUpDesc", _c("HW Shader:", "lowres"), _("Different hardware shaders give different visual effects"));
+ _shaderPopUp = new PopUpWidget(boss, prefix + "grShaderPopUp", _("Different shaders give different visual effects"));
+ const OSystem::GraphicsMode *p = g_system->getSupportedShaders();
+ while (p->name) {
+ _shaderPopUp->appendEntry(p->name, p->id);
+ p++;
+ }
+ }
+ _enableShaderSettings = true;
+}
+
void OptionsDialog::addGraphicControls(GuiObject *boss, const Common::String &prefix) {
#if 0 // ResidualVM specific
const OSystem::GraphicsMode *gm = g_system->getSupportedGraphicsModes();
@@ -1199,6 +1411,27 @@ int OptionsDialog::getSubtitleMode(bool subtitles, bool speech_mute) {
return kSubtitlesSubs;
}
+void OptionsDialog::updateMusicVolume(const int newValue) const {
+ _musicVolumeLabel->setValue(newValue);
+ _musicVolumeSlider->setValue(newValue);
+ _musicVolumeLabel->draw();
+ _musicVolumeSlider->draw();
+}
+
+void OptionsDialog::updateSfxVolume(const int newValue) const {
+ _sfxVolumeLabel->setValue(newValue);
+ _sfxVolumeSlider->setValue(newValue);
+ _sfxVolumeLabel->draw();
+ _sfxVolumeSlider->draw();
+}
+
+void OptionsDialog::updateSpeechVolume(const int newValue) const {
+ _speechVolumeLabel->setValue(newValue);
+ _speechVolumeSlider->setValue(newValue);
+ _speechVolumeLabel->draw();
+ _speechVolumeSlider->draw();
+}
+
void OptionsDialog::reflowLayout() {
if (_graphicsTabId != -1 && _tabWidget)
_tabWidget->setTabTitle(_graphicsTabId, g_system->getOverlayWidth() > 320 ? _("Graphics") : _("GFX"));
@@ -1278,7 +1511,7 @@ GlobalOptionsDialog::~GlobalOptionsDialog() {
delete _fluidSynthSettingsDialog;
#endif
}
-
+
void GlobalOptionsDialog::build() {
// The tab widget
TabWidget *tab = new TabWidget(this, "GlobalOptions.TabWidget");
@@ -1289,6 +1522,27 @@ void GlobalOptionsDialog::build() {
_graphicsTabId = tab->addTab(g_system->getOverlayWidth() > 320 ? _("Graphics") : _("GFX"));
addGraphicControls(tab, "GlobalOptions_Graphics.");
+ //
+ // The shader tab (currently visible only for Vita platform), visibility checking by features
+ //
+
+ if (g_system->hasFeature(OSystem::kFeatureShader)) {
+ tab->addTab(_("Shader"));
+ addShaderControls(tab, "GlobalOptions_Shader.");
+ }
+
+ //
+ // The control tab (currently visible only for AndroidSDL, SDL, and Vita platform, visibility checking by features
+ //
+ if (g_system->hasFeature(OSystem::kFeatureTouchpadMode) ||
+ g_system->hasFeature(OSystem::kFeatureOnScreenControl) ||
+ g_system->hasFeature(OSystem::kFeatureSwapMenuAndBackButtons) ||
+ g_system->hasFeature(OSystem::kFeatureKbdMouseSpeed) ||
+ g_system->hasFeature(OSystem::kFeatureJoystickDeadzone)) {
+ tab->addTab(_("Control"));
+ addControlControls(tab, "GlobalOptions_Control.");
+ }
+
//
// 2) The audio tab
//
@@ -1425,7 +1679,7 @@ void GlobalOptionsDialog::build() {
// Select the currently configured language or default/English if
// nothing is specified.
- if (ConfMan.hasKey("gui_language"))
+ if (ConfMan.hasKey("gui_language") && !ConfMan.get("gui_language").empty())
_guiLanguagePopUp->setSelectedTag(TransMan.parseLanguage(ConfMan.get("gui_language")));
else
#ifdef USE_DETECTLANG
@@ -1601,8 +1855,12 @@ void GlobalOptionsDialog::clean() {
OptionsDialog::clean();
}
-
+
void GlobalOptionsDialog::apply() {
+ OptionsDialog::apply();
+
+ bool isRebuildNeeded = false;
+
Common::String savePath(_savePath->getLabel());
if (!savePath.empty() && (savePath != _("Default")))
ConfMan.set("savepath", savePath, _domain);
@@ -1639,33 +1897,6 @@ void GlobalOptionsDialog::apply() {
ConfMan.setInt("autosave_period", _autosavePeriodPopUp->getSelectedTag(), _domain);
- GUI::ThemeEngine::GraphicsMode selected = (GUI::ThemeEngine::GraphicsMode)_rendererPopUp->getSelectedTag();
- const char *cfg = GUI::ThemeEngine::findModeConfigName(selected);
- if (!ConfMan.get("gui_renderer").equalsIgnoreCase(cfg)) {
- // FIXME: Actually, any changes (including the theme change) should
- // only become active *after* the options dialog has closed.
- g_gui.loadNewTheme(g_gui.theme()->getThemeId(), selected);
- ConfMan.set("gui_renderer", cfg, _domain);
- }
-#ifdef USE_TRANSLATION
- Common::String oldLang = ConfMan.get("gui_language");
- int selLang = _guiLanguagePopUp->getSelectedTag();
-
- ConfMan.set("gui_language", TransMan.getLangById(selLang));
-
- Common::String newLang = ConfMan.get("gui_language").c_str();
- if (newLang != oldLang) {
- // Activate the selected language
- TransMan.setLanguage(selLang);
-
- // Rebuild the Launcher and Options dialogs
- g_gui.loadNewTheme(g_gui.theme()->getThemeId(), ThemeEngine::kGfxDisabled, true);
- rebuild();
- if (_launcher != 0)
- _launcher->rebuild();
- }
-#endif // USE_TRANSLATION
-
#ifdef USE_UPDATES
ConfMan.setInt("updates_check", _updatesPopUp->getSelectedTag());
@@ -1708,35 +1939,74 @@ void GlobalOptionsDialog::apply() {
#endif // NETWORKING_LOCALWEBSERVER_ENABLE_PORT_OVERRIDE
#endif // USE_SDL_NET
#endif // USE_CLOUD
-
+
+ Common::String oldThemeId = g_gui.theme()->getThemeId();
+ Common::String oldThemeName = g_gui.theme()->getThemeName();
if (!_newTheme.empty()) {
-#ifdef USE_TRANSLATION
- Common::String lang = TransMan.getCurrentLanguage();
-#endif
- Common::String oldTheme = g_gui.theme()->getThemeId();
- if (g_gui.loadNewTheme(_newTheme)) {
-#ifdef USE_TRANSLATION
- // If the charset has changed, it means the font were not found for the
- // new theme. Since for the moment we do not support change of translation
- // language without restarting, we let the user know about this.
- if (lang != TransMan.getCurrentLanguage()) {
- TransMan.setLanguage(lang.c_str());
- g_gui.loadNewTheme(oldTheme);
- _curTheme->setLabel(g_gui.theme()->getThemeName());
- MessageDialog error(_("The theme you selected does not support your current language. If you want to use this theme you need to switch to another language first."));
- error.runModal();
- } else {
-#endif
- ConfMan.set("gui_theme", _newTheme);
-#ifdef USE_TRANSLATION
- }
-#endif
- }
- draw();
- _newTheme.clear();
+ ConfMan.set("gui_theme", _newTheme);
}
- OptionsDialog::apply();
+#ifdef USE_TRANSLATION
+ int selectedLang = _guiLanguagePopUp->getSelectedTag();
+ Common::String oldLang = ConfMan.get("gui_language");
+ Common::String newLang = TransMan.getLangById(selectedLang);
+ Common::String newCharset;
+ if (newLang != oldLang) {
+ TransMan.setLanguage(newLang);
+ ConfMan.set("gui_language", newLang);
+ newCharset = TransMan.getCurrentCharset();
+ isRebuildNeeded = true;
+ }
+#endif
+
+ GUI::ThemeEngine::GraphicsMode gfxMode = (GUI::ThemeEngine::GraphicsMode)_rendererPopUp->getSelectedTag();
+ Common::String oldGfxConfig = ConfMan.get("gui_renderer");
+ Common::String newGfxConfig = GUI::ThemeEngine::findModeConfigName(gfxMode);
+ if (newGfxConfig != oldGfxConfig) {
+ ConfMan.set("gui_renderer", newGfxConfig, _domain);
+ }
+
+ if (_newTheme.empty())
+ _newTheme = oldThemeId;
+
+ if (!g_gui.loadNewTheme(_newTheme, gfxMode, true)) {
+ Common::String errorMessage;
+
+ _curTheme->setLabel(oldThemeName);
+ _newTheme = oldThemeId;
+ ConfMan.set("gui_theme", _newTheme);
+ gfxMode = GUI::ThemeEngine::findMode(oldGfxConfig);
+ _rendererPopUp->setSelectedTag(gfxMode);
+ newGfxConfig = oldGfxConfig;
+ ConfMan.set("gui_renderer", newGfxConfig, _domain);
+#ifdef USE_TRANSLATION
+ bool isCharsetEqual = (newCharset == TransMan.getCurrentCharset());
+ TransMan.setLanguage(oldLang);
+ _guiLanguagePopUp->setSelectedTag(selectedLang);
+ ConfMan.set("gui_language", oldLang);
+
+ if (!isCharsetEqual)
+ errorMessage = _("Theme does not support selected language!");
+ else
+#endif
+ errorMessage = _("Theme cannot be loaded!");
+
+ g_gui.loadNewTheme(_newTheme, gfxMode, true);
+ errorMessage += _("\nMisc settings will be restored.");
+ MessageDialog error(errorMessage);
+ error.runModal();
+ }
+
+ if (isRebuildNeeded) {
+ rebuild();
+ if (_launcher != 0)
+ _launcher->rebuild();
+ }
+
+ _newTheme.clear();
+
+ // Save config file
+ ConfMan.flushToDisk();
}
void GlobalOptionsDialog::close() {
@@ -1860,7 +2130,7 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
#ifdef USE_LIBCURL
case kPopUpItemSelectedCmd:
{
- //update container's scrollbar
+ // update container's scrollbar
reflowLayout();
break;
}
@@ -1977,6 +2247,7 @@ void GlobalOptionsDialog::handleTickle() {
}
void GlobalOptionsDialog::reflowLayout() {
+ int firstVisible = _tabWidget->getFirstVisible();
int activeTab = _tabWidget->getActiveTab();
#if 0 // ResidualVM does not use it
@@ -1987,7 +2258,6 @@ void GlobalOptionsDialog::reflowLayout() {
_soundFontClearButton->setNext(0);
delete _soundFontClearButton;
_soundFontClearButton = addClearButton(_tabWidget, "GlobalOptions_MIDI.mcFontClearButton", kClearSoundFontCmd);
-
}
#endif
@@ -2011,6 +2281,8 @@ void GlobalOptionsDialog::reflowLayout() {
}
_tabWidget->setActiveTab(activeTab);
+ _tabWidget->setFirstVisible(firstVisible);
+
OptionsDialog::reflowLayout();
#ifdef USE_CLOUD
setupCloudTab();
@@ -2021,7 +2293,7 @@ void GlobalOptionsDialog::reflowLayout() {
void GlobalOptionsDialog::setupCloudTab() {
int serverLabelPosition = -1; //no override
#ifdef USE_LIBCURL
- _selectedStorageIndex = _storagePopUp->getSelectedTag();
+ _selectedStorageIndex = (_storagePopUp ? _storagePopUp->getSelectedTag() : (uint32) Cloud::kStorageNoneId);
if (_storagePopUpDesc) _storagePopUpDesc->setVisible(true);
if (_storagePopUp) _storagePopUp->setVisible(true);
@@ -2182,8 +2454,10 @@ void GlobalOptionsDialog::setupCloudTab() {
#else // USE_SDL_NET
if (_runServerButton)
_runServerButton->setVisible(false);
- if (_serverInfoLabel)
+ if (_serverInfoLabel) {
+ _serverInfoLabel->setPos(_serverInfoLabel->getRelX(), serverLabelPosition); // Prevent compiler warning from serverLabelPosition being unused.
_serverInfoLabel->setVisible(false);
+ }
if (_rootPathButton)
_rootPathButton->setVisible(false);
if (_rootPath)
diff --git a/gui/options.h b/gui/options.h
index 9a478ecdf0c..b89f2bd861d 100644
--- a/gui/options.h
+++ b/gui/options.h
@@ -86,7 +86,10 @@ protected:
virtual void clean();
void rebuild();
+
+ void addControlControls(GuiObject *boss, const Common::String &prefix);
void addGraphicControls(GuiObject *boss, const Common::String &prefix);
+ void addShaderControls(GuiObject *boss, const Common::String &prefix);
void addAudioControls(GuiObject *boss, const Common::String &prefix);
void addMIDIControls(GuiObject *boss, const Common::String &prefix);
void addMT32Controls(GuiObject *boss, const Common::String &prefix);
@@ -112,6 +115,23 @@ protected:
int _pathsTabId;
private:
+
+ //
+ // Control controls
+ //
+ bool _enableControlSettings;
+
+ CheckboxWidget *_touchpadCheckbox;
+ CheckboxWidget *_onscreenCheckbox;
+ CheckboxWidget *_swapMenuAndBackBtnsCheckbox;
+
+ StaticTextWidget *_kbdMouseSpeedDesc;
+ SliderWidget *_kbdMouseSpeedSlider;
+ StaticTextWidget *_kbdMouseSpeedLabel;
+ StaticTextWidget *_joystickDeadzoneDesc;
+ SliderWidget *_joystickDeadzoneSlider;
+ StaticTextWidget *_joystickDeadzoneLabel;
+
//
// Graphics controls
//
@@ -126,6 +146,13 @@ private:
PopUpWidget *_rendererTypePopUp; // ResidualVM specific
StaticTextWidget *_renderModePopUpDesc;
PopUpWidget *_renderModePopUp;
+
+ //
+ // Shader controls
+ //
+ bool _enableShaderSettings;
+ StaticTextWidget *_shaderPopUpDesc;
+ PopUpWidget *_shaderPopUp;
//
// Audio controls
@@ -178,6 +205,9 @@ private:
//
// Volume controls
//
+ void updateMusicVolume(const int newValue) const;
+ void updateSfxVolume(const int newValue) const;
+ void updateSpeechVolume(const int newValue) const;
bool _enableVolumeSettings;
StaticTextWidget *_musicVolumeDesc;
diff --git a/gui/saveload-dialog.cpp b/gui/saveload-dialog.cpp
index af02e36c131..4bf23b89743 100644
--- a/gui/saveload-dialog.cpp
+++ b/gui/saveload-dialog.cpp
@@ -474,12 +474,14 @@ void SaveLoadChooserSimple::reflowLayout() {
int thumbY = y + kLineHeight;
int textLines = 0;
- if (!_saveDateSupport)
- textLines++;
- if (!_playTimeSupport)
+ if (_saveDateSupport)
+ textLines += 2;
+ if (_playTimeSupport)
textLines++;
+ if (textLines > 0)
+ textLines++; // add a line of padding at the bottom
- _container->resize(x, y, w, h - (kLineHeight * textLines));
+ _container->resize(x, y, w, h + (kLineHeight * textLines));
_gfxWidget->resize(thumbX, thumbY, thumbW, thumbH);
int height = thumbY + thumbH + kLineHeight;
@@ -574,7 +576,7 @@ void SaveLoadChooserSimple::updateSelection(bool redraw) {
if (startEditMode) {
_list->startEditMode();
- if (_chooseButton->isEnabled() && _list->getSelectedString() == _("Untitled savestate") &&
+ if (_chooseButton->isEnabled() && _list->getSelectedString() == _("Untitled saved game") &&
_list->getSelectionColor() == ThemeEngine::kFontColorAlternate) {
_list->setEditString("");
_list->setEditColor(ThemeEngine::kFontColorNormal);
@@ -657,12 +659,12 @@ void SaveLoadChooserSimple::updateSaveList() {
}
}
- // Show "Untitled savestate" for empty/whitespace saved game descriptions
+ // Show "Untitled saved game" for empty/whitespace saved game descriptions
Common::String description = x->getDescription();
Common::String trimmedDescription = description;
trimmedDescription.trim();
if (trimmedDescription.empty()) {
- description = _("Untitled savestate");
+ description = _("Untitled saved game");
colors.push_back(ThemeEngine::kFontColorAlternate);
} else {
colors.push_back((x->getLocked() ? ThemeEngine::kFontColorAlternate : ThemeEngine::kFontColorNormal));
diff --git a/gui/saveload.cpp b/gui/saveload.cpp
index 2ac68dd8f91..ba17dacc48c 100644
--- a/gui/saveload.cpp
+++ b/gui/saveload.cpp
@@ -67,7 +67,7 @@ Common::String SaveLoadChooser::createDefaultSaveDescription(const int slot) con
g_system->getTimeAndDate(curTime);
curTime.tm_year += 1900; // fixup year
curTime.tm_mon++; // fixup month
- return Common::String::format("%04d.%02d.%02d / %02d:%02d:%02d", curTime.tm_year, curTime.tm_mon, curTime.tm_mday, curTime.tm_hour, curTime.tm_min, curTime.tm_sec);
+ return Common::String::format("%04d-%02d-%02d / %02d:%02d:%02d", curTime.tm_year, curTime.tm_mon, curTime.tm_mday, curTime.tm_hour, curTime.tm_min, curTime.tm_sec);
#else
return Common::String::format("Save %d", slot + 1);
#endif
diff --git a/gui/themes/default.inc b/gui/themes/default.inc
index 4a52fab3d88..25f90be03ec 100644
--- a/gui/themes/default.inc
+++ b/gui/themes/default.inc
@@ -675,7 +675,7 @@ const char *defaultXML1 = ""
"size='15,0' "
"/>"
""
""
""
""
""
+""
""
+""
""
+""
""
+""
"
+
+
+
+
+
+