ALL: Sync with ScummVM - rev. 87ebc7140c

This commit is contained in:
Bastien Bouclet 2017-09-02 13:27:05 +02:00
parent 0a212398b3
commit 84e62b6c8d
261 changed files with 27464 additions and 13852 deletions

1
.gitignore vendored
View file

@ -122,6 +122,7 @@ ipch/
*.bat *.bat
*.tss *.tss
*.VC.db *.VC.db
.vs/
#Ignore default Visual Studio build folders #Ignore default Visual Studio build folders
[Dd]ebug/ [Dd]ebug/

View file

@ -24,6 +24,11 @@ addons:
- libfreetype6-dev - libfreetype6-dev
- zlib1g-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: branches:
only: only:
- master - master
@ -34,6 +39,7 @@ compiler:
os: os:
- linux - linux
- osx
dist: trusty dist: trusty
@ -42,3 +48,10 @@ script:
- make -j 2 - make -j 2
- make test - make test
- make devtools - make devtools
notifications:
irc:
channels:
- "chat.freenode.net#residualvm"
on_success: change
on_failure: always

View file

@ -25,8 +25,8 @@ MODULES += \
backends \ backends \
engines \ engines \
video \ video \
graphics \
image \ image \
graphics \
audio \ audio \
math \ math \
common \ common \

View file

@ -213,9 +213,9 @@ private:
void fill_coding_method_array(sb_int8_array tone_level_idx, sb_int8_array tone_level_idx_temp, 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, sb_int8_array coding_method, int nb_channels,
int c, int superblocktype_2_3, int cm_table_select); 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 synthfilt_build_sb_samples(Common::BitStream32LELSB *gb, int length, int sb_min, int sb_max);
void init_quantized_coeffs_elem0(int8 *quantized_coeffs, Common::BitStream *gb, int length); void init_quantized_coeffs_elem0(int8 *quantized_coeffs, Common::BitStream32LELSB *gb, int length);
void init_tone_level_dequantization(Common::BitStream *gb, int length); void init_tone_level_dequantization(Common::BitStream32LELSB *gb, int length);
void process_subpacket_9(QDM2SubPNode *node); void process_subpacket_9(QDM2SubPNode *node);
void process_subpacket_10(QDM2SubPNode *node, int length); void process_subpacket_10(QDM2SubPNode *node, int length);
void process_subpacket_11(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_decode_super_block(void);
void qdm2_fft_init_coefficient(int sub_packet, int offset, int duration, void qdm2_fft_init_coefficient(int sub_packet, int offset, int duration,
int channel, int exp, int phase); 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_decode_fft_packets(void);
void qdm2_fft_generate_tone(FFTTone *tone); void qdm2_fft_generate_tone(FFTTone *tone);
void qdm2_fft_tone_synthesizer(uint8 sub_packet); 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 * read the longest vlc code
* = (max_vlc_length + bits - 1) / bits * = (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 index = s->peekBits(bits);
int code = table[index][0]; int code = table[index][0];
int n = table[index][1]; int n = table[index][1];
@ -1227,7 +1227,7 @@ QDM2Stream::~QDM2Stream() {
delete[] _compressedData; 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); int value = getVlc2(gb, vlc->table, vlc->bits, depth);
// stage-2, 3 bits exponent escape sequence // 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; 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); 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_min lower subband processed (sb_min included)
* @param sb_max higher subband processed (sb_max excluded) * @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 sb, j, k, n, ch, run, channels;
int joined_stereo, zero_encoding, chs; int joined_stereo, zero_encoding, chs;
int type34_first; int type34_first;
@ -1792,7 +1792,7 @@ void QDM2Stream::synthfilt_build_sb_samples(Common::BitStream *gb, int length, i
* @param gb bitreader context * @param gb bitreader context
* @param length packet length in bits * @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; int i, k, run, level, diff;
if ((length - gb->pos()) < 16) 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 gb bitreader context
* @param length packet length in bits * @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; int sb, j, k, n, ch;
for (ch = 0; ch < _channels; 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] average_quantized_coeffs(); // average elements in quantized_coeffs[max_ch][10][8]
Common::MemoryReadStream *d = new Common::MemoryReadStream(_compressedData, _packetSize*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 //qdm2_decode_sub_packet_header
header.type = gb->getBits(8); header.type = gb->getBits(8);
@ -2188,7 +2188,7 @@ void QDM2Stream::qdm2_fft_init_coefficient(int sub_packet, int offset, int durat
_fftCoefsIndex++; _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 channel, stereo, phase, exp;
int local_int_4, local_int_8, stereo_phase, local_int_10; int local_int_4, local_int_8, stereo_phase, local_int_10;
int local_int_14, stereo_exp, local_int_20, local_int_28; int local_int_14, stereo_exp, local_int_20, local_int_28;

View file

@ -61,6 +61,13 @@ bool loadWAVFromStream(Common::SeekableReadStream &stream, int &size, int &rate,
} }
stream.read(buf, 4); 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) { if (memcmp(buf, "fmt ", 4) != 0) {
warning("getWavInfo: No 'fmt' header"); warning("getWavInfo: No 'fmt' header");
return false; return false;

View file

@ -29,7 +29,6 @@
#include "common/error.h" #include "common/error.h"
#include "common/memstream.h" #include "common/memstream.h"
#include "common/mdct.h" #include "common/mdct.h"
#include "common/bitstream.h"
#include "common/huffman.h" #include "common/huffman.h"
#include "audio/audiostream.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); 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; _framePos = 0;
_curBlock = 0; _curBlock = 0;
@ -714,7 +713,7 @@ bool WMACodec::decodeFrame(Common::BitStream &bits, int16 *outputData) {
return true; return true;
} }
int WMACodec::decodeBlock(Common::BitStream &bits) { int WMACodec::decodeBlock(Common::BitStream8MSB &bits) {
// Computer new block length // Computer new block length
if (!evalBlockLength(bits)) if (!evalBlockLength(bits))
return -1; return -1;
@ -767,7 +766,7 @@ int WMACodec::decodeBlock(Common::BitStream &bits) {
return 0; return 0;
} }
bool WMACodec::decodeChannels(Common::BitStream &bits, int bSize, bool WMACodec::decodeChannels(Common::BitStream8MSB &bits, int bSize,
bool msStereo, bool *hasChannel) { bool msStereo, bool *hasChannel) {
int totalGain = readTotalGain(bits); int totalGain = readTotalGain(bits);
@ -823,7 +822,7 @@ bool WMACodec::calculateIMDCT(int bSize, bool msStereo, bool *hasChannel) {
return true; return true;
} }
bool WMACodec::evalBlockLength(Common::BitStream &bits) { bool WMACodec::evalBlockLength(Common::BitStream8MSB &bits) {
if (_useVariableBlockLen) { if (_useVariableBlockLen) {
// Variable block lengths // Variable block lengths
@ -899,7 +898,7 @@ void WMACodec::calculateCoefCount(int *coefCount, int bSize) const {
coefCount[i] = coefN; coefCount[i] = coefN;
} }
bool WMACodec::decodeNoise(Common::BitStream &bits, int bSize, bool WMACodec::decodeNoise(Common::BitStream8MSB &bits, int bSize,
bool *hasChannel, int *coefCount) { bool *hasChannel, int *coefCount) {
if (!_useNoiseCoding) if (!_useNoiseCoding)
return true; return true;
@ -950,7 +949,7 @@ bool WMACodec::decodeNoise(Common::BitStream &bits, int bSize,
return true; 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 // Exponents can be reused in short blocks
if (!((_blockLenBits == _frameLenBits) || bits.getBit())) if (!((_blockLenBits == _frameLenBits) || bits.getBit()))
return true; return true;
@ -973,7 +972,7 @@ bool WMACodec::decodeExponents(Common::BitStream &bits, int bSize, bool *hasChan
return true; 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) { int *coefCount, int coefBitCount) {
// Simple RLE encoding // Simple RLE encoding
@ -1213,7 +1212,7 @@ static const float powTab[] = {
7.4989420933246e+05, 8.6596432336007e+05, 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 float *ptab = powTab + 60;
const uint32 *iptab = (const uint32 *) ptab; 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) // 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]; float lspCoefs[kLSPCoefCount];
for (int i = 0; i < kLSPCoefCount; i++) { for (int i = 0; i < kLSPCoefCount; i++) {
@ -1329,7 +1328,7 @@ bool WMACodec::decodeExpLSP(Common::BitStream &bits, int ch) {
return true; 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, const float *levelTable, const uint16 *runTable, int version, float *ptr,
int offset, int numCoefs, int blockLen, int frameLenBits, int coefNbBits) { 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); return _lspPowETable[e] * (a + b * t.f);
} }
int WMACodec::readTotalGain(Common::BitStream &bits) { int WMACodec::readTotalGain(Common::BitStream8MSB &bits) {
int totalGain = 1; int totalGain = 1;
int v = 127; int v = 127;
@ -1492,7 +1491,7 @@ int WMACodec::totalGainToBits(int totalGain) {
else return 9; else return 9;
} }
uint32 WMACodec::getLargeVal(Common::BitStream &bits) { uint32 WMACodec::getLargeVal(Common::BitStream8MSB &bits) {
// Consumes up to 34 bits // Consumes up to 34 bits
int count = 8; int count = 8;

View file

@ -27,11 +27,11 @@
#define AUDIO_DECODERS_WMA_H #define AUDIO_DECODERS_WMA_H
#include "common/array.h" #include "common/array.h"
#include "common/bitstream.h"
#include "audio/decoders/codec.h" #include "audio/decoders/codec.h"
namespace Common { namespace Common {
class BitStream;
class Huffman; class Huffman;
class MDCT; class MDCT;
} }
@ -181,27 +181,27 @@ private:
// Decoding // Decoding
Common::SeekableReadStream *decodeSuperFrame(Common::SeekableReadStream &data); Common::SeekableReadStream *decodeSuperFrame(Common::SeekableReadStream &data);
bool decodeFrame(Common::BitStream &bits, int16 *outputData); bool decodeFrame(Common::BitStream8MSB &bits, int16 *outputData);
int decodeBlock(Common::BitStream &bits); int decodeBlock(Common::BitStream8MSB &bits);
// Decoding helpers // Decoding helpers
bool evalBlockLength(Common::BitStream &bits); bool evalBlockLength(Common::BitStream8MSB &bits);
bool decodeChannels(Common::BitStream &bits, int bSize, bool msStereo, bool *hasChannel); bool decodeChannels(Common::BitStream8MSB &bits, int bSize, bool msStereo, bool *hasChannel);
bool calculateIMDCT(int bSize, bool msStereo, bool *hasChannel); bool calculateIMDCT(int bSize, bool msStereo, bool *hasChannel);
void calculateCoefCount(int *coefCount, int bSize) const; void calculateCoefCount(int *coefCount, int bSize) const;
bool decodeNoise(Common::BitStream &bits, int bSize, bool *hasChannel, int *coefCount); bool decodeNoise(Common::BitStream8MSB &bits, int bSize, bool *hasChannel, int *coefCount);
bool decodeExponents(Common::BitStream &bits, int bSize, bool *hasChannel); bool decodeExponents(Common::BitStream8MSB &bits, int bSize, bool *hasChannel);
bool decodeSpectralCoef(Common::BitStream &bits, bool msStereo, bool *hasChannel, bool decodeSpectralCoef(Common::BitStream8MSB &bits, bool msStereo, bool *hasChannel,
int *coefCount, int coefBitCount); int *coefCount, int coefBitCount);
float getNormalizedMDCTLength() const; float getNormalizedMDCTLength() const;
void calculateMDCTCoefficients(int bSize, bool *hasChannel, void calculateMDCTCoefficients(int bSize, bool *hasChannel,
int *coefCount, int totalGain, float mdctNorm); int *coefCount, int totalGain, float mdctNorm);
bool decodeExpHuffman(Common::BitStream &bits, int ch); bool decodeExpHuffman(Common::BitStream8MSB &bits, int ch);
bool decodeExpLSP(Common::BitStream &bits, int ch); bool decodeExpLSP(Common::BitStream8MSB &bits, int ch);
bool decodeRunLevel(Common::BitStream &bits, const Common::Huffman &huffman, bool decodeRunLevel(Common::BitStream8MSB &bits, const Common::Huffman &huffman,
const float *levelTable, const uint16 *runTable, int version, float *ptr, const float *levelTable, const uint16 *runTable, int version, float *ptr,
int offset, int numCoefs, int blockLen, int frameLenBits, int coefNbBits); int offset, int numCoefs, int blockLen, int frameLenBits, int coefNbBits);
@ -211,9 +211,9 @@ private:
float pow_m1_4(float x) const; 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 int totalGainToBits(int totalGain);
static uint32 getLargeVal(Common::BitStream &bits); static uint32 getLargeVal(Common::BitStream8MSB &bits);
}; };
} // End of namespace Audio } // End of namespace Audio

View file

@ -25,9 +25,6 @@
#ifdef USE_MT32EMU #ifdef USE_MT32EMU
#include "audio/softsynth/mt32/mt32emu.h"
#include "audio/softsynth/mt32/ROMInfo.h"
#include "audio/softsynth/emumidi.h" #include "audio/softsynth/emumidi.h"
#include "audio/musicplugin.h" #include "audio/musicplugin.h"
#include "audio/mpu401.h" #include "audio/mpu401.h"
@ -52,16 +49,16 @@
#include "gui/message.h" #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 { namespace MT32Emu {
class ReportHandlerScummVM : public ReportHandler { class ScummVMReportHandler : public MT32Emu::IReportHandler {
friend class Synth;
public: public:
virtual ~ReportHandlerScummVM() {}
protected:
// Callback for debug messages, in vprintf() format // Callback for debug messages, in vprintf() format
void printDebug(const char *fmt, va_list list) { void printDebug(const char *fmt, va_list list) {
Common::String out = Common::String::vformat(fmt, list); Common::String out = Common::String::vformat(fmt, list);
@ -70,18 +67,32 @@ protected:
// Callbacks for reporting various errors and information // Callbacks for reporting various errors and information
void onErrorControlROM() { 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(); dialog.runModal();
error("MT32emu: Init Error - Missing or invalid Control ROM image"); error("MT32emu: Init Error - Missing or invalid Control ROM image");
} }
void onErrorPCMROM() { 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(); dialog.runModal();
error("MT32emu: Init Error - Missing PCM ROM image"); error("MT32emu: Init Error - Missing PCM ROM image");
} }
void showLCDMessage(const char *message) { void showLCDMessage(const char *message) {
Common::OSDMessageQueue::instance().addMessage(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 } // end of namespace MT32Emu
@ -95,11 +106,10 @@ class MidiDriver_MT32 : public MidiDriver_Emulated {
private: private:
MidiChannel_MT32 _midiChannels[16]; MidiChannel_MT32 _midiChannels[16];
uint16 _channelMask; uint16 _channelMask;
MT32Emu::Synth *_synth; MT32Emu::Service _service;
MT32Emu::ReportHandlerScummVM *_reportHandler; MT32Emu::ScummVMReportHandler _reportHandler;
const MT32Emu::ROMImage *_controlROM, *_pcmROM; byte *_controlData, *_pcmData;
Common::File *_controlFile, *_pcmFile; Common::Mutex _mutex;
void deleteMuntStructures();
int _outputRate; int _outputRate;
@ -107,15 +117,13 @@ protected:
void generateSamples(int16 *buf, int len); void generateSamples(int16 *buf, int len);
public: public:
bool _initializing;
MidiDriver_MT32(Audio::Mixer *mixer); MidiDriver_MT32(Audio::Mixer *mixer);
virtual ~MidiDriver_MT32(); virtual ~MidiDriver_MT32();
int open(); int open();
void close(); void close();
void send(uint32 b); void send(uint32 b);
void setPitchBendRange (byte channel, uint range); void setPitchBendRange(byte channel, uint range);
void sysEx(const byte *msg, uint16 length); void sysEx(const byte *msg, uint16 length);
uint32 property(int prop, uint32 param); 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) { for (i = 0; i < ARRAYSIZE(_midiChannels); ++i) {
_midiChannels[i].init(this, i); _midiChannels[i].init(this, i);
} }
_reportHandler = NULL;
_synth = NULL;
_outputRate = 0; _outputRate = 0;
_initializing = false; _controlData = nullptr;
_pcmData = nullptr;
// Initialized in open()
_controlROM = NULL;
_pcmROM = NULL;
_controlFile = NULL;
_pcmFile = NULL;
} }
MidiDriver_MT32::~MidiDriver_MT32() { MidiDriver_MT32::~MidiDriver_MT32() {
deleteMuntStructures(); close();
}
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;
} }
int MidiDriver_MT32::open() { int MidiDriver_MT32::open() {
if (_isOpen) if (_isOpen)
return MERR_ALREADY_OPEN; return MERR_ALREADY_OPEN;
_reportHandler = new MT32Emu::ReportHandlerScummVM();
_synth = new MT32Emu::Synth(_reportHandler);
Graphics::PixelFormat screenFormat = g_system->getScreenFormat(); Graphics::PixelFormat screenFormat = g_system->getScreenFormat();
if (screenFormat.bytesPerPixel == 1) { if (screenFormat.bytesPerPixel == 1) {
@ -193,75 +172,92 @@ int MidiDriver_MT32::open() {
g_system->getPaletteManager()->setPalette(dummy_palette, 0, 3); g_system->getPaletteManager()->setPalette(dummy_palette, 0, 3);
} }
_initializing = true;
debug(4, _s("Initializing MT-32 Emulator")); debug(4, _s("Initializing MT-32 Emulator"));
_controlFile = new Common::File();
if (!_controlFile->open("CM32L_CONTROL.ROM") && !_controlFile->open("MT32_CONTROL.ROM")) Common::File controlFile;
error("Error opening MT32_CONTROL.ROM / CM32L_CONTROL.ROM"); if (!controlFile.open("CM32L_CONTROL.ROM") && !controlFile.open("MT32_CONTROL.ROM"))
_pcmFile = new Common::File(); error("Error opening MT32_CONTROL.ROM / CM32L_CONTROL.ROM. Check that your Extra Path in Paths settings is set to the correct directory");
if (!_pcmFile->open("CM32L_PCM.ROM") && !_pcmFile->open("MT32_PCM.ROM"))
error("Error opening MT32_PCM.ROM / CM32L_PCM.ROM"); Common::File pcmFile;
_controlROM = MT32Emu::ROMImage::makeROMImage(_controlFile); if (!pcmFile.open("CM32L_PCM.ROM") && !pcmFile.open("MT32_PCM.ROM"))
_pcmROM = MT32Emu::ROMImage::makeROMImage(_pcmFile); error("Error opening MT32_PCM.ROM / CM32L_PCM.ROM. Check that your Extra Path in Paths settings is set to the correct directory");
if (!_synth->open(*_controlROM, *_pcmROM))
_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; return MERR_DEVICE_NOT_AVAILABLE;
double gain = (double)ConfMan.getInt("midi_gain") / 100.0; double gain = (double)ConfMan.getInt("midi_gain") / 100.0;
_synth->setOutputGain(1.0f * gain); _service.setOutputGain(1.0f * gain);
_synth->setReverbOutputGain(0.68f * gain); _service.setReverbOutputGain(1.0f * gain);
// We let the synthesizer play MIDI messages immediately. Our MIDI // We let the synthesizer play MIDI messages immediately. Our MIDI
// handling is synchronous to sample generation. This makes delaying MIDI // handling is synchronous to sample generation. This makes delaying MIDI
// events result in odd sound output in some cases. For example, the // events result in odd sound output in some cases. For example, the
// shattering window in the Indiana Jones and the Fate of Atlantis intro // shattering window in the Indiana Jones and the Fate of Atlantis intro
// will sound like a bell if we use any delay here. // will sound like a bell if we use any delay here.
// Bug #6242 "AUDIO: Built-In MT-32 MUNT Produces Wrong Sounds". // 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 // We need to report the sample rate MUNT renders at as sample rate of our
// AudioStream. // AudioStream.
_outputRate = _synth->getStereoOutputSampleRate(); _outputRate = _service.getActualStereoOutputSamplerate();
MidiDriver_Emulated::open(); 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); _mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
return 0; return 0;
} }
void MidiDriver_MT32::send(uint32 b) { 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) { void MidiDriver_MT32::setPitchBendRange(byte channel, uint range) {
if (range > 24) { if (range > 24) {
warning("setPitchBendRange() called with range > 24: %d", range); warning("setPitchBendRange() called with range > 24: %d", range);
} }
byte benderRangeSysex[9]; byte benderRangeSysex[4] = { 0, 0, 4, (uint8)range };
benderRangeSysex[0] = 0x41; // Roland Common::StackLock lock(_mutex);
benderRangeSysex[1] = channel; _service.writeSysex(channel, benderRangeSysex, 4);
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);
} }
void MidiDriver_MT32::sysEx(const byte *msg, uint16 length) { void MidiDriver_MT32::sysEx(const byte *msg, uint16 length) {
if (msg[0] == 0xf0) { if (msg[0] == 0xf0) {
_synth->playSysex(msg, length); Common::StackLock lock(_mutex);
_service.playSysex(msg, length);
} else { } 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 // Detach the mixer callback handler
_mixer->stopHandle(_mixerSoundHandle); _mixer->stopHandle(_mixerSoundHandle);
_synth->close(); Common::StackLock lock(_mutex);
deleteMuntStructures(); _service.closeSynth();
_service.freeContext();
delete[] _controlData;
_controlData = nullptr;
delete[] _pcmData;
_pcmData = nullptr;
} }
void MidiDriver_MT32::generateSamples(int16 *data, int len) { 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) { uint32 MidiDriver_MT32::property(int prop, uint32 param) {

View file

@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher /* 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 * 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 * 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 <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
//#include <cstring> #include <cstring>
#include "internals.h"
#include "Analog.h" #include "Analog.h"
#include "Synth.h"
namespace MT32Emu { namespace MT32Emu {
@ -106,7 +110,6 @@ static const Bit32u ACCURATE_LPF_DELTAS_OVERSAMPLED[][ACCURATE_LPF_NUMBER_OF_PHA
class AbstractLowPassFilter { class AbstractLowPassFilter {
public: public:
static AbstractLowPassFilter &createLowPassFilter(AnalogOutputMode mode, bool oldMT32AnalogLPF); static AbstractLowPassFilter &createLowPassFilter(AnalogOutputMode mode, bool oldMT32AnalogLPF);
static void muteRingBuffer(SampleEx *ringBuffer, unsigned int length);
virtual ~AbstractLowPassFilter() {} virtual ~AbstractLowPassFilter() {}
virtual SampleEx process(SampleEx sample) = 0; virtual SampleEx process(SampleEx sample) = 0;
@ -152,9 +155,9 @@ public:
void addPositionIncrement(unsigned int positionIncrement); void addPositionIncrement(unsigned int positionIncrement);
}; };
Analog::Analog(const AnalogOutputMode mode, const ControlROMFeatureSet *controlROMFeatures) : Analog::Analog(const AnalogOutputMode mode, const bool oldMT32AnalogLPF) :
leftChannelLPF(AbstractLowPassFilter::createLowPassFilter(mode, controlROMFeatures->isOldMT32AnalogLPF())), leftChannelLPF(AbstractLowPassFilter::createLowPassFilter(mode, oldMT32AnalogLPF)),
rightChannelLPF(AbstractLowPassFilter::createLowPassFilter(mode, controlROMFeatures->isOldMT32AnalogLPF())), rightChannelLPF(AbstractLowPassFilter::createLowPassFilter(mode, oldMT32AnalogLPF)),
synthGain(0), synthGain(0),
reverbGain(0) reverbGain(0)
{} {}
@ -164,7 +167,7 @@ Analog::~Analog() {
delete &rightChannelLPF; 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) { if (outStream == NULL) {
leftChannelLPF.addPositionIncrement(outLength); leftChannelLPF.addPositionIncrement(outLength);
rightChannelLPF.addPositionIncrement(outLength); rightChannelLPF.addPositionIncrement(outLength);
@ -179,8 +182,8 @@ void Analog::process(Sample **outStream, const Sample *nonReverbLeft, const Samp
outSampleL = leftChannelLPF.process(0); outSampleL = leftChannelLPF.process(0);
outSampleR = rightChannelLPF.process(0); outSampleR = rightChannelLPF.process(0);
} else { } else {
SampleEx inSampleL = ((SampleEx)*(nonReverbLeft++) + (SampleEx)*(reverbDryLeft++)) * synthGain + (SampleEx)*(reverbWetLeft++) * reverbGain; SampleEx inSampleL = (SampleEx(*(nonReverbLeft++)) + SampleEx(*(reverbDryLeft++))) * synthGain + SampleEx(*(reverbWetLeft++)) * reverbGain;
SampleEx inSampleR = ((SampleEx)*(nonReverbRight++) + (SampleEx)*(reverbDryRight++)) * synthGain + (SampleEx)*(reverbWetRight++) * reverbGain; SampleEx inSampleR = (SampleEx(*(nonReverbRight++)) + SampleEx(*(reverbDryRight++))) * synthGain + SampleEx(*(reverbWetRight++)) * reverbGain;
#if !MT32EMU_USE_FLOAT_SAMPLES #if !MT32EMU_USE_FLOAT_SAMPLES
inSampleL >>= OUTPUT_GAIN_FRACTION_BITS; inSampleL >>= OUTPUT_GAIN_FRACTION_BITS;
@ -191,8 +194,8 @@ void Analog::process(Sample **outStream, const Sample *nonReverbLeft, const Samp
outSampleR = rightChannelLPF.process(inSampleR); outSampleR = rightChannelLPF.process(inSampleR);
} }
*((*outStream)++) = Synth::clipSampleEx(outSampleL); *(outStream++) = Synth::clipSampleEx(outSampleL);
*((*outStream)++) = Synth::clipSampleEx(outSampleR); *(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 { bool AbstractLowPassFilter::hasNextSample() const {
return false; return false;
} }
@ -273,7 +259,7 @@ CoarseLowPassFilter::CoarseLowPassFilter(bool oldMT32AnalogLPF) :
LPF_TAPS(oldMT32AnalogLPF ? COARSE_LPF_TAPS_MT32 : COARSE_LPF_TAPS_CM32L), LPF_TAPS(oldMT32AnalogLPF ? COARSE_LPF_TAPS_MT32 : COARSE_LPF_TAPS_CM32L),
ringBufferPosition(0) ringBufferPosition(0)
{ {
muteRingBuffer(ringBuffer, COARSE_LPF_DELAY_LINE_LENGTH); Synth::muteSampleBuffer(ringBuffer, COARSE_LPF_DELAY_LINE_LENGTH);
} }
SampleEx CoarseLowPassFilter::process(const SampleEx inSample) { SampleEx CoarseLowPassFilter::process(const SampleEx inSample) {
@ -303,7 +289,7 @@ AccurateLowPassFilter::AccurateLowPassFilter(const bool oldMT32AnalogLPF, const
ringBufferPosition(0), ringBufferPosition(0),
phase(0) phase(0)
{ {
muteRingBuffer(ringBuffer, ACCURATE_LPF_DELAY_LINE_LENGTH); Synth::muteSampleBuffer(ringBuffer, ACCURATE_LPF_DELAY_LINE_LENGTH);
} }
SampleEx AccurateLowPassFilter::process(const SampleEx inSample) { 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; phase = (phase + positionIncrement * phaseIncrement) % ACCURATE_LPF_NUMBER_OF_PHASES;
} }
} } // namespace MT32Emu

View file

@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher /* 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 * 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 * it under the terms of the GNU Lesser General Public License as published by
@ -18,7 +18,10 @@
#ifndef MT32EMU_ANALOG_H #ifndef MT32EMU_ANALOG_H
#define MT32EMU_ANALOG_H #define MT32EMU_ANALOG_H
#include "mt32emu.h" #include "globals.h"
#include "internals.h"
#include "Types.h"
#include "Enumerations.h"
namespace MT32Emu { namespace MT32Emu {
@ -35,9 +38,9 @@ class AbstractLowPassFilter;
*/ */
class Analog { class Analog {
public: public:
Analog(AnalogOutputMode mode, const ControlROMFeatureSet *controlROMFeatures); Analog(const AnalogOutputMode mode, const bool oldMT32AnalogLPF);
~Analog(); ~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; unsigned int getOutputSampleRate() const;
Bit32u getDACStreamsLength(Bit32u outputLength) const; Bit32u getDACStreamsLength(Bit32u outputLength) const;
void setSynthOutputGain(float synthGain); void setSynthOutputGain(float synthGain);
@ -52,6 +55,6 @@ private:
Analog(Analog &); Analog(Analog &);
}; };
} } // namespace MT32Emu
#endif #endif // #ifndef MT32EMU_ANALOG_H

View file

@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher /* 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 * 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 * 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 <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
//#include <cstring> #include <cstddef>
#include "mt32emu.h"
#include "internals.h"
#include "BReverbModel.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 // 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) // 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_COMBS[] = {705 + PROCESS_DELAY, 2349, 2839, 3632};
static const Bit32u MODE_0_OUTL[] = {2349, 141, 1960}; static const Bit32u MODE_0_OUTL[] = {2349, 141, 1960};
static const Bit32u MODE_0_OUTR[] = {1174, 1570, 145}; static const Bit32u MODE_0_OUTR[] = {1174, 1570, 145};
static const Bit32u MODE_0_COMB_FACTOR[] = {0xA0, 0x60, 0x60, 0x60}; static const Bit8u 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_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, 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 Bit8u 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 Bit8u 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_LPF_AMP = 0x60;
static const Bit32u MODE_1_NUMBER_OF_ALLPASSES = 3; static const Bit32u MODE_1_NUMBER_OF_ALLPASSES = 3;
static const Bit32u MODE_1_ALLPASSES[] = {1324, 809, 176}; 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_COMBS[] = {961 + PROCESS_DELAY, 2619, 3545, 4519};
static const Bit32u MODE_1_OUTL[] = {2618, 1760, 4518}; static const Bit32u MODE_1_OUTL[] = {2618, 1760, 4518};
static const Bit32u MODE_1_OUTR[] = {1300, 3532, 2274}; static const Bit32u MODE_1_OUTR[] = {1300, 3532, 2274};
static const Bit32u MODE_1_COMB_FACTOR[] = {0x80, 0x60, 0x60, 0x60}; static const Bit8u 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_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98, 0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98,
0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, 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_1_DRY_AMP[] = {0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xE0}; static const Bit8u 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 Bit8u 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_LPF_AMP = 0x60;
static const Bit32u MODE_2_NUMBER_OF_ALLPASSES = 3; static const Bit32u MODE_2_NUMBER_OF_ALLPASSES = 3;
static const Bit32u MODE_2_ALLPASSES[] = {969, 644, 157}; 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_COMBS[] = {116 + PROCESS_DELAY, 2259, 2839, 3539};
static const Bit32u MODE_2_OUTL[] = {2259, 718, 1769}; static const Bit32u MODE_2_OUTL[] = {2259, 718, 1769};
static const Bit32u MODE_2_OUTR[] = {1136, 2128, 1}; static const Bit32u MODE_2_OUTR[] = {1136, 2128, 1};
static const Bit32u MODE_2_COMB_FACTOR[] = {0, 0x20, 0x20, 0x20}; static const Bit8u 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_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, 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 Bit8u 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 Bit8u 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_LPF_AMP = 0x80;
static const Bit32u MODE_3_NUMBER_OF_ALLPASSES = 0; static const Bit32u MODE_3_NUMBER_OF_ALLPASSES = 0;
static const Bit32u MODE_3_NUMBER_OF_COMBS = 1; 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_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_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_OUTR[] = {800, 1248, 1920, 2976, 4512, 6944, 10560, 16000};
static const Bit32u MODE_3_COMB_FACTOR[] = {0x68}; static const Bit8u MODE_3_COMB_FACTOR[] = {0x68};
static const Bit32u MODE_3_COMB_FEEDBACK[] = {0x68, 0x60}; static const Bit8u 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_DRY_AMP[] = {0x20, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50,
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_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}; 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_COMBS[] = {575 + PROCESS_DELAY, 2040, 2752, 3629};
static const Bit32u MODE_0_OUTL[] = {2040, 687, 1814}; static const Bit32u MODE_0_OUTL[] = {2040, 687, 1814};
static const Bit32u MODE_0_OUTR[] = {1019, 2072, 1}; static const Bit32u MODE_0_OUTR[] = {1019, 2072, 1};
static const Bit32u MODE_0_COMB_FACTOR[] = {0xB0, 0x60, 0x60, 0x60}; static const Bit8u 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_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98, 0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98,
0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, 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[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80}; static const Bit8u 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 Bit8u 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_LPF_AMP = 0x80;
static const Bit32u MODE_1_NUMBER_OF_ALLPASSES = 3; static const Bit32u MODE_1_NUMBER_OF_ALLPASSES = 3;
static const Bit32u MODE_1_ALLPASSES[] = {1324, 809, 176}; 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_COMBS[] = {961 + PROCESS_DELAY, 2619, 3545, 4519};
static const Bit32u MODE_1_OUTL[] = {2618, 1760, 4518}; static const Bit32u MODE_1_OUTL[] = {2618, 1760, 4518};
static const Bit32u MODE_1_OUTR[] = {1300, 3532, 2274}; static const Bit32u MODE_1_OUTR[] = {1300, 3532, 2274};
static const Bit32u MODE_1_COMB_FACTOR[] = {0x90, 0x60, 0x60, 0x60}; static const Bit8u 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_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98, 0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98,
0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, 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_1_DRY_AMP[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80}; static const Bit8u 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 Bit8u 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_LPF_AMP = 0x80;
static const Bit32u MODE_2_NUMBER_OF_ALLPASSES = 3; static const Bit32u MODE_2_NUMBER_OF_ALLPASSES = 3;
static const Bit32u MODE_2_ALLPASSES[] = {969, 644, 157}; 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_COMBS[] = {116 + PROCESS_DELAY, 2259, 2839, 3539};
static const Bit32u MODE_2_OUTL[] = {2259, 718, 1769}; static const Bit32u MODE_2_OUTL[] = {2259, 718, 1769};
static const Bit32u MODE_2_OUTR[] = {1136, 2128, 1}; static const Bit32u MODE_2_OUTR[] = {1136, 2128, 1};
static const Bit32u MODE_2_COMB_FACTOR[] = {0, 0x60, 0x60, 0x60}; static const Bit8u 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_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98, 0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98,
0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, 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_2_DRY_AMP[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80}; static const Bit8u 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 Bit8u 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_LPF_AMP = 0x80;
static const Bit32u MODE_3_NUMBER_OF_ALLPASSES = 0; static const Bit32u MODE_3_NUMBER_OF_ALLPASSES = 0;
static const Bit32u MODE_3_NUMBER_OF_COMBS = 1; 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_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_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_OUTR[] = {800, 1248, 1920, 2976, 4512, 6944, 10560, 16000};
static const Bit32u MODE_3_COMB_FACTOR[] = {0x68}; static const Bit8u MODE_3_COMB_FACTOR[] = {0x68};
static const Bit32u MODE_3_COMB_FEEDBACK[] = {0x68, 0x60}; static const Bit8u 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_DRY_AMP[] = {0x10, 0x10, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x10, 0x20, 0x20, 0x10, 0x20, 0x10, 0x20, 0x10}; 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_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}; 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; return res;
#else #else
return Sample(((Bit32s)a * addMask) >> 8); return Sample((Bit32s(a) * addMask) >> 8);
#endif #endif
} }
@ -252,7 +255,7 @@ Sample AllpassFilter::process(const Sample in) {
#endif #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) { void CombFilter::process(const Sample in) {
// This model corresponds to the comb filter implementation of the real CM-32L device // 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]; return buffer[(size + index - outIndex) % size];
} }
void CombFilter::setFeedbackFactor(const Bit32u useFeedbackFactor) { void CombFilter::setFeedbackFactor(const Bit8u useFeedbackFactor) {
feedbackFactor = 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) {} : CombFilter(useSize, useFilterFactor), amp(useAmp) {}
void DelayWithLowPassFilter::process(const Sample in) { void DelayWithLowPassFilter::process(const Sample in) {
@ -292,7 +295,7 @@ void DelayWithLowPassFilter::process(const Sample in) {
buffer[index] = weirdMul(lpfOut, amp, 0xFF); 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) { void TapDelayCombFilter::process(const Sample in) {
// the previously stored value // the previously stored value
@ -430,7 +433,7 @@ bool BReverbModel::isMT32Compatible(const ReverbMode mode) const {
return &currentSettings == &getMT32Settings(mode); return &currentSettings == &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) { if (combs == NULL) {
Synth::muteSampleBuffer(outLeft, numSamples); Synth::muteSampleBuffer(outLeft, numSamples);
Synth::muteSampleBuffer(outRight, 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. * 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. * 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 #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 #endif
*(outLeft++) = weirdMul(outSample, wetLevel, 0xFF); *(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; Sample outSample = 1.5f * (outR1 + outR2) + outR3;
#elif MT32EMU_BOSS_REVERB_PRECISE_MODE #elif MT32EMU_BOSS_REVERB_PRECISE_MODE
// See the note above for the left channel output. // 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 #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 #endif
*(outRight++) = weirdMul(outSample, wetLevel, 0xFF); *(outRight++) = weirdMul(outSample, wetLevel, 0xFF);
} }
@ -525,4 +528,4 @@ void BReverbModel::process(const Sample *inLeft, const Sample *inRight, Sample *
} }
} }
} } // namespace MT32Emu

View file

@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher /* 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 * 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 * it under the terms of the GNU Lesser General Public License as published by
@ -18,6 +18,10 @@
#ifndef MT32EMU_B_REVERB_MODEL_H #ifndef MT32EMU_B_REVERB_MODEL_H
#define MT32EMU_B_REVERB_MODEL_H #define MT32EMU_B_REVERB_MODEL_H
#include "globals.h"
#include "internals.h"
#include "Types.h"
namespace MT32Emu { namespace MT32Emu {
struct BReverbSettings { struct BReverbSettings {
@ -27,11 +31,11 @@ struct BReverbSettings {
const Bit32u * const combSizes; const Bit32u * const combSizes;
const Bit32u * const outLPositions; const Bit32u * const outLPositions;
const Bit32u * const outRPositions; const Bit32u * const outRPositions;
const Bit32u * const filterFactors; const Bit8u * const filterFactors;
const Bit32u * const feedbackFactors; const Bit8u * const feedbackFactors;
const Bit32u * const dryAmps; const Bit8u * const dryAmps;
const Bit32u * const wetLevels; const Bit8u * const wetLevels;
const Bit32u lpfAmp; const Bit8u lpfAmp;
}; };
class RingBuffer { class RingBuffer {
@ -56,23 +60,23 @@ public:
class CombFilter : public RingBuffer { class CombFilter : public RingBuffer {
protected: protected:
const Bit32u filterFactor; const Bit8u filterFactor;
Bit32u feedbackFactor; Bit8u feedbackFactor;
public: public:
CombFilter(const Bit32u size, const Bit32u useFilterFactor); CombFilter(const Bit32u size, const Bit8u useFilterFactor);
virtual void process(const Sample in); virtual void process(const Sample in);
Sample getOutputAt(const Bit32u outIndex) const; Sample getOutputAt(const Bit32u outIndex) const;
void setFeedbackFactor(const Bit32u useFeedbackFactor); void setFeedbackFactor(const Bit8u useFeedbackFactor);
}; };
class DelayWithLowPassFilter : public CombFilter { class DelayWithLowPassFilter : public CombFilter {
Bit32u amp; Bit8u amp;
public: 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 process(const Sample in);
void setFeedbackFactor(const Bit32u) {} void setFeedbackFactor(const Bit8u) {}
}; };
class TapDelayCombFilter : public CombFilter { class TapDelayCombFilter : public CombFilter {
@ -80,7 +84,7 @@ class TapDelayCombFilter : public CombFilter {
Bit32u outR; Bit32u outR;
public: public:
TapDelayCombFilter(const Bit32u useSize, const Bit32u useFilterFactor); TapDelayCombFilter(const Bit32u useSize, const Bit8u useFilterFactor);
void process(const Sample in); void process(const Sample in);
Sample getLeftOutput() const; Sample getLeftOutput() const;
Sample getRightOutput() const; Sample getRightOutput() const;
@ -93,8 +97,8 @@ class BReverbModel {
const BReverbSettings &currentSettings; const BReverbSettings &currentSettings;
const bool tapDelayMode; const bool tapDelayMode;
Bit32u dryAmp; Bit8u dryAmp;
Bit32u wetLevel; Bit8u wetLevel;
static const BReverbSettings &getCM32L_LAPCSettings(const ReverbMode mode); static const BReverbSettings &getCM32L_LAPCSettings(const ReverbMode mode);
static const BReverbSettings &getMT32Settings(const ReverbMode mode); static const BReverbSettings &getMT32Settings(const ReverbMode mode);
@ -108,11 +112,11 @@ public:
void close(); void close();
void mute(); void mute();
void setParameters(Bit8u time, Bit8u level); 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 isActive() const;
bool isMT32Compatible(const ReverbMode mode) const; bool isMT32Compatible(const ReverbMode mode) const;
}; };
} } // namespace MT32Emu
#endif #endif // #ifndef MT32EMU_B_REVERB_MODEL_H

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
/* 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) */

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <cstring>
#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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef MT32EMU_FILE_H
#define MT32EMU_FILE_H
#include <cstddef>
#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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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<char *>(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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef MT32EMU_FILE_STREAM_H
#define MT32EMU_FILE_STREAM_H
#include <fstream>
#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

View file

@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher /* 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 * 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 * 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 <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
//#include <cmath> #ifndef MT32EMU_LA32_WAVE_GENERATOR_CPP
#include "mt32emu.h" #error This file should be included from LA32WaveGenerator.cpp only.
#endif
#include "mmath.h" #include "mmath.h"
#include "internals.h"
namespace MT32Emu { namespace MT32Emu {
@ -38,10 +39,10 @@ float LA32WaveGenerator::getPCMSample(unsigned int position) {
return ((pcmSample & 32768) == 0) ? sampleValue : -sampleValue; return ((pcmSample & 32768) == 0) ? sampleValue : -sampleValue;
} }
void LA32WaveGenerator::initSynth(const bool sawtoothWaveform, const Bit8u pulseWidth, const Bit8u resonance) { void LA32WaveGenerator::initSynth(const bool useSawtoothWaveform, const Bit8u usePulseWidth, const Bit8u useResonance) {
this->sawtoothWaveform = sawtoothWaveform; sawtoothWaveform = useSawtoothWaveform;
this->pulseWidth = pulseWidth; pulseWidth = usePulseWidth;
this->resonance = resonance; resonance = useResonance;
wavePos = 0.0f; wavePos = 0.0f;
lastFreq = 0.0f; lastFreq = 0.0f;
@ -50,24 +51,24 @@ void LA32WaveGenerator::initSynth(const bool sawtoothWaveform, const Bit8u pulse
active = true; active = true;
} }
void LA32WaveGenerator::initPCM(const Bit16s * const pcmWaveAddress, const Bit32u pcmWaveLength, const bool pcmWaveLooped, const bool pcmWaveInterpolated) { void LA32WaveGenerator::initPCM(const Bit16s * const usePCMWaveAddress, const Bit32u usePCMWaveLength, const bool usePCMWaveLooped, const bool usePCMWaveInterpolated) {
this->pcmWaveAddress = pcmWaveAddress; pcmWaveAddress = usePCMWaveAddress;
this->pcmWaveLength = pcmWaveLength; pcmWaveLength = usePCMWaveLength;
this->pcmWaveLooped = pcmWaveLooped; pcmWaveLooped = usePCMWaveLooped;
this->pcmWaveInterpolated = pcmWaveInterpolated; pcmWaveInterpolated = usePCMWaveInterpolated;
pcmPosition = 0.0f; pcmPosition = 0.0f;
active = true; 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) { float LA32WaveGenerator::generateNextSample(const Bit32u ampVal, const Bit16u pitch, const Bit32u cutoffRampVal) {
if (!active) { if (!active) {
return 0.0f; return 0.0f;
} }
this->amp = amp;
this->pitch = pitch;
float sample = 0.0f; float sample = 0.0f;
// SEMI-CONFIRMED: From sample analysis: // SEMI-CONFIRMED: From sample analysis:
@ -284,9 +285,9 @@ bool LA32WaveGenerator::isPCMWave() const {
return pcmWaveAddress != NULL; return pcmWaveAddress != NULL;
} }
void LA32PartialPair::init(const bool ringModulated, const bool mixed) { void LA32PartialPair::init(const bool useRingModulated, const bool useMixed) {
this->ringModulated = ringModulated; ringModulated = useRingModulated;
this->mixed = mixed; mixed = useMixed;
masterOutputSample = 0.0f; masterOutputSample = 0.0f;
slaveOutputSample = 0.0f; slaveOutputSample = 0.0f;
} }
@ -354,4 +355,4 @@ bool LA32PartialPair::isActive(const PairType useMaster) const {
return useMaster == MASTER ? master.isActive() : slave.isActive(); return useMaster == MASTER ? master.isActive() : slave.isActive();
} }
} } // namespace MT32Emu

View file

@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher /* 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 * 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 * it under the terms of the GNU Lesser General Public License as published by
@ -16,14 +16,15 @@
*/ */
#ifndef MT32EMU_LA32_WAVE_GENERATOR_H #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 { namespace MT32Emu {
/** /**
* LA32WaveGenerator is aimed to represent the exact model of LA32 wave generator. * 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 output square wave is created by adding high / low linear segments in-between
* the rising and falling cosine segments. Basically, its 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. * 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. * 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. * 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 // True means the resulting square wave is to be multiplied by the synchronous cosine
bool sawtoothWaveform; bool sawtoothWaveform;
// Logarithmic amp of the wave generator
Bit32u amp;
// Logarithmic frequency of the resulting wave
Bit16u pitch;
// Values in range [1..31] // Values in range [1..31]
// Value 1 correspong to the minimum resonance // Value 1 correspong to the minimum resonance
Bit8u resonance; Bit8u resonance;
@ -53,9 +48,6 @@ class LA32WaveGenerator {
// Value 255 corresponds to the maximum possible asymmetric of the resulting wave // Value 255 corresponds to the maximum possible asymmetric of the resulting wave
Bit8u pulseWidth; 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 // Logarithmic PCM sample start address
const Bit16s *pcmWaveAddress; const Bit16s *pcmWaveAddress;
@ -96,7 +88,7 @@ public:
// Return true if the WG engine generates PCM wave samples // Return true if the WG engine generates PCM wave samples
bool isPCMWave() const; bool isPCMWave() const;
}; }; // class LA32WaveGenerator
// LA32PartialPair contains a structure of two partials being mixed / ring modulated // LA32PartialPair contains a structure of two partials being mixed / ring modulated
class LA32PartialPair { class LA32PartialPair {
@ -135,8 +127,6 @@ public:
// Return active state of the WG engine // Return active state of the WG engine
bool isActive(const PairType master) const; bool isActive(const PairType master) const;
}; }; // class LA32PartialPair
} // namespace MT32Emu } // namespace MT32Emu
#endif // #ifndef MT32EMU_LA32_WAVE_GENERATOR_H

View file

@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher /* 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 * 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 * 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). - Values when ramping between levels (though this is probably correct).
- Transition timing (may not be 100% accurate, especially for very fast ramps). - Transition timing (may not be 100% accurate, especially for very fast ramps).
*/ */
//#include <cmath>
#include "mt32emu.h"
#include "mmath.h"
#include "internals.h" #include "internals.h"
#include "LA32Ramp.h"
#include "Tables.h"
namespace MT32Emu { namespace MT32Emu {
// SEMI-CONFIRMED from sample analysis. // SEMI-CONFIRMED from sample analysis.
@ -152,4 +152,4 @@ void LA32Ramp::reset() {
interruptRaised = false; interruptRaised = false;
} }
} } // namespace MT32Emu

View file

@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher /* 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 * 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 * it under the terms of the GNU Lesser General Public License as published by
@ -18,6 +18,9 @@
#ifndef MT32EMU_LA32RAMP_H #ifndef MT32EMU_LA32RAMP_H
#define MT32EMU_LA32RAMP_H #define MT32EMU_LA32RAMP_H
#include "globals.h"
#include "Types.h"
namespace MT32Emu { namespace MT32Emu {
class LA32Ramp { class LA32Ramp {
@ -38,6 +41,6 @@ public:
void reset(); void reset();
}; };
} } // namespace MT32Emu
#endif /* TVA_H_ */ #endif // #ifndef MT32EMU_LA32RAMP_H

View file

@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher /* 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 * 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 * 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 <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if MT32EMU_USE_FLOAT_SAMPLES #include <cstddef>
#include "LA32FloatWaveGenerator.cpp"
#else
//#include <cmath>
#include "mt32emu.h"
#include "mmath.h"
#include "internals.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 { namespace MT32Emu {
static const Bit32u SINE_SEGMENT_RELATIVE_LENGTH = 1 << 18; 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) { void LA32Utilites::addLogSamples(LogSample &logSample1, const LogSample &logSample2) {
Bit32u logSampleValue = logSample1.logValue + logSample2.logValue; 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; 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; Bit32u lowLinearLength = (resonanceWaveLengthFactor << 8) - 4 * SINE_SEGMENT_RELATIVE_LENGTH - highLinearLength;
computePositions(highLinearLength, lowLinearLength, resonanceWaveLengthFactor); computePositions(highLinearLength, lowLinearLength, resonanceWaveLengthFactor);
// resonancePhase computation hack resonancePhase = ResonancePhase(((resonanceSinePosition >> 18) + (phase > POSITIVE_FALLING_SINE_SEGMENT ? 2 : 0)) & 3);
int *resonancePhaseAlias = (int *)&resonancePhase;
*resonancePhaseAlias = ((resonanceSinePosition >> 18) + (phase > POSITIVE_FALLING_SINE_SEGMENT ? 2 : 0)) & 3;
} }
void LA32WaveGenerator::generateNextSquareWaveLogSample() { void LA32WaveGenerator::generateNextSquareWaveLogSample() {
@ -158,7 +160,7 @@ void LA32WaveGenerator::generateNextSquareWaveLogSample() {
logSampleValue += (MIDDLE_CUTOFF_VALUE - cutoffVal) >> 9; 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; 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 // 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; 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; 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 { void LA32WaveGenerator::pcmSampleToLogSample(LogSample &logSample, const Bit16s pcmSample) const {
Bit32u logSampleValue = (32787 - (pcmSample & 32767)) << 1; Bit32u logSampleValue = (32787 - (pcmSample & 32767)) << 1;
logSampleValue += amp >> 10; 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; 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 firstSample = LA32Utilites::unlog(wg.getOutputLogSample(true));
Bit16s secondSample = LA32Utilites::unlog(wg.getOutputLogSample(false)); Bit16s secondSample = LA32Utilites::unlog(wg.getOutputLogSample(false));
if (wg.isPCMWave()) { 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; return firstSample + secondSample;
} }
@ -407,7 +409,7 @@ Bit16s LA32PartialPair::nextOutSample() {
Bit16s slaveSample = slave.isPCMWave() ? LA32Utilites::unlog(slave.getOutputLogSample(true)) : unlogAndMixWGOutput(slave); Bit16s slaveSample = slave.isPCMWave() ? LA32Utilites::unlog(slave.getOutputLogSample(true)) : unlogAndMixWGOutput(slave);
slaveSample <<= 2; slaveSample <<= 2;
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; return mixed ? nonOverdrivenMasterSample + ringModulatedSample : ringModulatedSample;
} }
@ -423,6 +425,6 @@ bool LA32PartialPair::isActive(const PairType useMaster) const {
return useMaster == MASTER ? master.isActive() : slave.isActive(); return useMaster == MASTER ? master.isActive() : slave.isActive();
} }
} } // namespace MT32Emu
#endif // #if MT32EMU_USE_FLOAT_SAMPLES #endif // #if MT32EMU_USE_FLOAT_SAMPLES

View file

@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher /* 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 * 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 * 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 <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#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 #if MT32EMU_USE_FLOAT_SAMPLES
#include "LA32FloatWaveGenerator.h" #include "LA32FloatWaveGenerator.h"
#else #else
#ifndef MT32EMU_LA32_WAVE_GENERATOR_H
#define MT32EMU_LA32_WAVE_GENERATOR_H
namespace MT32Emu { namespace MT32Emu {
/** /**
@ -55,7 +59,7 @@ public:
/** /**
* LA32WaveGenerator is aimed to represent the exact model of LA32 wave generator. * 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 output square wave is created by adding high / low linear segments in-between
* the rising and falling cosine segments. Basically, its 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. * 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. * 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. * To synthesise sawtooth waves, the resulting square wave is multiplied by synchronous cosine wave.
@ -143,7 +147,7 @@ class LA32WaveGenerator {
} phase; } phase;
// Current phase of the resonance wave // Current phase of the resonance wave
enum { enum ResonancePhase {
POSITIVE_RISING_RESONANCE_SINE_SEGMENT, POSITIVE_RISING_RESONANCE_SINE_SEGMENT,
POSITIVE_FALLING_RESONANCE_SINE_SEGMENT, POSITIVE_FALLING_RESONANCE_SINE_SEGMENT,
NEGATIVE_FALLING_RESONANCE_SINE_SEGMENT, NEGATIVE_FALLING_RESONANCE_SINE_SEGMENT,
@ -200,7 +204,7 @@ public:
// Return current PCM interpolation factor // Return current PCM interpolation factor
Bit32u getPCMInterpolationFactor() const; Bit32u getPCMInterpolationFactor() const;
}; }; // class LA32WaveGenerator
// LA32PartialPair contains a structure of two partials being mixed / ring modulated // LA32PartialPair contains a structure of two partials being mixed / ring modulated
class LA32PartialPair { class LA32PartialPair {
@ -239,10 +243,10 @@ public:
// Return active state of the WG engine // Return active state of the WG engine
bool isActive(const PairType master) const; bool isActive(const PairType master) const;
}; }; // class LA32PartialPair
} // namespace MT32Emu } // namespace MT32Emu
#endif // #ifndef MT32EMU_LA32_WAVE_GENERATOR_H
#endif // #if MT32EMU_USE_FLOAT_SAMPLES #endif // #if MT32EMU_USE_FLOAT_SAMPLES
#endif // #ifndef MT32EMU_LA32_WAVE_GENERATOR_H

View file

@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher /* 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 * 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 * it under the terms of the GNU Lesser General Public License as published by
@ -18,12 +18,20 @@
#ifndef MT32EMU_MEMORY_REGION_H #ifndef MT32EMU_MEMORY_REGION_H
#define MT32EMU_MEMORY_REGION_H #define MT32EMU_MEMORY_REGION_H
#include <cstddef>
#include "globals.h"
#include "Types.h"
#include "Structures.h"
namespace MT32Emu { namespace MT32Emu {
enum MemoryRegionType { enum MemoryRegionType {
MR_PatchTemp, MR_RhythmTemp, MR_TimbreTemp, MR_Patches, MR_Timbres, MR_System, MR_Display, MR_Reset MR_PatchTemp, MR_RhythmTemp, MR_TimbreTemp, MR_Patches, MR_Timbres, MR_System, MR_Display, MR_Reset
}; };
class Synth;
class MemoryRegion { class MemoryRegion {
private: private:
Synth *synth; Synth *synth;
@ -84,7 +92,7 @@ public:
} }
void read(unsigned int entry, unsigned int off, Bit8u *dst, unsigned int len) const; 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; void write(unsigned int entry, unsigned int off, const Bit8u *src, unsigned int len, bool init = false) const;
}; }; // class MemoryRegion
class PatchTempMemoryRegion : public MemoryRegion { class PatchTempMemoryRegion : public MemoryRegion {
public: public:
@ -112,13 +120,13 @@ public:
}; };
class DisplayMemoryRegion : public MemoryRegion { class DisplayMemoryRegion : public MemoryRegion {
public: 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 { class ResetMemoryRegion : public MemoryRegion {
public: public:
ResetMemoryRegion(Synth *useSynth) : MemoryRegion(useSynth, NULL, NULL, MR_Reset, MT32EMU_MEMADDR(0x7F0000), 0x3FFF, 1) {} ResetMemoryRegion(Synth *useSynth) : MemoryRegion(useSynth, NULL, NULL, MR_Reset, MT32EMU_MEMADDR(0x7F0000), 0x3FFF, 1) {}
}; };
} } // namespace MT32Emu
#endif #endif // #ifndef MT32EMU_MEMORY_REGION_H

View file

@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher /* 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 * 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 * it under the terms of the GNU Lesser General Public License as published by
@ -18,6 +18,9 @@
#ifndef MT32EMU_MIDI_EVENT_QUEUE_H #ifndef MT32EMU_MIDI_EVENT_QUEUE_H
#define MT32EMU_MIDI_EVENT_QUEUE_H #define MT32EMU_MIDI_EVENT_QUEUE_H
#include "globals.h"
#include "Types.h"
namespace MT32Emu { namespace MT32Emu {
/** /**
@ -60,8 +63,9 @@ public:
const MidiEvent *peekMidiEvent(); const MidiEvent *peekMidiEvent();
void dropMidiEvent(); void dropMidiEvent();
bool isFull() const; bool isFull() const;
bool inline isEmpty() const;
}; };
} } // namespace MT32Emu
#endif #endif // #ifndef MT32EMU_MIDI_EVENT_QUEUE_H

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <cstdio>
#include <cstring>
#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;
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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

View file

@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher /* 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 * 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 * 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 <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
//#include <cstdio> #include <cstdio>
//#include <cstring> #include <cstring>
#include "mt32emu.h"
#include "internals.h" #include "internals.h"
#include "Part.h"
#include "Partial.h"
#include "PartialManager.h" #include "PartialManager.h"
#include "Poly.h"
#include "Synth.h"
namespace MT32Emu { namespace MT32Emu {
@ -112,7 +116,7 @@ Bit32s Part::getPitchBend() const {
void Part::setBend(unsigned int midiBend) { void Part::setBend(unsigned int midiBend) {
// CONFIRMED: // 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 { Bit8u Part::getModulation() const {
@ -120,7 +124,7 @@ Bit8u Part::getModulation() const {
} }
void Part::setModulation(unsigned int midiModulation) { void Part::setModulation(unsigned int midiModulation) {
modulation = (Bit8u)midiModulation; modulation = Bit8u(midiModulation);
} }
void Part::resetAllControllers() { void Part::resetAllControllers() {
@ -162,7 +166,7 @@ void Part::refresh() {
patchCache[t].reverb = patchTemp->patch.reverbSwitch > 0; patchCache[t].reverb = patchTemp->patch.reverbSwitch > 0;
} }
memcpy(currentInstr, timbreTemp->common.name, 10); memcpy(currentInstr, timbreTemp->common.name, 10);
synth->newTimbreSet(partNum, patchTemp->patch.timbreGroup, currentInstr); synth->newTimbreSet(partNum, patchTemp->patch.timbreGroup, patchTemp->patch.timbreNum, currentInstr);
updatePitchBenderRange(); updatePitchBenderRange();
} }
@ -255,26 +259,26 @@ void Part::cacheTimbre(PatchCache cache[4], const TimbreParam *timbre) {
switch (t) { switch (t) {
case 0: case 0:
cache[t].PCMPartial = (PartialStruct[(int)timbre->common.partialStructure12] & 0x2) ? true : false; cache[t].PCMPartial = (PartialStruct[int(timbre->common.partialStructure12)] & 0x2) ? true : false;
cache[t].structureMix = PartialMixStruct[(int)timbre->common.partialStructure12]; cache[t].structureMix = PartialMixStruct[int(timbre->common.partialStructure12)];
cache[t].structurePosition = 0; cache[t].structurePosition = 0;
cache[t].structurePair = 1; cache[t].structurePair = 1;
break; break;
case 1: case 1:
cache[t].PCMPartial = (PartialStruct[(int)timbre->common.partialStructure12] & 0x1) ? true : false; cache[t].PCMPartial = (PartialStruct[int(timbre->common.partialStructure12)] & 0x1) ? true : false;
cache[t].structureMix = PartialMixStruct[(int)timbre->common.partialStructure12]; cache[t].structureMix = PartialMixStruct[int(timbre->common.partialStructure12)];
cache[t].structurePosition = 1; cache[t].structurePosition = 1;
cache[t].structurePair = 0; cache[t].structurePair = 0;
break; break;
case 2: case 2:
cache[t].PCMPartial = (PartialStruct[(int)timbre->common.partialStructure34] & 0x2) ? true : false; cache[t].PCMPartial = (PartialStruct[int(timbre->common.partialStructure34)] & 0x2) ? true : false;
cache[t].structureMix = PartialMixStruct[(int)timbre->common.partialStructure34]; cache[t].structureMix = PartialMixStruct[int(timbre->common.partialStructure34)];
cache[t].structurePosition = 0; cache[t].structurePosition = 0;
cache[t].structurePair = 3; cache[t].structurePair = 3;
break; break;
case 3: case 3:
cache[t].PCMPartial = (PartialStruct[(int)timbre->common.partialStructure34] & 0x1) ? true : false; cache[t].PCMPartial = (PartialStruct[int(timbre->common.partialStructure34)] & 0x1) ? true : false;
cache[t].structureMix = PartialMixStruct[(int)timbre->common.partialStructure34]; cache[t].structureMix = PartialMixStruct[int(timbre->common.partialStructure34)];
cache[t].structurePosition = 1; cache[t].structurePosition = 1;
cache[t].structurePair = 2; cache[t].structurePair = 2;
break; break;
@ -308,7 +312,7 @@ const char *Part::getName() const {
void Part::setVolume(unsigned int midiVolume) { void Part::setVolume(unsigned int midiVolume) {
// CONFIRMED: This calculation matches the table used in the control ROM // 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); //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) { void Part::setExpression(unsigned int midiExpression) {
// CONFIRMED: This calculation matches the table used in the control ROM // 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) { void RhythmPart::setPan(unsigned int midiPan) {
@ -337,9 +341,9 @@ void Part::setPan(unsigned int midiPan) {
// NOTE: Panning is inverted compared to GM. // NOTE: Panning is inverted compared to GM.
// CM-32L: Divide by 8.5 // CM-32L: Divide by 8.5
patchTemp->panpot = (Bit8u)((midiPan << 3) / 68); patchTemp->panpot = Bit8u((midiPan << 3) / 68);
// FIXME: MT-32: Divide by 9 // 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); //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 key = midiKey;
unsigned int drumNum = key - 24; unsigned int drumNum = key - 24;
int drumTimbreNum = rhythmTemp[drumNum].timbre; 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); synth->printDebug("%s: Attempted to play unmapped key %d (velocity %d)", name, midiKey, velocity);
return; return;
} }
@ -507,7 +512,7 @@ void Part::playPoly(const PatchCache cache[4], const MemParams::RhythmTemp *rhyt
#if MT32EMU_MONITOR_PARTIALS > 1 #if MT32EMU_MONITOR_PARTIALS > 1
synth->printPartialUsage(); synth->printPartialUsage();
#endif #endif
synth->polyStateChanged(partNum); synth->reportHandler->onPolyStateChanged(Bit8u(partNum));
} }
void Part::allNotesOff() { void Part::allNotesOff() {
@ -593,16 +598,14 @@ void Part::partialDeactivated(Poly *poly) {
if (!poly->isActive()) { if (!poly->isActive()) {
activePolys.remove(poly); activePolys.remove(poly);
synth->partialManager->polyFreed(poly); synth->partialManager->polyFreed(poly);
synth->polyStateChanged(partNum); synth->reportHandler->onPolyStateChanged(Bit8u(partNum));
} }
} }
//#define POLY_LIST_DEBUG
PolyList::PolyList() : firstPoly(NULL), lastPoly(NULL) {} PolyList::PolyList() : firstPoly(NULL), lastPoly(NULL) {}
bool PolyList::isEmpty() const { bool PolyList::isEmpty() const {
#ifdef POLY_LIST_DEBUG #ifdef MT32EMU_POLY_LIST_DEBUG
if ((firstPoly == NULL || lastPoly == NULL) && firstPoly != lastPoly) { if ((firstPoly == NULL || lastPoly == NULL) && firstPoly != lastPoly) {
printf("PolyList: desynchronised firstPoly & lastPoly pointers\n"); printf("PolyList: desynchronised firstPoly & lastPoly pointers\n");
} }
@ -619,7 +622,7 @@ Poly *PolyList::getLast() const {
} }
void PolyList::prepend(Poly *poly) { void PolyList::prepend(Poly *poly) {
#ifdef POLY_LIST_DEBUG #ifdef MT32EMU_POLY_LIST_DEBUG
if (poly->getNext() != NULL) { if (poly->getNext() != NULL) {
printf("PolyList: Non-NULL next field in a Poly being prepended is ignored\n"); 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) { void PolyList::append(Poly *poly) {
#ifdef POLY_LIST_DEBUG #ifdef MT32EMU_POLY_LIST_DEBUG
if (poly->getNext() != NULL) { if (poly->getNext() != NULL) {
printf("PolyList: Non-NULL next field in a Poly being appended is ignored\n"); printf("PolyList: Non-NULL next field in a Poly being appended is ignored\n");
} }
#endif #endif
poly->setNext(NULL); poly->setNext(NULL);
if (lastPoly != NULL) { if (lastPoly != NULL) {
#ifdef POLY_LIST_DEBUG #ifdef MT32EMU_POLY_LIST_DEBUG
if (lastPoly->getNext() != NULL) { if (lastPoly->getNext() != NULL) {
printf("PolyList: Non-NULL next field in the lastPoly\n"); printf("PolyList: Non-NULL next field in the lastPoly\n");
} }
@ -656,7 +659,7 @@ Poly *PolyList::takeFirst() {
Poly *oldFirst = firstPoly; Poly *oldFirst = firstPoly;
firstPoly = oldFirst->getNext(); firstPoly = oldFirst->getNext();
if (firstPoly == NULL) { if (firstPoly == NULL) {
#ifdef POLY_LIST_DEBUG #ifdef MT32EMU_POLY_LIST_DEBUG
if (lastPoly != oldFirst) { if (lastPoly != oldFirst) {
printf("PolyList: firstPoly != lastPoly in a list with a single Poly\n"); 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()) { for (Poly *poly = firstPoly; poly != NULL; poly = poly->getNext()) {
if (poly->getNext() == polyToRemove) { if (poly->getNext() == polyToRemove) {
if (polyToRemove == lastPoly) { if (polyToRemove == lastPoly) {
#ifdef POLY_LIST_DEBUG #ifdef MT32EMU_POLY_LIST_DEBUG
if (lastPoly->getNext() != NULL) { if (lastPoly->getNext() != NULL) {
printf("PolyList: Non-NULL next field in the lastPoly\n"); printf("PolyList: Non-NULL next field in the lastPoly\n");
} }
@ -689,4 +692,4 @@ void PolyList::remove(Poly * const polyToRemove) {
} }
} }
} } // namespace MT32Emu

View file

@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher /* 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 * 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 * it under the terms of the GNU Lesser General Public License as published by
@ -18,9 +18,14 @@
#ifndef MT32EMU_PART_H #ifndef MT32EMU_PART_H
#define MT32EMU_PART_H #define MT32EMU_PART_H
#include "globals.h"
#include "internals.h"
#include "Types.h"
#include "Structures.h"
namespace MT32Emu { namespace MT32Emu {
class PartialManager; class Poly;
class Synth; class Synth;
class PolyList { 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. // Abort the first poly in PolyState_HELD, or if none exists, the first active poly in any state.
bool abortFirstPolyPreferHeld(); bool abortFirstPolyPreferHeld();
bool abortFirstPoly(); bool abortFirstPoly();
}; }; // class Part
class RhythmPart: public Part { class RhythmPart: public Part {
// Pointer to the area of the MT-32's memory dedicated to rhythm // Pointer to the area of the MT-32's memory dedicated to rhythm
@ -143,5 +148,6 @@ public:
void setProgram(unsigned int patchNum); void setProgram(unsigned int patchNum);
}; };
} } // namespace MT32Emu
#endif
#endif // #ifndef MT32EMU_PART_H

View file

@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher /* 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 * 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 * 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 <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
//#include <cmath> #include <cstddef>
//#include <cstdlib>
//#include <cstring>
#include "mt32emu.h"
#include "mmath.h"
#include "internals.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 { namespace MT32Emu {
static const Bit8u PAN_NUMERATOR_MASTER[] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7}; 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 // Only used for debugging purposes
unsigned long Partial::debugGetSampleNum() const { Bit32u Partial::debugGetSampleNum() const {
return sampleNum; 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()) { if (!isActive() || alreadyOutputed || isRingModulatingSlave()) {
return false; 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. // Though, it is unknown whether this overflow is exploited somewhere.
Sample leftOut = Sample((sample * leftPanValue) >> 8); Sample leftOut = Sample((sample * leftPanValue) >> 8);
Sample rightOut = Sample((sample * rightPanValue) >> 8); Sample rightOut = Sample((sample * rightPanValue) >> 8);
*leftBuf = Synth::clipSampleEx((SampleEx)*leftBuf + (SampleEx)leftOut); *leftBuf = Synth::clipSampleEx(SampleEx(*leftBuf) + SampleEx(leftOut));
*rightBuf = Synth::clipSampleEx((SampleEx)*rightBuf + (SampleEx)rightOut); *rightBuf = Synth::clipSampleEx(SampleEx(*rightBuf) + SampleEx(rightOut));
leftBuf++; leftBuf++;
rightBuf++; rightBuf++;
#endif #endif
@ -341,4 +346,4 @@ void Partial::startDecayAll() {
tvf->startDecay(); tvf->startDecay();
} }
} } // namespace MT32Emu

View file

@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher /* 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 * 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 * it under the terms of the GNU Lesser General Public License as published by
@ -18,11 +18,21 @@
#ifndef MT32EMU_PARTIAL_H #ifndef MT32EMU_PARTIAL_H
#define 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 { namespace MT32Emu {
class Synth;
class Part; class Part;
class Poly;
class Synth;
class TVA; class TVA;
class TVF;
class TVP;
struct ControlROMPCMStruct; struct ControlROMPCMStruct;
// A partial represents one of up to four waveform generators currently playing within a poly. // 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 const int debugPartialNum; // Only used for debugging
// Number of the sample currently being rendered by produceOutput(), or 0 if no run is in progress // 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. // 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. // 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. // Also we double the value to enable INACCURATE_SMOOTH_PAN, with respect to MoK.
@ -77,7 +87,7 @@ public:
~Partial(); ~Partial();
int debugGetPartialNum() const; int debugGetPartialNum() const;
unsigned long debugGetSampleNum() const; Bit32u debugGetSampleNum() const;
int getOwnerPart() const; int getOwnerPart() const;
const Poly *getPoly() const; const Poly *getPoly() const;
@ -100,9 +110,9 @@ public:
// Returns true only if data written to buffer // Returns true only if data written to buffer
// This function (unlike the one below it) returns processed stereo samples // This function (unlike the one below it) returns processed stereo samples
// made from combining this single partial with its pair, if it has one. // 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

View file

@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher /* 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 * 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 * 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 <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
//#include <cstring> #include <cstddef>
#include <cstring>
#include "mt32emu.h"
#include "internals.h" #include "internals.h"
#include "PartialManager.h" #include "PartialManager.h"
#include "Part.h"
#include "Partial.h"
#include "Poly.h"
#include "Synth.h"
namespace MT32Emu { namespace MT32Emu {
@ -275,7 +280,7 @@ void PartialManager::polyFreed(Poly *poly) {
const Poly *activePoly = synth->getPart(partNum)->getFirstActivePoly(); const Poly *activePoly = synth->getPart(partNum)->getFirstActivePoly();
Bit32u polyCount = 0; Bit32u polyCount = 0;
while (activePoly != NULL) { while (activePoly != NULL) {
activePoly->getNext(); activePoly = activePoly->getNext();
polyCount++; polyCount++;
} }
synth->printDebug("Part: %i, active poly count: %i\n", partNum, polyCount); synth->printDebug("Part: %i, active poly count: %i\n", partNum, polyCount);
@ -286,4 +291,4 @@ void PartialManager::polyFreed(Poly *poly) {
freePolys[firstFreePolyIndex] = poly; freePolys[firstFreePolyIndex] = poly;
} }
} } // namespace MT32Emu

View file

@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher /* 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 * 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 * it under the terms of the GNU Lesser General Public License as published by
@ -18,8 +18,15 @@
#ifndef MT32EMU_PARTIALMANAGER_H #ifndef MT32EMU_PARTIALMANAGER_H
#define MT32EMU_PARTIALMANAGER_H #define MT32EMU_PARTIALMANAGER_H
#include "globals.h"
#include "internals.h"
#include "Types.h"
namespace MT32Emu { namespace MT32Emu {
class Part;
class Partial;
class Poly;
class Synth; class Synth;
class PartialManager { class PartialManager {
@ -49,8 +56,8 @@ public:
const Partial *getPartial(unsigned int partialNum) const; const Partial *getPartial(unsigned int partialNum) const;
Poly *assignPolyToPart(Part *part); Poly *assignPolyToPart(Part *part);
void polyFreed(Poly *poly); void polyFreed(Poly *poly);
}; }; // class PartialManager
} } // namespace MT32Emu
#endif #endif // #ifndef MT32EMU_PARTIALMANAGER_H

View file

@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher /* 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 * 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 * 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 <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "mt32emu.h" #include <cstddef>
#include "internals.h" #include "internals.h"
#include "Poly.h"
#include "Part.h"
#include "Partial.h"
#include "Synth.h"
namespace MT32Emu { namespace MT32Emu {
Poly::Poly() { Poly::Poly() {
@ -181,4 +187,4 @@ void Poly::setNext(Poly *poly) {
next = poly; next = poly;
} }
} } // namespace MT32Emu

View file

@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher /* 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 * 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 * it under the terms of the GNU Lesser General Public License as published by
@ -18,17 +18,14 @@
#ifndef MT32EMU_POLY_H #ifndef MT32EMU_POLY_H
#define MT32EMU_POLY_H #define MT32EMU_POLY_H
#include "globals.h"
#include "internals.h"
namespace MT32Emu { namespace MT32Emu {
class Part; class Part;
class Partial; class Partial;
struct PatchCache;
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
};
class Poly { class Poly {
private: private:
@ -66,8 +63,8 @@ public:
Poly *getNext() const; Poly *getNext() const;
void setNext(Poly *poly); void setNext(Poly *poly);
}; }; // class Poly
} } // namespace MT32Emu
#endif /* POLY_H_ */ #endif // #ifndef MT32EMU_POLY_H

View file

@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher /* 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 * 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 * 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 <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
//#include <cstring> #include <cstring>
#include "internals.h"
#include "ROMInfo.h" #include "ROMInfo.h"
namespace MT32Emu { namespace MT32Emu {
static const ROMInfo *getKnownROMInfoFromList(unsigned int index) { static const ROMInfo *getKnownROMInfoFromList(Bit32u index) {
static const ControlROMFeatureSet MT32_COMPATIBLE(true, true);
static const ControlROMFeatureSet CM32L_COMPATIBLE(false, false);
// Known ROMs // 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_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, &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};
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_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, &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};
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_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_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, &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};
static const ROMInfo PCM_MT32 = {524288, "f6b1eebc4b2d200ec6d3d21d51325d5b48c60252", ROMInfo::PCM, "pcm_mt32", "MT-32 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, 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[] = { static const ROMInfo * const ROM_INFOS[] = {
&CTRL_MT32_V1_04, &CTRL_MT32_V1_04,
@ -52,23 +52,11 @@ static const ROMInfo *getKnownROMInfoFromList(unsigned int index) {
return ROM_INFOS[index]; return ROM_INFOS[index];
} }
const ROMInfo* ROMInfo::getROMInfo(Common::File *file) { const ROMInfo* ROMInfo::getROMInfo(File *file) {
size_t fileSize = file->size(); size_t fileSize = file->getSize();
Common::String fileName = file->getName(); for (Bit32u i = 0; getKnownROMInfoFromList(i) != NULL; i++) {
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 = getKnownROMInfoFromList(i); const ROMInfo *romInfo = getKnownROMInfoFromList(i);
if (fileSize == romInfo->fileSize /*&& !strcmp(fileDigest, romInfo->sha1Digest)*/) { if (fileSize == romInfo->fileSize && !strcmp(file->getSHA1(), 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;
}
return romInfo; return romInfo;
} }
} }
@ -79,17 +67,17 @@ void ROMInfo::freeROMInfo(const ROMInfo *romInfo) {
(void) romInfo; (void) romInfo;
} }
static int getROMCount() { static Bit32u getROMCount() {
int count; Bit32u count;
for(count = 0; getKnownROMInfoFromList(count) != NULL; count++) { for(count = 0; getKnownROMInfoFromList(count) != NULL; count++) {
} }
return 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 **romInfoList = new const ROMInfo*[getROMCount() + 1];
const ROMInfo **currentROMInList = romInfoList; 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); const ROMInfo *romInfo = getKnownROMInfoFromList(i);
if ((types & (1 << romInfo->type)) && (pairTypes & (1 << romInfo->pairType))) { if ((types & (1 << romInfo->type)) && (pairTypes & (1 << romInfo->pairType))) {
*currentROMInList++ = romInfo; *currentROMInList++ = romInfo;
@ -103,19 +91,22 @@ void ROMInfo::freeROMInfoList(const ROMInfo **romInfoList) {
delete[] romInfoList; delete[] romInfoList;
} }
const ROMImage* ROMImage::makeROMImage(Common::File *file) { ROMImage::ROMImage(File *useFile) : file(useFile), romInfo(ROMInfo::getROMInfo(file))
ROMImage *romImage = new ROMImage; {}
romImage->file = file;
romImage->romInfo = ROMInfo::getROMInfo(romImage->file); ROMImage::~ROMImage() {
return romImage; ROMInfo::freeROMInfo(romInfo);
}
const ROMImage* ROMImage::makeROMImage(File *file) {
return new ROMImage(file);
} }
void ROMImage::freeROMImage(const ROMImage *romImage) { void ROMImage::freeROMImage(const ROMImage *romImage) {
ROMInfo::freeROMInfo(romImage->romInfo);
delete romImage; delete romImage;
} }
Common::File* ROMImage::getFile() const { File* ROMImage::getFile() const {
return file; return file;
} }
@ -123,17 +114,4 @@ const ROMInfo* ROMImage::getROMInfo() const {
return romInfo; return romInfo;
} }
ControlROMFeatureSet::ControlROMFeatureSet(bool useDefaultReverbMT32Compatible, bool useOldMT32AnalogLPF) : } // namespace MT32Emu
defaultReverbMT32Compatible(useDefaultReverbMT32Compatible),
oldMT32AnalogLPF(useOldMT32AnalogLPF)
{}
bool ControlROMFeatureSet::isDefaultReverbMT32Compatible() const {
return defaultReverbMT32Compatible;
}
bool ControlROMFeatureSet::isOldMT32AnalogLPF() const {
return oldMT32AnalogLPF;
}
}

View file

@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher /* 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 * 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 * it under the terms of the GNU Lesser General Public License as published by
@ -18,73 +18,63 @@
#ifndef MT32EMU_ROMINFO_H #ifndef MT32EMU_ROMINFO_H
#define MT32EMU_ROMINFO_H #define MT32EMU_ROMINFO_H
//#include <cstddef> #include <cstddef>
#include "common/file.h"
#include "globals.h"
#include "File.h"
namespace MT32Emu { namespace MT32Emu {
struct ControlROMFeatureSet;
// Defines vital info about ROM file to be used by synth and applications // Defines vital info about ROM file to be used by synth and applications
struct ROMInfo { struct ROMInfo {
public: public:
size_t fileSize; size_t fileSize;
const char *sha1Digest; const File::SHA1Digest &sha1Digest;
enum Type {PCM, Control, Reverb} type; enum Type {PCM, Control, Reverb} type;
const char *shortName; const char *shortName;
const char *description; const char *description;
enum PairType {Full, FirstHalf, SecondHalf, Mux0, Mux1} pairType; enum PairType {Full, FirstHalf, SecondHalf, Mux0, Mux1} pairType;
ROMInfo *pairROMInfo; ROMInfo *pairROMInfo;
const ControlROMFeatureSet *controlROMFeatures;
// Returns a ROMInfo struct by inspecting the size and the SHA1 hash // 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 // 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 // Allows retrieving a NULL-terminated list of ROMInfos for a range of types and pairTypes
// (specified by bitmasks) // (specified by bitmasks)
// Useful for GUI/console app to output information on what ROMs it supports // 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 // 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 // Synth::open() is to require a full control ROMImage and a full PCM ROMImage to work
class ROMImage { class ROMImage {
private: private:
Common::File *file; File * const file;
const ROMInfo *romInfo; const ROMInfo * const romInfo;
ROMImage(File *file);
~ROMImage();
public: public:
// Creates a ROMImage object given a ROMInfo and a File. Keeps a reference // 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 // to the File and ROMInfo given, which must be freed separately by the user
// after the ROMImage is freed // 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 // 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; MT32EMU_EXPORT File *getFile() const;
const ROMInfo *getROMInfo() const; MT32EMU_EXPORT const ROMInfo *getROMInfo() const;
}; };
struct ControlROMFeatureSet { } // namespace MT32Emu
private:
unsigned int defaultReverbMT32Compatible : 1;
unsigned int oldMT32AnalogLPF : 1;
public: #endif // #ifndef MT32EMU_ROMINFO_H
ControlROMFeatureSet(bool defaultReverbMT32Compatible, bool oldMT32AnalogLPF);
bool isDefaultReverbMT32Compatible() const;
bool isOldMT32AnalogLPF() const;
};
}
#endif

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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<SoxrAdapter *>(srcDelegate);
#elif MT32EMU_WITH_LIBSAMPLERATE_RESAMPLER
delete static_cast<SamplerateAdapter *>(srcDelegate);
#else
delete static_cast<InternalResampler *>(srcDelegate);
#endif
}
void SampleRateConverter::getOutputSamples(float *buffer, unsigned int length) {
#if MT32EMU_WITH_LIBSOXR_RESAMPLER
static_cast<SoxrAdapter *>(srcDelegate)->getOutputSamples(buffer, length);
#elif MT32EMU_WITH_LIBSAMPLERATE_RESAMPLER
static_cast<SamplerateAdapter *>(srcDelegate)->getOutputSamples(buffer, length);
#else
static_cast<InternalResampler *>(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;
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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

View file

@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher /* 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 * 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 * it under the terms of the GNU Lesser General Public License as published by
@ -18,6 +18,9 @@
#ifndef MT32EMU_STRUCTURES_H #ifndef MT32EMU_STRUCTURES_H
#define MT32EMU_STRUCTURES_H #define MT32EMU_STRUCTURES_H
#include "globals.h"
#include "Types.h"
namespace MT32Emu { namespace MT32Emu {
// MT32EMU_MEMADDR() converts from sysex-padded, MT32EMU_SYSEXMEMADDR converts to it // MT32EMU_MEMADDR() converts from sysex-padded, MT32EMU_SYSEXMEMADDR converts to it
@ -102,8 +105,8 @@ struct TimbreParam {
Bit8u envTime[5]; // 0-100 Bit8u envTime[5]; // 0-100
Bit8u envLevel[4]; // 0-100 // [3]: SUSTAIN LEVEL Bit8u envLevel[4]; // 0-100 // [3]: SUSTAIN LEVEL
} MT32EMU_ALIGN_PACKED tva; } MT32EMU_ALIGN_PACKED tva;
} MT32EMU_ALIGN_PACKED partial[4]; } MT32EMU_ALIGN_PACKED partial[4]; // struct PartialParam
} MT32EMU_ALIGN_PACKED; } MT32EMU_ALIGN_PACKED; // struct TimbreParam
struct PatchParam { struct PatchParam {
Bit8u timbreGroup; // TIMBRE GROUP 0-3 (group A, group B, Memory, Rhythm) 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 chanAssign[9]; // MIDI CHANNEL (PART1) 0-16 (1-16,OFF)
Bit8u masterVol; // MASTER VOLUME 0-100 Bit8u masterVol; // MASTER VOLUME 0-100
} MT32EMU_ALIGN_PACKED system; } 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__) #if defined(_MSC_VER) || defined(__MINGW32__)
#pragma pack(pop) #pragma pack(pop)
@ -171,10 +183,17 @@ struct MemParams {
#pragma pack() #pragma pack()
#endif #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 { struct ControlROMMap {
Bit16u idPos; const char *shortName;
Bit16u idLen; const ControlROMFeatureSet &featureSet;
const char *idBytes;
Bit16u pcmTable; // 4 * pcmCount bytes Bit16u pcmTable; // 4 * pcmCount bytes
Bit16u pcmCount; Bit16u pcmCount;
Bit16u timbreAMap; // 128 bytes Bit16u timbreAMap; // 128 bytes
@ -194,6 +213,8 @@ struct ControlROMMap {
Bit16u patchMaxTable; // 16 bytes Bit16u patchMaxTable; // 16 bytes
Bit16u systemMaxTable; // 23 bytes Bit16u systemMaxTable; // 23 bytes
Bit16u timbreMaxTable; // 72 bytes Bit16u timbreMaxTable; // 72 bytes
Bit16u soundGroupsTable; // 14 bytes each entry
Bit16u soundGroupsCount;
}; };
struct ControlROMPCMStruct { struct ControlROMPCMStruct {
@ -215,7 +236,7 @@ struct PatchCache {
bool playPartial; bool playPartial;
bool PCMPartial; bool PCMPartial;
int pcm; int pcm;
char waveform; Bit8u waveform;
Bit32u structureMix; Bit32u structureMix;
int structurePosition; int structurePosition;
@ -233,6 +254,6 @@ struct PatchCache {
const TimbreParam::PartialParam *partialParam; const TimbreParam::PartialParam *partialParam;
}; };
} } // namespace MT32Emu
#endif #endif // #ifndef MT32EMU_STRUCTURES_H

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher /* 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 * 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 * it under the terms of the GNU Lesser General Public License as published by
@ -18,8 +18,13 @@
#ifndef MT32EMU_SYNTH_H #ifndef MT32EMU_SYNTH_H
#define MT32EMU_SYNTH_H #define MT32EMU_SYNTH_H
//#include <cstdarg> #include <cstdarg>
//#include <cstring> #include <cstddef>
#include <cstring>
#include "globals.h"
#include "Types.h"
#include "Enumerations.h"
namespace MT32Emu { namespace MT32Emu {
@ -31,6 +36,8 @@ class Part;
class Poly; class Poly;
class Partial; class Partial;
class PartialManager; class PartialManager;
class Renderer;
class ROMImage;
class PatchTempMemoryRegion; class PatchTempMemoryRegion;
class RhythmTempMemoryRegion; class RhythmTempMemoryRegion;
@ -41,82 +48,11 @@ class SystemMemoryRegion;
class DisplayMemoryRegion; class DisplayMemoryRegion;
class ResetMemoryRegion; class ResetMemoryRegion;
struct ControlROMFeatureSet;
struct ControlROMMap; struct ControlROMMap;
struct PCMWaveEntry; struct PCMWaveEntry;
struct MemParams; 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_MANUFACTURER_ROLAND = 0x41;
const Bit8u SYSEX_MDL_MT32 = 0x16; 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_ERR = 0x4E; // Communications error
const Bit8u SYSEX_CMD_RJC = 0x4F; // Rejection 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; // Set of multiplexed output streams appeared at the DAC entrance.
template <class T>
class ReportHandler { struct DACOutputStreams {
friend class Synth; 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: public:
virtual ~ReportHandler() {} virtual ~ReportHandler() {}
protected:
// Callback for debug messages, in vprintf() format // Callback for debug messages, in vprintf() format
virtual void printDebug(const char *fmt, va_list list); virtual void printDebug(const char *fmt, va_list list);
// Callbacks for reporting errors
// Callbacks for reporting various errors and information
virtual void onErrorControlROM() {} virtual void onErrorControlROM() {}
virtual void onErrorPCMROM() {} virtual void onErrorPCMROM() {}
// Callback for reporting about displaying a new custom message on LCD
virtual void showLCDMessage(const char *message); virtual void showLCDMessage(const char *message);
// Callback for reporting actual processing of a MIDI message
virtual void onMIDIMessagePlayed() {} 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 onDeviceReset() {}
virtual void onDeviceReconfig() {} virtual void onDeviceReconfig() {}
// Callbacks for reporting changes of reverb settings
virtual void onNewReverbMode(Bit8u /* mode */) {} virtual void onNewReverbMode(Bit8u /* mode */) {}
virtual void onNewReverbTime(Bit8u /* time */) {} virtual void onNewReverbTime(Bit8u /* time */) {}
virtual void onNewReverbLevel(Bit8u /* level */) {} virtual void onNewReverbLevel(Bit8u /* level */) {}
virtual void onPolyStateChanged(int /* partNum */) {} // Callbacks for reporting various information
virtual void onProgramChanged(int /* partNum */, int /* bankNum */, const char * /* patchName */) {} virtual void onPolyStateChanged(Bit8u /* partNum */) {}
virtual void onProgramChanged(Bit8u /* partNum */, const char * /* soundGroupName */, const char * /* patchName */) {}
}; };
class Synth { class Synth {
friend class DefaultMidiStreamParser;
friend class Part; friend class Part;
friend class RhythmPart;
friend class Poly;
friend class Partial; friend class Partial;
friend class PartialManager; friend class PartialManager;
friend class Tables; friend class Poly;
friend class MemoryRegion; friend class Renderer;
friend class RhythmPart;
friend class SamplerateAdapter;
friend class SoxrAdapter;
friend class TVA; friend class TVA;
friend class TVF;
friend class TVP; friend class TVP;
private: private:
// **************************** Implementation fields **************************
PatchTempMemoryRegion *patchTempMemoryRegion; PatchTempMemoryRegion *patchTempMemoryRegion;
RhythmTempMemoryRegion *rhythmTempMemoryRegion; RhythmTempMemoryRegion *rhythmTempMemoryRegion;
TimbreTempMemoryRegion *timbreTempMemoryRegion; TimbreTempMemoryRegion *timbreTempMemoryRegion;
@ -184,8 +139,6 @@ private:
Bit8u *paddedTimbreMaxTable; Bit8u *paddedTimbreMaxTable;
bool isEnabled;
PCMWaveEntry *pcmWaves; // Array PCMWaveEntry *pcmWaves; // Array
const ControlROMFeatureSet *controlROMFeatures; const ControlROMFeatureSet *controlROMFeatures;
@ -194,8 +147,11 @@ private:
Bit16s *pcmROMData; Bit16s *pcmROMData;
size_t pcmROMSize; // This is in 16-bit samples, therefore half the number of bytes in the ROM size_t pcmROMSize; // This is in 16-bit samples, therefore half the number of bytes in the ROM
unsigned int partialCount; Bit8u soundGroupIx[128]; // For each standard timbre
Bit8s chantable[32]; // FIXME: Need explanation why 32 is set, obviously it should be 16 const char (*soundGroupNames)[9]; // Array
Bit32u partialCount;
Bit8u chantable[16]; // NOTE: value above 8 means that the channel is not assigned
MidiEventQueue *midiQueue; MidiEventQueue *midiQueue;
volatile Bit32u lastReceivedMIDIEventTimestamp; volatile Bit32u lastReceivedMIDIEventTimestamp;
@ -215,7 +171,8 @@ private:
bool reversedStereoEnabled; bool reversedStereoEnabled;
bool isOpen; bool opened;
bool activated;
bool isDefaultReportHandler; bool isDefaultReportHandler;
ReportHandler *reportHandler; ReportHandler *reportHandler;
@ -229,15 +186,17 @@ private:
Poly *abortingPoly; Poly *abortingPoly;
Analog *analog; Analog *analog;
Renderer &renderer;
// Binary compatibility helper.
void *reserved;
// **************************** Implementation methods **************************
Bit32u addMIDIInterfaceDelay(Bit32u len, Bit32u timestamp); Bit32u addMIDIInterfaceDelay(Bit32u len, Bit32u timestamp);
bool isAbortingPoly() const { return abortingPoly != NULL; }
void produceLA32Output(Sample *buffer, Bit32u len); void readSysex(Bit8u channel, const Bit8u *sysex, Bit32u len) const;
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 initMemoryRegions(); void initMemoryRegions();
void deleteMemoryRegions(); void deleteMemoryRegions();
MemoryRegion *findMemoryRegion(Bit32u addr); MemoryRegion *findMemoryRegion(Bit32u addr);
@ -248,79 +207,105 @@ private:
bool loadPCMROM(const ROMImage &pcmROMImage); bool loadPCMROM(const ROMImage &pcmROMImage);
bool initPCMList(Bit16u mapAddress, Bit16u count); bool initPCMList(Bit16u mapAddress, Bit16u count);
bool initTimbres(Bit16u mapAddress, Bit16u offset, int timbreCount, int startTimbre, bool compressed); bool initTimbres(Bit16u mapAddress, Bit16u offset, Bit16u timbreCount, Bit16u startTimbre, bool compressed);
bool initCompressedTimbre(int drumNum, const Bit8u *mem, unsigned int memLen); bool initCompressedTimbre(Bit16u drumNum, const Bit8u *mem, Bit32u memLen);
void initReverbModels(bool mt32CompatibleMode);
void initSoundGroups(char newSoundGroupNames[][9]);
void refreshSystemMasterTune(); void refreshSystemMasterTune();
void refreshSystemReverbParameters(); void refreshSystemReverbParameters();
void refreshSystemReserveSettings(); void refreshSystemReserveSettings();
void refreshSystemChanAssign(unsigned int firstPart, unsigned int lastPart); void refreshSystemChanAssign(Bit8u firstPart, Bit8u lastPart);
void refreshSystemMasterVol(); void refreshSystemMasterVol();
void refreshSystem(); void refreshSystem();
void reset(); void reset();
void dispose();
void printPartialUsage(unsigned long sampleOffset = 0); void printPartialUsage(Bit32u sampleOffset = 0);
void polyStateChanged(int partNum); void newTimbreSet(Bit8u partNum, Bit8u timbreGroup, Bit8u timbreNumber, const char patchName[]);
void newTimbreSet(int partNum, Bit8u timbreGroup, const char patchName[]);
void printDebug(const char *fmt, ...); void printDebug(const char *fmt, ...);
// partNum should be 0..7 for Part 1..8, or 8 for Rhythm // 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: public:
static inline Sample clipSampleEx(SampleEx sampleEx) { static inline Bit16s clipSampleEx(Bit32s sampleEx) {
#if MT32EMU_USE_FLOAT_SAMPLES
return sampleEx;
#else
// Clamp values above 32767 to 32767, and values below -32768 to -32768 // 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. // 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... // The version below is actually a bit faster on my system...
//return ((sampleEx + 0x8000) & ~0xFFFF) ? (sampleEx >> 31) ^ 0x7FFF : (Sample)sampleEx; //return ((sampleEx + 0x8000) & ~0xFFFF) ? Bit16s((sampleEx >> 31) ^ 0x7FFF) : (Bit16s)sampleEx;
return ((-0x8000 <= sampleEx) && (sampleEx <= 0x7FFF)) ? (Sample)sampleEx : (sampleEx >> 31) ^ 0x7FFF; return ((-0x8000 <= sampleEx) && (sampleEx <= 0x7FFF)) ? Bit16s(sampleEx) : Bit16s((sampleEx >> 31) ^ 0x7FFF);
#endif
} }
static inline void muteSampleBuffer(Sample *buffer, Bit32u len) { static inline float clipSampleEx(float sampleEx) {
if (buffer == NULL) return; return sampleEx;
}
#if MT32EMU_USE_FLOAT_SAMPLES template <class S>
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) // FIXME: Use memset() where compatibility is guaranteed (if this turns out to be a win)
while (len--) { while (len--) {
*(buffer++) = 0.0f; *(buffer++) = 0.0f;
} }
#else
memset(buffer, 0, len * sizeof(Sample));
#endif
} }
static Bit32u getShortMessageLength(Bit32u msg); static inline Bit16s convertSample(float sample) {
static Bit8u calcSysexChecksum(const Bit8u *data, const Bit32u len, const Bit8u initChecksum = 0); 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 // Optionally sets callbacks for reporting various errors, information and debug messages
Synth(ReportHandler *useReportHandler = NULL); MT32EMU_EXPORT explicit Synth(ReportHandler *useReportHandler = NULL);
~Synth(); MT32EMU_EXPORT ~Synth();
// Used to initialise the MT-32. Must be called before any other function. // Used to initialise the MT-32. Must be called before any other function.
// Returns true if initialization was sucessful, otherwise returns false. // Returns true if initialization was sucessful, otherwise returns false.
// controlROMImage and pcmROMImage represent Control and PCM ROM images for use by synth. // 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). // 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). // 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. // 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 // 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. // 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. // 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. // The queue is flushed before reallocation.
// Returns the actual queue size being used. // Returns the actual queue size being used.
Bit32u setMIDIEventQueueSize(Bit32u); MT32EMU_EXPORT Bit32u setMIDIEventQueueSize(Bit32u);
// Enqueues a MIDI event for subsequent playback. // Enqueues a MIDI event for subsequent playback.
// The MIDI event will be processed not before the specified timestamp. // 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. // 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. // 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. // Enqueues a single short MIDI message to play at specified time. The message must contain a status byte.
bool playMsg(Bit32u msg, Bit32u timestamp); MT32EMU_EXPORT bool playMsg(Bit32u msg, Bit32u timestamp);
// Enqueues a single well formed System Exclusive MIDI message. // Enqueues a single well formed System Exclusive MIDI message to play at specified time.
bool playSysex(const Bit8u *sysex, Bit32u len, Bit32u timestamp); MT32EMU_EXPORT bool playSysex(const Bit8u *sysex, Bit32u len, Bit32u timestamp);
// Overloaded methods for the MIDI events to be processed ASAP. // Enqueues a single short MIDI message to be processed ASAP. The message must contain a status byte.
bool playMsg(Bit32u msg); MT32EMU_EXPORT bool playMsg(Bit32u msg);
bool playSysex(const Bit8u *sysex, Bit32u len); // Enqueues a single well formed System Exclusive MIDI message to be processed ASAP.
MT32EMU_EXPORT bool playSysex(const Bit8u *sysex, Bit32u len);
// WARNING: // WARNING:
// The methods below don't ensure minimum 1-sample delay between sequential MIDI events, // 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. // 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. // Sends a short MIDI message to the synth for immediate playback. The message must contain a status byte.
void playMsgNow(Bit32u msg); // See the WARNING above.
void playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity); 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 // Sends a single well formed System Exclusive MIDI message for immediate processing. The length is in bytes.
// The length is in bytes // See the WARNING above.
void playSysexNow(const Bit8u *sysex, Bit32u len); MT32EMU_EXPORT void playSysexNow(const Bit8u *sysex, Bit32u len);
void playSysexWithoutFraming(const Bit8u *sysex, Bit32u len); // Sends inner body of a System Exclusive MIDI message for direct processing. The length is in bytes.
void playSysexWithoutHeader(unsigned char device, unsigned char command, const Bit8u *sysex, Bit32u len); // See the WARNING above.
void writeSysex(unsigned char channel, const Bit8u *sysex, Bit32u len); 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); // Allows to disable wet reverb output altogether.
bool isReverbEnabled() const; 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. // 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 // 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. // of the reverb model is reset to default.
void setReverbOverridden(bool reverbOverridden); MT32EMU_EXPORT void setReverbOverridden(bool reverbOverridden);
bool isReverbOverridden() const; // 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. // 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. // 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 // 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). // (these include CM-32L and LAPC-I).
void setReverbCompatibilityMode(bool mt32CompatibleMode); MT32EMU_EXPORT void setReverbCompatibilityMode(bool mt32CompatibleMode);
bool isMT32ReverbCompatibilityMode() const; // Returns whether reverb is in old MT-32 compatibility mode.
void setDACInputMode(DACInputMode mode); MT32EMU_EXPORT bool isMT32ReverbCompatibilityMode() const;
DACInputMode getDACInputMode() const; // Returns whether default reverb compatibility mode is the old MT-32 compatibility mode.
void setMIDIDelayMode(MIDIDelayMode mode); MT32EMU_EXPORT bool isDefaultReverbMT32Compatible() const;
MIDIDelayMode getMIDIDelayMode() 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, // 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 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. // it offers to the user a capability to control the gain of reverb and non-reverb output channels independently.
// Ignored in DACInputMode_PURE // Ignored in DACInputMode_PURE
void setOutputGain(float); MT32EMU_EXPORT void setOutputGain(float gain);
float getOutputGain() const; // 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 // 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 // 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 // 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. // of that for LA32 analogue output. This factor is applied to the reverb output gain.
// Ignored in DACInputMode_PURE // Ignored in DACInputMode_PURE
void setReverbOutputGain(float); MT32EMU_EXPORT void setReverbOutputGain(float gain);
float getReverbOutputGain() const; // Returns current output gain factor for reverb wet output channels.
MT32EMU_EXPORT float getReverbOutputGain() const;
void setReversedStereoEnabled(bool enabled); // Swaps left and right output channels.
bool isReversedStereoEnabled(); 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. // Returns actual sample rate used in emulation of stereo analog circuitry of hardware units.
// See comment for render() below. // 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. // 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. // 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. // 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). // The length is in frames, not bytes (in 16-bit stereo, one frame is 4 bytes). Uses NATIVE byte ordering.
void render(Sample *stream, Bit32u len); 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. // 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. // 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. // NULL may be specified in place of any or all of the stream buffers to skip it.
// The length is in samples, not bytes. // The length is in samples, not bytes. Uses NATIVE byte ordering.
void renderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample *reverbDryLeft, Sample *reverbDryRight, Sample *reverbWetLeft, Sample *reverbWetRight, Bit32u len); MT32EMU_EXPORT void renderStreams(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u len);
void renderStreams(const DACOutputStreams<Bit16s> &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<float> &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. // 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. // Returns true if the synth is active and subsequent calls to render() may result in non-trivial output (i.e. silence).
bool isActive() const; // 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. // 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. // 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. // 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. // 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. // 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 // 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. // 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. // 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. // 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. // Returns name of the patch set on the specified part.
// Argument partNumber should be 0..7 for Part 1..8, or 8 for Rhythm. // 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

View file

@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher /* 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 * 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 * 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. * 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. * Unless we introduced bugs, it should be pretty much 100% accurate according to Mok's specifications.
*/ */
//#include <cmath>
#include "mt32emu.h"
#include "mmath.h"
#include "internals.h" #include "internals.h"
#include "TVA.h"
#include "Part.h"
#include "Partial.h"
#include "Poly.h"
#include "Synth.h"
#include "Tables.h"
namespace MT32Emu { namespace MT32Emu {
// CONFIRMED: Matches a table in ROM - haven't got around to coming up with a formula for it yet. // 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}; static Bit8u biasLevelToAmpSubtractionCoeff[13] = {255, 187, 137, 100, 74, 54, 40, 29, 21, 15, 10, 5, 0};
TVA::TVA(const Partial *usePartial, LA32Ramp *useAmpRamp) : 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) { 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 // FIXME:KG: Better variable names
int velocityMult = veloSensitivity - 50; int velocityMult = veloSensitivity - 50;
int absVelocityMult = velocityMult < 0 ? -velocityMult : velocityMult; 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 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; int amp = 155;
if (!partial->isRingModulatingSlave()) { if (!partial->isRingModulatingSlave()) {
amp -= tables->masterVolToAmpSubtraction[system_->masterVol]; amp -= tables->masterVolToAmpSubtraction[system->masterVol];
if (amp < 0) { if (amp < 0) {
return 0; return 0;
} }
@ -140,7 +144,7 @@ static int calcBasicAmp(const Tables *tables, const Partial *partial, const MemP
return amp; return amp;
} }
int calcKeyTimeSubtraction(Bit8u envTimeKeyfollow, int key) { static int calcKeyTimeSubtraction(Bit8u envTimeKeyfollow, int key) {
if (envTimeKeyfollow == 0) { if (envTimeKeyfollow == 0) {
return 0; return 0;
} }
@ -165,7 +169,7 @@ void TVA::reset(const Part *newPart, const TimbreParam::PartialParam *newPartial
biasAmpSubtraction = calcBiasAmpSubtractions(partialParam, key); biasAmpSubtraction = calcBiasAmpSubtractions(partialParam, key);
veloAmpSubtraction = calcVeloAmpSubtraction(partialParam->tva.veloSensitivity, velocity); 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; int newPhase;
if (partialParam->tva.envTime[0] == 0) { 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 // 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". // "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, // 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. // 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() { void TVA::startAbort() {
@ -217,7 +221,7 @@ void TVA::recalcSustain() {
} }
// We're sustaining. Recalculate all the values // We're sustaining. Recalculate all the values
const Tables *tables = &Tables::getInstance(); 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]; 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. // 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; 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 // Calculate an increment to get to the new amp value in a short, more or less consistent amount of time
Bit8u newIncrement; Bit8u newIncrement;
if (targetDelta >= 0) { if (targetDelta >= 0) {
newIncrement = tables->envLogarithmicTime[(Bit8u)targetDelta] - 2; newIncrement = tables->envLogarithmicTime[Bit8u(targetDelta)] - 2;
} else { } 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). // 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); startRamp(newTarget, newIncrement, TVA_PHASE_SUSTAIN - 1);
@ -279,7 +283,7 @@ void TVA::nextPhase() {
int envPointIndex = phase; int envPointIndex = phase;
if (!allLevelsZeroFromNowOn) { 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 (newPhase == TVA_PHASE_SUSTAIN || newPhase == TVA_PHASE_RELEASE) {
if (partialParam->tva.envLevel[3] == 0) { if (partialParam->tva.envLevel[3] == 0) {
@ -311,7 +315,7 @@ void TVA::nextPhase() {
int envTimeSetting = partialParam->tva.envTime[envPointIndex]; int envTimeSetting = partialParam->tva.envTime[envPointIndex];
if (newPhase == TVA_PHASE_ATTACK) { 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) { if (envTimeSetting <= 0 && partialParam->tva.envTime[envPointIndex] != 0) {
envTimeSetting = 1; envTimeSetting = 1;
@ -338,14 +342,14 @@ void TVA::nextPhase() {
} }
} }
targetDelta = -targetDelta; targetDelta = -targetDelta;
newIncrement = tables->envLogarithmicTime[(Bit8u)targetDelta] - envTimeSetting; newIncrement = tables->envLogarithmicTime[Bit8u(targetDelta)] - envTimeSetting;
if (newIncrement <= 0) { if (newIncrement <= 0) {
newIncrement = 1; newIncrement = 1;
} }
newIncrement = newIncrement | 0x80; newIncrement = newIncrement | 0x80;
} else { } 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? // 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) { if (newIncrement <= 0) {
newIncrement = 1; newIncrement = 1;
} }
@ -360,7 +364,7 @@ void TVA::nextPhase() {
} }
} }
startRamp((Bit8u)newTarget, (Bit8u)newIncrement, newPhase); startRamp(Bit8u(newTarget), Bit8u(newIncrement), newPhase);
} }
} } // namespace MT32Emu

View file

@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher /* 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 * 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 * it under the terms of the GNU Lesser General Public License as published by
@ -18,9 +18,15 @@
#ifndef MT32EMU_TVA_H #ifndef MT32EMU_TVA_H
#define MT32EMU_TVA_H #define MT32EMU_TVA_H
#include "globals.h"
#include "Types.h"
#include "Structures.h"
namespace MT32Emu { namespace MT32Emu {
class LA32Ramp;
class Part; class Part;
class Partial;
// Note that when entering nextPhase(), newPhase is set to phase + 1, and the descriptions/names below refer to // Note that when entering nextPhase(), newPhase is set to phase + 1, and the descriptions/names below refer to
// newPhase's value. // newPhase's value.
@ -57,7 +63,7 @@ class TVA {
private: private:
const Partial * const partial; const Partial * const partial;
LA32Ramp *ampRamp; LA32Ramp *ampRamp;
const MemParams::System * const system_; const MemParams::System * const system;
const Part *part; const Part *part;
const TimbreParam::PartialParam *partialParam; const TimbreParam::PartialParam *partialParam;
@ -87,8 +93,8 @@ public:
bool isPlaying() const; bool isPlaying() const;
int getPhase() const; int getPhase() const;
}; }; // class TVA
} } // namespace MT32Emu
#endif /* TVA_H_ */ #endif // #ifndef MT32EMU_TVA_H

View file

@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher /* 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 * 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 * 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 <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
//#include <cmath>
#include "mt32emu.h"
#include "mmath.h"
#include "internals.h" #include "internals.h"
#include "TVF.h"
#include "LA32Ramp.h"
#include "Partial.h"
#include "Poly.h"
#include "Tables.h"
namespace MT32Emu { namespace MT32Emu {
// Note that when entering nextPhase(), newPhase is set to phase + 1, and the descriptions/names below refer to // 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}; 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]; int baseCutoff = keyfollowMult21[partialParam->tvf.keyfollow] - keyfollowMult21[partialParam->wg.pitchKeyfollow];
// baseCutoff range now: -63 to 63 // baseCutoff range now: -63 to 63
baseCutoff *= (int)key - 60; baseCutoff *= int(key) - 60;
// baseCutoff range now: -3024 to 3024 // baseCutoff range now: -3024 to 3024
int biasPoint = partialParam->tvf.biasPoint; int biasPoint = partialParam->tvf.biasPoint;
if ((biasPoint & 0x40) == 0) { if ((biasPoint & 0x40) == 0) {
@ -75,7 +77,7 @@ static int calcBaseCutoff(const TimbreParam::PartialParam *partialParam, Bit32u
// biasPoint range here: 64 to 127 // biasPoint range here: 64 to 127
int bias = biasPoint - 31 - key; // bias range here: -75 to 84 int bias = biasPoint - 31 - key; // bias range here: -75 to 84
if (bias < 0) { 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 // baseCutoff range now: -9399 to 9399
} }
} }
@ -96,7 +98,7 @@ static int calcBaseCutoff(const TimbreParam::PartialParam *partialParam, Bit32u
if (baseCutoff > 255) { if (baseCutoff > 255) {
baseCutoff = 255; baseCutoff = 255;
} }
return (Bit8u)baseCutoff; return Bit8u(baseCutoff);
} }
TVF::TVF(const Partial *usePartial, LA32Ramp *useCutoffModifierRamp) : 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; int newLevelMult = velocity * newPartialParam->tvf.envVeloSensitivity;
newLevelMult >>= 6; newLevelMult >>= 6;
newLevelMult += 109 - newPartialParam->tvf.envVeloSensitivity; newLevelMult += 109 - newPartialParam->tvf.envVeloSensitivity;
newLevelMult += ((signed)key - 60) >> (4 - newPartialParam->tvf.envDepthKeyfollow); newLevelMult += (signed(key) - 60) >> (4 - newPartialParam->tvf.envDepthKeyfollow);
if (newLevelMult < 0) { if (newLevelMult < 0) {
newLevelMult = 0; newLevelMult = 0;
} }
@ -140,7 +142,7 @@ void TVF::reset(const TimbreParam::PartialParam *newPartialParam, unsigned int b
levelMult = newLevelMult; levelMult = newLevelMult;
if (newPartialParam->tvf.envTimeKeyfollow != 0) { if (newPartialParam->tvf.envTimeKeyfollow != 0) {
keyTimeSubtraction = ((signed)key - 60) >> (5 - newPartialParam->tvf.envTimeKeyfollow); keyTimeSubtraction = (signed(key) - 60) >> (5 - newPartialParam->tvf.envTimeKeyfollow);
} else { } else {
keyTimeSubtraction = 0; keyTimeSubtraction = 0;
} }
@ -228,4 +230,4 @@ void TVF::nextPhase() {
startRamp(newTarget, newIncrement, newPhase); startRamp(newTarget, newIncrement, newPhase);
} }
} } // namespace MT32Emu

View file

@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher /* 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 * 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 * it under the terms of the GNU Lesser General Public License as published by
@ -18,8 +18,15 @@
#ifndef MT32EMU_TVF_H #ifndef MT32EMU_TVF_H
#define MT32EMU_TVF_H #define MT32EMU_TVF_H
#include "globals.h"
#include "Types.h"
#include "Structures.h"
namespace MT32Emu { namespace MT32Emu {
class LA32Ramp;
class Partial;
class TVF { class TVF {
private: private:
const Partial * const partial; const Partial * const partial;
@ -47,8 +54,8 @@ public:
Bit8u getBaseCutoff() const; Bit8u getBaseCutoff() const;
void handleInterrupt(); void handleInterrupt();
void startDecay(); void startDecay();
}; }; // class TVF
} } // namespace MT32Emu
#endif #endif // #ifndef MT32EMU_TVF_H

View file

@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher /* 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 * 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 * 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 <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
//#include <cmath> #include <cstdlib>
//#include <cstdlib>
#include "mt32emu.h"
#include "internals.h" #include "internals.h"
#include "TVP.h"
#include "Part.h"
#include "Partial.h"
#include "Poly.h"
#include "Synth.h"
#include "TVA.h"
namespace MT32Emu { namespace MT32Emu {
// FIXME: Add Explanation // FIXME: Add Explanation
@ -47,7 +52,7 @@ static Bit16u keyToPitchTable[] = {
}; };
TVP::TVP(const Partial *usePartial) : 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. // We want to do processing 4000 times per second. FIXME: This is pretty arbitrary.
maxCounter = SAMPLE_RATE / 4000; maxCounter = SAMPLE_RATE / 4000;
// The timer runs at 500kHz. We only need to bother updating it every maxCounter samples, before we do processing. // 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) { 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)) // 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++ // 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)]; Bit16s pitch = keyToPitchTable[abs(k - 60)];
return key < 60 ? -pitch : pitch; return key < 60 ? -pitch : pitch;
} }
@ -82,7 +87,7 @@ static Bit32u calcBasePitch(const Partial *partial, const TimbreParam::PartialPa
const ControlROMPCMStruct *controlROMPCMStruct = partial->getControlROMPCMStruct(); const ControlROMPCMStruct *controlROMPCMStruct = partial->getControlROMPCMStruct();
if (controlROMPCMStruct != NULL) { if (controlROMPCMStruct != NULL) {
basePitch += (Bit32s)((((Bit32s)controlROMPCMStruct->pitchMSB) << 8) | (Bit32s)controlROMPCMStruct->pitchLSB); basePitch += (Bit32s(controlROMPCMStruct->pitchMSB) << 8) | Bit32s(controlROMPCMStruct->pitchLSB);
} else { } else {
if ((partialParam->wg.waveform & 1) == 0) { if ((partialParam->wg.waveform & 1) == 0) {
basePitch += 37133; // This puts Middle C at around 261.64Hz (assuming no other modifications, masterTune of 64, etc.) 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) { if (basePitch > 59392) {
basePitch = 59392; basePitch = 59392;
} }
return (Bit32u)basePitch; return Bit32u(basePitch);
} }
static Bit32u calcVeloMult(Bit8u veloSensitivity, unsigned int velocity) { 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) { static Bit32s calcTargetPitchOffsetWithoutLFO(const TimbreParam::PartialParam *partialParam, int levelIndex, unsigned int velocity) {
int veloMult = calcVeloMult(partialParam->pitchEnv.veloSensitivity, velocity); int veloMult = calcVeloMult(partialParam->pitchEnv.veloSensitivity, velocity);
int targetPitchOffsetWithoutLFO = partialParam->pitchEnv.level[levelIndex] - 50; 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; return targetPitchOffsetWithoutLFO;
} }
@ -140,7 +145,7 @@ void TVP::reset(const Part *usePart, const TimbreParam::PartialParam *usePartial
phase = 0; phase = 0;
if (partialParam->pitchEnv.timeKeyfollow) { 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 { } else {
timeKeyfollowSubtraction = 0; timeKeyfollowSubtraction = 0;
} }
@ -163,7 +168,7 @@ void TVP::updatePitch() {
if (!partial->isPCM() || (partial->getControlROMPCMStruct()->len & 0x01) == 0) { // FIXME: Use !partial->pcmWaveEntry->unaffectedByMasterTune instead 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 // FIXME: masterTune recalculation doesn't really happen here, and there are various bugs not yet emulated
// 171 is ~half a semitone. // 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) { if ((partialParam->wg.pitchBenderEnabled & 1) != 0) {
newPitch += part->getPitchBend(); newPitch += part->getPitchBend();
@ -172,14 +177,13 @@ void TVP::updatePitch() {
newPitch = 0; newPitch = 0;
} }
// Note: Temporary #ifdef until we have proper "quirk" configuration // Skipping this check seems about right emulation of MT-32 GEN0 quirk exploited in Colonel's Bequest timbre "Lightning"
// This is about right emulation of MT-32 GEN0 quirk exploited in Colonel's Bequest timbre "Lightning" if (partial->getSynth()->controlROMFeatures->quirkPitchEnvelopeOverflow == 0) {
#ifndef MT32EMU_QUIRK_PITCH_ENVELOPE_OVERFLOW_MT32 if (newPitch > 59392) {
if (newPitch > 59392) { 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. // 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(); partial->getTVA()->recalcSustain();
@ -317,10 +321,10 @@ void TVP::process() {
negativeBigTicksRemaining = negativeBigTicksRemaining >> rightShifts; // PORTABILITY NOTE: Assumes arithmetic shift negativeBigTicksRemaining = negativeBigTicksRemaining >> rightShifts; // PORTABILITY NOTE: Assumes arithmetic shift
rightShifts = 13; 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; newResult += targetPitchOffsetWithoutLFO + lfoPitchOffset;
currentPitchOffset = newResult; currentPitchOffset = newResult;
updatePitch(); updatePitch();
} }
} } // namespace MT32Emu

View file

@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher /* 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 * 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 * it under the terms of the GNU Lesser General Public License as published by
@ -18,12 +18,19 @@
#ifndef MT32EMU_TVP_H #ifndef MT32EMU_TVP_H
#define MT32EMU_TVP_H #define MT32EMU_TVP_H
#include "globals.h"
#include "Types.h"
#include "Structures.h"
namespace MT32Emu { namespace MT32Emu {
class Part;
class Partial;
class TVP { class TVP {
private: private:
const Partial * const partial; 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 Part *part;
const TimbreParam::PartialParam *partialParam; const TimbreParam::PartialParam *partialParam;
@ -60,8 +67,8 @@ public:
Bit32u getBasePitch() const; Bit32u getBasePitch() const;
Bit16u nextPitch(); Bit16u nextPitch();
void startDecay(); void startDecay();
}; }; // class TVP
} } // namespace MT32Emu
#endif #endif // #ifndef MT32EMU_TVP_H

View file

@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher /* 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 * 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 * 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 <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
//#include <cmath> #include "internals.h"
#include "mt32emu.h"
#include "mmath.h"
#include "Tables.h" #include "Tables.h"
#include "mmath.h"
namespace MT32Emu { namespace MT32Emu {
@ -31,24 +30,25 @@ const Tables &Tables::getInstance() {
} }
Tables::Tables() { Tables::Tables() {
int lf; for (int lf = 0; lf <= 100; lf++) {
for (lf = 0; lf <= 100; lf++) {
// CONFIRMED:KG: This matches a ROM table found by Mok // CONFIRMED:KG: This matches a ROM table found by Mok
float fVal = (2.0f - LOG10F((float)lf + 1.0f)) * 128.0f; float fVal = (2.0f - LOG10F(float(lf) + 1.0f)) * 128.0f;
int val = (int)(fVal + 1.0); int val = int(fVal + 1.0);
if (val > 255) { if (val > 255) {
val = 255; val = 255;
} }
levelToAmpSubtraction[lf] = (Bit8u)val; levelToAmpSubtraction[lf] = Bit8u(val);
} }
envLogarithmicTime[0] = 64; 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 // 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 // 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. // Note that this matches the MT-32 table, but with the values clamped to a maximum of 8.
memset(masterVolToAmpSubtraction, 8, 71); memset(masterVolToAmpSubtraction, 8, 71);
@ -64,12 +64,12 @@ Tables::Tables() {
// CONFIRMED: Based on a table found by Mok in the MT-32 control ROM // CONFIRMED: Based on a table found by Mok in the MT-32 control ROM
masterVolToAmpSubtraction[0] = 255; masterVolToAmpSubtraction[0] = 255;
for (int masterVol = 1; masterVol <= 100; masterVol++) { 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 #endif
for (int i = 0; i <= 100; i++) { 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]); //synth->printDebug("%d: %d", i, pulseWidth100To255[i]);
} }
@ -94,4 +94,4 @@ Tables::Tables() {
resAmpDecayFactor = resAmpDecayFactorTable; resAmpDecayFactor = resAmpDecayFactorTable;
} }
} } // namespace MT32Emu

View file

@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher /* 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 * 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 * it under the terms of the GNU Lesser General Public License as published by
@ -18,6 +18,9 @@
#ifndef MT32EMU_TABLES_H #ifndef MT32EMU_TABLES_H
#define MT32EMU_TABLES_H #define MT32EMU_TABLES_H
#include "globals.h"
#include "Types.h"
namespace MT32Emu { namespace MT32Emu {
class Tables { class Tables {
@ -52,8 +55,8 @@ public:
Bit16u logsin9[512]; Bit16u logsin9[512];
const Bit8u *resAmpDecayFactor; const Bit8u *resAmpDecayFactor;
}; }; // class Tables
} } // namespace MT32Emu
#endif #endif // #ifndef MT32EMU_TABLES_H

View file

@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher /* 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 * 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 * 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 unsigned char Bit8u;
typedef signed char Bit8s; typedef signed char Bit8s;
#if MT32EMU_USE_FLOAT_SAMPLES
typedef float Sample;
typedef float SampleEx;
#else
typedef Bit16s Sample;
typedef Bit32s SampleEx;
#endif
} }
#endif #endif

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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 */

View file

@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher /* 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 * 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 * it under the terms of the GNU Lesser General Public License as published by
@ -18,66 +18,111 @@
#ifndef MT32EMU_INTERNALS_H #ifndef MT32EMU_INTERNALS_H
#define MT32EMU_INTERNALS_H #define MT32EMU_INTERNALS_H
#include "Types.h"
// Debugging // Debugging
// 0: Standard debug output is not stamped with the rendered sample count // 0: Standard debug output is not stamped with the rendered sample count
// 1: Standard debug output is 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. // 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. // This is important to bear in mind for debug output that occurs during a run.
#ifndef MT32EMU_DEBUG_SAMPLESTAMPS
#define MT32EMU_DEBUG_SAMPLESTAMPS 0 #define MT32EMU_DEBUG_SAMPLESTAMPS 0
#endif
// 0: No debug output for initialisation progress // 0: No debug output for initialisation progress
// 1: Debug output for initialisation progress // 1: Debug output for initialisation progress
#ifndef MT32EMU_MONITOR_INIT
#define MT32EMU_MONITOR_INIT 0 #define MT32EMU_MONITOR_INIT 0
#endif
// 0: No debug output for MIDI events // 0: No debug output for MIDI events
// 1: Debug output for weird MIDI events // 1: Debug output for weird MIDI events
#ifndef MT32EMU_MONITOR_MIDI
#define MT32EMU_MONITOR_MIDI 0 #define MT32EMU_MONITOR_MIDI 0
#endif
// 0: No debug output for note on/off // 0: No debug output for note on/off
// 1: Basic debug output for note on/off // 1: Basic debug output for note on/off
// 2: Comprehensive debug output for note on/off // 2: Comprehensive debug output for note on/off
#ifndef MT32EMU_MONITOR_INSTRUMENTS
#define MT32EMU_MONITOR_INSTRUMENTS 0 #define MT32EMU_MONITOR_INSTRUMENTS 0
#endif
// 0: No debug output for partial allocations // 0: No debug output for partial allocations
// 1: Show partial stats when an allocation fails // 1: Show partial stats when an allocation fails
// 2: Show partial stats with every new poly // 2: Show partial stats with every new poly
// 3: Show individual partial allocations/deactivations // 3: Show individual partial allocations/deactivations
#ifndef MT32EMU_MONITOR_PARTIALS
#define MT32EMU_MONITOR_PARTIALS 0 #define MT32EMU_MONITOR_PARTIALS 0
#endif
// 0: No debug output for sysex // 0: No debug output for sysex
// 1: Basic debug output for sysex // 1: Basic debug output for sysex
#ifndef MT32EMU_MONITOR_SYSEX
#define MT32EMU_MONITOR_SYSEX 0 #define MT32EMU_MONITOR_SYSEX 0
#endif
// 0: No debug output for sysex writes to the timbre areas // 0: No debug output for sysex writes to the timbre areas
// 1: Debug output with the name and location of newly-written timbres // 1: Debug output with the name and location of newly-written timbres
// 2: Complete dump of timbre parameters for newly-written timbres // 2: Complete dump of timbre parameters for newly-written timbres
#ifndef MT32EMU_MONITOR_TIMBRES
#define MT32EMU_MONITOR_TIMBRES 0 #define MT32EMU_MONITOR_TIMBRES 0
#endif
// 0: No TVA/TVF-related debug output. // 0: No TVA/TVF-related debug output.
// 1: Shows changes to TVA/TVF target, increment and phase. // 1: Shows changes to TVA/TVF target, increment and phase.
#ifndef MT32EMU_MONITOR_TVA
#define MT32EMU_MONITOR_TVA 0 #define MT32EMU_MONITOR_TVA 0
#endif
#ifndef MT32EMU_MONITOR_TVF
#define MT32EMU_MONITOR_TVF 0 #define MT32EMU_MONITOR_TVF 0
#endif
// Configuration // 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 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. // 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 #define MT32EMU_REDUCE_REVERB_MEMORY 1
#endif
// 0: Maximum speed at the cost of a bit lower emulation accuracy. // 0: Maximum speed at the cost of a bit lower emulation accuracy.
// 1: Maximum achievable emulation accuracy. // 1: Maximum achievable emulation accuracy.
#ifndef MT32EMU_BOSS_REVERB_PRECISE_MODE
#define MT32EMU_BOSS_REVERB_PRECISE_MODE 0 #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 #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

View file

@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher /* 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 * 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 * it under the terms of the GNU Lesser General Public License as published by
@ -18,12 +18,7 @@
#ifndef MT32EMU_MMATH_H #ifndef MT32EMU_MMATH_H
#define MT32EMU_MMATH_H #define MT32EMU_MMATH_H
#define FIXEDPOINT_UDIV(x, y, point) (((x) << (point)) / ((y))) #include <cmath>
#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))
namespace MT32Emu { namespace MT32Emu {
@ -46,7 +41,7 @@ static inline float EXPF(float x) {
static inline float EXP2F(float x) { static inline float EXP2F(float x) {
#ifdef __APPLE__ #ifdef __APPLE__
// on OSX exp2f() is 1.59 times faster than "exp() and the multiplication with FLOAT_LN_2" // on OSX exp2f() is 1.59 times faster than "exp() and the multiplication with FLOAT_LN_2"
return exp2(x); return exp2f(x);
#else #else
return exp(FLOAT_LN_2 * x); return exp(FLOAT_LN_2 * x);
#endif #endif
@ -68,6 +63,6 @@ static inline float LOG10F(float x) {
return log10(x); return log10(x);
} }
} } // namespace MT32Emu
#endif #endif // #ifndef MT32EMU_MMATH_H

View file

@ -3,8 +3,11 @@ MODULE := audio/softsynth/mt32
MODULE_OBJS := \ MODULE_OBJS := \
Analog.o \ Analog.o \
BReverbModel.o \ BReverbModel.o \
File.o \
FileStream.o \
LA32Ramp.o \ LA32Ramp.o \
LA32WaveGenerator.o \ LA32WaveGenerator.o \
MidiStreamParser.o \
Part.o \ Part.o \
Partial.o \ Partial.o \
PartialManager.o \ PartialManager.o \
@ -14,7 +17,24 @@ MODULE_OBJS := \
Tables.o \ Tables.o \
TVA.o \ TVA.o \
TVF.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 common rules
include $(srcdir)/rules.mk include $(srcdir)/rules.mk

View file

@ -1,5 +1,5 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher /* 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 * 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 * it under the terms of the GNU Lesser General Public License as published by
@ -18,44 +18,67 @@
#ifndef MT32EMU_MT32EMU_H #ifndef MT32EMU_MT32EMU_H
#define 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. /* API Configuration */
// 1: Use float samples in the wave generator and renderer. Maximum output quality and minimum noise.
#define MT32EMU_USE_FLOAT_SAMPLES 0
namespace MT32Emu /* 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
// Sample rate to use in mixing. With the progress of development, we've found way too many thing dependent. * only be used within a project comprising of several modules to share the library code.
// In order to achieve further advance in emulation accuracy, sample rate made fixed throughout the emulator, * 1: Use C-compatible API. Make the library looks as a regular C library with well-defined ABI.
// except the emulation of analogue path. * This is also crucial when the library is to be linked with modules in a different
// The output from the synth is supposed to be resampled externally in order to convert to the desired sample rate. * language, either statically or dynamically.
const unsigned int SAMPLE_RATE = 32000; * 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. #ifdef MT32EMU_API_TYPE
const unsigned int DEFAULT_MAX_PARTIALS = 32; #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 - /* MT32EMU_SHARED should be defined when building shared library, especially for Windows platforms. */
// 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 #define MT32EMU_SHARED
// 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;
// The default size of the internal MIDI event queue. #include "globals.h"
// 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 #if !defined(__cplusplus) || MT32EMU_API_TYPE == 1
// uses an internal MIDI event queue to gather incoming data as well as the delays
// introduced by transferring data via the MIDI interface. #include "c_interface/c_interface.h"
// This also facilitates building of an external rendering loop
// as the queue stores timestamped MIDI events. #elif MT32EMU_API_TYPE == 2 || MT32EMU_API_TYPE == 3
const unsigned int DEFAULT_MIDI_EVENT_QUEUE_SIZE = 1024;
} #include "c_interface/cpp_interface.h"
#else /* #if !defined(__cplusplus) || MT32EMU_API_TYPE == 1 */
#include "Types.h" #include "Types.h"
#include "File.h"
#include "FileStream.h"
#include "ROMInfo.h" #include "ROMInfo.h"
#include "Synth.h" #include "Synth.h"
#include "MidiStreamParser.h"
#include "SampleRateConverter.h"
#endif #endif /* #if !defined(__cplusplus) || MT32EMU_API_TYPE == 1 */
#endif /* #ifndef MT32EMU_MT32EMU_H */

View file

@ -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<const unsigned char*>(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<unsigned int>(sarray[currentBlock + 3])
| (static_cast<unsigned int>(sarray[currentBlock + 2]) << 8)
| (static_cast<unsigned int>(sarray[currentBlock + 1]) << 16)
| (static_cast<unsigned int>(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<unsigned int>(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

View file

@ -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

View file

@ -413,6 +413,7 @@ Bits Operator::TemplateVolume( ) {
return vol; return vol;
} }
//In sustain phase, but not sustaining, do regular release //In sustain phase, but not sustaining, do regular release
//fall through
case RELEASE: case RELEASE:
vol += RateForward( releaseAdd ); vol += RateForward( releaseAdd );
if ( GCC_UNLIKELY(vol >= ENV_MAX) ) { if ( GCC_UNLIKELY(vol >= ENV_MAX) ) {

View file

@ -67,8 +67,8 @@ void BoxStorage::loadKeyAndSecret() {
#endif #endif
} }
BoxStorage::BoxStorage(Common::String accessToken, Common::String refreshToken): BoxStorage::BoxStorage(Common::String token, Common::String refreshToken):
_token(accessToken), _refreshToken(refreshToken) {} _token(token), _refreshToken(refreshToken) {}
BoxStorage::BoxStorage(Common::String code) { BoxStorage::BoxStorage(Common::String code) {
getAccessToken( getAccessToken(
@ -191,36 +191,36 @@ void BoxStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networking
return; 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; uint64 quotaUsed = 0, quotaAllocated = 0;
// can check that "type": "user" // can check that "type": "user"
// there is also "max_upload_size", "phone" and "avatar_url" // there is also "max_upload_size", "phone" and "avatar_url"
if (Networking::CurlJsonRequest::jsonContainsString(info, "id", "BoxStorage::infoInnerCallback")) if (Networking::CurlJsonRequest::jsonContainsString(jsonInfo, "id", "BoxStorage::infoInnerCallback"))
uid = info.getVal("id")->asString(); uid = jsonInfo.getVal("id")->asString();
if (Networking::CurlJsonRequest::jsonContainsString(info, "name", "BoxStorage::infoInnerCallback")) if (Networking::CurlJsonRequest::jsonContainsString(jsonInfo, "name", "BoxStorage::infoInnerCallback"))
name = info.getVal("name")->asString(); displayName = jsonInfo.getVal("name")->asString();
if (Networking::CurlJsonRequest::jsonContainsString(info, "login", "BoxStorage::infoInnerCallback")) if (Networking::CurlJsonRequest::jsonContainsString(jsonInfo, "login", "BoxStorage::infoInnerCallback"))
email = info.getVal("login")->asString(); email = jsonInfo.getVal("login")->asString();
if (Networking::CurlJsonRequest::jsonContainsIntegerNumber(info, "space_amount", "BoxStorage::infoInnerCallback")) if (Networking::CurlJsonRequest::jsonContainsIntegerNumber(jsonInfo, "space_amount", "BoxStorage::infoInnerCallback"))
quotaAllocated = info.getVal("space_amount")->asIntegerNumber(); quotaAllocated = jsonInfo.getVal("space_amount")->asIntegerNumber();
if (Networking::CurlJsonRequest::jsonContainsIntegerNumber(info, "space_used", "BoxStorage::infoInnerCallback")) if (Networking::CurlJsonRequest::jsonContainsIntegerNumber(jsonInfo, "space_used", "BoxStorage::infoInnerCallback"))
quotaUsed = info.getVal("space_used")->asIntegerNumber(); quotaUsed = jsonInfo.getVal("space_used")->asIntegerNumber();
Common::String username = email; Common::String username = email;
if (username == "") username = name; if (username == "") username = displayName;
if (username == "") username = uid; if (username == "") username = uid;
CloudMan.setStorageUsername(kStorageBoxId, username); CloudMan.setStorageUsername(kStorageBoxId, username);
if (outerCallback) { if (outerCallback) {
(*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, name, email, quotaUsed, quotaAllocated))); (*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, displayName, email, quotaUsed, quotaAllocated)));
delete outerCallback; delete outerCallback;
} }
@ -245,8 +245,8 @@ void BoxStorage::createDirectoryInnerCallback(BoolCallback outerCallback, Networ
if (outerCallback) { if (outerCallback) {
if (Networking::CurlJsonRequest::jsonIsObject(json, "BoxStorage::createDirectoryInnerCallback")) { if (Networking::CurlJsonRequest::jsonIsObject(json, "BoxStorage::createDirectoryInnerCallback")) {
Common::JSONObject info = json->asObject(); Common::JSONObject jsonInfo = json->asObject();
(*outerCallback)(BoolResponse(nullptr, info.contains("id"))); (*outerCallback)(BoolResponse(nullptr, jsonInfo.contains("id")));
} else { } else {
(*outerCallback)(BoolResponse(nullptr, false)); (*outerCallback)(BoolResponse(nullptr, false));
} }
@ -256,7 +256,7 @@ void BoxStorage::createDirectoryInnerCallback(BoolCallback outerCallback, Networ
delete json; 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) if (!errorCallback)
errorCallback = getErrorPrintingCallback(); errorCallback = getErrorPrintingCallback();
@ -270,7 +270,7 @@ Networking::Request *BoxStorage::createDirectoryWithParentId(Common::String pare
parentObject.setVal("id", new Common::JSONValue(parentId)); parentObject.setVal("id", new Common::JSONValue(parentId));
Common::JSONObject jsonRequestParameters; Common::JSONObject jsonRequestParameters;
jsonRequestParameters.setVal("name", new Common::JSONValue(name)); jsonRequestParameters.setVal("name", new Common::JSONValue(directoryName));
jsonRequestParameters.setVal("parent", new Common::JSONValue(parentObject)); jsonRequestParameters.setVal("parent", new Common::JSONValue(parentObject));
Common::JSONValue value(jsonRequestParameters); Common::JSONValue value(jsonRequestParameters);

View file

@ -74,7 +74,7 @@ public:
/** Public Cloud API comes down there. */ /** Public Cloud API comes down there. */
virtual Networking::Request *listDirectoryById(Common::String id, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback); 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. */ /** Returns UploadStatus struct with info about uploaded file. */
virtual Networking::Request *upload(Common::String remotePath, Common::String localPath, UploadCallback callback, Networking::ErrorCallback errorCallback); virtual Networking::Request *upload(Common::String remotePath, Common::String localPath, UploadCallback callback, Networking::ErrorCallback errorCallback);

View file

@ -253,19 +253,18 @@ void CloudManager::setStorageLastSync(uint32 index, Common::String date) {
void CloudManager::connectStorage(uint32 index, Common::String code) { void CloudManager::connectStorage(uint32 index, Common::String code) {
freeStorages(); freeStorages();
Storage *storage = nullptr;
switch (index) { switch (index) {
case kStorageDropboxId: case kStorageDropboxId:
storage = new Dropbox::DropboxStorage(code); new Dropbox::DropboxStorage(code);
break; break;
case kStorageOneDriveId: case kStorageOneDriveId:
storage = new OneDrive::OneDriveStorage(code); new OneDrive::OneDriveStorage(code);
break; break;
case kStorageGoogleDriveId: case kStorageGoogleDriveId:
storage = new GoogleDrive::GoogleDriveStorage(code); new GoogleDrive::GoogleDriveStorage(code);
break; break;
case kStorageBoxId: case kStorageBoxId:
storage = new Box::BoxStorage(code); new Box::BoxStorage(code);
break; break;
} }
// in these constructors Storages request token using the passed code // in these constructors Storages request token using the passed code

View file

@ -52,8 +52,8 @@ void DropboxCreateDirectoryRequest::start() {
_ignoreCallback = false; _ignoreCallback = false;
Networking::JsonCallback innerCallback = new Common::Callback<DropboxCreateDirectoryRequest, Networking::JsonResponse>(this, &DropboxCreateDirectoryRequest::responseCallback); Networking::JsonCallback innerCallback = new Common::Callback<DropboxCreateDirectoryRequest, Networking::JsonResponse>(this, &DropboxCreateDirectoryRequest::responseCallback);
Networking::ErrorCallback errorCallback = new Common::Callback<DropboxCreateDirectoryRequest, Networking::ErrorResponse>(this, &DropboxCreateDirectoryRequest::errorCallback); Networking::ErrorCallback errorResponseCallback = new Common::Callback<DropboxCreateDirectoryRequest, Networking::ErrorResponse>(this, &DropboxCreateDirectoryRequest::errorCallback);
Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, DROPBOX_API_CREATE_FOLDER); Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorResponseCallback, DROPBOX_API_CREATE_FOLDER);
request->addHeader("Authorization: Bearer " + _token); request->addHeader("Authorization: Bearer " + _token);
request->addHeader("Content-Type: application/json"); request->addHeader("Content-Type: application/json");

View file

@ -54,8 +54,8 @@ void DropboxInfoRequest::start() {
_ignoreCallback = false; _ignoreCallback = false;
Networking::JsonCallback innerCallback = new Common::Callback<DropboxInfoRequest, Networking::JsonResponse>(this, &DropboxInfoRequest::userResponseCallback); Networking::JsonCallback innerCallback = new Common::Callback<DropboxInfoRequest, Networking::JsonResponse>(this, &DropboxInfoRequest::userResponseCallback);
Networking::ErrorCallback errorCallback = new Common::Callback<DropboxInfoRequest, Networking::ErrorResponse>(this, &DropboxInfoRequest::errorCallback); Networking::ErrorCallback errorResponseCallback = new Common::Callback<DropboxInfoRequest, Networking::ErrorResponse>(this, &DropboxInfoRequest::errorCallback);
Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, DROPBOX_API_GET_CURRENT_ACCOUNT); Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorResponseCallback, DROPBOX_API_GET_CURRENT_ACCOUNT);
request->addHeader("Authorization: Bearer " + _token); request->addHeader("Authorization: Bearer " + _token);
request->addHeader("Content-Type: application/json"); request->addHeader("Content-Type: application/json");
request->addPostField("null"); //use POST request->addPostField("null"); //use POST
@ -108,8 +108,8 @@ void DropboxInfoRequest::userResponseCallback(Networking::JsonResponse response)
delete json; delete json;
Networking::JsonCallback innerCallback = new Common::Callback<DropboxInfoRequest, Networking::JsonResponse>(this, &DropboxInfoRequest::quotaResponseCallback); Networking::JsonCallback innerCallback = new Common::Callback<DropboxInfoRequest, Networking::JsonResponse>(this, &DropboxInfoRequest::quotaResponseCallback);
Networking::ErrorCallback errorCallback = new Common::Callback<DropboxInfoRequest, Networking::ErrorResponse>(this, &DropboxInfoRequest::errorCallback); Networking::ErrorCallback errorResponseCallback = new Common::Callback<DropboxInfoRequest, Networking::ErrorResponse>(this, &DropboxInfoRequest::errorCallback);
Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, DROPBOX_API_GET_SPACE_USAGE); Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorResponseCallback, DROPBOX_API_GET_SPACE_USAGE);
request->addHeader("Authorization: Bearer " + _token); request->addHeader("Authorization: Bearer " + _token);
request->addHeader("Content-Type: application/json"); request->addHeader("Content-Type: application/json");
request->addPostField("null"); //use POST request->addPostField("null"); //use POST

View file

@ -68,8 +68,8 @@ void GoogleDriveStorage::loadKeyAndSecret() {
#endif #endif
} }
GoogleDriveStorage::GoogleDriveStorage(Common::String accessToken, Common::String refreshToken): GoogleDriveStorage::GoogleDriveStorage(Common::String token, Common::String refreshToken):
_token(accessToken), _refreshToken(refreshToken) {} _token(token), _refreshToken(refreshToken) {}
GoogleDriveStorage::GoogleDriveStorage(Common::String code) { GoogleDriveStorage::GoogleDriveStorage(Common::String code) {
getAccessToken( getAccessToken(
@ -192,28 +192,28 @@ void GoogleDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Ne
return; 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; uint64 quotaUsed = 0, quotaAllocated = 0;
if (Networking::CurlJsonRequest::jsonContainsAttribute(info, "user", "GoogleDriveStorage::infoInnerCallback") && if (Networking::CurlJsonRequest::jsonContainsAttribute(jsonInfo, "user", "GoogleDriveStorage::infoInnerCallback") &&
Networking::CurlJsonRequest::jsonIsObject(info.getVal("user"), "GoogleDriveStorage::infoInnerCallback")) { Networking::CurlJsonRequest::jsonIsObject(jsonInfo.getVal("user"), "GoogleDriveStorage::infoInnerCallback")) {
//"me":true, "kind":"drive#user","photoLink": "", //"me":true, "kind":"drive#user","photoLink": "",
//"displayName":"Alexander Tkachev","emailAddress":"alexander@tkachov.ru","permissionId":"" //"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")) if (Networking::CurlJsonRequest::jsonContainsString(user, "permissionId", "GoogleDriveStorage::infoInnerCallback"))
uid = user.getVal("permissionId")->asString(); //not sure it's user's id, but who cares anyway? uid = user.getVal("permissionId")->asString(); //not sure it's user's id, but who cares anyway?
if (Networking::CurlJsonRequest::jsonContainsString(user, "displayName", "GoogleDriveStorage::infoInnerCallback")) 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")) if (Networking::CurlJsonRequest::jsonContainsString(user, "emailAddress", "GoogleDriveStorage::infoInnerCallback"))
email = user.getVal("emailAddress")->asString(); email = user.getVal("emailAddress")->asString();
} }
if (Networking::CurlJsonRequest::jsonContainsAttribute(info, "storageQuota", "GoogleDriveStorage::infoInnerCallback") && if (Networking::CurlJsonRequest::jsonContainsAttribute(jsonInfo, "storageQuota", "GoogleDriveStorage::infoInnerCallback") &&
Networking::CurlJsonRequest::jsonIsObject(info.getVal("storageQuota"), "GoogleDriveStorage::infoInnerCallback")) { Networking::CurlJsonRequest::jsonIsObject(jsonInfo.getVal("storageQuota"), "GoogleDriveStorage::infoInnerCallback")) {
//"usageInDrive":"6332462","limit":"18253611008","usage":"6332462","usageInDriveTrash":"0" //"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")) { if (Networking::CurlJsonRequest::jsonContainsString(storageQuota, "usage", "GoogleDriveStorage::infoInnerCallback")) {
Common::String usage = storageQuota.getVal("usage")->asString(); Common::String usage = storageQuota.getVal("usage")->asString();
@ -229,7 +229,7 @@ void GoogleDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Ne
CloudMan.setStorageUsername(kStorageGoogleDriveId, email); CloudMan.setStorageUsername(kStorageGoogleDriveId, email);
if (outerCallback) { if (outerCallback) {
(*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, name, email, quotaUsed, quotaAllocated))); (*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, displayName, email, quotaUsed, quotaAllocated)));
delete outerCallback; delete outerCallback;
} }
@ -246,8 +246,8 @@ void GoogleDriveStorage::createDirectoryInnerCallback(BoolCallback outerCallback
if (outerCallback) { if (outerCallback) {
if (Networking::CurlJsonRequest::jsonIsObject(json, "GoogleDriveStorage::createDirectoryInnerCallback")) { if (Networking::CurlJsonRequest::jsonIsObject(json, "GoogleDriveStorage::createDirectoryInnerCallback")) {
Common::JSONObject info = json->asObject(); Common::JSONObject jsonInfo = json->asObject();
(*outerCallback)(BoolResponse(nullptr, info.contains("id"))); (*outerCallback)(BoolResponse(nullptr, jsonInfo.contains("id")));
} else { } else {
(*outerCallback)(BoolResponse(nullptr, false)); (*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()); 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) if (!errorCallback)
errorCallback = getErrorPrintingCallback(); errorCallback = getErrorPrintingCallback();
@ -304,7 +304,7 @@ Networking::Request *GoogleDriveStorage::createDirectoryWithParentId(Common::Str
Common::JSONObject jsonRequestParameters; Common::JSONObject jsonRequestParameters;
jsonRequestParameters.setVal("mimeType", new Common::JSONValue("application/vnd.google-apps.folder")); 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)); jsonRequestParameters.setVal("parents", new Common::JSONValue(parentsArray));
Common::JSONValue value(jsonRequestParameters); Common::JSONValue value(jsonRequestParameters);

View file

@ -86,7 +86,7 @@ public:
virtual Networking::Request *streamFileById(Common::String id, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback); virtual Networking::Request *streamFileById(Common::String id, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback);
/** Calls the callback when finished. */ /** 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. */ /** Returns the StorageInfo struct. */
virtual Networking::Request *info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback); virtual Networking::Request *info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback);

View file

@ -72,8 +72,8 @@ void OneDriveCreateDirectoryRequest::start() {
url += ":/" + ConnMan.urlEncode(parent) + ":"; url += ":/" + ConnMan.urlEncode(parent) + ":";
url += "/children"; url += "/children";
Networking::JsonCallback innerCallback = new Common::Callback<OneDriveCreateDirectoryRequest, Networking::JsonResponse>(this, &OneDriveCreateDirectoryRequest::responseCallback); Networking::JsonCallback innerCallback = new Common::Callback<OneDriveCreateDirectoryRequest, Networking::JsonResponse>(this, &OneDriveCreateDirectoryRequest::responseCallback);
Networking::ErrorCallback errorCallback = new Common::Callback<OneDriveCreateDirectoryRequest, Networking::ErrorResponse>(this, &OneDriveCreateDirectoryRequest::errorCallback); Networking::ErrorCallback errorResponseCallback = new Common::Callback<OneDriveCreateDirectoryRequest, Networking::ErrorResponse>(this, &OneDriveCreateDirectoryRequest::errorCallback);
Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(_storage, innerCallback, errorCallback, url.c_str()); Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(_storage, innerCallback, errorResponseCallback, url.c_str());
request->addHeader("Authorization: Bearer " + _storage->accessToken()); request->addHeader("Authorization: Bearer " + _storage->accessToken());
request->addHeader("Content-Type: application/json"); request->addHeader("Content-Type: application/json");

View file

@ -67,8 +67,8 @@ void OneDriveStorage::loadKeyAndSecret() {
#endif #endif
} }
OneDriveStorage::OneDriveStorage(Common::String accessToken, Common::String userId, Common::String refreshToken): OneDriveStorage::OneDriveStorage(Common::String token, Common::String uid, Common::String refreshToken):
_token(accessToken), _uid(userId), _refreshToken(refreshToken) {} _token(token), _uid(uid), _refreshToken(refreshToken) {}
OneDriveStorage::OneDriveStorage(Common::String code) { OneDriveStorage::OneDriveStorage(Common::String code) {
getAccessToken( getAccessToken(
@ -193,35 +193,35 @@ void OneDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Netwo
return; 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 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")) { if (Networking::CurlJsonRequest::jsonContainsObject(jsonInfo, "createdBy", "OneDriveStorage::infoInnerCallback")) {
Common::JSONObject createdBy = info.getVal("createdBy")->asObject(); Common::JSONObject createdBy = jsonInfo.getVal("createdBy")->asObject();
if (Networking::CurlJsonRequest::jsonContainsObject(createdBy, "user", "OneDriveStorage::infoInnerCallback")) { if (Networking::CurlJsonRequest::jsonContainsObject(createdBy, "user", "OneDriveStorage::infoInnerCallback")) {
Common::JSONObject user = createdBy.getVal("user")->asObject(); Common::JSONObject user = createdBy.getVal("user")->asObject();
if (Networking::CurlJsonRequest::jsonContainsString(user, "id", "OneDriveStorage::infoInnerCallback")) if (Networking::CurlJsonRequest::jsonContainsString(user, "id", "OneDriveStorage::infoInnerCallback"))
uid = user.getVal("id")->asString(); uid = user.getVal("id")->asString();
if (Networking::CurlJsonRequest::jsonContainsString(user, "displayName", "OneDriveStorage::infoInnerCallback")) 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")) { if (Networking::CurlJsonRequest::jsonContainsIntegerNumber(jsonInfo, "size", "OneDriveStorage::infoInnerCallback")) {
quotaUsed = info.getVal("size")->asIntegerNumber(); quotaUsed = jsonInfo.getVal("size")->asIntegerNumber();
} }
Common::String username = email; Common::String username = email;
if (username == "") if (username == "")
username = name; username = displayName;
if (username == "") if (username == "")
username = uid; username = uid;
CloudMan.setStorageUsername(kStorageOneDriveId, username); CloudMan.setStorageUsername(kStorageOneDriveId, username);
if (outerCallback) { if (outerCallback) {
(*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, name, email, quotaUsed, quotaAllocated))); (*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, displayName, email, quotaUsed, quotaAllocated)));
delete outerCallback; delete outerCallback;
} }

View file

@ -56,10 +56,10 @@ StorageFile::StorageFile(Common::String pth, uint32 sz, uint32 ts, bool dir) {
_isDirectory = dir; _isDirectory = dir;
} }
StorageFile::StorageFile(Common::String id, Common::String path, Common::String name, uint32 sz, uint32 ts, bool dir) { StorageFile::StorageFile(Common::String fileId, Common::String filePath, Common::String fileName, uint32 sz, uint32 ts, bool dir) {
_id = id; _id = fileId;
_path = path; _path = filePath;
_name = name; _name = fileName;
_size = sz; _size = sz;
_timestamp = ts; _timestamp = ts;
_isDirectory = dir; _isDirectory = dir;

View file

@ -48,7 +48,7 @@ class StorageFile {
public: public:
StorageFile(); //invalid empty file StorageFile(); //invalid empty file
StorageFile(Common::String pth, uint32 sz, uint32 ts, bool dir); 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 id() const { return _id; }
Common::String path() const { return _path; } Common::String path() const { return _path; }

View file

@ -60,11 +60,11 @@ bool PS3SdlEventSource::handleJoyButtonDown(SDL_Event &ev, Common::Event &event)
switch (ev.jbutton.button) { switch (ev.jbutton.button) {
case BTN_CROSS: // Left mouse button case BTN_CROSS: // Left mouse button
event.type = Common::EVENT_LBUTTONDOWN; event.type = Common::EVENT_LBUTTONDOWN;
processMouseEvent(event, _km.x, _km.y); processMouseEvent(event, _km.x / MULTIPLIER, _km.y / MULTIPLIER);
break; break;
case BTN_CIRCLE: // Right mouse button case BTN_CIRCLE: // Right mouse button
event.type = Common::EVENT_RBUTTONDOWN; event.type = Common::EVENT_RBUTTONDOWN;
processMouseEvent(event, _km.x, _km.y); processMouseEvent(event, _km.x / MULTIPLIER, _km.y / MULTIPLIER);
break; break;
case BTN_TRIANGLE: // Game menu case BTN_TRIANGLE: // Game menu
event.type = Common::EVENT_KEYDOWN; event.type = Common::EVENT_KEYDOWN;
@ -98,11 +98,11 @@ bool PS3SdlEventSource::handleJoyButtonUp(SDL_Event &ev, Common::Event &event) {
switch (ev.jbutton.button) { switch (ev.jbutton.button) {
case BTN_CROSS: // Left mouse button case BTN_CROSS: // Left mouse button
event.type = Common::EVENT_LBUTTONUP; event.type = Common::EVENT_LBUTTONUP;
processMouseEvent(event, _km.x, _km.y); processMouseEvent(event, _km.x / MULTIPLIER, _km.y / MULTIPLIER);
break; break;
case BTN_CIRCLE: // Right mouse button case BTN_CIRCLE: // Right mouse button
event.type = Common::EVENT_RBUTTONUP; event.type = Common::EVENT_RBUTTONUP;
processMouseEvent(event, _km.x, _km.y); processMouseEvent(event, _km.x / MULTIPLIER, _km.y / MULTIPLIER);
break; break;
case BTN_TRIANGLE: // Game menu case BTN_TRIANGLE: // Game menu
event.type = Common::EVENT_KEYUP; event.type = Common::EVENT_KEYUP;

View file

@ -40,6 +40,10 @@
#endif #endif
#endif #endif
#ifdef JOY_ANALOG
#include "math.h"
#endif
// FIXME move joystick defines out and replace with confile file options // FIXME move joystick defines out and replace with confile file options
// we should really allow users to map any key to a joystick button // we should really allow users to map any key to a joystick button
#define JOY_DEADZONE 3200 #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->notifyMousePos(Common::Point(x, y));
_graphicsManager->transformMouseCoordinates(event.mouse); _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 // Skip recording of these events
uint32 curTime = g_system->getMillis(true); uint32 curTime = g_system->getMillis(true);
if (curTime >= _km.last_time + _km.delay_time) { if (curTime >= _km.last_time + _km.delay_time) {
int16 oldKmX = _km.x;
int16 oldKmY = _km.y;
_km.last_time = curTime; _km.last_time = curTime;
if (_km.x_down_count == 1) { if (_km.x_down_count == 1) {
_km.x_down_time = curTime; _km.x_down_time = curTime;
@ -209,61 +214,119 @@ void SdlEventSource::handleKbdMouse() {
if (_km.x_vel || _km.y_vel) { if (_km.x_vel || _km.y_vel) {
if (_km.x_down_count) { 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) if (_km.x_vel > 0)
_km.x_vel++; _km.x_vel += MULTIPLIER;
else else
_km.x_vel--; _km.x_vel -= MULTIPLIER;
} else if (curTime > _km.x_down_time + _km.delay_time * 8) { } else if (curTime > _km.x_down_time + 200) {
if (_km.x_vel > 0) if (_km.x_vel > 0)
_km.x_vel = 5; _km.x_vel = 5 * MULTIPLIER;
else else
_km.x_vel = -5; _km.x_vel = -5 * MULTIPLIER;
} }
} }
if (_km.y_down_count) { 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) if (_km.y_vel > 0)
_km.y_vel++; _km.y_vel += MULTIPLIER;
else else
_km.y_vel--; _km.y_vel -= MULTIPLIER;
} else if (curTime > _km.y_down_time + _km.delay_time * 8) { } else if (curTime > _km.y_down_time + 200) {
if (_km.y_vel > 0) if (_km.y_vel > 0)
_km.y_vel = 5; _km.y_vel = 5 * MULTIPLIER;
else else
_km.y_vel = -5; _km.y_vel = -5 * MULTIPLIER;
} }
} }
_km.x += _km.x_vel; int16 speedFactor = 25;
_km.y += _km.y_vel;
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) { if (_km.x < 0) {
_km.x = 0; _km.x = 0;
_km.x_vel = -1; _km.x_vel = -1 * MULTIPLIER;
_km.x_down_count = 1; _km.x_down_count = 1;
} else if (_km.x > _km.x_max) { } else if (_km.x > _km.x_max * MULTIPLIER) {
_km.x = _km.x_max; _km.x = _km.x_max * MULTIPLIER;
_km.x_vel = 1; _km.x_vel = 1 * MULTIPLIER;
_km.x_down_count = 1; _km.x_down_count = 1;
} }
if (_km.y < 0) { if (_km.y < 0) {
_km.y = 0; _km.y = 0;
_km.y_vel = -1; _km.y_vel = -1 * MULTIPLIER;
_km.y_down_count = 1; _km.y_down_count = 1;
} else if (_km.y > _km.y_max) { } else if (_km.y > _km.y_max * MULTIPLIER) {
_km.y = _km.y_max; _km.y = _km.y_max * MULTIPLIER;
_km.y_vel = 1; _km.y_vel = 1 * MULTIPLIER;
_km.y_down_count = 1; _km.y_down_count = 1;
} }
// ResidualVM: disable wrap mouse for now, it's really annoying
if (_graphicsManager) { 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) { 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) { bool SdlEventSource::pollEvent(Common::Event &event) {
handleKbdMouse();
#if SDL_VERSION_ATLEAST(2, 0, 0) #if SDL_VERSION_ATLEAST(2, 0, 0)
// In case we still need to send a key up event for a key down from a // 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)) if (dispatchSDLEvent(ev, event))
return true; return true;
} }
// Handle mouse control via analog joystick and keyboard
if (handleKbdMouse(event)) {
return true;
}
return false; return false;
} }
@ -490,16 +558,11 @@ bool SdlEventSource::dispatchSDLEvent(SDL_Event &ev, Common::Event &event) {
#if SDL_VERSION_ATLEAST(2, 0, 0) #if SDL_VERSION_ATLEAST(2, 0, 0)
case SDL_MOUSEWHEEL: { case SDL_MOUSEWHEEL: {
Sint32 yDir = ev.wheel.y; 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 // HACK: It seems we want the mouse coordinates supplied
// with a mouse wheel event. However, SDL2 does not supply // with a mouse wheel event. However, SDL2 does not supply
// these, thus we use whatever we got last time. It seems // these, thus we use whatever we got last time. It seems
// these are always stored in _km.x, _km.y. // 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) { if (yDir < 0) {
event.type = Common::EVENT_WHEELDOWN; event.type = Common::EVENT_WHEELDOWN;
return true; return true;
@ -539,7 +602,18 @@ bool SdlEventSource::dispatchSDLEvent(SDL_Event &ev, Common::Event &event) {
_graphicsManager->notifyVideoExpose(); _graphicsManager->notifyVideoExpose();
return false; 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); return handleResizeEvent(event, ev.window.data1, ev.window.data2);
default: default:
@ -680,6 +754,9 @@ bool SdlEventSource::handleKeyUp(SDL_Event &ev, Common::Event &event) {
bool SdlEventSource::handleMouseMotion(SDL_Event &ev, Common::Event &event) { bool SdlEventSource::handleMouseMotion(SDL_Event &ev, Common::Event &event) {
event.type = Common::EVENT_MOUSEMOVE; event.type = Common::EVENT_MOUSEMOVE;
processMouseEvent(event, ev.motion.x, ev.motion.y, ev.motion.xrel, ev.motion.yrel); // ResidualVM xrel,yrel 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; return true;
} }
@ -703,6 +780,9 @@ bool SdlEventSource::handleMouseButtonDown(SDL_Event &ev, Common::Event &event)
return false; return false;
processMouseEvent(event, ev.button.x, ev.button.y); processMouseEvent(event, ev.button.x, ev.button.y);
// update KbdMouse
_km.x = ev.button.x * MULTIPLIER;
_km.y = ev.button.y * MULTIPLIER;
return true; return true;
} }
@ -719,6 +799,9 @@ bool SdlEventSource::handleMouseButtonUp(SDL_Event &ev, Common::Event &event) {
else else
return false; return false;
processMouseEvent(event, ev.button.x, ev.button.y); processMouseEvent(event, ev.button.x, ev.button.y);
// update KbdMouse
_km.x = ev.button.x * MULTIPLIER;
_km.y = ev.button.y * MULTIPLIER;
return true; return true;
} }
@ -726,10 +809,10 @@ bool SdlEventSource::handleMouseButtonUp(SDL_Event &ev, Common::Event &event) {
bool SdlEventSource::handleJoyButtonDown(SDL_Event &ev, Common::Event &event) { bool SdlEventSource::handleJoyButtonDown(SDL_Event &ev, Common::Event &event) {
if (ev.jbutton.button == JOY_BUT_LMOUSE) { if (ev.jbutton.button == JOY_BUT_LMOUSE) {
event.type = Common::EVENT_LBUTTONDOWN; 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) { } else if (ev.jbutton.button == JOY_BUT_RMOUSE) {
event.type = Common::EVENT_RBUTTONDOWN; event.type = Common::EVENT_RBUTTONDOWN;
processMouseEvent(event, _km.x, _km.y); processMouseEvent(event, _km.x / MULTIPLIER, _km.y / MULTIPLIER);
} else { } else {
event.type = Common::EVENT_KEYDOWN; event.type = Common::EVENT_KEYDOWN;
switch (ev.jbutton.button) { 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) { bool SdlEventSource::handleJoyButtonUp(SDL_Event &ev, Common::Event &event) {
if (ev.jbutton.button == JOY_BUT_LMOUSE) { if (ev.jbutton.button == JOY_BUT_LMOUSE) {
event.type = Common::EVENT_LBUTTONUP; 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) { } else if (ev.jbutton.button == JOY_BUT_RMOUSE) {
event.type = Common::EVENT_RBUTTONUP; event.type = Common::EVENT_RBUTTONUP;
processMouseEvent(event, _km.x, _km.y); processMouseEvent(event, _km.x / MULTIPLIER, _km.y / MULTIPLIER);
} else { } else {
event.type = Common::EVENT_KEYUP; event.type = Common::EVENT_KEYUP;
switch (ev.jbutton.button) { 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) { bool SdlEventSource::handleJoyAxisMotion(SDL_Event &ev, Common::Event &event) {
int axis = ev.jaxis.value; 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) { if (axis > JOY_DEADZONE) {
axis -= JOY_DEADZONE; axis -= JOY_DEADZONE;
event.type = Common::EVENT_MOUSEMOVE;
} else if (axis < -JOY_DEADZONE) { } else if (axis < -JOY_DEADZONE) {
axis += JOY_DEADZONE; axis += JOY_DEADZONE;
event.type = Common::EVENT_MOUSEMOVE;
} else } else
axis = 0; axis = 0;
#endif
if (ev.jaxis.axis == JOY_XAXIS) { if (ev.jaxis.axis == JOY_XAXIS) {
#ifdef JOY_ANALOG #ifdef JOY_ANALOG
_km.x_vel = axis / 2000; _km.joy_x = axis;
_km.x_down_count = 0;
#else #else
if (axis != 0) { if (axis != 0) {
_km.x_vel = (axis > 0) ? 1:-1; _km.x_vel = (axis > 0) ? 1 * MULTIPLIER:-1 * MULTIPLIER;
_km.x_down_count = 1; _km.x_down_count = 1;
} else { } else {
_km.x_vel = 0; _km.x_vel = 0;
@ -814,11 +900,10 @@ bool SdlEventSource::handleJoyAxisMotion(SDL_Event &ev, Common::Event &event) {
axis = -axis; axis = -axis;
#endif #endif
#ifdef JOY_ANALOG #ifdef JOY_ANALOG
_km.y_vel = -axis / 2000; _km.joy_y = -axis;
_km.y_down_count = 0;
#else #else
if (axis != 0) { if (axis != 0) {
_km.y_vel = (-axis > 0) ? 1: -1; _km.y_vel = (-axis > 0) ? 1 * MULTIPLIER: -1 * MULTIPLIER;
_km.y_down_count = 1; _km.y_down_count = 1;
} else { } else {
_km.y_vel = 0; _km.y_vel = 0;
@ -826,10 +911,31 @@ bool SdlEventSource::handleJoyAxisMotion(SDL_Event &ev, Common::Event &event) {
} }
#endif #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) { 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) { void SdlEventSource::resetKeyboardEmulation(int16 x_max, int16 y_max) {
_km.x_max = x_max; _km.x_max = x_max;
_km.y_max = y_max; _km.y_max = y_max;
_km.delay_time = 25; _km.delay_time = 12;
_km.last_time = 0; _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) { bool SdlEventSource::handleResizeEvent(Common::Event &event, int w, int h) {

View file

@ -28,6 +28,8 @@
#include "common/events.h" #include "common/events.h"
// multiplier used to increase resolution for keyboard/joystick mouse
#define MULTIPLIER 16
/** /**
* The SDL event source. * The SDL event source.
@ -58,8 +60,9 @@ protected:
//@{ //@{
struct KbdMouse { 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; uint32 last_time, delay_time, x_down_time, y_down_time;
bool modifier;
}; };
KbdMouse _km; KbdMouse _km;
@ -106,7 +109,7 @@ protected:
virtual bool handleJoyButtonDown(SDL_Event &ev, Common::Event &event); virtual bool handleJoyButtonDown(SDL_Event &ev, Common::Event &event);
virtual bool handleJoyButtonUp(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 bool handleJoyAxisMotion(SDL_Event &ev, Common::Event &event);
virtual void handleKbdMouse(); virtual bool handleKbdMouse(Common::Event &event);
//@} //@}

View file

@ -195,11 +195,11 @@ public:
/** /**
* Creates a file referred by this node. * 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 * @return true if file is created successfully
*/ */
virtual bool create(bool isDirectory) = 0; virtual bool create(bool isDirectoryFlag) = 0;
}; };

View file

@ -443,7 +443,7 @@ Common::WriteStream *AmigaOSFilesystemNode::createWriteStream() {
return StdioStream::makeFromPath(getPath(), true); return StdioStream::makeFromPath(getPath(), true);
} }
bool AmigaOSFilesystemNode::create(bool isDirectory) { bool AmigaOSFilesystemNode::create(bool isDirectoryFlag) {
error("Not supported"); error("Not supported");
return false; return false;
} }

View file

@ -116,7 +116,7 @@ public:
virtual Common::SeekableReadStream *createReadStream(); virtual Common::SeekableReadStream *createReadStream();
virtual Common::WriteStream *createWriteStream(); virtual Common::WriteStream *createWriteStream();
virtual bool create(bool isDirectory); virtual bool create(bool isDirectoryFlag);
}; };

View file

@ -108,7 +108,7 @@ Common::WriteStream *ChRootFilesystemNode::createWriteStream() {
return _realNode->createWriteStream(); return _realNode->createWriteStream();
} }
bool ChRootFilesystemNode::create(bool /*isDir*/) { bool ChRootFilesystemNode::create(bool isDirectoryFlag) {
error("Not supported"); error("Not supported");
return false; return false;
} }

View file

@ -49,7 +49,7 @@ public:
virtual Common::SeekableReadStream *createReadStream(); virtual Common::SeekableReadStream *createReadStream();
virtual Common::WriteStream *createWriteStream(); virtual Common::WriteStream *createWriteStream();
virtual bool create(bool isDirectory); virtual bool create(bool isDirectoryFlag);
private: private:
static Common::String addPathComponent(const Common::String &path, const Common::String &component); static Common::String addPathComponent(const Common::String &path, const Common::String &component);

View file

@ -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. // 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. // Also with clock() in sys/time.h in some Mac OS X SDKs.
@ -36,7 +36,12 @@
#include <sys/param.h> #include <sys/param.h>
#include <sys/stat.h> #include <sys/stat.h>
#ifdef PSP2
#include "backends/fs/psp2/psp2-dirent.h"
#define mkdir sceIoMkdir
#else
#include <dirent.h> #include <dirent.h>
#endif
#include <stdio.h> #include <stdio.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
@ -253,10 +258,10 @@ Common::WriteStream *POSIXFilesystemNode::createWriteStream() {
return StdioStream::makeFromPath(getPath(), true); return StdioStream::makeFromPath(getPath(), true);
} }
bool POSIXFilesystemNode::create(bool isDir) { bool POSIXFilesystemNode::create(bool isDirectoryFlag) {
bool success; bool success;
if (isDir) { if (isDirectoryFlag) {
success = mkdir(_path.c_str(), 0755) == 0; success = mkdir(_path.c_str(), 0755) == 0;
} else { } else {
int fd = open(_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0755); int fd = open(_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0755);
@ -270,12 +275,12 @@ bool POSIXFilesystemNode::create(bool isDir) {
if (success) { if (success) {
setFlags(); setFlags();
if (_isValid) { if (_isValid) {
if (_isDirectory != isDir) warning("failed to create %s: got %s", isDir ? "directory" : "file", _isDirectory ? "directory" : "file"); if (_isDirectory != isDirectoryFlag) warning("failed to create %s: got %s", isDirectoryFlag ? "directory" : "file", _isDirectory ? "directory" : "file");
return _isDirectory == isDir; return _isDirectory == isDirectoryFlag;
} }
warning("POSIXFilesystemNode: %s() was a success, but stat indicates there is no such %s", 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; return false;
} }

View file

@ -73,7 +73,7 @@ public:
virtual Common::SeekableReadStream *createReadStream(); virtual Common::SeekableReadStream *createReadStream();
virtual Common::WriteStream *createWriteStream(); virtual Common::WriteStream *createWriteStream();
virtual bool create(bool isDirectory); virtual bool create(bool isDirectoryFlag);
private: private:
/** /**

View file

@ -244,10 +244,10 @@ Common::WriteStream *WindowsFilesystemNode::createWriteStream() {
return StdioStream::makeFromPath(getPath(), true); return StdioStream::makeFromPath(getPath(), true);
} }
bool WindowsFilesystemNode::create(bool isDirectory) { bool WindowsFilesystemNode::create(bool isDirectoryFlag) {
bool success; bool success;
if (isDirectory) { if (isDirectoryFlag) {
success = CreateDirectory(toUnicode(_path.c_str()), NULL) != 0; success = CreateDirectory(toUnicode(_path.c_str()), NULL) != 0;
} else { } else {
success = CreateFile(toUnicode(_path.c_str()), GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL) != INVALID_HANDLE_VALUE; 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 += '\\'; _path += '\\';
} }
if (_isDirectory != isDirectory) warning("failed to create %s: got %s", isDirectory ? "directory" : "file", _isDirectory ? "directory" : "file"); if (_isDirectory != isDirectoryFlag) warning("failed to create %s: got %s", isDirectoryFlag ? "directory" : "file", _isDirectory ? "directory" : "file");
return _isDirectory == isDirectory; return _isDirectory == isDirectoryFlag;
} }
warning("WindowsFilesystemNode: Create%s() was a success, but GetFileAttributes() indicates there is no such %s", 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; return false;
} }

View file

@ -88,7 +88,7 @@ public:
virtual Common::SeekableReadStream *createReadStream(); virtual Common::SeekableReadStream *createReadStream();
virtual Common::WriteStream *createWriteStream(); virtual Common::WriteStream *createWriteStream();
virtual bool create(bool isDirectory); virtual bool create(bool isDirectoryFlag);
private: private:
/** /**

View file

@ -46,6 +46,13 @@ public:
virtual bool setGraphicsMode(int mode) = 0; virtual bool setGraphicsMode(int mode) = 0;
virtual void resetGraphicsScale() = 0; virtual void resetGraphicsScale() = 0;
virtual int getGraphicsMode() const = 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 #ifdef USE_RGB_COLOR
virtual Graphics::PixelFormat getScreenFormat() const = 0; virtual Graphics::PixelFormat getScreenFormat() const = 0;
virtual Common::List<Graphics::PixelFormat> getSupportedFormats() const = 0; virtual Common::List<Graphics::PixelFormat> getSupportedFormats() const = 0;

View file

@ -52,24 +52,18 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/param.h> #include <sys/param.h>
#include <netdb.h> /* for gethostbyname */ #include <netdb.h> /* for getaddrinfo */
#include <netinet/in.h> #include <netinet/in.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdlib.h> #include <stdlib.h>
#include <errno.h> #include <errno.h>
// 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) // BeOS BONE uses snooze (x/1000) in place of usleep(x)
#ifdef __BEOS__ #ifdef __BEOS__
#define usleep(v) snooze(v/1000) #define usleep(v) snooze(v/1000)
#endif #endif
#define SEQ_MIDIPUTC 5 #define SEQ_MIDIPUTC 5
#define TIMIDITY_LOW_DELAY #define TIMIDITY_LOW_DELAY
@ -84,7 +78,7 @@
/* default host & port */ /* default host & port */
#define DEFAULT_TIMIDITY_HOST "127.0.0.1" #define DEFAULT_TIMIDITY_HOST "127.0.0.1"
#define DEFAULT_TIMIDITY_PORT 7777 #define DEFAULT_TIMIDITY_PORT "7777"
class MidiDriver_TIMIDITY : public MidiDriver_MPU401 { class MidiDriver_TIMIDITY : public MidiDriver_MPU401 {
public: public:
@ -97,11 +91,8 @@ public:
void sysEx(const byte *msg, uint16 length); void sysEx(const byte *msg, uint16 length);
private: 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()) */ /* 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 */ /* send command to the server; printf-like; returns reply string */
char *timidity_ctl_command(const char *fmt, ...) GCC_PRINTF(2, 3); char *timidity_ctl_command(const char *fmt, ...) GCC_PRINTF(2, 3);
@ -150,7 +141,8 @@ MidiDriver_TIMIDITY::MidiDriver_TIMIDITY() {
int MidiDriver_TIMIDITY::open() { int MidiDriver_TIMIDITY::open() {
char *res; char *res;
char timidity_host[NI_MAXHOST]; char timidity_host[NI_MAXHOST];
int timidity_port, data_port, i; char timidity_port[6], data_port[6];
int num;
/* count ourselves open */ /* count ourselves open */
if (_isOpen) if (_isOpen)
@ -166,16 +158,16 @@ int MidiDriver_TIMIDITY::open() {
/* extract control port */ /* extract control port */
if ((res = strrchr(timidity_host, ':')) != NULL) { if ((res = strrchr(timidity_host, ':')) != NULL) {
*res++ = '\0'; *res++ = '\0';
timidity_port = atoi(res); Common::strlcpy(timidity_port, res, sizeof(timidity_port));
} else { } else {
timidity_port = DEFAULT_TIMIDITY_PORT; Common::strlcpy(timidity_port, DEFAULT_TIMIDITY_PORT, sizeof(timidity_port));
} }
/* /*
* create control connection to the server * create control connection to the server
*/ */
if ((_control_fd = connect_to_server(timidity_host, timidity_port)) < 0) { 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; return -1;
} }
@ -183,7 +175,7 @@ int MidiDriver_TIMIDITY::open() {
* "220 TiMidity++ v2.13.2 ready)" */ * "220 TiMidity++ v2.13.2 ready)" */
res = timidity_ctl_command(NULL); res = timidity_ctl_command(NULL);
if (atoi(res) != 220) { 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(); close_all();
return -1; return -1;
} }
@ -198,13 +190,11 @@ int MidiDriver_TIMIDITY::open() {
/* should read something like "200 63017 is ready acceptable", /* should read something like "200 63017 is ready acceptable",
* where 63017 is port for data connection */ * where 63017 is port for data connection */
// FIXME: The following looks like a cheap endian test. If this is true, then #ifdef SCUMM_LITTLE_ENDIAN
// it should be replaced by suitable #ifdef SCUMM_LITTLE_ENDIAN. res = timidity_ctl_command("OPEN lsb");
i = 1; #else
if (*(char *)&i == 1) res = timidity_ctl_command("OPEN msb");
res = timidity_ctl_command("OPEN lsb"); #endif
else
res = timidity_ctl_command("OPEN msb");
if (atoi(res) != 200) { if (atoi(res) != 200) {
warning("TiMidity: bad reply for OPEN command: %s", res); warning("TiMidity: bad reply for OPEN command: %s", res);
@ -215,9 +205,15 @@ int MidiDriver_TIMIDITY::open() {
/* /*
* open data connection * 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) { 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(); close_all();
return -1; return -1;
} }
@ -226,7 +222,7 @@ int MidiDriver_TIMIDITY::open() {
* "200 Ready data connection" */ * "200 Ready data connection" */
res = timidity_ctl_command(NULL); res = timidity_ctl_command(NULL);
if (atoi(res) != 200) { 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(); close_all();
return -1; return -1;
} }
@ -277,46 +273,33 @@ void MidiDriver_TIMIDITY::teardown() {
close_all(); close_all();
} }
in_addr_t MidiDriver_TIMIDITY::host_to_addr(const char* address) { int MidiDriver_TIMIDITY::connect_to_server(const char* hostname, const char* tcp_port) {
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 fd; int fd;
struct sockaddr_in in; struct addrinfo hints;
unsigned int addr; struct addrinfo *result, *rp;
/* create socket */ /* get all address(es) matching host and port */
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { memset(&hints, 0, sizeof(struct addrinfo));
warning("TiMidity: socket(): %s", strerror(errno)); 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; return -1;
} }
/* connect */ /* Try all address structures we have got previously */
memset(&in, 0, sizeof(in)); for (rp = result; rp != NULL; rp = rp->ai_next) {
in.sin_family = AF_INET; if ((fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) == -1)
in.sin_port = htons(tcp_port); continue;
addr = host_to_addr(hostname); if (connect(fd, rp->ai_addr, rp->ai_addrlen) != -1)
memcpy(&in.sin_addr, &addr, 4); break;
if (connect(fd, (struct sockaddr *)&in, sizeof(in)) < 0) {
warning("TiMidity: connect(): %s", strerror(errno));
::close(fd); ::close(fd);
}
freeaddrinfo(result);
if (rp == NULL) {
warning("TiMidity: Could not connect\n");
return -1; return -1;
} }
@ -561,4 +544,4 @@ Common::Error TimidityMusicPlugin::createInstance(MidiDriver **mididriver, MidiD
REGISTER_PLUGIN_STATIC(TIMIDITY, PLUGIN_TYPE_MUSIC, TimidityMusicPlugin); REGISTER_PLUGIN_STATIC(TIMIDITY, PLUGIN_TYPE_MUSIC, TimidityMusicPlugin);
//#endif //#endif
#endif // defined (USE_TIMIDITY) #endif // defined(USE_TIMIDITY)

View file

@ -32,7 +32,7 @@
#if defined(GP2X) #if defined(GP2X)
#define SAMPLES_PER_SEC 11025 #define SAMPLES_PER_SEC 11025
#elif defined(PLAYSTATION3) #elif defined(PLAYSTATION3) || defined(PSP2)
#define SAMPLES_PER_SEC 48000 #define SAMPLES_PER_SEC 48000
#else #else
#define SAMPLES_PER_SEC 44100 #define SAMPLES_PER_SEC 44100

View file

@ -82,6 +82,18 @@ int ModularBackend::getGraphicsMode() const {
return _graphicsManager->getGraphicsMode(); 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() { void ModularBackend::resetGraphicsScale() {
_graphicsManager->resetGraphicsScale(); _graphicsManager->resetGraphicsScale();
} }

View file

@ -66,6 +66,9 @@ public:
virtual int getDefaultGraphicsMode() const; virtual int getDefaultGraphicsMode() const;
virtual bool setGraphicsMode(int mode); virtual bool setGraphicsMode(int mode);
virtual int getGraphicsMode() const; virtual int getGraphicsMode() const;
virtual const GraphicsMode *getSupportedShaders() const;
virtual int getShader() const;
virtual bool setShader(int id);
virtual void resetGraphicsScale(); virtual void resetGraphicsScale();
#ifdef USE_RGB_COLOR #ifdef USE_RGB_COLOR
virtual Graphics::PixelFormat getScreenFormat() const; virtual Graphics::PixelFormat getScreenFormat() const;

View file

@ -271,6 +271,15 @@ MODULE_OBJS += \
timer/psp/timer.o timer/psp/timer.o
endif 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) ifeq ($(BACKEND),samsungtv)
MODULE_OBJS += \ MODULE_OBJS += \
events/samsungtvsdl/samsungtvsdl-events.o \ events/samsungtvsdl/samsungtvsdl-events.o \

View file

@ -81,7 +81,11 @@ Request *ConnectionManager::addRequest(Request *request, RequestCallback callbac
Common::String ConnectionManager::urlEncode(Common::String s) const { Common::String ConnectionManager::urlEncode(Common::String s) const {
if (!_multi) if (!_multi)
return ""; return "";
#if LIBCURL_VERSION_NUM >= 0x070F04
char *output = curl_easy_escape(_multi, s.c_str(), s.size()); char *output = curl_easy_escape(_multi, s.c_str(), s.size());
#else
char *output = curl_escape(s.c_str(), s.size());
#endif
if (output) { if (output) {
Common::String result = output; Common::String result = output;
curl_free(output); curl_free(output);

View file

@ -67,6 +67,7 @@ void NetworkReadStream::init(const char *url, curl_slist *headersList, const byt
_sendingContentsBuffer = nullptr; _sendingContentsBuffer = nullptr;
_sendingContentsSize = _sendingContentsPos = 0; _sendingContentsSize = _sendingContentsPos = 0;
_progressDownloaded = _progressTotal = 0; _progressDownloaded = _progressTotal = 0;
_bufferCopy = nullptr;
_easy = curl_easy_init(); _easy = curl_easy_init();
curl_easy_setopt(_easy, CURLOPT_WRITEFUNCTION, curlDataCallback); curl_easy_setopt(_easy, CURLOPT_WRITEFUNCTION, curlDataCallback);
@ -100,7 +101,14 @@ void NetworkReadStream::init(const char *url, curl_slist *headersList, const byt
} else { } else {
if (post || bufferSize != 0) { if (post || bufferSize != 0) {
curl_easy_setopt(_easy, CURLOPT_POSTFIELDSIZE, bufferSize); 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); 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); ConnMan.registerEasyHandle(_easy);
@ -111,6 +119,7 @@ void NetworkReadStream::init(const char *url, curl_slist *headersList, Common::H
_sendingContentsBuffer = nullptr; _sendingContentsBuffer = nullptr;
_sendingContentsSize = _sendingContentsPos = 0; _sendingContentsSize = _sendingContentsPos = 0;
_progressDownloaded = _progressTotal = 0; _progressDownloaded = _progressTotal = 0;
_bufferCopy = nullptr;
_easy = curl_easy_init(); _easy = curl_easy_init();
curl_easy_setopt(_easy, CURLOPT_WRITEFUNCTION, curlDataCallback); curl_easy_setopt(_easy, CURLOPT_WRITEFUNCTION, curlDataCallback);
@ -184,6 +193,7 @@ NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, c
NetworkReadStream::~NetworkReadStream() { NetworkReadStream::~NetworkReadStream() {
if (_easy) if (_easy)
curl_easy_cleanup(_easy); curl_easy_cleanup(_easy);
free(_bufferCopy);
} }
bool NetworkReadStream::eos() const { bool NetworkReadStream::eos() const {
@ -228,19 +238,19 @@ Common::String NetworkReadStream::responseHeaders() const {
} }
uint32 NetworkReadStream::fillWithSendingContents(char *bufferToFill, uint32 maxSize) { uint32 NetworkReadStream::fillWithSendingContents(char *bufferToFill, uint32 maxSize) {
uint32 size = _sendingContentsSize - _sendingContentsPos; uint32 sendSize = _sendingContentsSize - _sendingContentsPos;
if (size > maxSize) if (sendSize > maxSize)
size = maxSize; sendSize = maxSize;
for (uint32 i = 0; i < size; ++i) { for (uint32 i = 0; i < sendSize; ++i) {
bufferToFill[i] = _sendingContentsBuffer[_sendingContentsPos + i]; bufferToFill[i] = _sendingContentsBuffer[_sendingContentsPos + i];
} }
_sendingContentsPos += size; _sendingContentsPos += sendSize;
return size; return sendSize;
} }
uint32 NetworkReadStream::addResponseHeaders(char *buffer, uint32 size) { uint32 NetworkReadStream::addResponseHeaders(char *buffer, uint32 bufferSize) {
_responseHeaders += Common::String(buffer, size); _responseHeaders += Common::String(buffer, bufferSize);
return size; return bufferSize;
} }
double NetworkReadStream::getProgress() const { double NetworkReadStream::getProgress() const {

View file

@ -40,6 +40,7 @@ class NetworkReadStream: public Common::MemoryReadWriteStream {
const byte *_sendingContentsBuffer; const byte *_sendingContentsBuffer;
uint32 _sendingContentsSize; uint32 _sendingContentsSize;
uint32 _sendingContentsPos; uint32 _sendingContentsPos;
byte* _bufferCopy; // To use with old curl version where CURLOPT_COPYPOSTFIELDS is not available
Common::String _responseHeaders; Common::String _responseHeaders;
uint64 _progressDownloaded, _progressTotal; uint64 _progressDownloaded, _progressTotal;
void init(const char *url, curl_slist *headersList, const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post); 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 * @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". */ /** Returns a number in range [0, 1], where 1 is "complete". */
double getProgress() const; double getProgress() const;

View file

@ -84,7 +84,7 @@ bool Client::readMoreIfNeeded() {
return false; return false;
} }
if (_stream->write(_buffer, bytes) != bytes) { if (_stream->write(_buffer, bytes) != (uint32)bytes) {
warning("Client::readMoreIfNeeded: failed to write() into MemoryReadWriteStream"); warning("Client::readMoreIfNeeded: failed to write() into MemoryReadWriteStream");
close(); close();
return false; return false;

View file

@ -145,7 +145,7 @@ void GetClientHandler::handle(Client *client) {
} }
if (readBytes != 0) 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"); warning("GetClientHandler: unable to send all bytes to the client");
client->close(); client->close();
return; return;

View file

@ -56,7 +56,7 @@ Common::String encodeHtmlEntities(Common::String s) {
result += "&gt;"; result += "&gt;";
else if (s[i] == '&') else if (s[i] == '&')
result += "&amp;"; result += "&amp;";
else if (s[i] > 0x7F) else if (s[i] > (byte)0x7F)
result += Common::String::format("&#%d;", (int)s[i]); result += Common::String::format("&#%d;", (int)s[i]);
else result += s[i]; else result += s[i];
return result; return result;

View file

@ -37,9 +37,9 @@ IndexPageHandler::~IndexPageHandler() {}
Common::String IndexPageHandler::code() const { return _code; } Common::String IndexPageHandler::code() const { return _code; }
void IndexPageHandler::handle(Client &client) { void IndexPageHandler::handle(Client &client) {
Common::String code = client.queryParameter("code"); Common::String queryCode = client.queryParameter("code");
if (code == "") { if (queryCode == "") {
// redirect to "/filesAJAX" // redirect to "/filesAJAX"
HandlerUtils::setMessageHandler( HandlerUtils::setMessageHandler(
client, client,
@ -53,7 +53,7 @@ void IndexPageHandler::handle(Client &client) {
return; return;
} }
_code = code; _code = queryCode;
sendCommand(GUI::kStorageCodePassedCmd, 0); sendCommand(GUI::kStorageCodePassedCmd, 0);
HandlerUtils::setMessageHandler(client, _("ScummVM got the code and already connects to your cloud storage!")); HandlerUtils::setMessageHandler(client, _("ScummVM got the code and already connects to your cloud storage!"));
} }

View file

@ -33,10 +33,24 @@
#include <common/config-manager.h> #include <common/config-manager.h>
#ifdef POSIX #ifdef POSIX
#include <sys/types.h> #include <errno.h>
#include <ifaddrs.h> #include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#ifndef SIOCGIFCONF
#include <sys/sockio.h>
#endif
#ifndef _SIZEOF_ADDR_IFREQ
#define _SIZEOF_ADDR_IFREQ sizeof
#endif
#define LSSDP_BUFFER_LEN 2048
#endif #endif
namespace Common { namespace Common {
@ -231,8 +245,8 @@ void LocalWebserver::handleClient(uint32 i) {
break; break;
// if no handler, answer with default BAD REQUEST // if no handler, answer with default BAD REQUEST
// fallthrough
} }
// fall through
case BAD_REQUEST: case BAD_REQUEST:
setClientGetHandler(_client[i], "<html><head><title>ScummVM - Bad Request</title></head><body>BAD REQUEST</body></html>", 400); setClientGetHandler(_client[i], "<html><head><title>ScummVM - Bad Request</title></head><body>BAD REQUEST</body></html>", 400);
@ -295,56 +309,70 @@ void LocalWebserver::resolveAddress(void *ipAddress) {
// if not - try platform-specific // if not - try platform-specific
#ifdef POSIX #ifdef POSIX
struct ifaddrs *ifAddrStruct = NULL;
void *tmpAddrPtr = 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 (ioctl(fd, SIOCGIFCONF, &ifc) < 0) {
if (!i->ifa_addr) { warning("LocalWebserver: ioctl SIOCGIFCONF failed: %s (%d)", strerror(errno), errno);
continue; } 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; // close socket
if (close(fd) != 0) {
// IPv4 warning("LocalWebserver: failed to close socket [fd %d]: %s (%d)", fd, strerror(errno), errno);
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;
} }
// 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 #endif
} }

View file

@ -59,7 +59,7 @@ class LocalWebserver : public Common::Singleton<LocalWebserver> {
SDLNet_SocketSet _set; SDLNet_SocketSet _set;
TCPsocket _serverSocket; TCPsocket _serverSocket;
Client _client[MAX_CONNECTIONS]; Client _client[MAX_CONNECTIONS];
int _clients; uint32 _clients;
bool _timerStarted, _stopOnIdle, _minimalMode; bool _timerStarted, _stopOnIdle, _minimalMode;
Common::HashMap<Common::String, BaseHandler*> _pathHandlers; Common::HashMap<Common::String, BaseHandler*> _pathHandlers;
BaseHandler *_defaultHandler; BaseHandler *_defaultHandler;

View file

@ -169,27 +169,27 @@ void Reader::handleFirstHeaders(Common::MemoryReadWriteStream *headersStream) {
_availableBytes = _contentLength; _availableBytes = _contentLength;
} }
void Reader::parseFirstLine(const Common::String &headers) { void Reader::parseFirstLine(const Common::String &headersToParse) {
uint32 headersSize = headers.size(); uint32 headersSize = headersToParse.size();
bool bad = false; bool bad = false;
if (headersSize > 0) { if (headersSize > 0) {
const char *cstr = headers.c_str(); const char *cstr = headersToParse.c_str();
const char *position = strstr(cstr, "\r\n"); const char *position = strstr(cstr, "\r\n");
if (position) { //we have at least one line - and we want the first one if (position) { //we have at least one line - and we want the first one
//"<METHOD> <path> HTTP/<VERSION>\r\n" //"<METHOD> <path> HTTP/<VERSION>\r\n"
Common::String method, path, http, buf; Common::String methodParsed, pathParsed, http, buf;
uint32 length = position - cstr; uint32 length = position - cstr;
if (headersSize > length) if (headersSize > length)
headersSize = length; headersSize = length;
for (uint32 i = 0; i < headersSize; ++i) { for (uint32 i = 0; i < headersSize; ++i) {
if (headers[i] != ' ') if (headersToParse[i] != ' ')
buf += headers[i]; buf += headersToParse[i];
if (headers[i] == ' ' || i == headersSize - 1) { if (headersToParse[i] == ' ' || i == headersSize - 1) {
if (method == "") { if (methodParsed == "") {
method = buf; methodParsed = buf;
} else if (path == "") { } else if (pathParsed == "") {
path = buf; pathParsed = buf;
} else if (http == "") { } else if (http == "") {
http = buf; http = buf;
} else { } else {
@ -201,44 +201,44 @@ void Reader::parseFirstLine(const Common::String &headers) {
} }
//check that method is supported //check that method is supported
if (method != "GET" && method != "PUT" && method != "POST") if (methodParsed != "GET" && methodParsed != "PUT" && methodParsed != "POST")
bad = true; bad = true;
//check that HTTP/<VERSION> is OK //check that HTTP/<VERSION> is OK
if (!http.hasPrefix("HTTP/")) if (!http.hasPrefix("HTTP/"))
bad = true; bad = true;
_method = method; _method = methodParsed;
parsePathQueryAndAnchor(path); parsePathQueryAndAnchor(pathParsed);
} }
} }
if (bad) _isBadRequest = true; if (bad) _isBadRequest = true;
} }
void Reader::parsePathQueryAndAnchor(Common::String path) { void Reader::parsePathQueryAndAnchor(Common::String pathToParse) {
//<path>[?query][#anchor] //<path>[?query][#anchor]
bool readingPath = true; bool readingPath = true;
bool readingQuery = false; bool readingQuery = false;
_path = ""; _path = "";
_query = ""; _query = "";
_anchor = ""; _anchor = "";
for (uint32 i = 0; i < path.size(); ++i) { for (uint32 i = 0; i < pathToParse.size(); ++i) {
if (readingPath) { if (readingPath) {
if (path[i] == '?') { if (pathToParse[i] == '?') {
readingPath = false; readingPath = false;
readingQuery = true; readingQuery = true;
} else { } else {
_path += path[i]; _path += pathToParse[i];
} }
} else if (readingQuery) { } else if (readingQuery) {
if (path[i] == '#') { if (pathToParse[i] == '#') {
readingQuery = false; readingQuery = false;
} else { } else {
_query += path[i]; _query += pathToParse[i];
} }
} else { } else {
_anchor += path[i]; _anchor += pathToParse[i];
} }
} }
@ -350,7 +350,7 @@ bool Reader::readOneByteInStream(Common::WriteStream *stream, const Common::Stri
} }
byte Reader::readOne() { byte Reader::readOne() {
byte b; byte b = 0;
_content->read(&b, 1); _content->read(&b, 1);
--_availableBytes; --_availableBytes;
--_bytesLeft; --_bytesLeft;

View file

@ -100,7 +100,7 @@ class Reader {
void handleFirstHeaders(Common::MemoryReadWriteStream *headers); void handleFirstHeaders(Common::MemoryReadWriteStream *headers);
void parseFirstLine(const Common::String &headers); void parseFirstLine(const Common::String &headers);
void parsePathQueryAndAnchor(Common::String path); void parsePathQueryAndAnchor(Common::String pathToParse);
void parseQueryParameters(); void parseQueryParameters();
void makeWindow(uint32 size); void makeWindow(uint32 size);

Some files were not shown because too many files have changed in this diff Show more