ALL: Sync with ScummVM - rev. 87ebc7140c
This commit is contained in:
parent
0a212398b3
commit
84e62b6c8d
261 changed files with 27464 additions and 13852 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -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/
|
||||||
|
|
13
.travis.yml
13
.travis.yml
|
@ -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
|
||||||
|
|
|
@ -25,8 +25,8 @@ MODULES += \
|
||||||
backends \
|
backends \
|
||||||
engines \
|
engines \
|
||||||
video \
|
video \
|
||||||
graphics \
|
|
||||||
image \
|
image \
|
||||||
|
graphics \
|
||||||
audio \
|
audio \
|
||||||
math \
|
math \
|
||||||
common \
|
common \
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 ¤tSettings == &getMT32Settings(mode);
|
return ¤tSettings == &getMT32Settings(mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BReverbModel::process(const Sample *inLeft, const Sample *inRight, Sample *outLeft, Sample *outRight, unsigned long numSamples) {
|
void BReverbModel::process(const Sample *inLeft, const Sample *inRight, Sample *outLeft, Sample *outRight, Bit32u numSamples) {
|
||||||
if (combs == NULL) {
|
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
|
||||||
|
|
|
@ -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 ¤tSettings;
|
const BReverbSettings ¤tSettings;
|
||||||
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
|
||||||
|
|
155
audio/softsynth/mt32/Enumerations.h
Normal file
155
audio/softsynth/mt32/Enumerations.h
Normal 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) */
|
77
audio/softsynth/mt32/File.cpp
Normal file
77
audio/softsynth/mt32/File.cpp
Normal 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
|
73
audio/softsynth/mt32/File.h
Normal file
73
audio/softsynth/mt32/File.h
Normal 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
|
83
audio/softsynth/mt32/FileStream.cpp
Normal file
83
audio/softsynth/mt32/FileStream.cpp
Normal 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
|
46
audio/softsynth/mt32/FileStream.h
Normal file
46
audio/softsynth/mt32/FileStream.h
Normal 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
|
|
@ -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
|
||||||
|
|
|
@ -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, it’s very similar to the phase distortion synthesis.
|
* the rising and falling cosine segments. Basically, it's very similar to the phase distortion synthesis.
|
||||||
* Behaviour of a true resonance filter is emulated by adding decaying sine wave.
|
* 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
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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, it’s very similar to the phase distortion synthesis.
|
* the rising and falling cosine segments. Basically, it's very similar to the phase distortion synthesis.
|
||||||
* Behaviour of a true resonance filter is emulated by adding decaying sine wave.
|
* 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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
289
audio/softsynth/mt32/MidiStreamParser.cpp
Normal file
289
audio/softsynth/mt32/MidiStreamParser.cpp
Normal 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;
|
||||||
|
}
|
124
audio/softsynth/mt32/MidiStreamParser.h
Normal file
124
audio/softsynth/mt32/MidiStreamParser.h
Normal 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
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
97
audio/softsynth/mt32/SampleRateConverter.cpp
Normal file
97
audio/softsynth/mt32/SampleRateConverter.cpp
Normal 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;
|
||||||
|
}
|
78
audio/softsynth/mt32/SampleRateConverter.h
Normal file
78
audio/softsynth/mt32/SampleRateConverter.h
Normal 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
|
|
@ -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
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
28
audio/softsynth/mt32/config.h
Normal file
28
audio/softsynth/mt32/config.h
Normal 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
|
119
audio/softsynth/mt32/globals.h
Normal file
119
audio/softsynth/mt32/globals.h
Normal 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 */
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
185
audio/softsynth/mt32/sha1/sha1.cpp
Executable file
185
audio/softsynth/mt32/sha1/sha1.cpp
Executable 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
|
49
audio/softsynth/mt32/sha1/sha1.h
Executable file
49
audio/softsynth/mt32/sha1/sha1.h
Executable 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
|
|
@ -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) ) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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");
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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");
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -56,7 +56,7 @@ Common::String encodeHtmlEntities(Common::String s) {
|
||||||
result += ">";
|
result += ">";
|
||||||
else if (s[i] == '&')
|
else if (s[i] == '&')
|
||||||
result += "&";
|
result += "&";
|
||||||
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;
|
||||||
|
|
|
@ -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!"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue