From 84e62b6c8d1fb49c0bffb9569f49cfbb29fbcae7 Mon Sep 17 00:00:00 2001 From: Bastien Bouclet Date: Sat, 2 Sep 2017 13:27:05 +0200 Subject: [PATCH] ALL: Sync with ScummVM - rev. 87ebc7140c1bc650e7a6ac89b102f8f2d2970f63 --- .gitignore | 1 + .travis.yml | 13 + Makefile.common | 2 +- audio/decoders/qdm2.cpp | 24 +- audio/decoders/wave.cpp | 7 + audio/decoders/wma.cpp | 25 +- audio/decoders/wma.h | 26 +- audio/softsynth/mt32.cpp | 186 +- audio/softsynth/mt32/Analog.cpp | 48 +- audio/softsynth/mt32/Analog.h | 15 +- audio/softsynth/mt32/BReverbModel.cpp | 107 +- audio/softsynth/mt32/BReverbModel.h | 42 +- audio/softsynth/mt32/Enumerations.h | 155 ++ audio/softsynth/mt32/File.cpp | 77 + audio/softsynth/mt32/File.h | 73 + audio/softsynth/mt32/FileStream.cpp | 83 + audio/softsynth/mt32/FileStream.h | 46 + .../softsynth/mt32/LA32FloatWaveGenerator.cpp | 41 +- audio/softsynth/mt32/LA32FloatWaveGenerator.h | 22 +- audio/softsynth/mt32/LA32Ramp.cpp | 10 +- audio/softsynth/mt32/LA32Ramp.h | 9 +- audio/softsynth/mt32/LA32WaveGenerator.cpp | 36 +- audio/softsynth/mt32/LA32WaveGenerator.h | 24 +- audio/softsynth/mt32/MemoryRegion.h | 18 +- audio/softsynth/mt32/MidiEventQueue.h | 10 +- audio/softsynth/mt32/MidiStreamParser.cpp | 289 +++ audio/softsynth/mt32/MidiStreamParser.h | 124 + audio/softsynth/mt32/Part.cpp | 65 +- audio/softsynth/mt32/Part.h | 16 +- audio/softsynth/mt32/Partial.cpp | 27 +- audio/softsynth/mt32/Partial.h | 26 +- audio/softsynth/mt32/PartialManager.cpp | 15 +- audio/softsynth/mt32/PartialManager.h | 15 +- audio/softsynth/mt32/Poly.cpp | 12 +- audio/softsynth/mt32/Poly.h | 19 +- audio/softsynth/mt32/ROMInfo.cpp | 90 +- audio/softsynth/mt32/ROMInfo.h | 52 +- audio/softsynth/mt32/SampleRateConverter.cpp | 97 + audio/softsynth/mt32/SampleRateConverter.h | 78 + audio/softsynth/mt32/Structures.h | 41 +- audio/softsynth/mt32/Synth.cpp | 774 ++++-- audio/softsynth/mt32/Synth.h | 394 +-- audio/softsynth/mt32/TVA.cpp | 44 +- audio/softsynth/mt32/TVA.h | 16 +- audio/softsynth/mt32/TVF.cpp | 24 +- audio/softsynth/mt32/TVF.h | 15 +- audio/softsynth/mt32/TVP.cpp | 44 +- audio/softsynth/mt32/TVP.h | 17 +- audio/softsynth/mt32/Tables.cpp | 30 +- audio/softsynth/mt32/Tables.h | 11 +- audio/softsynth/mt32/Types.h | 10 +- audio/softsynth/mt32/config.h | 28 + audio/softsynth/mt32/globals.h | 119 + audio/softsynth/mt32/internals.h | 71 +- audio/softsynth/mt32/mmath.h | 15 +- audio/softsynth/mt32/module.mk | 22 +- audio/softsynth/mt32/mt32emu.h | 87 +- audio/softsynth/mt32/sha1/sha1.cpp | 185 ++ audio/softsynth/mt32/sha1/sha1.h | 49 + audio/softsynth/opl/dbopl.cpp | 1 + backends/cloud/box/boxstorage.cpp | 40 +- backends/cloud/box/boxstorage.h | 2 +- backends/cloud/cloudmanager.cpp | 9 +- .../dropbox/dropboxcreatedirectoryrequest.cpp | 4 +- backends/cloud/dropbox/dropboxinforequest.cpp | 8 +- .../cloud/googledrive/googledrivestorage.cpp | 32 +- .../cloud/googledrive/googledrivestorage.h | 2 +- .../onedrivecreatedirectoryrequest.cpp | 4 +- backends/cloud/onedrive/onedrivestorage.cpp | 22 +- backends/cloud/storagefile.cpp | 8 +- backends/cloud/storagefile.h | 2 +- backends/events/ps3sdl/ps3sdl-events.cpp | 8 +- backends/events/sdl/sdl-events.cpp | 213 +- backends/events/sdl/sdl-events.h | 7 +- backends/fs/abstract-fs.h | 4 +- backends/fs/amigaos4/amigaos4-fs.cpp | 2 +- backends/fs/amigaos4/amigaos4-fs.h | 2 +- backends/fs/chroot/chroot-fs.cpp | 2 +- backends/fs/chroot/chroot-fs.h | 2 +- backends/fs/posix/posix-fs.cpp | 17 +- backends/fs/posix/posix-fs.h | 2 +- backends/fs/windows/windows-fs.cpp | 10 +- backends/fs/windows/windows-fs.h | 2 +- backends/graphics/graphics.h | 7 + backends/midi/timidity.cpp | 107 +- backends/mixer/sdl/sdl-mixer.cpp | 2 +- backends/modular-backend.cpp | 12 + backends/modular-backend.h | 3 + backends/module.mk | 9 + .../networking/curl/connectionmanager.cpp | 4 + .../networking/curl/networkreadstream.cpp | 28 +- backends/networking/curl/networkreadstream.h | 3 +- backends/networking/sdl_net/client.cpp | 2 +- .../networking/sdl_net/getclienthandler.cpp | 2 +- .../sdl_net/handlers/filespagehandler.cpp | 2 +- .../sdl_net/handlers/indexpagehandler.cpp | 6 +- .../networking/sdl_net/localwebserver.cpp | 122 +- backends/networking/sdl_net/localwebserver.h | 2 +- backends/networking/sdl_net/reader.cpp | 44 +- backends/networking/sdl_net/reader.h | 2 +- backends/platform/sdl/macosx/macosx.cpp | 11 +- backends/platform/sdl/macosx/macosx.h | 3 + backends/platform/sdl/macosx/macosx_wrapper.h | 1 + .../platform/sdl/macosx/macosx_wrapper.mm | 16 + backends/platform/sdl/module.mk | 7 + backends/platform/sdl/posix/posix-main.cpp | 2 +- backends/platform/sdl/sdl.cpp | 25 +- backends/platform/sdl/sdl.h | 3 + backends/platform/sdl/win32/win32.cpp | 33 + backends/platform/sdl/win32/win32.h | 2 + common/EventDispatcher.cpp | 9 + common/EventMapper.cpp | 1 - common/bitstream.h | 345 ++- common/debug-channels.h | 2 +- common/debug.cpp | 8 +- common/debug.h | 1 + common/endian.h | 32 + common/error.cpp | 2 +- common/events.h | 6 + common/forbidden.h | 4 +- common/gui_options.cpp | 8 + common/gui_options.h | 6 + common/huffman.cpp | 15 - common/huffman.h | 18 +- common/json.cpp | 8 +- common/language.cpp | 1 + common/language.h | 1 + common/macresman.h | 6 +- common/memstream.h | 16 +- common/scummsys.h | 33 + common/serializer.h | 4 + common/str.cpp | 7 + common/str.h | 11 + common/stream.cpp | 21 + common/stream.h | 50 + common/system.h | 67 +- common/zlib.h | 2 + configure | 123 +- devtools/create_project/cmake.cpp | 8 +- devtools/create_project/create_project.cpp | 47 +- devtools/create_project/create_project.h | 5 + devtools/create_project/msbuild.cpp | 14 +- .../msvc10/create_project.vcxproj | 2 +- .../msvc11/create_project.vcxproj | 2 +- .../msvc12/create_project.vcxproj | 2 +- .../msvc9/create_project.vcproj | 2 +- devtools/create_project/visualstudio.cpp | 4 +- devtools/create_project/xcode.cpp | 5 + devtools/create_translations/cp_parser.cpp | 8 +- devtools/encode-macbinary.sh | 18 +- engines/advancedDetector.cpp | 21 +- engines/advancedDetector.h | 13 +- engines/dialogs.cpp | 6 +- engines/engine.cpp | 2 +- engines/grim/movie/codecs/smush_decoder.cpp | 7 +- engines/grim/movie/codecs/smush_decoder.h | 2 +- engines/grim/stuffit.cpp | 5 +- engines/grim/stuffit.h | 7 +- engines/logo_data.h | 2 +- engines/savestate.cpp | 2 +- graphics/VectorRendererSpec.cpp | 28 +- graphics/font.cpp | 12 +- graphics/font.h | 11 +- graphics/managed_surface.cpp | 115 +- graphics/pixelformat.cpp | 2 +- graphics/transform_struct.h | 1 + graphics/transparent_surface.cpp | 553 +++-- graphics/transparent_surface.h | 13 + gui/KeysDialog.cpp | 18 +- gui/ThemeEngine.cpp | 86 +- gui/ThemeEngine.h | 2 +- gui/browser.h | 1 - gui/browser_osx.mm | 126 +- gui/console.cpp | 9 + gui/dialog.cpp | 24 +- gui/dialog.h | 2 + gui/editgamedialog.cpp | 2 +- gui/gui-manager.cpp | 35 +- gui/gui-manager.h | 16 + gui/launcher.cpp | 9 +- gui/options.cpp | 462 +++- gui/options.h | 30 + gui/saveload-dialog.cpp | 16 +- gui/saveload.cpp | 2 +- gui/themes/default.inc | 118 +- gui/themes/modern.zip | Bin 1645428 -> 1648226 bytes gui/themes/modern/modern_layout.stx | 53 +- gui/themes/modern/modern_layout_lowres.stx | 53 +- gui/widget.cpp | 15 + gui/widget.h | 4 + gui/widgets/list.cpp | 13 + gui/widgets/list.h | 1 + gui/widgets/popup.cpp | 4 + gui/widgets/scrollcontainer.cpp | 6 + gui/widgets/scrollcontainer.h | 2 + gui/widgets/tab.cpp | 206 +- gui/widgets/tab.h | 15 +- image/codecs/codec.cpp | 6 +- image/codecs/indeo/get_bits.cpp | 60 - image/codecs/indeo/get_bits.h | 37 +- image/codecs/indeo/indeo.cpp | 69 +- image/codecs/indeo/indeo.h | 11 +- image/codecs/indeo/mem.cpp | 14 - image/codecs/indeo/mem.h | 14 +- image/codecs/indeo3.cpp | 17 +- image/codecs/indeo3.h | 2 +- image/codecs/indeo4.cpp | 261 +- image/codecs/indeo4.h | 12 +- image/codecs/indeo5.cpp | 19 +- image/codecs/indeo5.h | 2 +- image/codecs/rpza.cpp | 1 + image/codecs/svq1.cpp | 12 +- image/codecs/svq1.h | 14 +- image/module.mk | 1 - image/png.cpp | 112 +- image/png.h | 13 + image/tga.cpp | 17 + po/be_BY.po | 1362 +++++++---- po/ca_ES.po | 1406 +++++++---- po/cs_CZ.po | 1382 +++++++---- po/da_DK.po | 1305 ++++++---- po/de_DE.po | 1548 +++++++----- po/es_ES.po | 1491 +++++++----- po/eu.po | 1348 +++++++---- po/fi_FI.po | 2122 ++++++++++------- po/fr_FR.po | 1556 +++++++----- po/gl_ES.po | 1340 +++++++---- po/hu_HU.po | 1385 +++++++---- po/it_IT.po | 1878 +++++++++------ po/nb_NO.po | 1340 +++++++---- po/nl_NL.po | 1456 +++++++---- po/nn_NO.po | 1303 ++++++---- po/pl_PL.po | 1471 ++++++++---- po/pt_BR.po | 1328 +++++++---- po/ru_RU.po | 1313 ++++++---- po/scummvm.pot | 1124 +++++---- po/sv_SE.po | 1402 +++++++---- po/uk_UA.po | 1600 ++++++++----- po/zh-Latn_CN.po | 1371 +++++++---- ports.mk | 4 +- rules.mk | 7 +- test/common/bitstream.h | 118 +- test/common/str.h | 23 + test/module.mk | 7 +- video/avi_decoder.cpp | 170 +- video/avi_decoder.h | 43 +- video/bink_decoder.cpp | 6 +- video/bink_decoder.h | 8 +- video/coktel_decoder.cpp | 245 +- video/coktel_decoder.h | 20 +- video/mpegps_decoder.cpp | 10 +- video/mpegps_decoder.h | 4 +- video/psx_decoder.cpp | 22 +- video/psx_decoder.h | 17 +- video/qt_decoder.cpp | 6 +- video/smk_decoder.cpp | 38 +- video/smk_decoder.h | 13 +- video/theora_decoder.cpp | 7 +- video/theora_decoder.h | 7 +- video/video_decoder.cpp | 42 +- video/video_decoder.h | 32 +- 261 files changed, 27464 insertions(+), 13852 deletions(-) create mode 100644 audio/softsynth/mt32/Enumerations.h create mode 100644 audio/softsynth/mt32/File.cpp create mode 100644 audio/softsynth/mt32/File.h create mode 100644 audio/softsynth/mt32/FileStream.cpp create mode 100644 audio/softsynth/mt32/FileStream.h create mode 100644 audio/softsynth/mt32/MidiStreamParser.cpp create mode 100644 audio/softsynth/mt32/MidiStreamParser.h create mode 100644 audio/softsynth/mt32/SampleRateConverter.cpp create mode 100644 audio/softsynth/mt32/SampleRateConverter.h create mode 100644 audio/softsynth/mt32/config.h create mode 100644 audio/softsynth/mt32/globals.h create mode 100755 audio/softsynth/mt32/sha1/sha1.cpp create mode 100755 audio/softsynth/mt32/sha1/sha1.h delete mode 100644 image/codecs/indeo/get_bits.cpp diff --git a/.gitignore b/.gitignore index e95faf9a840..3f33da09518 100644 --- a/.gitignore +++ b/.gitignore @@ -122,6 +122,7 @@ ipch/ *.bat *.tss *.VC.db +.vs/ #Ignore default Visual Studio build folders [Dd]ebug/ diff --git a/.travis.yml b/.travis.yml index f7ce86ec7d1..7c10aebd883 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,6 +24,11 @@ addons: - libfreetype6-dev - zlib1g-dev +before_install: + - "if [ ${TRAVIS_OS_NAME:-'linux'} = 'osx' ]; then brew update; fi" + - "if [ ${TRAVIS_OS_NAME:-'linux'} = 'osx' ]; then brew install sdl2 sdl2_net libvorbis flac mad theora faad2 libmpeg2 glew; fi" + + branches: only: - master @@ -34,6 +39,7 @@ compiler: os: - linux + - osx dist: trusty @@ -42,3 +48,10 @@ script: - make -j 2 - make test - make devtools + +notifications: + irc: + channels: + - "chat.freenode.net#residualvm" + on_success: change + on_failure: always diff --git a/Makefile.common b/Makefile.common index 36cb138e4ca..3c46010bee6 100644 --- a/Makefile.common +++ b/Makefile.common @@ -25,8 +25,8 @@ MODULES += \ backends \ engines \ video \ - graphics \ image \ + graphics \ audio \ math \ common \ diff --git a/audio/decoders/qdm2.cpp b/audio/decoders/qdm2.cpp index a2f43261529..b8d007da51d 100644 --- a/audio/decoders/qdm2.cpp +++ b/audio/decoders/qdm2.cpp @@ -213,9 +213,9 @@ private: void fill_coding_method_array(sb_int8_array tone_level_idx, sb_int8_array tone_level_idx_temp, sb_int8_array coding_method, int nb_channels, int c, int superblocktype_2_3, int cm_table_select); - void synthfilt_build_sb_samples(Common::BitStream *gb, int length, int sb_min, int sb_max); - void init_quantized_coeffs_elem0(int8 *quantized_coeffs, Common::BitStream *gb, int length); - void init_tone_level_dequantization(Common::BitStream *gb, int length); + void synthfilt_build_sb_samples(Common::BitStream32LELSB *gb, int length, int sb_min, int sb_max); + void init_quantized_coeffs_elem0(int8 *quantized_coeffs, Common::BitStream32LELSB *gb, int length); + void init_tone_level_dequantization(Common::BitStream32LELSB *gb, int length); void process_subpacket_9(QDM2SubPNode *node); void process_subpacket_10(QDM2SubPNode *node, int length); void process_subpacket_11(QDM2SubPNode *node, int length); @@ -224,7 +224,7 @@ private: void qdm2_decode_super_block(void); void qdm2_fft_init_coefficient(int sub_packet, int offset, int duration, int channel, int exp, int phase); - void qdm2_fft_decode_tones(int duration, Common::BitStream *gb, int b); + void qdm2_fft_decode_tones(int duration, Common::BitStream32LELSB *gb, int b); void qdm2_decode_fft_packets(void); void qdm2_fft_generate_tone(FFTTone *tone); void qdm2_fft_tone_synthesizer(uint8 sub_packet); @@ -677,7 +677,7 @@ void ff_mpa_synth_filter(int16 *synth_buf_ptr, int *synth_buf_offset, * read the longest vlc code * = (max_vlc_length + bits - 1) / bits */ -static int getVlc2(Common::BitStream *s, int16 (*table)[2], int bits, int maxDepth) { +static int getVlc2(Common::BitStream32LELSB *s, int16 (*table)[2], int bits, int maxDepth) { int index = s->peekBits(bits); int code = table[index][0]; int n = table[index][1]; @@ -1227,7 +1227,7 @@ QDM2Stream::~QDM2Stream() { delete[] _compressedData; } -static int qdm2_get_vlc(Common::BitStream *gb, VLC *vlc, int flag, int depth) { +static int qdm2_get_vlc(Common::BitStream32LELSB *gb, VLC *vlc, int flag, int depth) { int value = getVlc2(gb, vlc->table, vlc->bits, depth); // stage-2, 3 bits exponent escape sequence @@ -1246,7 +1246,7 @@ static int qdm2_get_vlc(Common::BitStream *gb, VLC *vlc, int flag, int depth) { return value; } -static int qdm2_get_se_vlc(VLC *vlc, Common::BitStream *gb, int depth) +static int qdm2_get_se_vlc(VLC *vlc, Common::BitStream32LELSB *gb, int depth) { int value = qdm2_get_vlc(gb, vlc, 0, depth); @@ -1612,7 +1612,7 @@ void QDM2Stream::fill_coding_method_array(sb_int8_array tone_level_idx, sb_int8_ * @param sb_min lower subband processed (sb_min included) * @param sb_max higher subband processed (sb_max excluded) */ -void QDM2Stream::synthfilt_build_sb_samples(Common::BitStream *gb, int length, int sb_min, int sb_max) { +void QDM2Stream::synthfilt_build_sb_samples(Common::BitStream32LELSB *gb, int length, int sb_min, int sb_max) { int sb, j, k, n, ch, run, channels; int joined_stereo, zero_encoding, chs; int type34_first; @@ -1792,7 +1792,7 @@ void QDM2Stream::synthfilt_build_sb_samples(Common::BitStream *gb, int length, i * @param gb bitreader context * @param length packet length in bits */ -void QDM2Stream::init_quantized_coeffs_elem0(int8 *quantized_coeffs, Common::BitStream *gb, int length) { +void QDM2Stream::init_quantized_coeffs_elem0(int8 *quantized_coeffs, Common::BitStream32LELSB *gb, int length) { int i, k, run, level, diff; if ((length - gb->pos()) < 16) @@ -1826,7 +1826,7 @@ void QDM2Stream::init_quantized_coeffs_elem0(int8 *quantized_coeffs, Common::Bit * @param gb bitreader context * @param length packet length in bits */ -void QDM2Stream::init_tone_level_dequantization(Common::BitStream *gb, int length) { +void QDM2Stream::init_tone_level_dequantization(Common::BitStream32LELSB *gb, int length) { int sb, j, k, n, ch; for (ch = 0; ch < _channels; ch++) { @@ -2021,7 +2021,7 @@ void QDM2Stream::qdm2_decode_super_block(void) { average_quantized_coeffs(); // average elements in quantized_coeffs[max_ch][10][8] Common::MemoryReadStream *d = new Common::MemoryReadStream(_compressedData, _packetSize*8); - Common::BitStream *gb = new Common::BitStream32LELSB(d); + Common::BitStream32LELSB *gb = new Common::BitStream32LELSB(d); //qdm2_decode_sub_packet_header header.type = gb->getBits(8); @@ -2188,7 +2188,7 @@ void QDM2Stream::qdm2_fft_init_coefficient(int sub_packet, int offset, int durat _fftCoefsIndex++; } -void QDM2Stream::qdm2_fft_decode_tones(int duration, Common::BitStream *gb, int b) { +void QDM2Stream::qdm2_fft_decode_tones(int duration, Common::BitStream32LELSB *gb, int b) { int channel, stereo, phase, exp; int local_int_4, local_int_8, stereo_phase, local_int_10; int local_int_14, stereo_exp, local_int_20, local_int_28; diff --git a/audio/decoders/wave.cpp b/audio/decoders/wave.cpp index 803bdf3cf02..55c7034df69 100644 --- a/audio/decoders/wave.cpp +++ b/audio/decoders/wave.cpp @@ -61,6 +61,13 @@ bool loadWAVFromStream(Common::SeekableReadStream &stream, int &size, int &rate, } stream.read(buf, 4); + if (memcmp(buf, "fact", 4) == 0) { + // Initial fact chunk, so skip over it + uint32 factLen = stream.readUint32LE(); + stream.skip(factLen); + stream.read(buf, 4); + } + if (memcmp(buf, "fmt ", 4) != 0) { warning("getWavInfo: No 'fmt' header"); return false; diff --git a/audio/decoders/wma.cpp b/audio/decoders/wma.cpp index ee8e35fbf23..5b16ecb5d4e 100644 --- a/audio/decoders/wma.cpp +++ b/audio/decoders/wma.cpp @@ -29,7 +29,6 @@ #include "common/error.h" #include "common/memstream.h" #include "common/mdct.h" -#include "common/bitstream.h" #include "common/huffman.h" #include "audio/audiostream.h" @@ -684,7 +683,7 @@ Common::SeekableReadStream *WMACodec::decodeSuperFrame(Common::SeekableReadStrea return new Common::MemoryReadStream((byte *) outputData, outputDataSize * 2, DisposeAfterUse::YES); } -bool WMACodec::decodeFrame(Common::BitStream &bits, int16 *outputData) { +bool WMACodec::decodeFrame(Common::BitStream8MSB &bits, int16 *outputData) { _framePos = 0; _curBlock = 0; @@ -714,7 +713,7 @@ bool WMACodec::decodeFrame(Common::BitStream &bits, int16 *outputData) { return true; } -int WMACodec::decodeBlock(Common::BitStream &bits) { +int WMACodec::decodeBlock(Common::BitStream8MSB &bits) { // Computer new block length if (!evalBlockLength(bits)) return -1; @@ -767,7 +766,7 @@ int WMACodec::decodeBlock(Common::BitStream &bits) { return 0; } -bool WMACodec::decodeChannels(Common::BitStream &bits, int bSize, +bool WMACodec::decodeChannels(Common::BitStream8MSB &bits, int bSize, bool msStereo, bool *hasChannel) { int totalGain = readTotalGain(bits); @@ -823,7 +822,7 @@ bool WMACodec::calculateIMDCT(int bSize, bool msStereo, bool *hasChannel) { return true; } -bool WMACodec::evalBlockLength(Common::BitStream &bits) { +bool WMACodec::evalBlockLength(Common::BitStream8MSB &bits) { if (_useVariableBlockLen) { // Variable block lengths @@ -899,7 +898,7 @@ void WMACodec::calculateCoefCount(int *coefCount, int bSize) const { coefCount[i] = coefN; } -bool WMACodec::decodeNoise(Common::BitStream &bits, int bSize, +bool WMACodec::decodeNoise(Common::BitStream8MSB &bits, int bSize, bool *hasChannel, int *coefCount) { if (!_useNoiseCoding) return true; @@ -950,7 +949,7 @@ bool WMACodec::decodeNoise(Common::BitStream &bits, int bSize, return true; } -bool WMACodec::decodeExponents(Common::BitStream &bits, int bSize, bool *hasChannel) { +bool WMACodec::decodeExponents(Common::BitStream8MSB &bits, int bSize, bool *hasChannel) { // Exponents can be reused in short blocks if (!((_blockLenBits == _frameLenBits) || bits.getBit())) return true; @@ -973,7 +972,7 @@ bool WMACodec::decodeExponents(Common::BitStream &bits, int bSize, bool *hasChan return true; } -bool WMACodec::decodeSpectralCoef(Common::BitStream &bits, bool msStereo, bool *hasChannel, +bool WMACodec::decodeSpectralCoef(Common::BitStream8MSB &bits, bool msStereo, bool *hasChannel, int *coefCount, int coefBitCount) { // Simple RLE encoding @@ -1213,7 +1212,7 @@ static const float powTab[] = { 7.4989420933246e+05, 8.6596432336007e+05, }; -bool WMACodec::decodeExpHuffman(Common::BitStream &bits, int ch) { +bool WMACodec::decodeExpHuffman(Common::BitStream8MSB &bits, int ch) { const float *ptab = powTab + 60; const uint32 *iptab = (const uint32 *) ptab; @@ -1311,7 +1310,7 @@ void WMACodec::lspToCurve(float *out, float *val_max_ptr, int n, float *lsp) { } // Decode exponents coded with LSP coefficients (same idea as Vorbis) -bool WMACodec::decodeExpLSP(Common::BitStream &bits, int ch) { +bool WMACodec::decodeExpLSP(Common::BitStream8MSB &bits, int ch) { float lspCoefs[kLSPCoefCount]; for (int i = 0; i < kLSPCoefCount; i++) { @@ -1329,7 +1328,7 @@ bool WMACodec::decodeExpLSP(Common::BitStream &bits, int ch) { return true; } -bool WMACodec::decodeRunLevel(Common::BitStream &bits, const Common::Huffman &huffman, +bool WMACodec::decodeRunLevel(Common::BitStream8MSB &bits, const Common::Huffman &huffman, const float *levelTable, const uint16 *runTable, int version, float *ptr, int offset, int numCoefs, int blockLen, int frameLenBits, int coefNbBits) { @@ -1471,7 +1470,7 @@ float WMACodec::pow_m1_4(float x) const { return _lspPowETable[e] * (a + b * t.f); } -int WMACodec::readTotalGain(Common::BitStream &bits) { +int WMACodec::readTotalGain(Common::BitStream8MSB &bits) { int totalGain = 1; int v = 127; @@ -1492,7 +1491,7 @@ int WMACodec::totalGainToBits(int totalGain) { else return 9; } -uint32 WMACodec::getLargeVal(Common::BitStream &bits) { +uint32 WMACodec::getLargeVal(Common::BitStream8MSB &bits) { // Consumes up to 34 bits int count = 8; diff --git a/audio/decoders/wma.h b/audio/decoders/wma.h index 8ea82538c67..286903c4828 100644 --- a/audio/decoders/wma.h +++ b/audio/decoders/wma.h @@ -27,11 +27,11 @@ #define AUDIO_DECODERS_WMA_H #include "common/array.h" +#include "common/bitstream.h" #include "audio/decoders/codec.h" namespace Common { - class BitStream; class Huffman; class MDCT; } @@ -181,27 +181,27 @@ private: // Decoding Common::SeekableReadStream *decodeSuperFrame(Common::SeekableReadStream &data); - bool decodeFrame(Common::BitStream &bits, int16 *outputData); - int decodeBlock(Common::BitStream &bits); + bool decodeFrame(Common::BitStream8MSB &bits, int16 *outputData); + int decodeBlock(Common::BitStream8MSB &bits); // Decoding helpers - bool evalBlockLength(Common::BitStream &bits); - bool decodeChannels(Common::BitStream &bits, int bSize, bool msStereo, bool *hasChannel); + bool evalBlockLength(Common::BitStream8MSB &bits); + bool decodeChannels(Common::BitStream8MSB &bits, int bSize, bool msStereo, bool *hasChannel); bool calculateIMDCT(int bSize, bool msStereo, bool *hasChannel); void calculateCoefCount(int *coefCount, int bSize) const; - bool decodeNoise(Common::BitStream &bits, int bSize, bool *hasChannel, int *coefCount); - bool decodeExponents(Common::BitStream &bits, int bSize, bool *hasChannel); - bool decodeSpectralCoef(Common::BitStream &bits, bool msStereo, bool *hasChannel, + bool decodeNoise(Common::BitStream8MSB &bits, int bSize, bool *hasChannel, int *coefCount); + bool decodeExponents(Common::BitStream8MSB &bits, int bSize, bool *hasChannel); + bool decodeSpectralCoef(Common::BitStream8MSB &bits, bool msStereo, bool *hasChannel, int *coefCount, int coefBitCount); float getNormalizedMDCTLength() const; void calculateMDCTCoefficients(int bSize, bool *hasChannel, int *coefCount, int totalGain, float mdctNorm); - bool decodeExpHuffman(Common::BitStream &bits, int ch); - bool decodeExpLSP(Common::BitStream &bits, int ch); - bool decodeRunLevel(Common::BitStream &bits, const Common::Huffman &huffman, + bool decodeExpHuffman(Common::BitStream8MSB &bits, int ch); + bool decodeExpLSP(Common::BitStream8MSB &bits, int ch); + bool decodeRunLevel(Common::BitStream8MSB &bits, const Common::Huffman &huffman, const float *levelTable, const uint16 *runTable, int version, float *ptr, int offset, int numCoefs, int blockLen, int frameLenBits, int coefNbBits); @@ -211,9 +211,9 @@ private: float pow_m1_4(float x) const; - static int readTotalGain(Common::BitStream &bits); + static int readTotalGain(Common::BitStream8MSB &bits); static int totalGainToBits(int totalGain); - static uint32 getLargeVal(Common::BitStream &bits); + static uint32 getLargeVal(Common::BitStream8MSB &bits); }; } // End of namespace Audio diff --git a/audio/softsynth/mt32.cpp b/audio/softsynth/mt32.cpp index 9471fcb6a78..1b2e6929a31 100644 --- a/audio/softsynth/mt32.cpp +++ b/audio/softsynth/mt32.cpp @@ -25,9 +25,6 @@ #ifdef USE_MT32EMU -#include "audio/softsynth/mt32/mt32emu.h" -#include "audio/softsynth/mt32/ROMInfo.h" - #include "audio/softsynth/emumidi.h" #include "audio/musicplugin.h" #include "audio/mpu401.h" @@ -52,16 +49,16 @@ #include "gui/message.h" +// prevents load of unused FileStream API because it includes a standard library +// include, per _sev +#define MT32EMU_FILE_STREAM_H + +#include "audio/softsynth/mt32/c_interface/cpp_interface.h" + namespace MT32Emu { -class ReportHandlerScummVM : public ReportHandler { -friend class Synth; - +class ScummVMReportHandler : public MT32Emu::IReportHandler { public: - virtual ~ReportHandlerScummVM() {} - -protected: - // Callback for debug messages, in vprintf() format void printDebug(const char *fmt, va_list list) { Common::String out = Common::String::vformat(fmt, list); @@ -70,18 +67,32 @@ protected: // Callbacks for reporting various errors and information void onErrorControlROM() { - GUI::MessageDialog dialog("MT32emu: Init Error - Missing or invalid Control ROM image", "OK"); + GUI::MessageDialog dialog("MT32Emu: Init Error - Missing or invalid Control ROM image", "OK"); dialog.runModal(); error("MT32emu: Init Error - Missing or invalid Control ROM image"); } void onErrorPCMROM() { - GUI::MessageDialog dialog("MT32emu: Init Error - Missing PCM ROM image", "OK"); + GUI::MessageDialog dialog("MT32Emu: Init Error - Missing PCM ROM image", "OK"); dialog.runModal(); error("MT32emu: Init Error - Missing PCM ROM image"); } void showLCDMessage(const char *message) { Common::OSDMessageQueue::instance().addMessage(message); } + + // Unused callbacks + virtual void onMIDIMessagePlayed() {} + virtual bool onMIDIQueueOverflow() { return false; } + virtual void onMIDISystemRealtime(Bit8u /* system_realtime */) {} + virtual void onDeviceReset() {} + virtual void onDeviceReconfig() {} + virtual void onNewReverbMode(Bit8u /* mode */) {} + virtual void onNewReverbTime(Bit8u /* time */) {} + virtual void onNewReverbLevel(Bit8u /* level */) {} + virtual void onPolyStateChanged(Bit8u /* part_num */) {} + virtual void onProgramChanged(Bit8u /* part_num */, const char * /* sound_group_name */, const char * /* patch_name */) {} + + virtual ~ScummVMReportHandler() {} }; } // end of namespace MT32Emu @@ -95,11 +106,10 @@ class MidiDriver_MT32 : public MidiDriver_Emulated { private: MidiChannel_MT32 _midiChannels[16]; uint16 _channelMask; - MT32Emu::Synth *_synth; - MT32Emu::ReportHandlerScummVM *_reportHandler; - const MT32Emu::ROMImage *_controlROM, *_pcmROM; - Common::File *_controlFile, *_pcmFile; - void deleteMuntStructures(); + MT32Emu::Service _service; + MT32Emu::ScummVMReportHandler _reportHandler; + byte *_controlData, *_pcmData; + Common::Mutex _mutex; int _outputRate; @@ -107,15 +117,13 @@ protected: void generateSamples(int16 *buf, int len); public: - bool _initializing; - MidiDriver_MT32(Audio::Mixer *mixer); virtual ~MidiDriver_MT32(); int open(); void close(); void send(uint32 b); - void setPitchBendRange (byte channel, uint range); + void setPitchBendRange(byte channel, uint range); void sysEx(const byte *msg, uint16 length); uint32 property(int prop, uint32 param); @@ -139,48 +147,19 @@ MidiDriver_MT32::MidiDriver_MT32(Audio::Mixer *mixer) : MidiDriver_Emulated(mixe for (i = 0; i < ARRAYSIZE(_midiChannels); ++i) { _midiChannels[i].init(this, i); } - _reportHandler = NULL; - _synth = NULL; _outputRate = 0; - _initializing = false; - - // Initialized in open() - _controlROM = NULL; - _pcmROM = NULL; - _controlFile = NULL; - _pcmFile = NULL; + _controlData = nullptr; + _pcmData = nullptr; } MidiDriver_MT32::~MidiDriver_MT32() { - deleteMuntStructures(); -} - -void MidiDriver_MT32::deleteMuntStructures() { - delete _synth; - _synth = NULL; - delete _reportHandler; - _reportHandler = NULL; - - if (_controlROM) - MT32Emu::ROMImage::freeROMImage(_controlROM); - _controlROM = NULL; - if (_pcmROM) - MT32Emu::ROMImage::freeROMImage(_pcmROM); - _pcmROM = NULL; - - delete _controlFile; - _controlFile = NULL; - delete _pcmFile; - _pcmFile = NULL; + close(); } int MidiDriver_MT32::open() { if (_isOpen) return MERR_ALREADY_OPEN; - _reportHandler = new MT32Emu::ReportHandlerScummVM(); - _synth = new MT32Emu::Synth(_reportHandler); - Graphics::PixelFormat screenFormat = g_system->getScreenFormat(); if (screenFormat.bytesPerPixel == 1) { @@ -193,75 +172,92 @@ int MidiDriver_MT32::open() { g_system->getPaletteManager()->setPalette(dummy_palette, 0, 3); } - _initializing = true; debug(4, _s("Initializing MT-32 Emulator")); - _controlFile = new Common::File(); - if (!_controlFile->open("CM32L_CONTROL.ROM") && !_controlFile->open("MT32_CONTROL.ROM")) - error("Error opening MT32_CONTROL.ROM / CM32L_CONTROL.ROM"); - _pcmFile = new Common::File(); - if (!_pcmFile->open("CM32L_PCM.ROM") && !_pcmFile->open("MT32_PCM.ROM")) - error("Error opening MT32_PCM.ROM / CM32L_PCM.ROM"); - _controlROM = MT32Emu::ROMImage::makeROMImage(_controlFile); - _pcmROM = MT32Emu::ROMImage::makeROMImage(_pcmFile); - if (!_synth->open(*_controlROM, *_pcmROM)) + + Common::File controlFile; + if (!controlFile.open("CM32L_CONTROL.ROM") && !controlFile.open("MT32_CONTROL.ROM")) + error("Error opening MT32_CONTROL.ROM / CM32L_CONTROL.ROM. Check that your Extra Path in Paths settings is set to the correct directory"); + + Common::File pcmFile; + if (!pcmFile.open("CM32L_PCM.ROM") && !pcmFile.open("MT32_PCM.ROM")) + error("Error opening MT32_PCM.ROM / CM32L_PCM.ROM. Check that your Extra Path in Paths settings is set to the correct directory"); + + _controlData = new byte[controlFile.size()]; + controlFile.read(_controlData, controlFile.size()); + _pcmData = new byte[pcmFile.size()]; + pcmFile.read(_pcmData, pcmFile.size()); + + _service.createContext(_reportHandler); + + if (_service.addROMData(_controlData, controlFile.size()) != MT32EMU_RC_ADDED_CONTROL_ROM) { + error("Adding control ROM failed. Check that your control ROM is valid"); + } + + controlFile.close(); + + if (_service.addROMData(_pcmData, pcmFile.size()) != MT32EMU_RC_ADDED_PCM_ROM) { + error("Adding PCM ROM failed. Check that your PCM ROM is valid"); + } + + pcmFile.close(); + + if (_service.openSynth() != MT32EMU_RC_OK) return MERR_DEVICE_NOT_AVAILABLE; double gain = (double)ConfMan.getInt("midi_gain") / 100.0; - _synth->setOutputGain(1.0f * gain); - _synth->setReverbOutputGain(0.68f * gain); + _service.setOutputGain(1.0f * gain); + _service.setReverbOutputGain(1.0f * gain); // We let the synthesizer play MIDI messages immediately. Our MIDI // handling is synchronous to sample generation. This makes delaying MIDI // events result in odd sound output in some cases. For example, the // shattering window in the Indiana Jones and the Fate of Atlantis intro // will sound like a bell if we use any delay here. // Bug #6242 "AUDIO: Built-In MT-32 MUNT Produces Wrong Sounds". - _synth->setMIDIDelayMode(MT32Emu::MIDIDelayMode_IMMEDIATE); + _service.setMIDIDelayMode(MT32Emu::MIDIDelayMode_IMMEDIATE); // We need to report the sample rate MUNT renders at as sample rate of our // AudioStream. - _outputRate = _synth->getStereoOutputSampleRate(); + _outputRate = _service.getActualStereoOutputSamplerate(); + MidiDriver_Emulated::open(); - _initializing = false; - - if (screenFormat.bytesPerPixel > 1) - g_system->fillScreen(screenFormat.RGBToColor(0, 0, 0)); - else - g_system->fillScreen(0); - - g_system->updateScreen(); - _mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); return 0; } void MidiDriver_MT32::send(uint32 b) { - _synth->playMsg(b); + Common::StackLock lock(_mutex); + _service.playMsg(b); } +// Indiana Jones and the Fate of Atlantis (including the demo) uses +// setPitchBendRange, if you need a game for testing purposes void MidiDriver_MT32::setPitchBendRange(byte channel, uint range) { if (range > 24) { warning("setPitchBendRange() called with range > 24: %d", range); } - byte benderRangeSysex[9]; - benderRangeSysex[0] = 0x41; // Roland - benderRangeSysex[1] = channel; - benderRangeSysex[2] = 0x16; // MT-32 - benderRangeSysex[3] = 0x12; // Write - benderRangeSysex[4] = 0x00; - benderRangeSysex[5] = 0x00; - benderRangeSysex[6] = 0x04; - benderRangeSysex[7] = (byte)range; - benderRangeSysex[8] = MT32Emu::Synth::calcSysexChecksum(&benderRangeSysex[4], 4, 0); - sysEx(benderRangeSysex, 9); + byte benderRangeSysex[4] = { 0, 0, 4, (uint8)range }; + Common::StackLock lock(_mutex); + _service.writeSysex(channel, benderRangeSysex, 4); } void MidiDriver_MT32::sysEx(const byte *msg, uint16 length) { if (msg[0] == 0xf0) { - _synth->playSysex(msg, length); + Common::StackLock lock(_mutex); + _service.playSysex(msg, length); } else { - _synth->playSysexWithoutFraming(msg, length); + enum { + SYSEX_CMD_DT1 = 0x12, + SYSEX_CMD_DAT = 0x42 + }; + + if (msg[3] == SYSEX_CMD_DT1 || msg[3] == SYSEX_CMD_DAT) { + Common::StackLock lock(_mutex); + _service.writeSysex(msg[1], msg + 4, length - 5); + } else { + warning("Unused sysEx command %d", msg[3]); + } } } @@ -275,12 +271,18 @@ void MidiDriver_MT32::close() { // Detach the mixer callback handler _mixer->stopHandle(_mixerSoundHandle); - _synth->close(); - deleteMuntStructures(); + Common::StackLock lock(_mutex); + _service.closeSynth(); + _service.freeContext(); + delete[] _controlData; + _controlData = nullptr; + delete[] _pcmData; + _pcmData = nullptr; } void MidiDriver_MT32::generateSamples(int16 *data, int len) { - _synth->render(data, len); + Common::StackLock lock(_mutex); + _service.renderBit16s(data, len); } uint32 MidiDriver_MT32::property(int prop, uint32 param) { diff --git a/audio/softsynth/mt32/Analog.cpp b/audio/softsynth/mt32/Analog.cpp index 8ac28e401a2..31e88561c46 100644 --- a/audio/softsynth/mt32/Analog.cpp +++ b/audio/softsynth/mt32/Analog.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -15,8 +15,12 @@ * along with this program. If not, see . */ -//#include +#include + +#include "internals.h" + #include "Analog.h" +#include "Synth.h" namespace MT32Emu { @@ -106,7 +110,6 @@ static const Bit32u ACCURATE_LPF_DELTAS_OVERSAMPLED[][ACCURATE_LPF_NUMBER_OF_PHA class AbstractLowPassFilter { public: static AbstractLowPassFilter &createLowPassFilter(AnalogOutputMode mode, bool oldMT32AnalogLPF); - static void muteRingBuffer(SampleEx *ringBuffer, unsigned int length); virtual ~AbstractLowPassFilter() {} virtual SampleEx process(SampleEx sample) = 0; @@ -152,9 +155,9 @@ public: void addPositionIncrement(unsigned int positionIncrement); }; -Analog::Analog(const AnalogOutputMode mode, const ControlROMFeatureSet *controlROMFeatures) : - leftChannelLPF(AbstractLowPassFilter::createLowPassFilter(mode, controlROMFeatures->isOldMT32AnalogLPF())), - rightChannelLPF(AbstractLowPassFilter::createLowPassFilter(mode, controlROMFeatures->isOldMT32AnalogLPF())), +Analog::Analog(const AnalogOutputMode mode, const bool oldMT32AnalogLPF) : + leftChannelLPF(AbstractLowPassFilter::createLowPassFilter(mode, oldMT32AnalogLPF)), + rightChannelLPF(AbstractLowPassFilter::createLowPassFilter(mode, oldMT32AnalogLPF)), synthGain(0), reverbGain(0) {} @@ -164,7 +167,7 @@ Analog::~Analog() { delete &rightChannelLPF; } -void Analog::process(Sample **outStream, const Sample *nonReverbLeft, const Sample *nonReverbRight, const Sample *reverbDryLeft, const Sample *reverbDryRight, const Sample *reverbWetLeft, const Sample *reverbWetRight, Bit32u outLength) { +void Analog::process(Sample *outStream, const Sample *nonReverbLeft, const Sample *nonReverbRight, const Sample *reverbDryLeft, const Sample *reverbDryRight, const Sample *reverbWetLeft, const Sample *reverbWetRight, Bit32u outLength) { if (outStream == NULL) { leftChannelLPF.addPositionIncrement(outLength); rightChannelLPF.addPositionIncrement(outLength); @@ -179,8 +182,8 @@ void Analog::process(Sample **outStream, const Sample *nonReverbLeft, const Samp outSampleL = leftChannelLPF.process(0); outSampleR = rightChannelLPF.process(0); } else { - SampleEx inSampleL = ((SampleEx)*(nonReverbLeft++) + (SampleEx)*(reverbDryLeft++)) * synthGain + (SampleEx)*(reverbWetLeft++) * reverbGain; - SampleEx inSampleR = ((SampleEx)*(nonReverbRight++) + (SampleEx)*(reverbDryRight++)) * synthGain + (SampleEx)*(reverbWetRight++) * reverbGain; + SampleEx inSampleL = (SampleEx(*(nonReverbLeft++)) + SampleEx(*(reverbDryLeft++))) * synthGain + SampleEx(*(reverbWetLeft++)) * reverbGain; + SampleEx inSampleR = (SampleEx(*(nonReverbRight++)) + SampleEx(*(reverbDryRight++))) * synthGain + SampleEx(*(reverbWetRight++)) * reverbGain; #if !MT32EMU_USE_FLOAT_SAMPLES inSampleL >>= OUTPUT_GAIN_FRACTION_BITS; @@ -191,8 +194,8 @@ void Analog::process(Sample **outStream, const Sample *nonReverbLeft, const Samp outSampleR = rightChannelLPF.process(inSampleR); } - *((*outStream)++) = Synth::clipSampleEx(outSampleL); - *((*outStream)++) = Synth::clipSampleEx(outSampleR); + *(outStream++) = Synth::clipSampleEx(outSampleL); + *(outStream++) = Synth::clipSampleEx(outSampleR); } } @@ -236,23 +239,6 @@ AbstractLowPassFilter &AbstractLowPassFilter::createLowPassFilter(AnalogOutputMo } } -void AbstractLowPassFilter::muteRingBuffer(SampleEx *ringBuffer, unsigned int length) { - -#if MT32EMU_USE_FLOAT_SAMPLES - - SampleEx *p = ringBuffer; - while (length--) { - *(p++) = 0.0f; - } - -#else - - memset(ringBuffer, 0, length * sizeof(SampleEx)); - -#endif - -} - bool AbstractLowPassFilter::hasNextSample() const { return false; } @@ -273,7 +259,7 @@ CoarseLowPassFilter::CoarseLowPassFilter(bool oldMT32AnalogLPF) : LPF_TAPS(oldMT32AnalogLPF ? COARSE_LPF_TAPS_MT32 : COARSE_LPF_TAPS_CM32L), ringBufferPosition(0) { - muteRingBuffer(ringBuffer, COARSE_LPF_DELAY_LINE_LENGTH); + Synth::muteSampleBuffer(ringBuffer, COARSE_LPF_DELAY_LINE_LENGTH); } SampleEx CoarseLowPassFilter::process(const SampleEx inSample) { @@ -303,7 +289,7 @@ AccurateLowPassFilter::AccurateLowPassFilter(const bool oldMT32AnalogLPF, const ringBufferPosition(0), phase(0) { - muteRingBuffer(ringBuffer, ACCURATE_LPF_DELAY_LINE_LENGTH); + Synth::muteSampleBuffer(ringBuffer, ACCURATE_LPF_DELAY_LINE_LENGTH); } SampleEx AccurateLowPassFilter::process(const SampleEx inSample) { @@ -345,4 +331,4 @@ void AccurateLowPassFilter::addPositionIncrement(const unsigned int positionIncr phase = (phase + positionIncrement * phaseIncrement) % ACCURATE_LPF_NUMBER_OF_PHASES; } -} +} // namespace MT32Emu diff --git a/audio/softsynth/mt32/Analog.h b/audio/softsynth/mt32/Analog.h index a48db724855..ee642f280d0 100644 --- a/audio/softsynth/mt32/Analog.h +++ b/audio/softsynth/mt32/Analog.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -18,7 +18,10 @@ #ifndef MT32EMU_ANALOG_H #define MT32EMU_ANALOG_H -#include "mt32emu.h" +#include "globals.h" +#include "internals.h" +#include "Types.h" +#include "Enumerations.h" namespace MT32Emu { @@ -35,9 +38,9 @@ class AbstractLowPassFilter; */ class Analog { public: - Analog(AnalogOutputMode mode, const ControlROMFeatureSet *controlROMFeatures); + Analog(const AnalogOutputMode mode, const bool oldMT32AnalogLPF); ~Analog(); - void process(Sample **outStream, const Sample *nonReverbLeft, const Sample *nonReverbRight, const Sample *reverbDryLeft, const Sample *reverbDryRight, const Sample *reverbWetLeft, const Sample *reverbWetRight, const Bit32u outLength); + void process(Sample *outStream, const Sample *nonReverbLeft, const Sample *nonReverbRight, const Sample *reverbDryLeft, const Sample *reverbDryRight, const Sample *reverbWetLeft, const Sample *reverbWetRight, Bit32u outLength); unsigned int getOutputSampleRate() const; Bit32u getDACStreamsLength(Bit32u outputLength) const; void setSynthOutputGain(float synthGain); @@ -52,6 +55,6 @@ private: Analog(Analog &); }; -} +} // namespace MT32Emu -#endif +#endif // #ifndef MT32EMU_ANALOG_H diff --git a/audio/softsynth/mt32/BReverbModel.cpp b/audio/softsynth/mt32/BReverbModel.cpp index 5e02db8f990..2be1b418a23 100644 --- a/audio/softsynth/mt32/BReverbModel.cpp +++ b/audio/softsynth/mt32/BReverbModel.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -15,9 +15,12 @@ * along with this program. If not, see . */ -//#include -#include "mt32emu.h" +#include + +#include "internals.h" + #include "BReverbModel.h" +#include "Synth.h" // Analysing of state of reverb RAM address lines gives exact sizes of the buffers of filters used. This also indicates that // the reverb model implemented in the real devices consists of three series allpass filters preceded by a non-feedback comb (or a delay with a LPF) @@ -43,14 +46,14 @@ const BReverbSettings &BReverbModel::getCM32L_LAPCSettings(const ReverbMode mode static const Bit32u MODE_0_COMBS[] = {705 + PROCESS_DELAY, 2349, 2839, 3632}; static const Bit32u MODE_0_OUTL[] = {2349, 141, 1960}; static const Bit32u MODE_0_OUTR[] = {1174, 1570, 145}; - static const Bit32u MODE_0_COMB_FACTOR[] = {0xA0, 0x60, 0x60, 0x60}; - static const Bit32u MODE_0_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + static const Bit8u MODE_0_COMB_FACTOR[] = {0xA0, 0x60, 0x60, 0x60}; + static const Bit8u MODE_0_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98}; - static const Bit32u MODE_0_DRY_AMP[] = {0xA0, 0xA0, 0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xD0}; - static const Bit32u MODE_0_WET_AMP[] = {0x10, 0x30, 0x50, 0x70, 0x90, 0xC0, 0xF0, 0xF0}; - static const Bit32u MODE_0_LPF_AMP = 0x60; + static const Bit8u MODE_0_DRY_AMP[] = {0xA0, 0xA0, 0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xD0}; + static const Bit8u MODE_0_WET_AMP[] = {0x10, 0x30, 0x50, 0x70, 0x90, 0xC0, 0xF0, 0xF0}; + static const Bit8u MODE_0_LPF_AMP = 0x60; static const Bit32u MODE_1_NUMBER_OF_ALLPASSES = 3; static const Bit32u MODE_1_ALLPASSES[] = {1324, 809, 176}; @@ -58,14 +61,14 @@ const BReverbSettings &BReverbModel::getCM32L_LAPCSettings(const ReverbMode mode static const Bit32u MODE_1_COMBS[] = {961 + PROCESS_DELAY, 2619, 3545, 4519}; static const Bit32u MODE_1_OUTL[] = {2618, 1760, 4518}; static const Bit32u MODE_1_OUTR[] = {1300, 3532, 2274}; - static const Bit32u MODE_1_COMB_FACTOR[] = {0x80, 0x60, 0x60, 0x60}; - static const Bit32u MODE_1_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + static const Bit8u MODE_1_COMB_FACTOR[] = {0x80, 0x60, 0x60, 0x60}; + static const Bit8u MODE_1_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98, 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98}; - static const Bit32u MODE_1_DRY_AMP[] = {0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xE0}; - static const Bit32u MODE_1_WET_AMP[] = {0x10, 0x30, 0x50, 0x70, 0x90, 0xC0, 0xF0, 0xF0}; - static const Bit32u MODE_1_LPF_AMP = 0x60; + static const Bit8u MODE_1_DRY_AMP[] = {0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xE0}; + static const Bit8u MODE_1_WET_AMP[] = {0x10, 0x30, 0x50, 0x70, 0x90, 0xC0, 0xF0, 0xF0}; + static const Bit8u MODE_1_LPF_AMP = 0x60; static const Bit32u MODE_2_NUMBER_OF_ALLPASSES = 3; static const Bit32u MODE_2_ALLPASSES[] = {969, 644, 157}; @@ -73,25 +76,25 @@ const BReverbSettings &BReverbModel::getCM32L_LAPCSettings(const ReverbMode mode static const Bit32u MODE_2_COMBS[] = {116 + PROCESS_DELAY, 2259, 2839, 3539}; static const Bit32u MODE_2_OUTL[] = {2259, 718, 1769}; static const Bit32u MODE_2_OUTR[] = {1136, 2128, 1}; - static const Bit32u MODE_2_COMB_FACTOR[] = {0, 0x20, 0x20, 0x20}; - static const Bit32u MODE_2_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + static const Bit8u MODE_2_COMB_FACTOR[] = {0, 0x20, 0x20, 0x20}; + static const Bit8u MODE_2_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0, 0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0, 0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0}; - static const Bit32u MODE_2_DRY_AMP[] = {0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xB0, 0xC0, 0xE0}; - static const Bit32u MODE_2_WET_AMP[] = {0x10, 0x30, 0x50, 0x70, 0x90, 0xC0, 0xF0, 0xF0}; - static const Bit32u MODE_2_LPF_AMP = 0x80; + static const Bit8u MODE_2_DRY_AMP[] = {0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xB0, 0xC0, 0xE0}; + static const Bit8u MODE_2_WET_AMP[] = {0x10, 0x30, 0x50, 0x70, 0x90, 0xC0, 0xF0, 0xF0}; + static const Bit8u MODE_2_LPF_AMP = 0x80; static const Bit32u MODE_3_NUMBER_OF_ALLPASSES = 0; static const Bit32u MODE_3_NUMBER_OF_COMBS = 1; static const Bit32u MODE_3_DELAY[] = {16000 + MODE_3_FEEDBACK_DELAY + PROCESS_DELAY + MODE_3_ADDITIONAL_DELAY}; static const Bit32u MODE_3_OUTL[] = {400, 624, 960, 1488, 2256, 3472, 5280, 8000}; static const Bit32u MODE_3_OUTR[] = {800, 1248, 1920, 2976, 4512, 6944, 10560, 16000}; - static const Bit32u MODE_3_COMB_FACTOR[] = {0x68}; - static const Bit32u MODE_3_COMB_FEEDBACK[] = {0x68, 0x60}; - static const Bit32u MODE_3_DRY_AMP[] = {0x20, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, + static const Bit8u MODE_3_COMB_FACTOR[] = {0x68}; + static const Bit8u MODE_3_COMB_FEEDBACK[] = {0x68, 0x60}; + static const Bit8u MODE_3_DRY_AMP[] = {0x20, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x20, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50}; - static const Bit32u MODE_3_WET_AMP[] = {0x18, 0x18, 0x28, 0x40, 0x60, 0x80, 0xA8, 0xF8}; + static const Bit8u MODE_3_WET_AMP[] = {0x18, 0x18, 0x28, 0x40, 0x60, 0x80, 0xA8, 0xF8}; static const BReverbSettings REVERB_MODE_0_SETTINGS = {MODE_0_NUMBER_OF_ALLPASSES, MODE_0_ALLPASSES, MODE_0_NUMBER_OF_COMBS, MODE_0_COMBS, MODE_0_OUTL, MODE_0_OUTR, MODE_0_COMB_FACTOR, MODE_0_COMB_FEEDBACK, MODE_0_DRY_AMP, MODE_0_WET_AMP, MODE_0_LPF_AMP}; static const BReverbSettings REVERB_MODE_1_SETTINGS = {MODE_1_NUMBER_OF_ALLPASSES, MODE_1_ALLPASSES, MODE_1_NUMBER_OF_COMBS, MODE_1_COMBS, MODE_1_OUTL, MODE_1_OUTR, MODE_1_COMB_FACTOR, MODE_1_COMB_FEEDBACK, MODE_1_DRY_AMP, MODE_1_WET_AMP, MODE_1_LPF_AMP}; @@ -112,14 +115,14 @@ const BReverbSettings &BReverbModel::getMT32Settings(const ReverbMode mode) { static const Bit32u MODE_0_COMBS[] = {575 + PROCESS_DELAY, 2040, 2752, 3629}; static const Bit32u MODE_0_OUTL[] = {2040, 687, 1814}; static const Bit32u MODE_0_OUTR[] = {1019, 2072, 1}; - static const Bit32u MODE_0_COMB_FACTOR[] = {0xB0, 0x60, 0x60, 0x60}; - static const Bit32u MODE_0_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + static const Bit8u MODE_0_COMB_FACTOR[] = {0xB0, 0x60, 0x60, 0x60}; + static const Bit8u MODE_0_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98, 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98}; - static const Bit32u MODE_0_DRY_AMP[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80}; - static const Bit32u MODE_0_WET_AMP[] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x70, 0xA0, 0xE0}; - static const Bit32u MODE_0_LPF_AMP = 0x80; + static const Bit8u MODE_0_DRY_AMP[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80}; + static const Bit8u MODE_0_WET_AMP[] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x70, 0xA0, 0xE0}; + static const Bit8u MODE_0_LPF_AMP = 0x80; static const Bit32u MODE_1_NUMBER_OF_ALLPASSES = 3; static const Bit32u MODE_1_ALLPASSES[] = {1324, 809, 176}; @@ -127,14 +130,14 @@ const BReverbSettings &BReverbModel::getMT32Settings(const ReverbMode mode) { static const Bit32u MODE_1_COMBS[] = {961 + PROCESS_DELAY, 2619, 3545, 4519}; static const Bit32u MODE_1_OUTL[] = {2618, 1760, 4518}; static const Bit32u MODE_1_OUTR[] = {1300, 3532, 2274}; - static const Bit32u MODE_1_COMB_FACTOR[] = {0x90, 0x60, 0x60, 0x60}; - static const Bit32u MODE_1_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + static const Bit8u MODE_1_COMB_FACTOR[] = {0x90, 0x60, 0x60, 0x60}; + static const Bit8u MODE_1_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98, 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98}; - static const Bit32u MODE_1_DRY_AMP[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80}; - static const Bit32u MODE_1_WET_AMP[] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x70, 0xA0, 0xE0}; - static const Bit32u MODE_1_LPF_AMP = 0x80; + static const Bit8u MODE_1_DRY_AMP[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80}; + static const Bit8u MODE_1_WET_AMP[] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x70, 0xA0, 0xE0}; + static const Bit8u MODE_1_LPF_AMP = 0x80; static const Bit32u MODE_2_NUMBER_OF_ALLPASSES = 3; static const Bit32u MODE_2_ALLPASSES[] = {969, 644, 157}; @@ -142,25 +145,25 @@ const BReverbSettings &BReverbModel::getMT32Settings(const ReverbMode mode) { static const Bit32u MODE_2_COMBS[] = {116 + PROCESS_DELAY, 2259, 2839, 3539}; static const Bit32u MODE_2_OUTL[] = {2259, 718, 1769}; static const Bit32u MODE_2_OUTR[] = {1136, 2128, 1}; - static const Bit32u MODE_2_COMB_FACTOR[] = {0, 0x60, 0x60, 0x60}; - static const Bit32u MODE_2_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + static const Bit8u MODE_2_COMB_FACTOR[] = {0, 0x60, 0x60, 0x60}; + static const Bit8u MODE_2_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98, 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98}; - static const Bit32u MODE_2_DRY_AMP[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80}; - static const Bit32u MODE_2_WET_AMP[] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x70, 0xA0, 0xE0}; - static const Bit32u MODE_2_LPF_AMP = 0x80; + static const Bit8u MODE_2_DRY_AMP[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80}; + static const Bit8u MODE_2_WET_AMP[] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x70, 0xA0, 0xE0}; + static const Bit8u MODE_2_LPF_AMP = 0x80; static const Bit32u MODE_3_NUMBER_OF_ALLPASSES = 0; static const Bit32u MODE_3_NUMBER_OF_COMBS = 1; static const Bit32u MODE_3_DELAY[] = {16000 + MODE_3_FEEDBACK_DELAY + PROCESS_DELAY + MODE_3_ADDITIONAL_DELAY}; static const Bit32u MODE_3_OUTL[] = {400, 624, 960, 1488, 2256, 3472, 5280, 8000}; static const Bit32u MODE_3_OUTR[] = {800, 1248, 1920, 2976, 4512, 6944, 10560, 16000}; - static const Bit32u MODE_3_COMB_FACTOR[] = {0x68}; - static const Bit32u MODE_3_COMB_FEEDBACK[] = {0x68, 0x60}; - static const Bit32u MODE_3_DRY_AMP[] = {0x10, 0x10, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + static const Bit8u MODE_3_COMB_FACTOR[] = {0x68}; + static const Bit8u MODE_3_COMB_FEEDBACK[] = {0x68, 0x60}; + static const Bit8u MODE_3_DRY_AMP[] = {0x10, 0x10, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x10, 0x20, 0x20, 0x10, 0x20, 0x10, 0x20, 0x10}; - static const Bit32u MODE_3_WET_AMP[] = {0x08, 0x18, 0x28, 0x40, 0x60, 0x80, 0xA8, 0xF8}; + static const Bit8u MODE_3_WET_AMP[] = {0x08, 0x18, 0x28, 0x40, 0x60, 0x80, 0xA8, 0xF8}; static const BReverbSettings REVERB_MODE_0_SETTINGS = {MODE_0_NUMBER_OF_ALLPASSES, MODE_0_ALLPASSES, MODE_0_NUMBER_OF_COMBS, MODE_0_COMBS, MODE_0_OUTL, MODE_0_OUTR, MODE_0_COMB_FACTOR, MODE_0_COMB_FEEDBACK, MODE_0_DRY_AMP, MODE_0_WET_AMP, MODE_0_LPF_AMP}; static const BReverbSettings REVERB_MODE_1_SETTINGS = {MODE_1_NUMBER_OF_ALLPASSES, MODE_1_ALLPASSES, MODE_1_NUMBER_OF_COMBS, MODE_1_COMBS, MODE_1_OUTL, MODE_1_OUTR, MODE_1_COMB_FACTOR, MODE_1_COMB_FEEDBACK, MODE_1_DRY_AMP, MODE_1_WET_AMP, MODE_1_LPF_AMP}; @@ -189,7 +192,7 @@ static Sample weirdMul(Sample a, Bit8u addMask, Bit8u carryMask) { } return res; #else - return Sample(((Bit32s)a * addMask) >> 8); + return Sample((Bit32s(a) * addMask) >> 8); #endif } @@ -252,7 +255,7 @@ Sample AllpassFilter::process(const Sample in) { #endif } -CombFilter::CombFilter(const Bit32u useSize, const Bit32u useFilterFactor) : RingBuffer(useSize), filterFactor(useFilterFactor) {} +CombFilter::CombFilter(const Bit32u useSize, const Bit8u useFilterFactor) : RingBuffer(useSize), filterFactor(useFilterFactor) {} void CombFilter::process(const Sample in) { // This model corresponds to the comb filter implementation of the real CM-32L device @@ -271,11 +274,11 @@ Sample CombFilter::getOutputAt(const Bit32u outIndex) const { return buffer[(size + index - outIndex) % size]; } -void CombFilter::setFeedbackFactor(const Bit32u useFeedbackFactor) { +void CombFilter::setFeedbackFactor(const Bit8u useFeedbackFactor) { feedbackFactor = useFeedbackFactor; } -DelayWithLowPassFilter::DelayWithLowPassFilter(const Bit32u useSize, const Bit32u useFilterFactor, const Bit32u useAmp) +DelayWithLowPassFilter::DelayWithLowPassFilter(const Bit32u useSize, const Bit8u useFilterFactor, const Bit8u useAmp) : CombFilter(useSize, useFilterFactor), amp(useAmp) {} void DelayWithLowPassFilter::process(const Sample in) { @@ -292,7 +295,7 @@ void DelayWithLowPassFilter::process(const Sample in) { buffer[index] = weirdMul(lpfOut, amp, 0xFF); } -TapDelayCombFilter::TapDelayCombFilter(const Bit32u useSize, const Bit32u useFilterFactor) : CombFilter(useSize, useFilterFactor) {} +TapDelayCombFilter::TapDelayCombFilter(const Bit32u useSize, const Bit8u useFilterFactor) : CombFilter(useSize, useFilterFactor) {} void TapDelayCombFilter::process(const Sample in) { // the previously stored value @@ -430,7 +433,7 @@ bool BReverbModel::isMT32Compatible(const ReverbMode mode) const { return ¤tSettings == &getMT32Settings(mode); } -void BReverbModel::process(const Sample *inLeft, const Sample *inRight, Sample *outLeft, Sample *outRight, unsigned long numSamples) { +void BReverbModel::process(const Sample *inLeft, const Sample *inRight, Sample *outLeft, Sample *outRight, Bit32u numSamples) { if (combs == NULL) { Synth::muteSampleBuffer(outLeft, numSamples); Synth::muteSampleBuffer(outRight, numSamples); @@ -501,9 +504,9 @@ void BReverbModel::process(const Sample *inLeft, const Sample *inRight, Sample * * Analysing of the algorithm suggests that the overflow is most probable when the combs output is added below. * So, despite this isn't actually accurate, we only add the check here for performance reasons. */ - Sample outSample = Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx((SampleEx)outL1 + SampleEx(outL1 >> 1)) + (SampleEx)outL2) + SampleEx(outL2 >> 1)) + (SampleEx)outL3); + Sample outSample = Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx(SampleEx(outL1) + (SampleEx(outL1) >> 1)) + SampleEx(outL2)) + (SampleEx(outL2) >> 1)) + SampleEx(outL3)); #else - Sample outSample = Synth::clipSampleEx((SampleEx)outL1 + SampleEx(outL1 >> 1) + (SampleEx)outL2 + SampleEx(outL2 >> 1) + (SampleEx)outL3); + Sample outSample = Synth::clipSampleEx(SampleEx(outL1) + (SampleEx(outL1) >> 1) + SampleEx(outL2) + (SampleEx(outL2) >> 1) + SampleEx(outL3)); #endif *(outLeft++) = weirdMul(outSample, wetLevel, 0xFF); } @@ -515,9 +518,9 @@ void BReverbModel::process(const Sample *inLeft, const Sample *inRight, Sample * Sample outSample = 1.5f * (outR1 + outR2) + outR3; #elif MT32EMU_BOSS_REVERB_PRECISE_MODE // See the note above for the left channel output. - Sample outSample = Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx((SampleEx)outR1 + SampleEx(outR1 >> 1)) + (SampleEx)outR2) + SampleEx(outR2 >> 1)) + (SampleEx)outR3); + Sample outSample = Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx(SampleEx(outR1) + (SampleEx(outR1) >> 1)) + SampleEx(outR2)) + (SampleEx(outR2) >> 1)) + SampleEx(outR3)); #else - Sample outSample = Synth::clipSampleEx((SampleEx)outR1 + SampleEx(outR1 >> 1) + (SampleEx)outR2 + SampleEx(outR2 >> 1) + (SampleEx)outR3); + Sample outSample = Synth::clipSampleEx(SampleEx(outR1) + (SampleEx(outR1) >> 1) + SampleEx(outR2) + (SampleEx(outR2) >> 1) + SampleEx(outR3)); #endif *(outRight++) = weirdMul(outSample, wetLevel, 0xFF); } @@ -525,4 +528,4 @@ void BReverbModel::process(const Sample *inLeft, const Sample *inRight, Sample * } } -} +} // namespace MT32Emu diff --git a/audio/softsynth/mt32/BReverbModel.h b/audio/softsynth/mt32/BReverbModel.h index 764daf1a9ec..8cfc5da8a33 100644 --- a/audio/softsynth/mt32/BReverbModel.h +++ b/audio/softsynth/mt32/BReverbModel.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -18,6 +18,10 @@ #ifndef MT32EMU_B_REVERB_MODEL_H #define MT32EMU_B_REVERB_MODEL_H +#include "globals.h" +#include "internals.h" +#include "Types.h" + namespace MT32Emu { struct BReverbSettings { @@ -27,11 +31,11 @@ struct BReverbSettings { const Bit32u * const combSizes; const Bit32u * const outLPositions; const Bit32u * const outRPositions; - const Bit32u * const filterFactors; - const Bit32u * const feedbackFactors; - const Bit32u * const dryAmps; - const Bit32u * const wetLevels; - const Bit32u lpfAmp; + const Bit8u * const filterFactors; + const Bit8u * const feedbackFactors; + const Bit8u * const dryAmps; + const Bit8u * const wetLevels; + const Bit8u lpfAmp; }; class RingBuffer { @@ -56,23 +60,23 @@ public: class CombFilter : public RingBuffer { protected: - const Bit32u filterFactor; - Bit32u feedbackFactor; + const Bit8u filterFactor; + Bit8u feedbackFactor; public: - CombFilter(const Bit32u size, const Bit32u useFilterFactor); + CombFilter(const Bit32u size, const Bit8u useFilterFactor); virtual void process(const Sample in); Sample getOutputAt(const Bit32u outIndex) const; - void setFeedbackFactor(const Bit32u useFeedbackFactor); + void setFeedbackFactor(const Bit8u useFeedbackFactor); }; class DelayWithLowPassFilter : public CombFilter { - Bit32u amp; + Bit8u amp; public: - DelayWithLowPassFilter(const Bit32u useSize, const Bit32u useFilterFactor, const Bit32u useAmp); + DelayWithLowPassFilter(const Bit32u useSize, const Bit8u useFilterFactor, const Bit8u useAmp); void process(const Sample in); - void setFeedbackFactor(const Bit32u) {} + void setFeedbackFactor(const Bit8u) {} }; class TapDelayCombFilter : public CombFilter { @@ -80,7 +84,7 @@ class TapDelayCombFilter : public CombFilter { Bit32u outR; public: - TapDelayCombFilter(const Bit32u useSize, const Bit32u useFilterFactor); + TapDelayCombFilter(const Bit32u useSize, const Bit8u useFilterFactor); void process(const Sample in); Sample getLeftOutput() const; Sample getRightOutput() const; @@ -93,8 +97,8 @@ class BReverbModel { const BReverbSettings ¤tSettings; const bool tapDelayMode; - Bit32u dryAmp; - Bit32u wetLevel; + Bit8u dryAmp; + Bit8u wetLevel; static const BReverbSettings &getCM32L_LAPCSettings(const ReverbMode mode); static const BReverbSettings &getMT32Settings(const ReverbMode mode); @@ -108,11 +112,11 @@ public: void close(); void mute(); void setParameters(Bit8u time, Bit8u level); - void process(const Sample *inLeft, const Sample *inRight, Sample *outLeft, Sample *outRight, unsigned long numSamples); + void process(const Sample *inLeft, const Sample *inRight, Sample *outLeft, Sample *outRight, Bit32u numSamples); bool isActive() const; bool isMT32Compatible(const ReverbMode mode) const; }; -} +} // namespace MT32Emu -#endif +#endif // #ifndef MT32EMU_B_REVERB_MODEL_H diff --git a/audio/softsynth/mt32/Enumerations.h b/audio/softsynth/mt32/Enumerations.h new file mode 100644 index 00000000000..9b0a35d0bfe --- /dev/null +++ b/audio/softsynth/mt32/Enumerations.h @@ -0,0 +1,155 @@ +/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +/* Using two guards since this file may be included twice with different MT32EMU_C_ENUMERATIONS define. */ + +#if (!defined MT32EMU_CPP_ENUMERATIONS_H && !defined MT32EMU_C_ENUMERATIONS) || (!defined MT32EMU_C_ENUMERATIONS_H && defined MT32EMU_C_ENUMERATIONS) + +#ifdef MT32EMU_C_ENUMERATIONS + +#define MT32EMU_C_ENUMERATIONS_H + +#define MT32EMU_DAC_INPUT_MODE_NAME mt32emu_dac_input_mode +#define MT32EMU_DAC_INPUT_MODE(ident) MT32EMU_DAC_##ident + +#define MT32EMU_MIDI_DELAY_MODE_NAME mt32emu_midi_delay_mode +#define MT32EMU_MIDI_DELAY_MODE(ident) MT32EMU_MDM_##ident + +#define MT32EMU_ANALOG_OUTPUT_MODE_NAME mt32emu_analog_output_mode +#define MT32EMU_ANALOG_OUTPUT_MODE(ident) MT32EMU_AOM_##ident + +#define MT32EMU_PARTIAL_STATE_NAME mt32emu_partial_state +#define MT32EMU_PARTIAL_STATE(ident) MT32EMU_PS_##ident + +#else /* #ifdef MT32EMU_C_ENUMERATIONS */ + +#define MT32EMU_CPP_ENUMERATIONS_H + +#define MT32EMU_DAC_INPUT_MODE_NAME DACInputMode +#define MT32EMU_DAC_INPUT_MODE(ident) DACInputMode_##ident + +#define MT32EMU_MIDI_DELAY_MODE_NAME MIDIDelayMode +#define MT32EMU_MIDI_DELAY_MODE(ident) MIDIDelayMode_##ident + +#define MT32EMU_ANALOG_OUTPUT_MODE_NAME AnalogOutputMode +#define MT32EMU_ANALOG_OUTPUT_MODE(ident) AnalogOutputMode_##ident + +#define MT32EMU_PARTIAL_STATE_NAME PartialState +#define MT32EMU_PARTIAL_STATE(ident) PartialState_##ident + +namespace MT32Emu { + +#endif /* #ifdef MT32EMU_C_ENUMERATIONS */ + +/** + * Methods for emulating the connection between the LA32 and the DAC, which involves + * some hacks in the real devices for doubling the volume. + * See also http://en.wikipedia.org/wiki/Roland_MT-32#Digital_overflow + */ +enum MT32EMU_DAC_INPUT_MODE_NAME { + /** + * Produces samples at double the volume, without tricks. + * Nicer overdrive characteristics than the DAC hacks (it simply clips samples within range) + * Higher quality than the real devices + */ + MT32EMU_DAC_INPUT_MODE(NICE), + + /** + * Produces samples that exactly match the bits output from the emulated LA32. + * Nicer overdrive characteristics than the DAC hacks (it simply clips samples within range) + * Much less likely to overdrive than any other mode. + * Half the volume of any of the other modes. + * Output gain is ignored for both LA32 and reverb output. + * Perfect for developers while debugging :) + */ + MT32EMU_DAC_INPUT_MODE(PURE), + + /** + * Re-orders the LA32 output bits as in early generation MT-32s (according to Wikipedia). + * Bit order at DAC (where each number represents the original LA32 output bit number, and XX means the bit is always low): + * 15 13 12 11 10 09 08 07 06 05 04 03 02 01 00 XX + */ + MT32EMU_DAC_INPUT_MODE(GENERATION1), + + /** + * Re-orders the LA32 output bits as in later generations (personally confirmed on my CM-32L - KG). + * Bit order at DAC (where each number represents the original LA32 output bit number): + * 15 13 12 11 10 09 08 07 06 05 04 03 02 01 00 14 + */ + MT32EMU_DAC_INPUT_MODE(GENERATION2) +}; + +/** Methods for emulating the effective delay of incoming MIDI messages introduced by a MIDI interface. */ +enum MT32EMU_MIDI_DELAY_MODE_NAME { + /** Process incoming MIDI events immediately. */ + MT32EMU_MIDI_DELAY_MODE(IMMEDIATE), + + /** + * Delay incoming short MIDI messages as if they where transferred via a MIDI cable to a real hardware unit and immediate sysex processing. + * This ensures more accurate timing of simultaneous NoteOn messages. + */ + MT32EMU_MIDI_DELAY_MODE(DELAY_SHORT_MESSAGES_ONLY), + + /** Delay all incoming MIDI events as if they where transferred via a MIDI cable to a real hardware unit.*/ + MT32EMU_MIDI_DELAY_MODE(DELAY_ALL) +}; + +/** Methods for emulating the effects of analogue circuits of real hardware units on the output signal. */ +enum MT32EMU_ANALOG_OUTPUT_MODE_NAME { + /** Only digital path is emulated. The output samples correspond to the digital signal at the DAC entrance. */ + MT32EMU_ANALOG_OUTPUT_MODE(DIGITAL_ONLY), + /** Coarse emulation of LPF circuit. High frequencies are boosted, sample rate remains unchanged. */ + MT32EMU_ANALOG_OUTPUT_MODE(COARSE), + /** + * Finer emulation of LPF circuit. Output signal is upsampled to 48 kHz to allow emulation of audible mirror spectra above 16 kHz, + * which is passed through the LPF circuit without significant attenuation. + */ + MT32EMU_ANALOG_OUTPUT_MODE(ACCURATE), + /** + * Same as AnalogOutputMode_ACCURATE mode but the output signal is 2x oversampled, i.e. the output sample rate is 96 kHz. + * This makes subsequent resampling easier. Besides, due to nonlinear passband of the LPF emulated, it takes fewer number of MACs + * compared to a regular LPF FIR implementations. + */ + MT32EMU_ANALOG_OUTPUT_MODE(OVERSAMPLED) +}; + +enum MT32EMU_PARTIAL_STATE_NAME { + MT32EMU_PARTIAL_STATE(INACTIVE), + MT32EMU_PARTIAL_STATE(ATTACK), + MT32EMU_PARTIAL_STATE(SUSTAIN), + MT32EMU_PARTIAL_STATE(RELEASE) +}; + +#ifndef MT32EMU_C_ENUMERATIONS + +} // namespace MT32Emu + +#endif + +#undef MT32EMU_DAC_INPUT_MODE_NAME +#undef MT32EMU_DAC_INPUT_MODE + +#undef MT32EMU_MIDI_DELAY_MODE_NAME +#undef MT32EMU_MIDI_DELAY_MODE + +#undef MT32EMU_ANALOG_OUTPUT_MODE_NAME +#undef MT32EMU_ANALOG_OUTPUT_MODE + +#undef MT32EMU_PARTIAL_STATE_NAME +#undef MT32EMU_PARTIAL_STATE + +#endif /* #if (!defined MT32EMU_CPP_ENUMERATIONS_H && !defined MT32EMU_C_ENUMERATIONS) || (!defined MT32EMU_C_ENUMERATIONS_H && defined MT32EMU_C_ENUMERATIONS) */ diff --git a/audio/softsynth/mt32/File.cpp b/audio/softsynth/mt32/File.cpp new file mode 100644 index 00000000000..07164823d30 --- /dev/null +++ b/audio/softsynth/mt32/File.cpp @@ -0,0 +1,77 @@ +/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include + +#include "internals.h" + +#include "File.h" +#include "sha1/sha1.h" + +namespace MT32Emu { + +AbstractFile::AbstractFile() : sha1DigestCalculated(false) { + sha1Digest[0] = 0; + + reserved = NULL; +} + +AbstractFile::AbstractFile(const SHA1Digest &useSHA1Digest) : sha1DigestCalculated(true) { + memcpy(sha1Digest, useSHA1Digest, sizeof(SHA1Digest) - 1); + sha1Digest[sizeof(SHA1Digest) - 1] = 0; // Ensure terminator char. + + reserved = NULL; +} + +const File::SHA1Digest &AbstractFile::getSHA1() { + if (sha1DigestCalculated) { + return sha1Digest; + } + sha1DigestCalculated = true; + + size_t size = getSize(); + if (size == 0) { + return sha1Digest; + } + + const Bit8u *data = getData(); + if (data == NULL) { + return sha1Digest; + } + + unsigned char fileDigest[20]; + + sha1::calc(data, int(size), fileDigest); + sha1::toHexString(fileDigest, sha1Digest); + return sha1Digest; +} + +ArrayFile::ArrayFile(const Bit8u *useData, size_t useSize) : data(useData), size(useSize) +{} + +ArrayFile::ArrayFile(const Bit8u *useData, size_t useSize, const SHA1Digest &useSHA1Digest) : AbstractFile(useSHA1Digest), data(useData), size(useSize) +{} + +size_t ArrayFile::getSize() { + return size; +} + +const Bit8u *ArrayFile::getData() { + return data; +} + +} // namespace MT32Emu diff --git a/audio/softsynth/mt32/File.h b/audio/softsynth/mt32/File.h new file mode 100644 index 00000000000..c9a7d582b4c --- /dev/null +++ b/audio/softsynth/mt32/File.h @@ -0,0 +1,73 @@ +/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#ifndef MT32EMU_FILE_H +#define MT32EMU_FILE_H + +#include + +#include "globals.h" +#include "Types.h" + +namespace MT32Emu { + +class MT32EMU_EXPORT File { +public: + // Includes terminator char. + typedef char SHA1Digest[41]; + + virtual ~File() {} + virtual size_t getSize() = 0; + virtual const Bit8u *getData() = 0; + virtual const SHA1Digest &getSHA1() = 0; + + virtual void close() = 0; +}; + +class MT32EMU_EXPORT AbstractFile : public File { +public: + const SHA1Digest &getSHA1(); + +protected: + AbstractFile(); + AbstractFile(const SHA1Digest &sha1Digest); + +private: + bool sha1DigestCalculated; + SHA1Digest sha1Digest; + + // Binary compatibility helper. + void *reserved; +}; + +class MT32EMU_EXPORT ArrayFile : public AbstractFile { +public: + ArrayFile(const Bit8u *data, size_t size); + ArrayFile(const Bit8u *data, size_t size, const SHA1Digest &sha1Digest); + + size_t getSize(); + const Bit8u *getData(); + void close() {} + +private: + const Bit8u *data; + size_t size; +}; + +} // namespace MT32Emu + +#endif // #ifndef MT32EMU_FILE_H diff --git a/audio/softsynth/mt32/FileStream.cpp b/audio/softsynth/mt32/FileStream.cpp new file mode 100644 index 00000000000..768c5fcdabc --- /dev/null +++ b/audio/softsynth/mt32/FileStream.cpp @@ -0,0 +1,83 @@ +/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include "internals.h" + +#include "FileStream.h" + +namespace MT32Emu { + +using std::ios_base; + +FileStream::FileStream() : ifsp(*new std::ifstream), data(NULL), size(0) +{} + +FileStream::~FileStream() { + // destructor closes ifsp + delete &ifsp; + delete[] data; +} + +size_t FileStream::getSize() { + if (size != 0) { + return size; + } + if (!ifsp.is_open()) { + return 0; + } + ifsp.seekg(0, ios_base::end); + size = size_t(ifsp.tellg()); + return size; +} + +const Bit8u *FileStream::getData() { + if (data != NULL) { + return data; + } + if (!ifsp.is_open()) { + return NULL; + } + if (getSize() == 0) { + return NULL; + } + Bit8u *fileData = new Bit8u[size]; + if (fileData == NULL) { + return NULL; + } + ifsp.seekg(0); + ifsp.read(reinterpret_cast(fileData), std::streamsize(size)); + if (size_t(ifsp.tellg()) != size) { + delete[] fileData; + return NULL; + } + data = fileData; + close(); + return data; +} + +bool FileStream::open(const char *filename) { + ifsp.clear(); + ifsp.open(filename, ios_base::in | ios_base::binary); + return !ifsp.fail(); +} + +void FileStream::close() { + ifsp.close(); + ifsp.clear(); +} + +} // namespace MT32Emu diff --git a/audio/softsynth/mt32/FileStream.h b/audio/softsynth/mt32/FileStream.h new file mode 100644 index 00000000000..2de6e801ffc --- /dev/null +++ b/audio/softsynth/mt32/FileStream.h @@ -0,0 +1,46 @@ +/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#ifndef MT32EMU_FILE_STREAM_H +#define MT32EMU_FILE_STREAM_H + +#include + +#include "globals.h" +#include "Types.h" +#include "File.h" + +namespace MT32Emu { + +class FileStream : public AbstractFile { +public: + MT32EMU_EXPORT FileStream(); + MT32EMU_EXPORT ~FileStream(); + MT32EMU_EXPORT size_t getSize(); + MT32EMU_EXPORT const Bit8u *getData(); + MT32EMU_EXPORT bool open(const char *filename); + MT32EMU_EXPORT void close(); + +private: + std::ifstream &ifsp; + const Bit8u *data; + size_t size; +}; + +} // namespace MT32Emu + +#endif // #ifndef MT32EMU_FILE_STREAM_H diff --git a/audio/softsynth/mt32/LA32FloatWaveGenerator.cpp b/audio/softsynth/mt32/LA32FloatWaveGenerator.cpp index 42d820ebad9..824204e81bb 100644 --- a/audio/softsynth/mt32/LA32FloatWaveGenerator.cpp +++ b/audio/softsynth/mt32/LA32FloatWaveGenerator.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -15,10 +15,11 @@ * along with this program. If not, see . */ -//#include -#include "mt32emu.h" +#ifndef MT32EMU_LA32_WAVE_GENERATOR_CPP +#error This file should be included from LA32WaveGenerator.cpp only. +#endif + #include "mmath.h" -#include "internals.h" namespace MT32Emu { @@ -38,10 +39,10 @@ float LA32WaveGenerator::getPCMSample(unsigned int position) { return ((pcmSample & 32768) == 0) ? sampleValue : -sampleValue; } -void LA32WaveGenerator::initSynth(const bool sawtoothWaveform, const Bit8u pulseWidth, const Bit8u resonance) { - this->sawtoothWaveform = sawtoothWaveform; - this->pulseWidth = pulseWidth; - this->resonance = resonance; +void LA32WaveGenerator::initSynth(const bool useSawtoothWaveform, const Bit8u usePulseWidth, const Bit8u useResonance) { + sawtoothWaveform = useSawtoothWaveform; + pulseWidth = usePulseWidth; + resonance = useResonance; wavePos = 0.0f; lastFreq = 0.0f; @@ -50,24 +51,24 @@ void LA32WaveGenerator::initSynth(const bool sawtoothWaveform, const Bit8u pulse active = true; } -void LA32WaveGenerator::initPCM(const Bit16s * const pcmWaveAddress, const Bit32u pcmWaveLength, const bool pcmWaveLooped, const bool pcmWaveInterpolated) { - this->pcmWaveAddress = pcmWaveAddress; - this->pcmWaveLength = pcmWaveLength; - this->pcmWaveLooped = pcmWaveLooped; - this->pcmWaveInterpolated = pcmWaveInterpolated; +void LA32WaveGenerator::initPCM(const Bit16s * const usePCMWaveAddress, const Bit32u usePCMWaveLength, const bool usePCMWaveLooped, const bool usePCMWaveInterpolated) { + pcmWaveAddress = usePCMWaveAddress; + pcmWaveLength = usePCMWaveLength; + pcmWaveLooped = usePCMWaveLooped; + pcmWaveInterpolated = usePCMWaveInterpolated; pcmPosition = 0.0f; active = true; } +// ampVal - Logarithmic amp of the wave generator +// pitch - Logarithmic frequency of the resulting wave +// cutoffRampVal - Composed of the base cutoff in range [78..178] left-shifted by 18 bits and the TVF modifier float LA32WaveGenerator::generateNextSample(const Bit32u ampVal, const Bit16u pitch, const Bit32u cutoffRampVal) { if (!active) { return 0.0f; } - this->amp = amp; - this->pitch = pitch; - float sample = 0.0f; // SEMI-CONFIRMED: From sample analysis: @@ -284,9 +285,9 @@ bool LA32WaveGenerator::isPCMWave() const { return pcmWaveAddress != NULL; } -void LA32PartialPair::init(const bool ringModulated, const bool mixed) { - this->ringModulated = ringModulated; - this->mixed = mixed; +void LA32PartialPair::init(const bool useRingModulated, const bool useMixed) { + ringModulated = useRingModulated; + mixed = useMixed; masterOutputSample = 0.0f; slaveOutputSample = 0.0f; } @@ -354,4 +355,4 @@ bool LA32PartialPair::isActive(const PairType useMaster) const { return useMaster == MASTER ? master.isActive() : slave.isActive(); } -} +} // namespace MT32Emu diff --git a/audio/softsynth/mt32/LA32FloatWaveGenerator.h b/audio/softsynth/mt32/LA32FloatWaveGenerator.h index 329e5de2585..89b6fe479a1 100644 --- a/audio/softsynth/mt32/LA32FloatWaveGenerator.h +++ b/audio/softsynth/mt32/LA32FloatWaveGenerator.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -16,14 +16,15 @@ */ #ifndef MT32EMU_LA32_WAVE_GENERATOR_H -#define MT32EMU_LA32_WAVE_GENERATOR_H +#error This file should be included from LA32WaveGenerator.h only. +#endif namespace MT32Emu { /** * LA32WaveGenerator is aimed to represent the exact model of LA32 wave generator. * The output square wave is created by adding high / low linear segments in-between - * the rising and falling cosine segments. Basically, it’s very similar to the phase distortion synthesis. + * the rising and falling cosine segments. Basically, it's very similar to the phase distortion synthesis. * Behaviour of a true resonance filter is emulated by adding decaying sine wave. * The beginning and the ending of the resonant sine is multiplied by a cosine window. * To synthesise sawtooth waves, the resulting square wave is multiplied by synchronous cosine wave. @@ -38,12 +39,6 @@ class LA32WaveGenerator { // True means the resulting square wave is to be multiplied by the synchronous cosine bool sawtoothWaveform; - // Logarithmic amp of the wave generator - Bit32u amp; - - // Logarithmic frequency of the resulting wave - Bit16u pitch; - // Values in range [1..31] // Value 1 correspong to the minimum resonance Bit8u resonance; @@ -53,9 +48,6 @@ class LA32WaveGenerator { // Value 255 corresponds to the maximum possible asymmetric of the resulting wave Bit8u pulseWidth; - // Composed of the base cutoff in range [78..178] left-shifted by 18 bits and the TVF modifier - Bit32u cutoffVal; - // Logarithmic PCM sample start address const Bit16s *pcmWaveAddress; @@ -96,7 +88,7 @@ public: // Return true if the WG engine generates PCM wave samples bool isPCMWave() const; -}; +}; // class LA32WaveGenerator // LA32PartialPair contains a structure of two partials being mixed / ring modulated class LA32PartialPair { @@ -135,8 +127,6 @@ public: // Return active state of the WG engine bool isActive(const PairType master) const; -}; +}; // class LA32PartialPair } // namespace MT32Emu - -#endif // #ifndef MT32EMU_LA32_WAVE_GENERATOR_H diff --git a/audio/softsynth/mt32/LA32Ramp.cpp b/audio/softsynth/mt32/LA32Ramp.cpp index 2b31a330d2f..a4da4f57a88 100644 --- a/audio/softsynth/mt32/LA32Ramp.cpp +++ b/audio/softsynth/mt32/LA32Ramp.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -47,12 +47,12 @@ We haven't fully explored: - Values when ramping between levels (though this is probably correct). - Transition timing (may not be 100% accurate, especially for very fast ramps). */ -//#include -#include "mt32emu.h" -#include "mmath.h" #include "internals.h" +#include "LA32Ramp.h" +#include "Tables.h" + namespace MT32Emu { // SEMI-CONFIRMED from sample analysis. @@ -152,4 +152,4 @@ void LA32Ramp::reset() { interruptRaised = false; } -} +} // namespace MT32Emu diff --git a/audio/softsynth/mt32/LA32Ramp.h b/audio/softsynth/mt32/LA32Ramp.h index 796b4d1eec1..5e4ddf720ee 100644 --- a/audio/softsynth/mt32/LA32Ramp.h +++ b/audio/softsynth/mt32/LA32Ramp.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -18,6 +18,9 @@ #ifndef MT32EMU_LA32RAMP_H #define MT32EMU_LA32RAMP_H +#include "globals.h" +#include "Types.h" + namespace MT32Emu { class LA32Ramp { @@ -38,6 +41,6 @@ public: void reset(); }; -} +} // namespace MT32Emu -#endif /* TVA_H_ */ +#endif // #ifndef MT32EMU_LA32RAMP_H diff --git a/audio/softsynth/mt32/LA32WaveGenerator.cpp b/audio/softsynth/mt32/LA32WaveGenerator.cpp index 765f75fa618..a9c425beac5 100644 --- a/audio/softsynth/mt32/LA32WaveGenerator.cpp +++ b/audio/softsynth/mt32/LA32WaveGenerator.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -15,15 +15,19 @@ * along with this program. If not, see . */ -#if MT32EMU_USE_FLOAT_SAMPLES -#include "LA32FloatWaveGenerator.cpp" -#else +#include -//#include -#include "mt32emu.h" -#include "mmath.h" #include "internals.h" +#include "LA32WaveGenerator.h" +#include "Tables.h" + +#if MT32EMU_USE_FLOAT_SAMPLES +#define MT32EMU_LA32_WAVE_GENERATOR_CPP +#include "LA32FloatWaveGenerator.cpp" +#undef MT32EMU_LA32_WAVE_GENERATOR_CPP +#else + namespace MT32Emu { static const Bit32u SINE_SEGMENT_RELATIVE_LENGTH = 1 << 18; @@ -50,7 +54,7 @@ Bit16s LA32Utilites::unlog(const LogSample &logSample) { void LA32Utilites::addLogSamples(LogSample &logSample1, const LogSample &logSample2) { Bit32u logSampleValue = logSample1.logValue + logSample2.logValue; - logSample1.logValue = logSampleValue < 65536 ? (Bit16u)logSampleValue : 65535; + logSample1.logValue = logSampleValue < 65536 ? Bit16u(logSampleValue) : 65535; logSample1.sign = logSample1.sign == logSample2.sign ? LogSample::POSITIVE : LogSample::NEGATIVE; } @@ -130,9 +134,7 @@ void LA32WaveGenerator::advancePosition() { Bit32u lowLinearLength = (resonanceWaveLengthFactor << 8) - 4 * SINE_SEGMENT_RELATIVE_LENGTH - highLinearLength; computePositions(highLinearLength, lowLinearLength, resonanceWaveLengthFactor); - // resonancePhase computation hack - int *resonancePhaseAlias = (int *)&resonancePhase; - *resonancePhaseAlias = ((resonanceSinePosition >> 18) + (phase > POSITIVE_FALLING_SINE_SEGMENT ? 2 : 0)) & 3; + resonancePhase = ResonancePhase(((resonanceSinePosition >> 18) + (phase > POSITIVE_FALLING_SINE_SEGMENT ? 2 : 0)) & 3); } void LA32WaveGenerator::generateNextSquareWaveLogSample() { @@ -158,7 +160,7 @@ void LA32WaveGenerator::generateNextSquareWaveLogSample() { logSampleValue += (MIDDLE_CUTOFF_VALUE - cutoffVal) >> 9; } - squareLogSample.logValue = logSampleValue < 65536 ? (Bit16u)logSampleValue : 65535; + squareLogSample.logValue = logSampleValue < 65536 ? Bit16u(logSampleValue) : 65535; squareLogSample.sign = phase < NEGATIVE_FALLING_SINE_SEGMENT ? LogSample::POSITIVE : LogSample::NEGATIVE; } @@ -198,7 +200,7 @@ void LA32WaveGenerator::generateNextResonanceWaveLogSample() { // After all the amp decrements are added, it should be safe now to adjust the amp of the resonance wave to what we see on captures logSampleValue -= 1 << 12; - resonanceLogSample.logValue = logSampleValue < 65536 ? (Bit16u)logSampleValue : 65535; + resonanceLogSample.logValue = logSampleValue < 65536 ? Bit16u(logSampleValue) : 65535; resonanceLogSample.sign = resonancePhase < NEGATIVE_FALLING_RESONANCE_SINE_SEGMENT ? LogSample::POSITIVE : LogSample::NEGATIVE; } @@ -216,7 +218,7 @@ void LA32WaveGenerator::generateNextSawtoothCosineLogSample(LogSample &logSample void LA32WaveGenerator::pcmSampleToLogSample(LogSample &logSample, const Bit16s pcmSample) const { Bit32u logSampleValue = (32787 - (pcmSample & 32767)) << 1; logSampleValue += amp >> 10; - logSample.logValue = logSampleValue < 65536 ? (Bit16u)logSampleValue : 65535; + logSample.logValue = logSampleValue < 65536 ? Bit16u(logSampleValue) : 65535; logSample.sign = pcmSample < 0 ? LogSample::NEGATIVE : LogSample::POSITIVE; } @@ -377,7 +379,7 @@ Bit16s LA32PartialPair::unlogAndMixWGOutput(const LA32WaveGenerator &wg) { Bit16s firstSample = LA32Utilites::unlog(wg.getOutputLogSample(true)); Bit16s secondSample = LA32Utilites::unlog(wg.getOutputLogSample(false)); if (wg.isPCMWave()) { - return Bit16s(firstSample + ((Bit32s(secondSample - firstSample) * wg.getPCMInterpolationFactor()) >> 7)); + return Bit16s(firstSample + (((Bit32s(secondSample) - Bit32s(firstSample)) * wg.getPCMInterpolationFactor()) >> 7)); } return firstSample + secondSample; } @@ -407,7 +409,7 @@ Bit16s LA32PartialPair::nextOutSample() { Bit16s slaveSample = slave.isPCMWave() ? LA32Utilites::unlog(slave.getOutputLogSample(true)) : unlogAndMixWGOutput(slave); slaveSample <<= 2; slaveSample >>= 2; - Bit16s ringModulatedSample = Bit16s(((Bit32s)masterSample * (Bit32s)slaveSample) >> 13); + Bit16s ringModulatedSample = Bit16s((Bit32s(masterSample) * Bit32s(slaveSample)) >> 13); return mixed ? nonOverdrivenMasterSample + ringModulatedSample : ringModulatedSample; } @@ -423,6 +425,6 @@ bool LA32PartialPair::isActive(const PairType useMaster) const { return useMaster == MASTER ? master.isActive() : slave.isActive(); } -} +} // namespace MT32Emu #endif // #if MT32EMU_USE_FLOAT_SAMPLES diff --git a/audio/softsynth/mt32/LA32WaveGenerator.h b/audio/softsynth/mt32/LA32WaveGenerator.h index 212abe2b19c..6a40e44c98d 100644 --- a/audio/softsynth/mt32/LA32WaveGenerator.h +++ b/audio/softsynth/mt32/LA32WaveGenerator.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -15,13 +15,17 @@ * along with this program. If not, see . */ +#ifndef MT32EMU_LA32_WAVE_GENERATOR_H +#define MT32EMU_LA32_WAVE_GENERATOR_H + +#include "globals.h" +#include "internals.h" +#include "Types.h" + #if MT32EMU_USE_FLOAT_SAMPLES #include "LA32FloatWaveGenerator.h" #else -#ifndef MT32EMU_LA32_WAVE_GENERATOR_H -#define MT32EMU_LA32_WAVE_GENERATOR_H - namespace MT32Emu { /** @@ -55,7 +59,7 @@ public: /** * LA32WaveGenerator is aimed to represent the exact model of LA32 wave generator. * The output square wave is created by adding high / low linear segments in-between - * the rising and falling cosine segments. Basically, it’s very similar to the phase distortion synthesis. + * the rising and falling cosine segments. Basically, it's very similar to the phase distortion synthesis. * Behaviour of a true resonance filter is emulated by adding decaying sine wave. * The beginning and the ending of the resonant sine is multiplied by a cosine window. * To synthesise sawtooth waves, the resulting square wave is multiplied by synchronous cosine wave. @@ -143,7 +147,7 @@ class LA32WaveGenerator { } phase; // Current phase of the resonance wave - enum { + enum ResonancePhase { POSITIVE_RISING_RESONANCE_SINE_SEGMENT, POSITIVE_FALLING_RESONANCE_SINE_SEGMENT, NEGATIVE_FALLING_RESONANCE_SINE_SEGMENT, @@ -200,7 +204,7 @@ public: // Return current PCM interpolation factor Bit32u getPCMInterpolationFactor() const; -}; +}; // class LA32WaveGenerator // LA32PartialPair contains a structure of two partials being mixed / ring modulated class LA32PartialPair { @@ -239,10 +243,10 @@ public: // Return active state of the WG engine bool isActive(const PairType master) const; -}; +}; // class LA32PartialPair } // namespace MT32Emu -#endif // #ifndef MT32EMU_LA32_WAVE_GENERATOR_H - #endif // #if MT32EMU_USE_FLOAT_SAMPLES + +#endif // #ifndef MT32EMU_LA32_WAVE_GENERATOR_H diff --git a/audio/softsynth/mt32/MemoryRegion.h b/audio/softsynth/mt32/MemoryRegion.h index c0cb041e115..f8d7da18f83 100644 --- a/audio/softsynth/mt32/MemoryRegion.h +++ b/audio/softsynth/mt32/MemoryRegion.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -18,12 +18,20 @@ #ifndef MT32EMU_MEMORY_REGION_H #define MT32EMU_MEMORY_REGION_H +#include + +#include "globals.h" +#include "Types.h" +#include "Structures.h" + namespace MT32Emu { enum MemoryRegionType { MR_PatchTemp, MR_RhythmTemp, MR_TimbreTemp, MR_Patches, MR_Timbres, MR_System, MR_Display, MR_Reset }; +class Synth; + class MemoryRegion { private: Synth *synth; @@ -84,7 +92,7 @@ public: } void read(unsigned int entry, unsigned int off, Bit8u *dst, unsigned int len) const; void write(unsigned int entry, unsigned int off, const Bit8u *src, unsigned int len, bool init = false) const; -}; +}; // class MemoryRegion class PatchTempMemoryRegion : public MemoryRegion { public: @@ -112,13 +120,13 @@ public: }; class DisplayMemoryRegion : public MemoryRegion { public: - DisplayMemoryRegion(Synth *useSynth) : MemoryRegion(useSynth, NULL, NULL, MR_Display, MT32EMU_MEMADDR(0x200000), MAX_SYSEX_SIZE - 1, 1) {} + DisplayMemoryRegion(Synth *useSynth) : MemoryRegion(useSynth, NULL, NULL, MR_Display, MT32EMU_MEMADDR(0x200000), SYSEX_BUFFER_SIZE - 1, 1) {} }; class ResetMemoryRegion : public MemoryRegion { public: ResetMemoryRegion(Synth *useSynth) : MemoryRegion(useSynth, NULL, NULL, MR_Reset, MT32EMU_MEMADDR(0x7F0000), 0x3FFF, 1) {} }; -} +} // namespace MT32Emu -#endif +#endif // #ifndef MT32EMU_MEMORY_REGION_H diff --git a/audio/softsynth/mt32/MidiEventQueue.h b/audio/softsynth/mt32/MidiEventQueue.h index b1948c5f8ea..1a5ff0ad335 100644 --- a/audio/softsynth/mt32/MidiEventQueue.h +++ b/audio/softsynth/mt32/MidiEventQueue.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -18,6 +18,9 @@ #ifndef MT32EMU_MIDI_EVENT_QUEUE_H #define MT32EMU_MIDI_EVENT_QUEUE_H +#include "globals.h" +#include "Types.h" + namespace MT32Emu { /** @@ -60,8 +63,9 @@ public: const MidiEvent *peekMidiEvent(); void dropMidiEvent(); bool isFull() const; + bool inline isEmpty() const; }; -} +} // namespace MT32Emu -#endif +#endif // #ifndef MT32EMU_MIDI_EVENT_QUEUE_H diff --git a/audio/softsynth/mt32/MidiStreamParser.cpp b/audio/softsynth/mt32/MidiStreamParser.cpp new file mode 100644 index 00000000000..f9ead3369c0 --- /dev/null +++ b/audio/softsynth/mt32/MidiStreamParser.cpp @@ -0,0 +1,289 @@ +/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "internals.h" + +#include "MidiStreamParser.h" +#include "Synth.h" + +using namespace MT32Emu; + +DefaultMidiStreamParser::DefaultMidiStreamParser(Synth &useSynth, Bit32u initialStreamBufferCapacity) : + MidiStreamParser(initialStreamBufferCapacity), synth(useSynth), timestampSet(false) {} + +void DefaultMidiStreamParser::setTimestamp(const Bit32u useTimestamp) { + timestampSet = true; + timestamp = useTimestamp; +} + +void DefaultMidiStreamParser::resetTimestamp() { + timestampSet = false; +} + +void DefaultMidiStreamParser::handleShortMessage(const Bit32u message) { + do { + if (timestampSet) { + if (synth.playMsg(message, timestamp)) return; + } + else { + if (synth.playMsg(message)) return; + } + } while (synth.reportHandler->onMIDIQueueOverflow()); +} + +void DefaultMidiStreamParser::handleSysex(const Bit8u *stream, const Bit32u length) { + do { + if (timestampSet) { + if (synth.playSysex(stream, length, timestamp)) return; + } + else { + if (synth.playSysex(stream, length)) return; + } + } while (synth.reportHandler->onMIDIQueueOverflow()); +} + +void DefaultMidiStreamParser::handleSystemRealtimeMessage(const Bit8u realtime) { + synth.reportHandler->onMIDISystemRealtime(realtime); +} + +void DefaultMidiStreamParser::printDebug(const char *debugMessage) { + synth.printDebug("%s", debugMessage); +} + +MidiStreamParser::MidiStreamParser(Bit32u initialStreamBufferCapacity) : + MidiStreamParserImpl(*this, *this, initialStreamBufferCapacity) {} + +MidiStreamParserImpl::MidiStreamParserImpl(MidiReceiver &useReceiver, MidiReporter &useReporter, Bit32u initialStreamBufferCapacity) : + midiReceiver(useReceiver), midiReporter(useReporter) +{ + if (initialStreamBufferCapacity < SYSEX_BUFFER_SIZE) initialStreamBufferCapacity = SYSEX_BUFFER_SIZE; + if (MAX_STREAM_BUFFER_SIZE < initialStreamBufferCapacity) initialStreamBufferCapacity = MAX_STREAM_BUFFER_SIZE; + streamBufferCapacity = initialStreamBufferCapacity; + streamBuffer = new Bit8u[streamBufferCapacity]; + streamBufferSize = 0; + runningStatus = 0; + + reserved = NULL; +} + +MidiStreamParserImpl::~MidiStreamParserImpl() { + delete[] streamBuffer; +} + +void MidiStreamParserImpl::parseStream(const Bit8u *stream, Bit32u length) { + while (length > 0) { + Bit32u parsedMessageLength = 0; + if (0xF8 <= *stream) { + // Process System Realtime immediately and go on + midiReceiver.handleSystemRealtimeMessage(*stream); + parsedMessageLength = 1; + // No effect on the running status + } else if (streamBufferSize > 0) { + // Check if there is something in streamBuffer waiting for being processed + if (*streamBuffer == 0xF0) { + parsedMessageLength = parseSysexFragment(stream, length); + } else { + parsedMessageLength = parseShortMessageDataBytes(stream, length); + } + } else { + if (*stream == 0xF0) { + runningStatus = 0; // SysEx clears the running status + parsedMessageLength = parseSysex(stream, length); + } else { + parsedMessageLength = parseShortMessageStatus(stream); + } + } + + // Parsed successfully + stream += parsedMessageLength; + length -= parsedMessageLength; + } +} + +void MidiStreamParserImpl::processShortMessage(const Bit32u message) { + // Adds running status to the MIDI message if it doesn't contain one + Bit8u status = Bit8u(message); + if (0xF8 <= status) { + midiReceiver.handleSystemRealtimeMessage(status); + } else if (processStatusByte(status)) { + midiReceiver.handleShortMessage((message << 8) | status); + } else if (0x80 <= status) { // If no running status available yet, skip this message + midiReceiver.handleShortMessage(message); + } +} + +// We deal with SysEx messages below 512 bytes long in most cases. Nevertheless, it seems reasonable to support a possibility +// to load bulk dumps using a single message. However, this is known to fail with a real device due to limited input buffer size. +bool MidiStreamParserImpl::checkStreamBufferCapacity(const bool preserveContent) { + if (streamBufferSize < streamBufferCapacity) return true; + if (streamBufferCapacity < MAX_STREAM_BUFFER_SIZE) { + Bit8u *oldStreamBuffer = streamBuffer; + streamBufferCapacity = MAX_STREAM_BUFFER_SIZE; + streamBuffer = new Bit8u[streamBufferCapacity]; + if (preserveContent) memcpy(streamBuffer, oldStreamBuffer, streamBufferSize); + delete[] oldStreamBuffer; + return true; + } + return false; +} + +// Checks input byte whether it is a status byte. If not, replaces it with running status when available. +// Returns true if the input byte was changed to running status. +bool MidiStreamParserImpl::processStatusByte(Bit8u &status) { + if (status < 0x80) { + // First byte isn't status, try running status + if (runningStatus < 0x80) { + // No running status available yet + midiReporter.printDebug("processStatusByte: No valid running status yet, MIDI message ignored"); + return false; + } + status = runningStatus; + return true; + } else if (status < 0xF0) { + // Store current status as running for a Voice message + runningStatus = status; + } else if (status < 0xF8) { + // System Common clears running status + runningStatus = 0; + } // System Realtime doesn't affect running status + return false; +} + +// Returns # of bytes parsed +Bit32u MidiStreamParserImpl::parseShortMessageStatus(const Bit8u stream[]) { + Bit8u status = *stream; + Bit32u parsedLength = processStatusByte(status) ? 0 : 1; + if (0x80 <= status) { // If no running status available yet, skip one byte + *streamBuffer = status; + ++streamBufferSize; + } + return parsedLength; +} + +// Returns # of bytes parsed +Bit32u MidiStreamParserImpl::parseShortMessageDataBytes(const Bit8u stream[], Bit32u length) { + const Bit32u shortMessageLength = Synth::getShortMessageLength(*streamBuffer); + Bit32u parsedLength = 0; + + // Append incoming bytes to streamBuffer + while ((streamBufferSize < shortMessageLength) && (length-- > 0)) { + Bit8u dataByte = *(stream++); + if (dataByte < 0x80) { + // Add data byte to streamBuffer + streamBuffer[streamBufferSize++] = dataByte; + } else if (dataByte < 0xF8) { + // Discard invalid bytes and start over + char s[128]; + sprintf(s, "parseShortMessageDataBytes: Invalid short message: status %02x, expected length %i, actual %i -> ignored", *streamBuffer, shortMessageLength, streamBufferSize); + midiReporter.printDebug(s); + streamBufferSize = 0; // Clear streamBuffer + return parsedLength; + } else { + // Bypass System Realtime message + midiReceiver.handleSystemRealtimeMessage(dataByte); + } + ++parsedLength; + } + if (streamBufferSize < shortMessageLength) return parsedLength; // Still lacks data bytes + + // Assemble short message + Bit32u shortMessage = streamBuffer[0]; + for (Bit32u i = 1; i < shortMessageLength; ++i) { + shortMessage |= streamBuffer[i] << (i << 3); + } + midiReceiver.handleShortMessage(shortMessage); + streamBufferSize = 0; // Clear streamBuffer + return parsedLength; +} + +// Returns # of bytes parsed +Bit32u MidiStreamParserImpl::parseSysex(const Bit8u stream[], const Bit32u length) { + // Find SysEx length + Bit32u sysexLength = 1; + while (sysexLength < length) { + Bit8u nextByte = stream[sysexLength++]; + if (0x80 <= nextByte) { + if (nextByte == 0xF7) { + // End of SysEx + midiReceiver.handleSysex(stream, sysexLength); + return sysexLength; + } + if (0xF8 <= nextByte) { + // The System Realtime message must be processed right after return + // but the SysEx is actually fragmented and to be reconstructed in streamBuffer + --sysexLength; + break; + } + // Illegal status byte in SysEx message, aborting + midiReporter.printDebug("parseSysex: SysEx message lacks end-of-sysex (0xf7), ignored"); + // Continue parsing from that point + return sysexLength - 1; + } + } + + // Store incomplete SysEx message for further processing + streamBufferSize = sysexLength; + if (checkStreamBufferCapacity(false)) { + memcpy(streamBuffer, stream, sysexLength); + } else { + // Not enough buffer capacity, don't care about the real buffer content, just mark the first byte + *streamBuffer = *stream; + streamBufferSize = streamBufferCapacity; + } + return sysexLength; +} + +// Returns # of bytes parsed +Bit32u MidiStreamParserImpl::parseSysexFragment(const Bit8u stream[], const Bit32u length) { + Bit32u parsedLength = 0; + while (parsedLength < length) { + Bit8u nextByte = stream[parsedLength++]; + if (nextByte < 0x80) { + // Add SysEx data byte to streamBuffer + if (checkStreamBufferCapacity(true)) streamBuffer[streamBufferSize++] = nextByte; + continue; + } + if (0xF8 <= nextByte) { + // Bypass System Realtime message + midiReceiver.handleSystemRealtimeMessage(nextByte); + continue; + } + if (nextByte != 0xF7) { + // Illegal status byte in SysEx message, aborting + midiReporter.printDebug("parseSysexFragment: SysEx message lacks end-of-sysex (0xf7), ignored"); + // Clear streamBuffer and continue parsing from that point + streamBufferSize = 0; + --parsedLength; + break; + } + // End of SysEx + if (checkStreamBufferCapacity(true)) { + streamBuffer[streamBufferSize++] = nextByte; + midiReceiver.handleSysex(streamBuffer, streamBufferSize); + streamBufferSize = 0; // Clear streamBuffer + break; + } + // Encountered streamBuffer overrun + midiReporter.printDebug("parseSysexFragment: streamBuffer overrun while receiving SysEx message, ignored. Max allowed size of fragmented SysEx is 32768 bytes."); + streamBufferSize = 0; // Clear streamBuffer + break; + } + return parsedLength; +} diff --git a/audio/softsynth/mt32/MidiStreamParser.h b/audio/softsynth/mt32/MidiStreamParser.h new file mode 100644 index 00000000000..bd31b77c897 --- /dev/null +++ b/audio/softsynth/mt32/MidiStreamParser.h @@ -0,0 +1,124 @@ +/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#ifndef MT32EMU_MIDI_STREAM_PARSER_H +#define MT32EMU_MIDI_STREAM_PARSER_H + +#include "globals.h" +#include "Types.h" + +namespace MT32Emu { + +class Synth; + +// Interface for a user-supplied class to receive parsed well-formed MIDI messages. +class MT32EMU_EXPORT MidiReceiver { +public: + // Invoked when a complete short MIDI message is parsed in the input MIDI stream. + virtual void handleShortMessage(const Bit32u message) = 0; + + // Invoked when a complete well-formed System Exclusive MIDI message is parsed in the input MIDI stream. + virtual void handleSysex(const Bit8u stream[], const Bit32u length) = 0; + + // Invoked when a System Realtime MIDI message is parsed in the input MIDI stream. + virtual void handleSystemRealtimeMessage(const Bit8u realtime) = 0; + +protected: + ~MidiReceiver() {} +}; + +// Interface for a user-supplied class to receive notifications of input MIDI stream parse errors. +class MT32EMU_EXPORT MidiReporter { +public: + // Invoked when an error occurs during processing the input MIDI stream. + virtual void printDebug(const char *debugMessage) = 0; + +protected: + ~MidiReporter() {} +}; + +// Provides a context for parsing a stream of MIDI events coming from a single source. +// There can be multiple MIDI sources feeding MIDI events to a single Synth object. +// NOTE: Calls from multiple threads which feed a single Synth object with data must be explicitly synchronised, +// although, no synchronisation is required with the rendering thread. +class MT32EMU_EXPORT MidiStreamParserImpl { +public: + // The first two arguments provide for implementations of essential interfaces needed. + // The third argument specifies streamBuffer initial capacity. The buffer capacity should be large enough to fit the longest SysEx expected. + // If a longer SysEx occurs, streamBuffer is reallocated to the maximum size of MAX_STREAM_BUFFER_SIZE (32768 bytes). + // Default capacity is SYSEX_BUFFER_SIZE (1000 bytes) which is enough to fit SysEx messages in common use. + MidiStreamParserImpl(MidiReceiver &, MidiReporter &, Bit32u initialStreamBufferCapacity = SYSEX_BUFFER_SIZE); + virtual ~MidiStreamParserImpl(); + + // Parses a block of raw MIDI bytes. All the parsed MIDI messages are sent in sequence to the user-supplied methods for further processing. + // SysEx messages are allowed to be fragmented across several calls to this method. Running status is also handled for short messages. + // NOTE: the total length of a SysEx message being fragmented shall not exceed MAX_STREAM_BUFFER_SIZE (32768 bytes). + void parseStream(const Bit8u *stream, Bit32u length); + + // Convenience method which accepts a Bit32u-encoded short MIDI message and sends it to the user-supplied method for further processing. + // The short MIDI message may contain no status byte, the running status is used in this case. + void processShortMessage(const Bit32u message); + +private: + Bit8u runningStatus; + Bit8u *streamBuffer; + Bit32u streamBufferCapacity; + Bit32u streamBufferSize; + MidiReceiver &midiReceiver; + MidiReporter &midiReporter; + + // Binary compatibility helper. + void *reserved; + + bool checkStreamBufferCapacity(const bool preserveContent); + bool processStatusByte(Bit8u &status); + Bit32u parseShortMessageStatus(const Bit8u stream[]); + Bit32u parseShortMessageDataBytes(const Bit8u stream[], Bit32u length); + Bit32u parseSysex(const Bit8u stream[], const Bit32u length); + Bit32u parseSysexFragment(const Bit8u stream[], const Bit32u length); +}; // class MidiStreamParserImpl + +// An abstract class that provides a context for parsing a stream of MIDI events coming from a single source. +class MT32EMU_EXPORT MidiStreamParser : public MidiStreamParserImpl, protected MidiReceiver, protected MidiReporter { +public: + // The argument specifies streamBuffer initial capacity. The buffer capacity should be large enough to fit the longest SysEx expected. + // If a longer SysEx occurs, streamBuffer is reallocated to the maximum size of MAX_STREAM_BUFFER_SIZE (32768 bytes). + // Default capacity is SYSEX_BUFFER_SIZE (1000 bytes) which is enough to fit SysEx messages in common use. + explicit MidiStreamParser(Bit32u initialStreamBufferCapacity = SYSEX_BUFFER_SIZE); +}; + +class MT32EMU_EXPORT DefaultMidiStreamParser : public MidiStreamParser { +public: + explicit DefaultMidiStreamParser(Synth &synth, Bit32u initialStreamBufferCapacity = SYSEX_BUFFER_SIZE); + void setTimestamp(const Bit32u useTimestamp); + void resetTimestamp(); + +protected: + void handleShortMessage(const Bit32u message); + void handleSysex(const Bit8u *stream, const Bit32u length); + void handleSystemRealtimeMessage(const Bit8u realtime); + void printDebug(const char *debugMessage); + +private: + Synth &synth; + bool timestampSet; + Bit32u timestamp; +}; + +} // namespace MT32Emu + +#endif // MT32EMU_MIDI_STREAM_PARSER_H diff --git a/audio/softsynth/mt32/Part.cpp b/audio/softsynth/mt32/Part.cpp index cffc3ed7440..9b5119ee8bb 100644 --- a/audio/softsynth/mt32/Part.cpp +++ b/audio/softsynth/mt32/Part.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -15,12 +15,16 @@ * along with this program. If not, see . */ -//#include -//#include +#include +#include -#include "mt32emu.h" #include "internals.h" + +#include "Part.h" +#include "Partial.h" #include "PartialManager.h" +#include "Poly.h" +#include "Synth.h" namespace MT32Emu { @@ -112,7 +116,7 @@ Bit32s Part::getPitchBend() const { void Part::setBend(unsigned int midiBend) { // CONFIRMED: - pitchBend = (((signed)midiBend - 8192) * pitchBenderRange) >> 14; // PORTABILITY NOTE: Assumes arithmetic shift + pitchBend = ((signed(midiBend) - 8192) * pitchBenderRange) >> 14; // PORTABILITY NOTE: Assumes arithmetic shift } Bit8u Part::getModulation() const { @@ -120,7 +124,7 @@ Bit8u Part::getModulation() const { } void Part::setModulation(unsigned int midiModulation) { - modulation = (Bit8u)midiModulation; + modulation = Bit8u(midiModulation); } void Part::resetAllControllers() { @@ -162,7 +166,7 @@ void Part::refresh() { patchCache[t].reverb = patchTemp->patch.reverbSwitch > 0; } memcpy(currentInstr, timbreTemp->common.name, 10); - synth->newTimbreSet(partNum, patchTemp->patch.timbreGroup, currentInstr); + synth->newTimbreSet(partNum, patchTemp->patch.timbreGroup, patchTemp->patch.timbreNum, currentInstr); updatePitchBenderRange(); } @@ -255,26 +259,26 @@ void Part::cacheTimbre(PatchCache cache[4], const TimbreParam *timbre) { switch (t) { case 0: - cache[t].PCMPartial = (PartialStruct[(int)timbre->common.partialStructure12] & 0x2) ? true : false; - cache[t].structureMix = PartialMixStruct[(int)timbre->common.partialStructure12]; + cache[t].PCMPartial = (PartialStruct[int(timbre->common.partialStructure12)] & 0x2) ? true : false; + cache[t].structureMix = PartialMixStruct[int(timbre->common.partialStructure12)]; cache[t].structurePosition = 0; cache[t].structurePair = 1; break; case 1: - cache[t].PCMPartial = (PartialStruct[(int)timbre->common.partialStructure12] & 0x1) ? true : false; - cache[t].structureMix = PartialMixStruct[(int)timbre->common.partialStructure12]; + cache[t].PCMPartial = (PartialStruct[int(timbre->common.partialStructure12)] & 0x1) ? true : false; + cache[t].structureMix = PartialMixStruct[int(timbre->common.partialStructure12)]; cache[t].structurePosition = 1; cache[t].structurePair = 0; break; case 2: - cache[t].PCMPartial = (PartialStruct[(int)timbre->common.partialStructure34] & 0x2) ? true : false; - cache[t].structureMix = PartialMixStruct[(int)timbre->common.partialStructure34]; + cache[t].PCMPartial = (PartialStruct[int(timbre->common.partialStructure34)] & 0x2) ? true : false; + cache[t].structureMix = PartialMixStruct[int(timbre->common.partialStructure34)]; cache[t].structurePosition = 0; cache[t].structurePair = 3; break; case 3: - cache[t].PCMPartial = (PartialStruct[(int)timbre->common.partialStructure34] & 0x1) ? true : false; - cache[t].structureMix = PartialMixStruct[(int)timbre->common.partialStructure34]; + cache[t].PCMPartial = (PartialStruct[int(timbre->common.partialStructure34)] & 0x1) ? true : false; + cache[t].structureMix = PartialMixStruct[int(timbre->common.partialStructure34)]; cache[t].structurePosition = 1; cache[t].structurePair = 2; break; @@ -308,7 +312,7 @@ const char *Part::getName() const { void Part::setVolume(unsigned int midiVolume) { // CONFIRMED: This calculation matches the table used in the control ROM - patchTemp->outputLevel = (Bit8u)(midiVolume * 100 / 127); + patchTemp->outputLevel = Bit8u(midiVolume * 100 / 127); //synth->printDebug("%s (%s): Set volume to %d", name, currentInstr, midiVolume); } @@ -322,7 +326,7 @@ Bit8u Part::getExpression() const { void Part::setExpression(unsigned int midiExpression) { // CONFIRMED: This calculation matches the table used in the control ROM - expression = (Bit8u)(midiExpression * 100 / 127); + expression = Bit8u(midiExpression * 100 / 127); } void RhythmPart::setPan(unsigned int midiPan) { @@ -337,9 +341,9 @@ void Part::setPan(unsigned int midiPan) { // NOTE: Panning is inverted compared to GM. // CM-32L: Divide by 8.5 - patchTemp->panpot = (Bit8u)((midiPan << 3) / 68); + patchTemp->panpot = Bit8u((midiPan << 3) / 68); // FIXME: MT-32: Divide by 9 - //patchTemp->panpot = (Bit8u)(midiPan / 9); + //patchTemp->panpot = Bit8u(midiPan / 9); //synth->printDebug("%s (%s): Set pan to %d", name, currentInstr, panpot); } @@ -372,7 +376,8 @@ void RhythmPart::noteOn(unsigned int midiKey, unsigned int velocity) { unsigned int key = midiKey; unsigned int drumNum = key - 24; int drumTimbreNum = rhythmTemp[drumNum].timbre; - if (drumTimbreNum >= 127) { // 94 on MT-32 + const int drumTimbreCount = 64 + synth->controlROMMap->timbreRCount; // 94 on MT-32, 128 on LAPC-I/CM32-L + if (drumTimbreNum == 127 || drumTimbreNum >= drumTimbreCount) { // timbre #127 is OFF, no sense to play it synth->printDebug("%s: Attempted to play unmapped key %d (velocity %d)", name, midiKey, velocity); return; } @@ -507,7 +512,7 @@ void Part::playPoly(const PatchCache cache[4], const MemParams::RhythmTemp *rhyt #if MT32EMU_MONITOR_PARTIALS > 1 synth->printPartialUsage(); #endif - synth->polyStateChanged(partNum); + synth->reportHandler->onPolyStateChanged(Bit8u(partNum)); } void Part::allNotesOff() { @@ -593,16 +598,14 @@ void Part::partialDeactivated(Poly *poly) { if (!poly->isActive()) { activePolys.remove(poly); synth->partialManager->polyFreed(poly); - synth->polyStateChanged(partNum); + synth->reportHandler->onPolyStateChanged(Bit8u(partNum)); } } -//#define POLY_LIST_DEBUG - PolyList::PolyList() : firstPoly(NULL), lastPoly(NULL) {} bool PolyList::isEmpty() const { -#ifdef POLY_LIST_DEBUG +#ifdef MT32EMU_POLY_LIST_DEBUG if ((firstPoly == NULL || lastPoly == NULL) && firstPoly != lastPoly) { printf("PolyList: desynchronised firstPoly & lastPoly pointers\n"); } @@ -619,7 +622,7 @@ Poly *PolyList::getLast() const { } void PolyList::prepend(Poly *poly) { -#ifdef POLY_LIST_DEBUG +#ifdef MT32EMU_POLY_LIST_DEBUG if (poly->getNext() != NULL) { printf("PolyList: Non-NULL next field in a Poly being prepended is ignored\n"); } @@ -632,14 +635,14 @@ void PolyList::prepend(Poly *poly) { } void PolyList::append(Poly *poly) { -#ifdef POLY_LIST_DEBUG +#ifdef MT32EMU_POLY_LIST_DEBUG if (poly->getNext() != NULL) { printf("PolyList: Non-NULL next field in a Poly being appended is ignored\n"); } #endif poly->setNext(NULL); if (lastPoly != NULL) { -#ifdef POLY_LIST_DEBUG +#ifdef MT32EMU_POLY_LIST_DEBUG if (lastPoly->getNext() != NULL) { printf("PolyList: Non-NULL next field in the lastPoly\n"); } @@ -656,7 +659,7 @@ Poly *PolyList::takeFirst() { Poly *oldFirst = firstPoly; firstPoly = oldFirst->getNext(); if (firstPoly == NULL) { -#ifdef POLY_LIST_DEBUG +#ifdef MT32EMU_POLY_LIST_DEBUG if (lastPoly != oldFirst) { printf("PolyList: firstPoly != lastPoly in a list with a single Poly\n"); } @@ -675,7 +678,7 @@ void PolyList::remove(Poly * const polyToRemove) { for (Poly *poly = firstPoly; poly != NULL; poly = poly->getNext()) { if (poly->getNext() == polyToRemove) { if (polyToRemove == lastPoly) { -#ifdef POLY_LIST_DEBUG +#ifdef MT32EMU_POLY_LIST_DEBUG if (lastPoly->getNext() != NULL) { printf("PolyList: Non-NULL next field in the lastPoly\n"); } @@ -689,4 +692,4 @@ void PolyList::remove(Poly * const polyToRemove) { } } -} +} // namespace MT32Emu diff --git a/audio/softsynth/mt32/Part.h b/audio/softsynth/mt32/Part.h index b458fb39882..f5171589d63 100644 --- a/audio/softsynth/mt32/Part.h +++ b/audio/softsynth/mt32/Part.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -18,9 +18,14 @@ #ifndef MT32EMU_PART_H #define MT32EMU_PART_H +#include "globals.h" +#include "internals.h" +#include "Types.h" +#include "Structures.h" + namespace MT32Emu { -class PartialManager; +class Poly; class Synth; class PolyList { @@ -123,7 +128,7 @@ public: // Abort the first poly in PolyState_HELD, or if none exists, the first active poly in any state. bool abortFirstPolyPreferHeld(); bool abortFirstPoly(); -}; +}; // class Part class RhythmPart: public Part { // Pointer to the area of the MT-32's memory dedicated to rhythm @@ -143,5 +148,6 @@ public: void setProgram(unsigned int patchNum); }; -} -#endif +} // namespace MT32Emu + +#endif // #ifndef MT32EMU_PART_H diff --git a/audio/softsynth/mt32/Partial.cpp b/audio/softsynth/mt32/Partial.cpp index 73480875098..6afef364df1 100644 --- a/audio/softsynth/mt32/Partial.cpp +++ b/audio/softsynth/mt32/Partial.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -15,14 +15,19 @@ * along with this program. If not, see . */ -//#include -//#include -//#include +#include -#include "mt32emu.h" -#include "mmath.h" #include "internals.h" +#include "Partial.h" +#include "Part.h" +#include "Poly.h" +#include "Synth.h" +#include "Tables.h" +#include "TVA.h" +#include "TVF.h" +#include "TVP.h" + namespace MT32Emu { static const Bit8u PAN_NUMERATOR_MASTER[] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7}; @@ -54,7 +59,7 @@ int Partial::debugGetPartialNum() const { } // Only used for debugging purposes -unsigned long Partial::debugGetSampleNum() const { +Bit32u Partial::debugGetSampleNum() const { return sampleNum; } @@ -266,7 +271,7 @@ void Partial::backupCache(const PatchCache &cache) { } } -bool Partial::produceOutput(Sample *leftBuf, Sample *rightBuf, unsigned long length) { +bool Partial::produceOutput(Sample *leftBuf, Sample *rightBuf, Bit32u length) { if (!isActive() || alreadyOutputed || isRingModulatingSlave()) { return false; } @@ -313,8 +318,8 @@ bool Partial::produceOutput(Sample *leftBuf, Sample *rightBuf, unsigned long len // Though, it is unknown whether this overflow is exploited somewhere. Sample leftOut = Sample((sample * leftPanValue) >> 8); Sample rightOut = Sample((sample * rightPanValue) >> 8); - *leftBuf = Synth::clipSampleEx((SampleEx)*leftBuf + (SampleEx)leftOut); - *rightBuf = Synth::clipSampleEx((SampleEx)*rightBuf + (SampleEx)rightOut); + *leftBuf = Synth::clipSampleEx(SampleEx(*leftBuf) + SampleEx(leftOut)); + *rightBuf = Synth::clipSampleEx(SampleEx(*rightBuf) + SampleEx(rightOut)); leftBuf++; rightBuf++; #endif @@ -341,4 +346,4 @@ void Partial::startDecayAll() { tvf->startDecay(); } -} +} // namespace MT32Emu diff --git a/audio/softsynth/mt32/Partial.h b/audio/softsynth/mt32/Partial.h index 749468135dd..187665de512 100644 --- a/audio/softsynth/mt32/Partial.h +++ b/audio/softsynth/mt32/Partial.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -18,11 +18,21 @@ #ifndef MT32EMU_PARTIAL_H #define MT32EMU_PARTIAL_H +#include "globals.h" +#include "internals.h" +#include "Types.h" +#include "Structures.h" +#include "LA32Ramp.h" +#include "LA32WaveGenerator.h" + namespace MT32Emu { -class Synth; class Part; +class Poly; +class Synth; class TVA; +class TVF; +class TVP; struct ControlROMPCMStruct; // A partial represents one of up to four waveform generators currently playing within a poly. @@ -32,7 +42,7 @@ private: const int debugPartialNum; // Only used for debugging // Number of the sample currently being rendered by produceOutput(), or 0 if no run is in progress // This is only kept available for debugging purposes. - unsigned long sampleNum; + Bit32u sampleNum; // Actually, this is a 4-bit register but we abuse this to emulate inverted mixing. // Also we double the value to enable INACCURATE_SMOOTH_PAN, with respect to MoK. @@ -77,7 +87,7 @@ public: ~Partial(); int debugGetPartialNum() const; - unsigned long debugGetSampleNum() const; + Bit32u debugGetSampleNum() const; int getOwnerPart() const; const Poly *getPoly() const; @@ -100,9 +110,9 @@ public: // Returns true only if data written to buffer // This function (unlike the one below it) returns processed stereo samples // made from combining this single partial with its pair, if it has one. - bool produceOutput(Sample *leftBuf, Sample *rightBuf, unsigned long length); -}; + bool produceOutput(Sample *leftBuf, Sample *rightBuf, Bit32u length); +}; // class Partial -} +} // namespace MT32Emu -#endif +#endif // #ifndef MT32EMU_PARTIAL_H diff --git a/audio/softsynth/mt32/PartialManager.cpp b/audio/softsynth/mt32/PartialManager.cpp index 8ca6e4e3d7b..7c702f75821 100644 --- a/audio/softsynth/mt32/PartialManager.cpp +++ b/audio/softsynth/mt32/PartialManager.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -15,11 +15,16 @@ * along with this program. If not, see . */ -//#include +#include +#include -#include "mt32emu.h" #include "internals.h" + #include "PartialManager.h" +#include "Part.h" +#include "Partial.h" +#include "Poly.h" +#include "Synth.h" namespace MT32Emu { @@ -275,7 +280,7 @@ void PartialManager::polyFreed(Poly *poly) { const Poly *activePoly = synth->getPart(partNum)->getFirstActivePoly(); Bit32u polyCount = 0; while (activePoly != NULL) { - activePoly->getNext(); + activePoly = activePoly->getNext(); polyCount++; } synth->printDebug("Part: %i, active poly count: %i\n", partNum, polyCount); @@ -286,4 +291,4 @@ void PartialManager::polyFreed(Poly *poly) { freePolys[firstFreePolyIndex] = poly; } -} +} // namespace MT32Emu diff --git a/audio/softsynth/mt32/PartialManager.h b/audio/softsynth/mt32/PartialManager.h index f2e8767bbb5..b2908a5c213 100644 --- a/audio/softsynth/mt32/PartialManager.h +++ b/audio/softsynth/mt32/PartialManager.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -18,8 +18,15 @@ #ifndef MT32EMU_PARTIALMANAGER_H #define MT32EMU_PARTIALMANAGER_H +#include "globals.h" +#include "internals.h" +#include "Types.h" + namespace MT32Emu { +class Part; +class Partial; +class Poly; class Synth; class PartialManager { @@ -49,8 +56,8 @@ public: const Partial *getPartial(unsigned int partialNum) const; Poly *assignPolyToPart(Part *part); void polyFreed(Poly *poly); -}; +}; // class PartialManager -} +} // namespace MT32Emu -#endif +#endif // #ifndef MT32EMU_PARTIALMANAGER_H diff --git a/audio/softsynth/mt32/Poly.cpp b/audio/softsynth/mt32/Poly.cpp index badcd8fb965..9a3948cf114 100644 --- a/audio/softsynth/mt32/Poly.cpp +++ b/audio/softsynth/mt32/Poly.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -15,9 +15,15 @@ * along with this program. If not, see . */ -#include "mt32emu.h" +#include + #include "internals.h" +#include "Poly.h" +#include "Part.h" +#include "Partial.h" +#include "Synth.h" + namespace MT32Emu { Poly::Poly() { @@ -181,4 +187,4 @@ void Poly::setNext(Poly *poly) { next = poly; } -} +} // namespace MT32Emu diff --git a/audio/softsynth/mt32/Poly.h b/audio/softsynth/mt32/Poly.h index e2614369bb7..4b283231c80 100644 --- a/audio/softsynth/mt32/Poly.h +++ b/audio/softsynth/mt32/Poly.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -18,17 +18,14 @@ #ifndef MT32EMU_POLY_H #define MT32EMU_POLY_H +#include "globals.h" +#include "internals.h" + namespace MT32Emu { class Part; class Partial; - -enum PolyState { - POLY_Playing, - POLY_Held, // This marks keys that have been released on the keyboard, but are being held by the pedal - POLY_Releasing, - POLY_Inactive -}; +struct PatchCache; class Poly { private: @@ -66,8 +63,8 @@ public: Poly *getNext() const; void setNext(Poly *poly); -}; +}; // class Poly -} +} // namespace MT32Emu -#endif /* POLY_H_ */ +#endif // #ifndef MT32EMU_POLY_H diff --git a/audio/softsynth/mt32/ROMInfo.cpp b/audio/softsynth/mt32/ROMInfo.cpp index f6817c1a4d0..ce78e693aa1 100644 --- a/audio/softsynth/mt32/ROMInfo.cpp +++ b/audio/softsynth/mt32/ROMInfo.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -15,27 +15,27 @@ * along with this program. If not, see . */ -//#include +#include + +#include "internals.h" + #include "ROMInfo.h" namespace MT32Emu { -static const ROMInfo *getKnownROMInfoFromList(unsigned int index) { - static const ControlROMFeatureSet MT32_COMPATIBLE(true, true); - static const ControlROMFeatureSet CM32L_COMPATIBLE(false, false); - +static const ROMInfo *getKnownROMInfoFromList(Bit32u index) { // Known ROMs - static const ROMInfo CTRL_MT32_V1_04 = {65536, "5a5cb5a77d7d55ee69657c2f870416daed52dea7", ROMInfo::Control, "ctrl_mt32_1_04", "MT-32 Control v1.04", ROMInfo::Full, NULL, &MT32_COMPATIBLE}; - static const ROMInfo CTRL_MT32_V1_05 = {65536, "e17a3a6d265bf1fa150312061134293d2b58288c", ROMInfo::Control, "ctrl_mt32_1_05", "MT-32 Control v1.05", ROMInfo::Full, NULL, &MT32_COMPATIBLE}; - static const ROMInfo CTRL_MT32_V1_06 = {65536, "a553481f4e2794c10cfe597fef154eef0d8257de", ROMInfo::Control, "ctrl_mt32_1_06", "MT-32 Control v1.06", ROMInfo::Full, NULL, &MT32_COMPATIBLE}; - static const ROMInfo CTRL_MT32_V1_07 = {65536, "b083518fffb7f66b03c23b7eb4f868e62dc5a987", ROMInfo::Control, "ctrl_mt32_1_07", "MT-32 Control v1.07", ROMInfo::Full, NULL, &MT32_COMPATIBLE}; - static const ROMInfo CTRL_MT32_BLUER = {65536, "7b8c2a5ddb42fd0732e2f22b3340dcf5360edf92", ROMInfo::Control, "ctrl_mt32_bluer", "MT-32 Control BlueRidge", ROMInfo::Full, NULL, &MT32_COMPATIBLE}; + static const ROMInfo CTRL_MT32_V1_04 = {65536, "5a5cb5a77d7d55ee69657c2f870416daed52dea7", ROMInfo::Control, "ctrl_mt32_1_04", "MT-32 Control v1.04", ROMInfo::Full, NULL}; + static const ROMInfo CTRL_MT32_V1_05 = {65536, "e17a3a6d265bf1fa150312061134293d2b58288c", ROMInfo::Control, "ctrl_mt32_1_05", "MT-32 Control v1.05", ROMInfo::Full, NULL}; + static const ROMInfo CTRL_MT32_V1_06 = {65536, "a553481f4e2794c10cfe597fef154eef0d8257de", ROMInfo::Control, "ctrl_mt32_1_06", "MT-32 Control v1.06", ROMInfo::Full, NULL}; + static const ROMInfo CTRL_MT32_V1_07 = {65536, "b083518fffb7f66b03c23b7eb4f868e62dc5a987", ROMInfo::Control, "ctrl_mt32_1_07", "MT-32 Control v1.07", ROMInfo::Full, NULL}; + static const ROMInfo CTRL_MT32_BLUER = {65536, "7b8c2a5ddb42fd0732e2f22b3340dcf5360edf92", ROMInfo::Control, "ctrl_mt32_bluer", "MT-32 Control BlueRidge", ROMInfo::Full, NULL}; - static const ROMInfo CTRL_CM32L_V1_00 = {65536, "73683d585cd6948cc19547942ca0e14a0319456d", ROMInfo::Control, "ctrl_cm32l_1_00", "CM-32L/LAPC-I Control v1.00", ROMInfo::Full, NULL, &CM32L_COMPATIBLE}; - static const ROMInfo CTRL_CM32L_V1_02 = {65536, "a439fbb390da38cada95a7cbb1d6ca199cd66ef8", ROMInfo::Control, "ctrl_cm32l_1_02", "CM-32L/LAPC-I Control v1.02", ROMInfo::Full, NULL, &CM32L_COMPATIBLE}; + static const ROMInfo CTRL_CM32L_V1_00 = {65536, "73683d585cd6948cc19547942ca0e14a0319456d", ROMInfo::Control, "ctrl_cm32l_1_00", "CM-32L/LAPC-I Control v1.00", ROMInfo::Full, NULL}; + static const ROMInfo CTRL_CM32L_V1_02 = {65536, "a439fbb390da38cada95a7cbb1d6ca199cd66ef8", ROMInfo::Control, "ctrl_cm32l_1_02", "CM-32L/LAPC-I Control v1.02", ROMInfo::Full, NULL}; - static const ROMInfo PCM_MT32 = {524288, "f6b1eebc4b2d200ec6d3d21d51325d5b48c60252", ROMInfo::PCM, "pcm_mt32", "MT-32 PCM ROM", ROMInfo::Full, NULL, NULL}; - static const ROMInfo PCM_CM32L = {1048576, "289cc298ad532b702461bfc738009d9ebe8025ea", ROMInfo::PCM, "pcm_cm32l", "CM-32L/CM-64/LAPC-I PCM ROM", ROMInfo::Full, NULL, NULL}; + static const ROMInfo PCM_MT32 = {524288, "f6b1eebc4b2d200ec6d3d21d51325d5b48c60252", ROMInfo::PCM, "pcm_mt32", "MT-32 PCM ROM", ROMInfo::Full, NULL}; + static const ROMInfo PCM_CM32L = {1048576, "289cc298ad532b702461bfc738009d9ebe8025ea", ROMInfo::PCM, "pcm_cm32l", "CM-32L/CM-64/LAPC-I PCM ROM", ROMInfo::Full, NULL}; static const ROMInfo * const ROM_INFOS[] = { &CTRL_MT32_V1_04, @@ -52,23 +52,11 @@ static const ROMInfo *getKnownROMInfoFromList(unsigned int index) { return ROM_INFOS[index]; } -const ROMInfo* ROMInfo::getROMInfo(Common::File *file) { - size_t fileSize = file->size(); - Common::String fileName = file->getName(); - fileName.toUppercase(); - bool isCM32LROM = fileName.hasPrefix("CM32L_"); - // We haven't added the SHA1 checksum code in ScummVM, as the file size - // and ROM name suffices for our needs for now. - //const char *fileDigest = file->getSHA1(); - for (int i = 0; getKnownROMInfoFromList(i) != NULL; i++) { +const ROMInfo* ROMInfo::getROMInfo(File *file) { + size_t fileSize = file->getSize(); + for (Bit32u i = 0; getKnownROMInfoFromList(i) != NULL; i++) { const ROMInfo *romInfo = getKnownROMInfoFromList(i); - if (fileSize == romInfo->fileSize /*&& !strcmp(fileDigest, romInfo->sha1Digest)*/) { - if (fileSize == 65536) { - // If we are looking for a CM-32L ROM, make sure we return the first matching - // CM-32L ROM from the list, instead of the first matching MT-32 ROM - if (isCM32LROM && romInfo->controlROMFeatures->isDefaultReverbMT32Compatible()) - continue; - } + if (fileSize == romInfo->fileSize && !strcmp(file->getSHA1(), romInfo->sha1Digest)) { return romInfo; } } @@ -79,17 +67,17 @@ void ROMInfo::freeROMInfo(const ROMInfo *romInfo) { (void) romInfo; } -static int getROMCount() { - int count; +static Bit32u getROMCount() { + Bit32u count; for(count = 0; getKnownROMInfoFromList(count) != NULL; count++) { } return count; } -const ROMInfo** ROMInfo::getROMInfoList(unsigned int types, unsigned int pairTypes) { +const ROMInfo** ROMInfo::getROMInfoList(Bit32u types, Bit32u pairTypes) { const ROMInfo **romInfoList = new const ROMInfo*[getROMCount() + 1]; const ROMInfo **currentROMInList = romInfoList; - for(int i = 0; getKnownROMInfoFromList(i) != NULL; i++) { + for (Bit32u i = 0; getKnownROMInfoFromList(i) != NULL; i++) { const ROMInfo *romInfo = getKnownROMInfoFromList(i); if ((types & (1 << romInfo->type)) && (pairTypes & (1 << romInfo->pairType))) { *currentROMInList++ = romInfo; @@ -103,19 +91,22 @@ void ROMInfo::freeROMInfoList(const ROMInfo **romInfoList) { delete[] romInfoList; } -const ROMImage* ROMImage::makeROMImage(Common::File *file) { - ROMImage *romImage = new ROMImage; - romImage->file = file; - romImage->romInfo = ROMInfo::getROMInfo(romImage->file); - return romImage; +ROMImage::ROMImage(File *useFile) : file(useFile), romInfo(ROMInfo::getROMInfo(file)) +{} + +ROMImage::~ROMImage() { + ROMInfo::freeROMInfo(romInfo); +} + +const ROMImage* ROMImage::makeROMImage(File *file) { + return new ROMImage(file); } void ROMImage::freeROMImage(const ROMImage *romImage) { - ROMInfo::freeROMInfo(romImage->romInfo); delete romImage; } -Common::File* ROMImage::getFile() const { +File* ROMImage::getFile() const { return file; } @@ -123,17 +114,4 @@ const ROMInfo* ROMImage::getROMInfo() const { return romInfo; } -ControlROMFeatureSet::ControlROMFeatureSet(bool useDefaultReverbMT32Compatible, bool useOldMT32AnalogLPF) : - defaultReverbMT32Compatible(useDefaultReverbMT32Compatible), - oldMT32AnalogLPF(useOldMT32AnalogLPF) -{} - -bool ControlROMFeatureSet::isDefaultReverbMT32Compatible() const { - return defaultReverbMT32Compatible; -} - -bool ControlROMFeatureSet::isOldMT32AnalogLPF() const { - return oldMT32AnalogLPF; -} - -} +} // namespace MT32Emu diff --git a/audio/softsynth/mt32/ROMInfo.h b/audio/softsynth/mt32/ROMInfo.h index 4682620a159..8a5ad141b6e 100644 --- a/audio/softsynth/mt32/ROMInfo.h +++ b/audio/softsynth/mt32/ROMInfo.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -18,73 +18,63 @@ #ifndef MT32EMU_ROMINFO_H #define MT32EMU_ROMINFO_H -//#include -#include "common/file.h" +#include + +#include "globals.h" +#include "File.h" namespace MT32Emu { -struct ControlROMFeatureSet; - // Defines vital info about ROM file to be used by synth and applications struct ROMInfo { public: size_t fileSize; - const char *sha1Digest; + const File::SHA1Digest &sha1Digest; enum Type {PCM, Control, Reverb} type; const char *shortName; const char *description; enum PairType {Full, FirstHalf, SecondHalf, Mux0, Mux1} pairType; ROMInfo *pairROMInfo; - const ControlROMFeatureSet *controlROMFeatures; // Returns a ROMInfo struct by inspecting the size and the SHA1 hash - static const ROMInfo* getROMInfo(Common::File *file); + MT32EMU_EXPORT static const ROMInfo* getROMInfo(File *file); // Currently no-op - static void freeROMInfo(const ROMInfo *romInfo); + MT32EMU_EXPORT static void freeROMInfo(const ROMInfo *romInfo); // Allows retrieving a NULL-terminated list of ROMInfos for a range of types and pairTypes // (specified by bitmasks) // Useful for GUI/console app to output information on what ROMs it supports - static const ROMInfo** getROMInfoList(unsigned int types, unsigned int pairTypes); + MT32EMU_EXPORT static const ROMInfo** getROMInfoList(Bit32u types, Bit32u pairTypes); // Frees the list of ROMInfos given - static void freeROMInfoList(const ROMInfo **romInfos); + MT32EMU_EXPORT static void freeROMInfoList(const ROMInfo **romInfos); }; // Synth::open() is to require a full control ROMImage and a full PCM ROMImage to work class ROMImage { private: - Common::File *file; - const ROMInfo *romInfo; + File * const file; + const ROMInfo * const romInfo; + + ROMImage(File *file); + ~ROMImage(); public: - // Creates a ROMImage object given a ROMInfo and a File. Keeps a reference // to the File and ROMInfo given, which must be freed separately by the user // after the ROMImage is freed - static const ROMImage* makeROMImage(Common::File *file); + MT32EMU_EXPORT static const ROMImage* makeROMImage(File *file); // Must only be done after all Synths using the ROMImage are deleted - static void freeROMImage(const ROMImage *romImage); + MT32EMU_EXPORT static void freeROMImage(const ROMImage *romImage); - Common::File *getFile() const; - const ROMInfo *getROMInfo() const; + MT32EMU_EXPORT File *getFile() const; + MT32EMU_EXPORT const ROMInfo *getROMInfo() const; }; -struct ControlROMFeatureSet { -private: - unsigned int defaultReverbMT32Compatible : 1; - unsigned int oldMT32AnalogLPF : 1; +} // namespace MT32Emu -public: - ControlROMFeatureSet(bool defaultReverbMT32Compatible, bool oldMT32AnalogLPF); - bool isDefaultReverbMT32Compatible() const; - bool isOldMT32AnalogLPF() const; -}; - -} - -#endif +#endif // #ifndef MT32EMU_ROMINFO_H diff --git a/audio/softsynth/mt32/SampleRateConverter.cpp b/audio/softsynth/mt32/SampleRateConverter.cpp new file mode 100644 index 00000000000..70f860f3952 --- /dev/null +++ b/audio/softsynth/mt32/SampleRateConverter.cpp @@ -0,0 +1,97 @@ +/* Copyright (C) 2015-2017 Sergey V. Mikayev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include "SampleRateConverter.h" + +#if MT32EMU_WITH_LIBSOXR_RESAMPLER +#include "srchelper/SoxrAdapter.h" +#elif MT32EMU_WITH_LIBSAMPLERATE_RESAMPLER +#include "srchelper/SamplerateAdapter.h" +#else +#include "srchelper/InternalResampler.h" +#endif + +#include "Synth.h" + +using namespace MT32Emu; + +static inline void *createDelegate(Synth &synth, double targetSampleRate, SampleRateConverter::Quality quality) { +#if MT32EMU_WITH_LIBSOXR_RESAMPLER + return new SoxrAdapter(synth, targetSampleRate, quality); +#elif MT32EMU_WITH_LIBSAMPLERATE_RESAMPLER + return new SamplerateAdapter(synth, targetSampleRate, quality); +#else + return new InternalResampler(synth, targetSampleRate, quality); +#endif +} + +AnalogOutputMode SampleRateConverter::getBestAnalogOutputMode(double targetSampleRate) { + if (Synth::getStereoOutputSampleRate(AnalogOutputMode_ACCURATE) < targetSampleRate) { + return AnalogOutputMode_OVERSAMPLED; + } else if (Synth::getStereoOutputSampleRate(AnalogOutputMode_COARSE) < targetSampleRate) { + return AnalogOutputMode_ACCURATE; + } + return AnalogOutputMode_COARSE; +} + +SampleRateConverter::SampleRateConverter(Synth &useSynth, double targetSampleRate, Quality useQuality) : + synthInternalToTargetSampleRateRatio(SAMPLE_RATE / targetSampleRate), + srcDelegate(createDelegate(useSynth, targetSampleRate, useQuality)) +{} + +SampleRateConverter::~SampleRateConverter() { +#if MT32EMU_WITH_LIBSOXR_RESAMPLER + delete static_cast(srcDelegate); +#elif MT32EMU_WITH_LIBSAMPLERATE_RESAMPLER + delete static_cast(srcDelegate); +#else + delete static_cast(srcDelegate); +#endif +} + +void SampleRateConverter::getOutputSamples(float *buffer, unsigned int length) { +#if MT32EMU_WITH_LIBSOXR_RESAMPLER + static_cast(srcDelegate)->getOutputSamples(buffer, length); +#elif MT32EMU_WITH_LIBSAMPLERATE_RESAMPLER + static_cast(srcDelegate)->getOutputSamples(buffer, length); +#else + static_cast(srcDelegate)->getOutputSamples(buffer, length); +#endif +} + +void SampleRateConverter::getOutputSamples(Bit16s *outBuffer, unsigned int length) { + static const unsigned int CHANNEL_COUNT = 2; + + float floatBuffer[CHANNEL_COUNT * MAX_SAMPLES_PER_RUN]; + while (length > 0) { + const unsigned int size = MAX_SAMPLES_PER_RUN < length ? MAX_SAMPLES_PER_RUN : length; + getOutputSamples(floatBuffer, size); + float *outs = floatBuffer; + float *ends = floatBuffer + CHANNEL_COUNT * size; + while (outs < ends) { + *(outBuffer++) = Synth::convertSample(*(outs++)); + } + length -= size; + } +} + +double SampleRateConverter::convertOutputToSynthTimestamp(double outputTimestamp) const { + return outputTimestamp * synthInternalToTargetSampleRateRatio; +} + +double SampleRateConverter::convertSynthToOutputTimestamp(double synthTimestamp) const { + return synthTimestamp / synthInternalToTargetSampleRateRatio; +} diff --git a/audio/softsynth/mt32/SampleRateConverter.h b/audio/softsynth/mt32/SampleRateConverter.h new file mode 100644 index 00000000000..24bce0891b9 --- /dev/null +++ b/audio/softsynth/mt32/SampleRateConverter.h @@ -0,0 +1,78 @@ +/* Copyright (C) 2015-2017 Sergey V. Mikayev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#ifndef SAMPLE_RATE_CONVERTER_H +#define SAMPLE_RATE_CONVERTER_H + +#include "globals.h" +#include "Types.h" +#include "Enumerations.h" + +namespace MT32Emu { + +class Synth; + +/* SampleRateConverter class allows to convert the synthesiser output to any desired sample rate. + * It processes the completely mixed stereo output signal as it passes the analogue circuit emulation, + * so emulating the synthesiser output signal passing further through an ADC. + * Several conversion quality options are provided which allow to trade-off the conversion speed vs. the passband width. + * All the options except FASTEST guarantee full suppression of the aliasing noise in terms of the 16-bit integer samples. + */ +class MT32EMU_EXPORT SampleRateConverter { +public: + enum Quality { + // Use this only when the speed is more important than the audio quality. + FASTEST, + FAST, + GOOD, + BEST + }; + + // Returns the value of AnalogOutputMode for which the output signal may retain its full frequency spectrum + // at the sample rate specified by the targetSampleRate argument. + static AnalogOutputMode getBestAnalogOutputMode(double targetSampleRate); + + // Creates a SampleRateConverter instance that converts output signal from the synth to the given sample rate + // with the specified conversion quality. + SampleRateConverter(Synth &synth, double targetSampleRate, Quality quality); + ~SampleRateConverter(); + + // Fills the provided output buffer with the results of the sample rate conversion. + // The input samples are automatically retrieved from the synth as necessary. + void getOutputSamples(MT32Emu::Bit16s *buffer, unsigned int length); + + // Fills the provided output buffer with the results of the sample rate conversion. + // The input samples are automatically retrieved from the synth as necessary. + void getOutputSamples(float *buffer, unsigned int length); + + // Returns the number of samples produced at the internal synth sample rate (32000 Hz) + // that correspond to the number of samples at the target sample rate. + // Intended to facilitate audio time synchronisation. + double convertOutputToSynthTimestamp(double outputTimestamp) const; + + // Returns the number of samples produced at the target sample rate + // that correspond to the number of samples at the internal synth sample rate (32000 Hz). + // Intended to facilitate audio time synchronisation. + double convertSynthToOutputTimestamp(double synthTimestamp) const; + +private: + const double synthInternalToTargetSampleRateRatio; + void * const srcDelegate; +}; // class SampleRateConverter + +} // namespace MT32Emu + +#endif // SAMPLE_RATE_CONVERTER_H diff --git a/audio/softsynth/mt32/Structures.h b/audio/softsynth/mt32/Structures.h index 4dada3a8478..6dcb9c86a91 100644 --- a/audio/softsynth/mt32/Structures.h +++ b/audio/softsynth/mt32/Structures.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -18,6 +18,9 @@ #ifndef MT32EMU_STRUCTURES_H #define MT32EMU_STRUCTURES_H +#include "globals.h" +#include "Types.h" + namespace MT32Emu { // MT32EMU_MEMADDR() converts from sysex-padded, MT32EMU_SYSEXMEMADDR converts to it @@ -102,8 +105,8 @@ struct TimbreParam { Bit8u envTime[5]; // 0-100 Bit8u envLevel[4]; // 0-100 // [3]: SUSTAIN LEVEL } MT32EMU_ALIGN_PACKED tva; - } MT32EMU_ALIGN_PACKED partial[4]; -} MT32EMU_ALIGN_PACKED; + } MT32EMU_ALIGN_PACKED partial[4]; // struct PartialParam +} MT32EMU_ALIGN_PACKED; // struct TimbreParam struct PatchParam { Bit8u timbreGroup; // TIMBRE GROUP 0-3 (group A, group B, Memory, Rhythm) @@ -163,7 +166,16 @@ struct MemParams { Bit8u chanAssign[9]; // MIDI CHANNEL (PART1) 0-16 (1-16,OFF) Bit8u masterVol; // MASTER VOLUME 0-100 } MT32EMU_ALIGN_PACKED system; -}; +}; // struct MemParams + +struct SoundGroup { + Bit8u timbreNumberTableAddrLow; + Bit8u timbreNumberTableAddrHigh; + Bit8u displayPosition; + Bit8u name[9]; + Bit8u timbreCount; + Bit8u pad; +} MT32EMU_ALIGN_PACKED; #if defined(_MSC_VER) || defined(__MINGW32__) #pragma pack(pop) @@ -171,10 +183,17 @@ struct MemParams { #pragma pack() #endif +struct ControlROMFeatureSet { + unsigned int quirkPitchEnvelopeOverflow : 1; + + // Features below don't actually depend on control ROM version, which is used to identify hardware model + unsigned int defaultReverbMT32Compatible : 1; + unsigned int oldMT32AnalogLPF : 1; +}; + struct ControlROMMap { - Bit16u idPos; - Bit16u idLen; - const char *idBytes; + const char *shortName; + const ControlROMFeatureSet &featureSet; Bit16u pcmTable; // 4 * pcmCount bytes Bit16u pcmCount; Bit16u timbreAMap; // 128 bytes @@ -194,6 +213,8 @@ struct ControlROMMap { Bit16u patchMaxTable; // 16 bytes Bit16u systemMaxTable; // 23 bytes Bit16u timbreMaxTable; // 72 bytes + Bit16u soundGroupsTable; // 14 bytes each entry + Bit16u soundGroupsCount; }; struct ControlROMPCMStruct { @@ -215,7 +236,7 @@ struct PatchCache { bool playPartial; bool PCMPartial; int pcm; - char waveform; + Bit8u waveform; Bit32u structureMix; int structurePosition; @@ -233,6 +254,6 @@ struct PatchCache { const TimbreParam::PartialParam *partialParam; }; -} +} // namespace MT32Emu -#endif +#endif // #ifndef MT32EMU_STRUCTURES_H diff --git a/audio/softsynth/mt32/Synth.cpp b/audio/softsynth/mt32/Synth.cpp index 6df7eb9e310..3a478b5b624 100644 --- a/audio/softsynth/mt32/Synth.cpp +++ b/audio/softsynth/mt32/Synth.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -15,42 +15,136 @@ * along with this program. If not, see . */ -//#include -//#include -//#include -//#include +#include -#include "mt32emu.h" -#include "mmath.h" #include "internals.h" +#include "Synth.h" #include "Analog.h" #include "BReverbModel.h" +#include "File.h" #include "MemoryRegion.h" #include "MidiEventQueue.h" +#include "Part.h" +#include "Partial.h" #include "PartialManager.h" +#include "Poly.h" +#include "ROMInfo.h" +#include "TVA.h" namespace MT32Emu { // MIDI interface data transfer rate in samples. Used to simulate the transfer delay. -static const double MIDI_DATA_TRANSFER_RATE = (double)SAMPLE_RATE / 31250.0 * 8.0; +static const double MIDI_DATA_TRANSFER_RATE = double(SAMPLE_RATE) / 31250.0 * 8.0; + +// FIXME: there should be more specific feature sets for various MT-32 control ROM versions +static const ControlROMFeatureSet OLD_MT32_COMPATIBLE = { true, true, true }; +static const ControlROMFeatureSet CM32L_COMPATIBLE = { false, false, false }; static const ControlROMMap ControlROMMaps[7] = { - // ID IDc IDbytes PCMmap PCMc tmbrA tmbrAO, tmbrAC tmbrB tmbrBO, tmbrBC tmbrR trC rhythm rhyC rsrv panpot prog rhyMax patMax sysMax timMax - {0x4014, 22, "\000 ver1.04 14 July 87 ", 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x73A6, 85, 0x57C7, 0x57E2, 0x57D0, 0x5252, 0x525E, 0x526E, 0x520A}, - {0x4014, 22, "\000 ver1.05 06 Aug, 87 ", 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x7414, 85, 0x57C7, 0x57E2, 0x57D0, 0x5252, 0x525E, 0x526E, 0x520A}, - {0x4014, 22, "\000 ver1.06 31 Aug, 87 ", 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x7414, 85, 0x57D9, 0x57F4, 0x57E2, 0x5264, 0x5270, 0x5280, 0x521C}, - {0x4010, 22, "\000 ver1.07 10 Oct, 87 ", 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x73fe, 85, 0x57B1, 0x57CC, 0x57BA, 0x523C, 0x5248, 0x5258, 0x51F4}, // MT-32 revision 1 - {0x4010, 22, "\000verX.XX 30 Sep, 88 ", 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x741C, 85, 0x57E5, 0x5800, 0x57EE, 0x5270, 0x527C, 0x528C, 0x5228}, // MT-32 Blue Ridge mod - {0x2205, 22, "\000CM32/LAPC1.00 890404", 0x8100, 256, 0x8000, 0x8000, false, 0x8080, 0x8000, false, 0x8500, 64, 0x8580, 85, 0x4F65, 0x4F80, 0x4F6E, 0x48A1, 0x48A5, 0x48BE, 0x48D5}, - {0x2205, 22, "\000CM32/LAPC1.02 891205", 0x8100, 256, 0x8000, 0x8000, true, 0x8080, 0x8000, true, 0x8500, 64, 0x8580, 85, 0x4F93, 0x4FAE, 0x4F9C, 0x48CB, 0x48CF, 0x48E8, 0x48FF} // CM-32L + // ID Features PCMmap PCMc tmbrA tmbrAO, tmbrAC tmbrB tmbrBO tmbrBC tmbrR trC rhythm rhyC rsrv panpot prog rhyMax patMax sysMax timMax sndGrp sGC + { "ctrl_mt32_1_04", OLD_MT32_COMPATIBLE, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x73A6, 85, 0x57C7, 0x57E2, 0x57D0, 0x5252, 0x525E, 0x526E, 0x520A, 0x7064, 19 }, + { "ctrl_mt32_1_05", OLD_MT32_COMPATIBLE, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x7414, 85, 0x57C7, 0x57E2, 0x57D0, 0x5252, 0x525E, 0x526E, 0x520A, 0x70CA, 19 }, + { "ctrl_mt32_1_06", OLD_MT32_COMPATIBLE, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x7414, 85, 0x57D9, 0x57F4, 0x57E2, 0x5264, 0x5270, 0x5280, 0x521C, 0x70CA, 19 }, + { "ctrl_mt32_1_07", OLD_MT32_COMPATIBLE, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x73fe, 85, 0x57B1, 0x57CC, 0x57BA, 0x523C, 0x5248, 0x5258, 0x51F4, 0x70B0, 19 }, // MT-32 revision 1 + {"ctrl_mt32_bluer", OLD_MT32_COMPATIBLE, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x741C, 85, 0x57E5, 0x5800, 0x57EE, 0x5270, 0x527C, 0x528C, 0x5228, 0x70CE, 19 }, // MT-32 Blue Ridge mod + {"ctrl_cm32l_1_00", CM32L_COMPATIBLE, 0x8100, 256, 0x8000, 0x8000, true, 0x8080, 0x8000, true, 0x8500, 64, 0x8580, 85, 0x4F65, 0x4F80, 0x4F6E, 0x48A1, 0x48A5, 0x48BE, 0x48D5, 0x5A6C, 19 }, + {"ctrl_cm32l_1_02", CM32L_COMPATIBLE, 0x8100, 256, 0x8000, 0x8000, true, 0x8080, 0x8000, true, 0x8500, 64, 0x8580, 85, 0x4F93, 0x4FAE, 0x4F9C, 0x48CB, 0x48CF, 0x48E8, 0x48FF, 0x5A96, 19 } // CM-32L // (Note that all but CM-32L ROM actually have 86 entries for rhythmTemp) }; -static inline void advanceStreamPosition(Sample *&stream, Bit32u posDelta) { - if (stream != NULL) { - stream += posDelta; +static const PartialState PARTIAL_PHASE_TO_STATE[8] = { + PartialState_ATTACK, PartialState_ATTACK, PartialState_ATTACK, PartialState_ATTACK, + PartialState_SUSTAIN, PartialState_SUSTAIN, PartialState_RELEASE, PartialState_INACTIVE +}; + +static inline PartialState getPartialState(PartialManager *partialManager, unsigned int partialNum) { + const Partial *partial = partialManager->getPartial(partialNum); + return partial->isActive() ? PARTIAL_PHASE_TO_STATE[partial->getTVA()->getPhase()] : PartialState_INACTIVE; +} + +class SampleFormatConverter { +protected: +#if MT32EMU_USE_FLOAT_SAMPLES + Bit16s *outBuffer; +#else + float *outBuffer; +#endif + +public: + Sample *sampleBuffer; + + SampleFormatConverter(Sample *buffer) : outBuffer(NULL), sampleBuffer(buffer) {} + + inline bool isConversionNeeded() { + return outBuffer != NULL; } + + inline void convert(Bit32u len) { + if (sampleBuffer == NULL) return; + if (outBuffer == NULL) { + sampleBuffer += len; + return; + } + Sample *inBuffer = sampleBuffer; + while (len--) { + *(outBuffer++) = Synth::convertSample(*(inBuffer++)); + } + } + + inline void addSilence(Bit32u len) { + if (outBuffer != NULL) { + Synth::muteSampleBuffer(outBuffer, len); + outBuffer += len; + } else if (sampleBuffer != NULL) { + Synth::muteSampleBuffer(sampleBuffer, len); + sampleBuffer += len; + } + } +}; + +template +class BufferedSampleFormatConverter : public SampleFormatConverter { + Sample renderingBuffer[BUFFER_SIZE_MULTIPLIER * MAX_SAMPLES_PER_RUN]; + +public: +#if MT32EMU_USE_FLOAT_SAMPLES + BufferedSampleFormatConverter(Bit16s *buffer) +#else + BufferedSampleFormatConverter(float *buffer) +#endif + : SampleFormatConverter(renderingBuffer) + { + outBuffer = buffer; + if (buffer == NULL) sampleBuffer = NULL; + } +}; + +class Renderer { + Synth &synth; + + // These buffers are used for building the output streams as they are found at the DAC entrance. + // The output is mixed down to stereo interleaved further in the analog circuitry emulation. + Sample tmpNonReverbLeft[MAX_SAMPLES_PER_RUN], tmpNonReverbRight[MAX_SAMPLES_PER_RUN]; + Sample tmpReverbDryLeft[MAX_SAMPLES_PER_RUN], tmpReverbDryRight[MAX_SAMPLES_PER_RUN]; + Sample tmpReverbWetLeft[MAX_SAMPLES_PER_RUN], tmpReverbWetRight[MAX_SAMPLES_PER_RUN]; + +public: + Renderer(Synth &useSynth) : synth(useSynth) {} + + void render(SampleFormatConverter &converter, Bit32u len); + void renderStreams(SampleFormatConverter &nonReverbLeft, SampleFormatConverter &nonReverbRight, SampleFormatConverter &reverbDryLeft, SampleFormatConverter &reverbDryRight, SampleFormatConverter &reverbWetLeft, SampleFormatConverter &reverbWetRight, Bit32u len); + void produceLA32Output(Sample *buffer, Bit32u len); + void convertSamplesToOutput(Sample *buffer, Bit32u len); + void doRenderStreams(DACOutputStreams &streams, Bit32u len); +}; + +Bit32u Synth::getLibraryVersionInt() { + return (MT32EMU_VERSION_MAJOR << 16) | (MT32EMU_VERSION_MINOR << 8) | (MT32EMU_VERSION_PATCH); +} + +const char *Synth::getLibraryVersionString() { + return MT32EMU_VERSION; } Bit8u Synth::calcSysexChecksum(const Bit8u *data, const Bit32u len, const Bit8u initChecksum) { @@ -61,10 +155,17 @@ Bit8u Synth::calcSysexChecksum(const Bit8u *data, const Bit32u len, const Bit8u return Bit8u(checksum & 0x7f); } -Synth::Synth(ReportHandler *useReportHandler) : mt32ram(*new MemParams()), mt32default(*new MemParams()) { - isOpen = false; +Bit32u Synth::getStereoOutputSampleRate(AnalogOutputMode analogOutputMode) { + static const unsigned int SAMPLE_RATES[] = {SAMPLE_RATE, SAMPLE_RATE, SAMPLE_RATE * 3 / 2, SAMPLE_RATE * 3}; + + return SAMPLE_RATES[analogOutputMode]; +} + +Synth::Synth(ReportHandler *useReportHandler) : mt32ram(*new MemParams), mt32default(*new MemParams), renderer(*new Renderer(*this)) { + opened = false; reverbOverridden = false; partialCount = DEFAULT_MAX_PARTIALS; + controlROMMap = NULL; controlROMFeatures = NULL; if (useReportHandler == NULL) { @@ -85,11 +186,27 @@ Synth::Synth(ReportHandler *useReportHandler) : mt32ram(*new MemParams()), mt32d setOutputGain(1.0f); setReverbOutputGain(1.0f); setReversedStereoEnabled(false); + + patchTempMemoryRegion = NULL; + rhythmTempMemoryRegion = NULL; + timbreTempMemoryRegion = NULL; + patchesMemoryRegion = NULL; + timbresMemoryRegion = NULL; + systemMemoryRegion = NULL; + displayMemoryRegion = NULL; + resetMemoryRegion = NULL; + paddedTimbreMaxTable = NULL; + partialManager = NULL; + pcmWaves = NULL; + pcmROMData = NULL; + soundGroupNames = NULL; midiQueue = NULL; lastReceivedMIDIEventTimestamp = 0; memset(parts, 0, sizeof(parts)); renderedSampleCount = 0; + + reserved = NULL; } Synth::~Synth() { @@ -99,47 +216,52 @@ Synth::~Synth() { } delete &mt32ram; delete &mt32default; + delete &renderer; } void ReportHandler::showLCDMessage(const char *data) { - // We cannot use printf here. Since we already implement our own - // ReportHandler we simply disable the default implementation since it is - // never called anyway. -#if 0 - printf("WRITE-LCD: %s", data); - printf("\n"); -#endif + printf("WRITE-LCD: %s\n", data); } void ReportHandler::printDebug(const char *fmt, va_list list) { - // We cannot use (v)printf here. Since we already implement our own - // ReportHandler we simply disable the default implementation since it is - // never called anyway. -#if 0 vprintf(fmt, list); printf("\n"); -#endif } -void Synth::polyStateChanged(int partNum) { - reportHandler->onPolyStateChanged(partNum); -} - -void Synth::newTimbreSet(int partNum, Bit8u timbreGroup, const char patchName[]) { - reportHandler->onProgramChanged(partNum, timbreGroup, patchName); +void Synth::newTimbreSet(Bit8u partNum, Bit8u timbreGroup, Bit8u timbreNumber, const char patchName[]) { + const char *soundGroupName; + switch (timbreGroup) { + case 1: + timbreNumber += 64; + // Fall-through + case 0: + soundGroupName = soundGroupNames[soundGroupIx[timbreNumber]]; + break; + case 2: + soundGroupName = soundGroupNames[controlROMMap->soundGroupsCount - 2]; + break; + case 3: + soundGroupName = soundGroupNames[controlROMMap->soundGroupsCount - 1]; + break; + default: + soundGroupName = NULL; + break; + } + reportHandler->onProgramChanged(partNum, soundGroupName, patchName); } void Synth::printDebug(const char *fmt, ...) { va_list ap; va_start(ap, fmt); #if MT32EMU_DEBUG_SAMPLESTAMPS > 0 - reportHandler->printDebug("[%u] ", (char *)&renderedSampleCount); + reportHandler->printDebug("[%u]", (va_list)&renderedSampleCount); #endif reportHandler->printDebug(fmt, ap); va_end(ap); } void Synth::setReverbEnabled(bool newReverbEnabled) { + if (!opened) return; if (isReverbEnabled() == newReverbEnabled) return; if (newReverbEnabled) { bool oldReverbOverridden = reverbOverridden; @@ -167,30 +289,23 @@ bool Synth::isReverbOverridden() const { } void Synth::setReverbCompatibilityMode(bool mt32CompatibleMode) { - if (reverbModels[REVERB_MODE_ROOM] != NULL) { - if (isMT32ReverbCompatibilityMode() == mt32CompatibleMode) return; - setReverbEnabled(false); - for (int i = 0; i < 4; i++) { - delete reverbModels[i]; - } - } - reverbModels[REVERB_MODE_ROOM] = new BReverbModel(REVERB_MODE_ROOM, mt32CompatibleMode); - reverbModels[REVERB_MODE_HALL] = new BReverbModel(REVERB_MODE_HALL, mt32CompatibleMode); - reverbModels[REVERB_MODE_PLATE] = new BReverbModel(REVERB_MODE_PLATE, mt32CompatibleMode); - reverbModels[REVERB_MODE_TAP_DELAY] = new BReverbModel(REVERB_MODE_TAP_DELAY, mt32CompatibleMode); -#if !MT32EMU_REDUCE_REVERB_MEMORY - for (int i = REVERB_MODE_ROOM; i <= REVERB_MODE_TAP_DELAY; i++) { - reverbModels[i]->open(); - } -#endif - if (isOpen) { - setReverbOutputGain(reverbOutputGain); - setReverbEnabled(true); + if (!opened || (isMT32ReverbCompatibilityMode() == mt32CompatibleMode)) return; + bool oldReverbEnabled = isReverbEnabled(); + setReverbEnabled(false); + for (int i = 0; i < 4; i++) { + delete reverbModels[i]; } + initReverbModels(mt32CompatibleMode); + setReverbEnabled(oldReverbEnabled); + setReverbOutputGain(reverbOutputGain); } bool Synth::isMT32ReverbCompatibilityMode() const { - return isOpen && (reverbModels[REVERB_MODE_ROOM]->isMT32Compatible(REVERB_MODE_ROOM)); + return opened && (reverbModels[REVERB_MODE_ROOM]->isMT32Compatible(REVERB_MODE_ROOM)); +} + +bool Synth::isDefaultReverbMT32Compatible() const { + return opened && controlROMFeatures->defaultReverbMT32Compatible; } void Synth::setDACInputMode(DACInputMode mode) { @@ -239,22 +354,18 @@ void Synth::setReversedStereoEnabled(bool enabled) { reversedStereoEnabled = enabled; } -bool Synth::isReversedStereoEnabled() { +bool Synth::isReversedStereoEnabled() const { return reversedStereoEnabled; } bool Synth::loadControlROM(const ROMImage &controlROMImage) { - Common::File *file = controlROMImage.getFile(); + File *file = controlROMImage.getFile(); const ROMInfo *controlROMInfo = controlROMImage.getROMInfo(); if ((controlROMInfo == NULL) || (controlROMInfo->type != ROMInfo::Control) || (controlROMInfo->pairType != ROMInfo::Full)) { - return false; - } - controlROMFeatures = controlROMImage.getROMInfo()->controlROMFeatures; - if (controlROMFeatures == NULL) { #if MT32EMU_MONITOR_INIT - printDebug("Invalid Control ROM Info provided without feature set"); + printDebug("Invalid Control ROM Info provided"); #endif return false; } @@ -262,13 +373,16 @@ bool Synth::loadControlROM(const ROMImage &controlROMImage) { #if MT32EMU_MONITOR_INIT printDebug("Found Control ROM: %s, %s", controlROMInfo->shortName, controlROMInfo->description); #endif - file->read(controlROMData, CONTROL_ROM_SIZE); + const Bit8u *fileData = file->getData(); + memcpy(controlROMData, fileData, CONTROL_ROM_SIZE); // Control ROM successfully loaded, now check whether it's a known type controlROMMap = NULL; + controlROMFeatures = NULL; for (unsigned int i = 0; i < sizeof(ControlROMMaps) / sizeof(ControlROMMaps[0]); i++) { - if (memcmp(&controlROMData[ControlROMMaps[i].idPos], ControlROMMaps[i].idBytes, ControlROMMaps[i].idLen) == 0) { + if (strcmp(controlROMInfo->shortName, ControlROMMaps[i].shortName) == 0) { controlROMMap = &ControlROMMaps[i]; + controlROMFeatures = &controlROMMap->featureSet; return true; } } @@ -279,7 +393,7 @@ bool Synth::loadControlROM(const ROMImage &controlROMImage) { } bool Synth::loadPCMROM(const ROMImage &pcmROMImage) { - Common::File *file = pcmROMImage.getFile(); + File *file = pcmROMImage.getFile(); const ROMInfo *pcmROMInfo = pcmROMImage.getROMInfo(); if ((pcmROMInfo == NULL) || (pcmROMInfo->type != ROMInfo::PCM) @@ -289,24 +403,21 @@ bool Synth::loadPCMROM(const ROMImage &pcmROMImage) { #if MT32EMU_MONITOR_INIT printDebug("Found PCM ROM: %s, %s", pcmROMInfo->shortName, pcmROMInfo->description); #endif - size_t fileSize = file->size(); + size_t fileSize = file->getSize(); if (fileSize != (2 * pcmROMSize)) { #if MT32EMU_MONITOR_INIT printDebug("PCM ROM file has wrong size (expected %d, got %d)", 2 * pcmROMSize, fileSize); #endif return false; } - - byte *buffer = new byte[file->size()]; - file->read(buffer, file->size()); - const byte *fileData = buffer; + const Bit8u *fileData = file->getData(); for (size_t i = 0; i < pcmROMSize; i++) { Bit8u s = *(fileData++); Bit8u c = *(fileData++); int order[16] = {0, 9, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 8}; - signed short log = 0; + Bit16s log = 0; for (int u = 0; u < 15; u++) { int bit; if (order[u] < 8) { @@ -314,18 +425,15 @@ bool Synth::loadPCMROM(const ROMImage &pcmROMImage) { } else { bit = (c >> (7 - (order[u] - 8))) & 0x1; } - log = log | (short)(bit << (15 - u)); + log = log | Bit16s(bit << (15 - u)); } pcmROMData[i] = log; } - - delete[] buffer; - return true; } bool Synth::initPCMList(Bit16u mapAddress, Bit16u count) { - ControlROMPCMStruct *tps = (ControlROMPCMStruct *)&controlROMData[mapAddress]; + ControlROMPCMStruct *tps = reinterpret_cast(&controlROMData[mapAddress]); for (int i = 0; i < count; i++) { Bit32u rAddr = tps[i].pos * 0x800; Bit32u rLenExp = (tps[i].len & 0x70) >> 4; @@ -345,7 +453,7 @@ bool Synth::initPCMList(Bit16u mapAddress, Bit16u count) { return false; } -bool Synth::initCompressedTimbre(int timbreNum, const Bit8u *src, unsigned int srcLen) { +bool Synth::initCompressedTimbre(Bit16u timbreNum, const Bit8u *src, Bit32u srcLen) { // "Compressed" here means that muted partials aren't present in ROM (except in the case of partial 0 being muted). // Instead the data from the previous unmuted partial is used. if (srcLen < sizeof(TimbreParam::CommonParam)) { @@ -369,7 +477,7 @@ bool Synth::initCompressedTimbre(int timbreNum, const Bit8u *src, unsigned int s return true; } -bool Synth::initTimbres(Bit16u mapAddress, Bit16u offset, int count, int startTimbre, bool compressed) { +bool Synth::initTimbres(Bit16u mapAddress, Bit16u offset, Bit16u count, Bit16u startTimbre, bool compressed) { const Bit8u *timbreMap = &controlROMData[mapAddress]; for (Bit16u i = 0; i < count * 2; i += 2) { Bit16u address = (timbreMap[i + 1] << 8) | timbreMap[i]; @@ -391,12 +499,32 @@ bool Synth::initTimbres(Bit16u mapAddress, Bit16u offset, int count, int startTi return true; } +void Synth::initReverbModels(bool mt32CompatibleMode) { + reverbModels[REVERB_MODE_ROOM] = new BReverbModel(REVERB_MODE_ROOM, mt32CompatibleMode); + reverbModels[REVERB_MODE_HALL] = new BReverbModel(REVERB_MODE_HALL, mt32CompatibleMode); + reverbModels[REVERB_MODE_PLATE] = new BReverbModel(REVERB_MODE_PLATE, mt32CompatibleMode); + reverbModels[REVERB_MODE_TAP_DELAY] = new BReverbModel(REVERB_MODE_TAP_DELAY, mt32CompatibleMode); +#if !MT32EMU_REDUCE_REVERB_MEMORY + for (int i = REVERB_MODE_ROOM; i <= REVERB_MODE_TAP_DELAY; i++) { + reverbModels[i]->open(); + } +#endif +} + +void Synth::initSoundGroups(char newSoundGroupNames[][9]) { + memcpy(soundGroupIx, &controlROMData[controlROMMap->soundGroupsTable - sizeof(soundGroupIx)], sizeof(soundGroupIx)); + const SoundGroup *table = reinterpret_cast(&controlROMData[controlROMMap->soundGroupsTable]); + for (unsigned int i = 0; i < controlROMMap->soundGroupsCount; i++) { + memcpy(&newSoundGroupNames[i][0], table[i].name, sizeof(table[i].name)); + } +} + bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, AnalogOutputMode analogOutputMode) { return open(controlROMImage, pcmROMImage, DEFAULT_MAX_PARTIALS, analogOutputMode); } -bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, unsigned int usePartialCount, AnalogOutputMode analogOutputMode) { - if (isOpen) { +bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, Bit32u usePartialCount, AnalogOutputMode analogOutputMode) { + if (opened) { return false; } partialCount = usePartialCount; @@ -411,6 +539,7 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, u if (!loadControlROM(controlROMImage)) { printDebug("Init Error - Missing or invalid Control ROM image"); reportHandler->onErrorControlROM(); + dispose(); return false; } @@ -428,22 +557,24 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, u if (!loadPCMROM(pcmROMImage)) { printDebug("Init Error - Missing PCM ROM image"); reportHandler->onErrorPCMROM(); + dispose(); return false; } #if MT32EMU_MONITOR_INIT printDebug("Initialising Reverb Models"); #endif - bool mt32CompatibleReverb = controlROMFeatures->isDefaultReverbMT32Compatible(); + bool mt32CompatibleReverb = controlROMFeatures->defaultReverbMT32Compatible; #if MT32EMU_MONITOR_INIT printDebug("Using %s Compatible Reverb Models", mt32CompatibleReverb ? "MT-32" : "CM-32L"); #endif - setReverbCompatibilityMode(mt32CompatibleReverb); + initReverbModels(mt32CompatibleReverb); #if MT32EMU_MONITOR_INIT printDebug("Initialising Timbre Bank A"); #endif if (!initTimbres(controlROMMap->timbreAMap, controlROMMap->timbreAOffset, 0x40, 0, controlROMMap->timbreACompressed)) { + dispose(); return false; } @@ -451,6 +582,7 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, u printDebug("Initialising Timbre Bank B"); #endif if (!initTimbres(controlROMMap->timbreBMap, controlROMMap->timbreBOffset, 0x40, 64, controlROMMap->timbreBCompressed)) { + dispose(); return false; } @@ -458,6 +590,7 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, u printDebug("Initialising Timbre Bank R"); #endif if (!initTimbres(controlROMMap->timbreRMap, 0, controlROMMap->timbreRCount, 192, true)) { + dispose(); return false; } @@ -519,6 +652,10 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, u refreshSystem(); reverbOverridden = oldReverbOverridden; + char(*writableSoundGroupNames)[9] = new char[controlROMMap->soundGroupsCount][9]; + soundGroupNames = writableSoundGroupNames; + initSoundGroups(writableSoundGroupNames); + for (int i = 0; i < 9; i++) { MemParams::PatchTemp *patchTemp = &mt32ram.patchTemp[i]; @@ -550,12 +687,12 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, u midiQueue = new MidiEventQueue(); - analog = new Analog(analogOutputMode, controlROMFeatures); + analog = new Analog(analogOutputMode, controlROMFeatures->oldMT32AnalogLPF); setOutputGain(outputGain); setReverbOutputGain(reverbOutputGain); - isOpen = true; - isEnabled = false; + opened = true; + activated = false; #if MT32EMU_MONITOR_INIT printDebug("*** Initialisation complete ***"); @@ -563,10 +700,8 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, u return true; } -void Synth::close(bool forced) { - if (!forced && !isOpen) { - return; - } +void Synth::dispose() { + opened = false; delete midiQueue; midiQueue = NULL; @@ -582,8 +717,14 @@ void Synth::close(bool forced) { parts[i] = NULL; } + delete[] soundGroupNames; + soundGroupNames = NULL; + delete[] pcmWaves; + pcmWaves = NULL; + delete[] pcmROMData; + pcmROMData = NULL; deleteMemoryRegions(); @@ -593,7 +734,17 @@ void Synth::close(bool forced) { } reverbModel = NULL; controlROMFeatures = NULL; - isOpen = false; + controlROMMap = NULL; +} + +void Synth::close() { + if (opened) { + dispose(); + } +} + +bool Synth::isOpen() const { + return opened; } void Synth::flushMIDIQueue() { @@ -649,7 +800,7 @@ Bit32u Synth::getShortMessageLength(Bit32u msg) { } Bit32u Synth::addMIDIInterfaceDelay(Bit32u len, Bit32u timestamp) { - Bit32u transferTime = Bit32u((double)len * MIDI_DATA_TRANSFER_RATE); + Bit32u transferTime = Bit32u(double(len) * MIDI_DATA_TRANSFER_RATE); // Dealing with wrapping if (Bit32s(timestamp - lastReceivedMIDIEventTimestamp) < 0) { timestamp = lastReceivedMIDIEventTimestamp; @@ -664,12 +815,19 @@ bool Synth::playMsg(Bit32u msg) { } bool Synth::playMsg(Bit32u msg, Bit32u timestamp) { + if ((msg & 0xF8) == 0xF8) { + reportHandler->onMIDISystemRealtime(Bit8u(msg)); + return true; + } if (midiQueue == NULL) return false; if (midiDelayMode != MIDIDelayMode_IMMEDIATE) { timestamp = addMIDIInterfaceDelay(getShortMessageLength(msg), timestamp); } - if (!isEnabled) isEnabled = true; - return midiQueue->pushShortMessage(msg, timestamp); + if (!activated) activated = true; + do { + if (midiQueue->pushShortMessage(msg, timestamp)) return true; + } while (reportHandler->onMIDIQueueOverflow()); + return false; } bool Synth::playSysex(const Bit8u *sysex, Bit32u len) { @@ -681,24 +839,28 @@ bool Synth::playSysex(const Bit8u *sysex, Bit32u len, Bit32u timestamp) { if (midiDelayMode == MIDIDelayMode_DELAY_ALL) { timestamp = addMIDIInterfaceDelay(len, timestamp); } - if (!isEnabled) isEnabled = true; - return midiQueue->pushSysex(sysex, len, timestamp); + if (!activated) activated = true; + do { + if (midiQueue->pushSysex(sysex, len, timestamp)) return true; + } while (reportHandler->onMIDIQueueOverflow()); + return false; } void Synth::playMsgNow(Bit32u msg) { + if (!opened) return; + // NOTE: Active sense IS implemented in real hardware. However, realtime processing is clearly out of the library scope. // It is assumed that realtime consumers of the library respond to these MIDI events as appropriate. - unsigned char code = (unsigned char)((msg & 0x0000F0) >> 4); - unsigned char chan = (unsigned char)(msg & 0x00000F); - unsigned char note = (unsigned char)((msg & 0x007F00) >> 8); - unsigned char velocity = (unsigned char)((msg & 0x7F0000) >> 16); - if (!isEnabled) isEnabled = true; + Bit8u code = Bit8u((msg & 0x0000F0) >> 4); + Bit8u chan = Bit8u(msg & 0x00000F); + Bit8u note = Bit8u((msg & 0x007F00) >> 8); + Bit8u velocity = Bit8u((msg & 0x7F0000) >> 16); //printDebug("Playing chan %d, code 0x%01x note: 0x%02x", chan, code, note); - char part = chantable[chan]; - if (part < 0 || part > 8) { + Bit8u part = chantable[chan]; + if (part > 8) { #if MT32EMU_MONITOR_MIDI > 0 printDebug("Play msg on unreg chan %d (%d): code=0x%01x, vel=%d", chan, part, code, velocity); #endif @@ -707,9 +869,12 @@ void Synth::playMsgNow(Bit32u msg) { playMsgOnPart(part, code, note, velocity); } -void Synth::playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity) { +void Synth::playMsgOnPart(Bit8u part, Bit8u code, Bit8u note, Bit8u velocity) { + if (!opened) return; + Bit32u bend; + if (!activated) activated = true; //printDebug("Synth::playMsgOnPart(%02x, %02x, %02x, %02x)", part, code, note, velocity); switch (code) { case 0x8: @@ -836,23 +1001,23 @@ void Synth::playSysexWithoutFraming(const Bit8u *sysex, Bit32u len) { return; } if (sysex[0] != SYSEX_MANUFACTURER_ROLAND) { - printDebug("playSysexWithoutFraming: Header not intended for this device manufacturer: %02x %02x %02x %02x", (int)sysex[0], (int)sysex[1], (int)sysex[2], (int)sysex[3]); + printDebug("playSysexWithoutFraming: Header not intended for this device manufacturer: %02x %02x %02x %02x", int(sysex[0]), int(sysex[1]), int(sysex[2]), int(sysex[3])); return; } if (sysex[2] == SYSEX_MDL_D50) { - printDebug("playSysexWithoutFraming: Header is intended for model D-50 (not yet supported): %02x %02x %02x %02x", (int)sysex[0], (int)sysex[1], (int)sysex[2], (int)sysex[3]); + printDebug("playSysexWithoutFraming: Header is intended for model D-50 (not yet supported): %02x %02x %02x %02x", int(sysex[0]), int(sysex[1]), int(sysex[2]), int(sysex[3])); return; } else if (sysex[2] != SYSEX_MDL_MT32) { - printDebug("playSysexWithoutFraming: Header not intended for model MT-32: %02x %02x %02x %02x", (int)sysex[0], (int)sysex[1], (int)sysex[2], (int)sysex[3]); + printDebug("playSysexWithoutFraming: Header not intended for model MT-32: %02x %02x %02x %02x", int(sysex[0]), int(sysex[1]), int(sysex[2]), int(sysex[3])); return; } playSysexWithoutHeader(sysex[1], sysex[3], sysex + 4, len - 4); } -void Synth::playSysexWithoutHeader(unsigned char device, unsigned char command, const Bit8u *sysex, Bit32u len) { +void Synth::playSysexWithoutHeader(Bit8u device, Bit8u command, const Bit8u *sysex, Bit32u len) { if (device > 0x10) { // We have device ID 0x10 (default, but changeable, on real MT-32), < 0x10 is for channels - printDebug("playSysexWithoutHeader: Message is not intended for this device ID (provided: %02x, expected: 0x10 or channel)", (int)device); + printDebug("playSysexWithoutHeader: Message is not intended for this device ID (provided: %02x, expected: 0x10 or channel)", int(device)); return; } // This is checked early in the real devices (before any sysex length checks or further processing) @@ -861,6 +1026,13 @@ void Synth::playSysexWithoutHeader(unsigned char device, unsigned char command, reset(); return; } + + if (command == SYSEX_CMD_EOD) { +#if MT32EMU_MONITOR_SYSEX > 0 + printDebug("playSysexWithoutHeader: Ignored unsupported command %02x", command); +#endif + return; + } if (len < 4) { printDebug("playSysexWithoutHeader: Message is too short (%d bytes)!", len); return; @@ -872,13 +1044,20 @@ void Synth::playSysexWithoutHeader(unsigned char device, unsigned char command, } len -= 1; // Exclude checksum switch (command) { + case SYSEX_CMD_WSD: +#if MT32EMU_MONITOR_SYSEX > 0 + printDebug("playSysexWithoutHeader: Ignored unsupported command %02x", command); +#endif + break; case SYSEX_CMD_DAT: + /* Outcommented until we (ever) actually implement handshake communication if (hasActivePartials()) { printDebug("playSysexWithoutHeader: Got SYSEX_CMD_DAT but partials are active - ignoring"); // FIXME: We should send SYSEX_CMD_RJC in this case break; } - // Deliberate fall-through + */ + // fall through case SYSEX_CMD_DT1: writeSysex(device, sysex, len); break; @@ -888,7 +1067,7 @@ void Synth::playSysexWithoutHeader(unsigned char device, unsigned char command, // FIXME: We should send SYSEX_CMD_RJC in this case break; } - // Deliberate fall-through + // fall through case SYSEX_CMD_RQ1: readSysex(device, sysex, len); break; @@ -898,11 +1077,12 @@ void Synth::playSysexWithoutHeader(unsigned char device, unsigned char command, } } -void Synth::readSysex(unsigned char /*device*/, const Bit8u * /*sysex*/, Bit32u /*len*/) const { +void Synth::readSysex(Bit8u /*device*/, const Bit8u * /*sysex*/, Bit32u /*len*/) const { // NYI } -void Synth::writeSysex(unsigned char device, const Bit8u *sysex, Bit32u len) { +void Synth::writeSysex(Bit8u device, const Bit8u *sysex, Bit32u len) { + if (!opened) return; reportHandler->onMIDIMessagePlayed(); Bit32u addr = (sysex[0] << 16) | (sysex[1] << 8) | (sysex[2]); addr = MT32EMU_MEMADDR(addr); @@ -918,7 +1098,7 @@ void Synth::writeSysex(unsigned char device, const Bit8u *sysex, Bit32u len) { #endif if (/*addr >= MT32EMU_MEMADDR(0x000000) && */addr < MT32EMU_MEMADDR(0x010000)) { int offset; - if (chantable[device] == -1) { + if (chantable[device] > 8) { #if MT32EMU_MONITOR_SYSEX > 0 printDebug(" (Channel not mapped to a part... 0 offset)"); #endif @@ -939,7 +1119,7 @@ void Synth::writeSysex(unsigned char device, const Bit8u *sysex, Bit32u len) { addr += MT32EMU_MEMADDR(0x030110) - MT32EMU_MEMADDR(0x010000); } else if (/*addr >= MT32EMU_MEMADDR(0x020000) && */ addr < MT32EMU_MEMADDR(0x030000)) { int offset; - if (chantable[device] == -1) { + if (chantable[device] > 8) { #if MT32EMU_MONITOR_SYSEX > 0 printDebug(" (Channel not mapped to a part... 0 offset)"); #endif @@ -986,6 +1166,7 @@ void Synth::writeSysex(unsigned char device, const Bit8u *sysex, Bit32u len) { } void Synth::readMemory(Bit32u addr, Bit32u len, Bit8u *data) { + if (!opened) return; const MemoryRegion *region = findMemoryRegion(addr); if (region != NULL) { readMemoryRegion(region, addr, len, data); @@ -1004,12 +1185,12 @@ void Synth::initMemoryRegions() { pos += sizeof(TimbreParam::PartialParam); } memset(&paddedTimbreMaxTable[pos], 0, 10); // Padding - patchTempMemoryRegion = new PatchTempMemoryRegion(this, (Bit8u *)&mt32ram.patchTemp[0], &controlROMData[controlROMMap->patchMaxTable]); - rhythmTempMemoryRegion = new RhythmTempMemoryRegion(this, (Bit8u *)&mt32ram.rhythmTemp[0], &controlROMData[controlROMMap->rhythmMaxTable]); - timbreTempMemoryRegion = new TimbreTempMemoryRegion(this, (Bit8u *)&mt32ram.timbreTemp[0], paddedTimbreMaxTable); - patchesMemoryRegion = new PatchesMemoryRegion(this, (Bit8u *)&mt32ram.patches[0], &controlROMData[controlROMMap->patchMaxTable]); - timbresMemoryRegion = new TimbresMemoryRegion(this, (Bit8u *)&mt32ram.timbres[0], paddedTimbreMaxTable); - systemMemoryRegion = new SystemMemoryRegion(this, (Bit8u *)&mt32ram.system, &controlROMData[controlROMMap->systemMaxTable]); + patchTempMemoryRegion = new PatchTempMemoryRegion(this, reinterpret_cast(&mt32ram.patchTemp[0]), &controlROMData[controlROMMap->patchMaxTable]); + rhythmTempMemoryRegion = new RhythmTempMemoryRegion(this, reinterpret_cast(&mt32ram.rhythmTemp[0]), &controlROMData[controlROMMap->rhythmMaxTable]); + timbreTempMemoryRegion = new TimbreTempMemoryRegion(this, reinterpret_cast(&mt32ram.timbreTemp[0]), paddedTimbreMaxTable); + patchesMemoryRegion = new PatchesMemoryRegion(this, reinterpret_cast(&mt32ram.patches[0]), &controlROMData[controlROMMap->patchMaxTable]); + timbresMemoryRegion = new TimbresMemoryRegion(this, reinterpret_cast(&mt32ram.timbres[0]), paddedTimbreMaxTable); + systemMemoryRegion = new SystemMemoryRegion(this, reinterpret_cast(&mt32ram.system), &controlROMData[controlROMMap->systemMaxTable]); displayMemoryRegion = new DisplayMemoryRegion(this); resetMemoryRegion = new ResetMemoryRegion(this); } @@ -1071,7 +1252,7 @@ void Synth::readMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len for (m = 0; m < len; m += 2) { data[m] = 0xff; if (m + 1 < len) { - data[m+1] = (Bit8u)region->type; + data[m+1] = Bit8u(region->type); } } } @@ -1279,16 +1460,16 @@ void Synth::writeMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u le if(firstPart < 0) firstPart = 0; int lastPart = off + len - SYSTEM_CHAN_ASSIGN_START_OFF; - if(lastPart > 9) - lastPart = 9; - refreshSystemChanAssign(firstPart, lastPart); + if(lastPart > 8) + lastPart = 8; + refreshSystemChanAssign(Bit8u(firstPart), Bit8u(lastPart)); } if (off <= SYSTEM_MASTER_VOL_OFF && off + len > SYSTEM_MASTER_VOL_OFF) { refreshSystemMasterVol(); } break; case MR_Display: - char buf[MAX_SYSEX_SIZE]; + char buf[SYSEX_BUFFER_SIZE]; memcpy(&buf, &data[0], len); buf[len] = 0; #if MT32EMU_MONITOR_SYSEX > 0 @@ -1361,19 +1542,19 @@ void Synth::refreshSystemReserveSettings() { partialManager->setReserve(rset); } -void Synth::refreshSystemChanAssign(unsigned int firstPart, unsigned int lastPart) { - memset(chantable, -1, sizeof(chantable)); +void Synth::refreshSystemChanAssign(Bit8u firstPart, Bit8u lastPart) { + memset(chantable, 0xFF, sizeof(chantable)); // CONFIRMED: In the case of assigning a channel to multiple parts, the lower part wins. - for (unsigned int i = 0; i <= 8; i++) { + for (Bit32u i = 0; i <= 8; i++) { if (parts[i] != NULL && i >= firstPart && i <= lastPart) { // CONFIRMED: Decay is started for all polys, and all controllers are reset, for every part whose assignment was touched by the sysex write. parts[i]->allSoundOff(); parts[i]->resetAllControllers(); } - int chan = mt32ram.system.chanAssign[i]; - if (chan != 16 && chantable[chan] == -1) { - chantable[chan] = i; + Bit8u chan = mt32ram.system.chanAssign[i]; + if (chan < 16 && chantable[chan] > 8) { + chantable[chan] = Bit8u(i); } } @@ -1398,6 +1579,7 @@ void Synth::refreshSystem() { } void Synth::reset() { + if (!opened) return; #if MT32EMU_MONITOR_SYSEX > 0 printDebug("RESET"); #endif @@ -1413,7 +1595,7 @@ void Synth::reset() { } } refreshSystem(); - isEnabled = false; + isActive(); } MidiEvent::~MidiEvent() { @@ -1477,7 +1659,7 @@ bool MidiEventQueue::pushSysex(const Bit8u *sysexData, Bit32u sysexLength, Bit32 } const MidiEvent *MidiEventQueue::peekMidiEvent() { - return (startPosition == endPosition) ? NULL : &ringBuffer[startPosition]; + return isEmpty() ? NULL : &ringBuffer[startPosition]; } void MidiEventQueue::dropMidiEvent() { @@ -1491,76 +1673,164 @@ bool MidiEventQueue::isFull() const { return startPosition == ((endPosition + 1) & ringBufferMask); } -unsigned int Synth::getStereoOutputSampleRate() const { +bool MidiEventQueue::isEmpty() const { + return startPosition == endPosition; +} + +Bit32u Synth::getStereoOutputSampleRate() const { return (analog == NULL) ? SAMPLE_RATE : analog->getOutputSampleRate(); } -void Synth::render(Sample *stream, Bit32u len) { - if (!isEnabled) { - renderedSampleCount += analog->getDACStreamsLength(len); - analog->process(NULL, NULL, NULL, NULL, NULL, NULL, NULL, len); - muteSampleBuffer(stream, len << 1); +void Renderer::render(SampleFormatConverter &converter, Bit32u len) { + if (!synth.opened) { + converter.addSilence(len << 1); return; } - // As in AnalogOutputMode_ACCURATE mode output is upsampled, buffer size MAX_SAMPLES_PER_RUN is more than enough. - Sample tmpNonReverbLeft[MAX_SAMPLES_PER_RUN], tmpNonReverbRight[MAX_SAMPLES_PER_RUN]; - Sample tmpReverbDryLeft[MAX_SAMPLES_PER_RUN], tmpReverbDryRight[MAX_SAMPLES_PER_RUN]; - Sample tmpReverbWetLeft[MAX_SAMPLES_PER_RUN], tmpReverbWetRight[MAX_SAMPLES_PER_RUN]; + if (!synth.activated) { + synth.renderedSampleCount += synth.analog->getDACStreamsLength(len); + synth.analog->process(NULL, NULL, NULL, NULL, NULL, NULL, NULL, len); + converter.addSilence(len << 1); + return; + } while (len > 0) { + // As in AnalogOutputMode_ACCURATE mode output is upsampled, MAX_SAMPLES_PER_RUN is more than enough for the temp buffers. Bit32u thisPassLen = len > MAX_SAMPLES_PER_RUN ? MAX_SAMPLES_PER_RUN : len; - renderStreams(tmpNonReverbLeft, tmpNonReverbRight, tmpReverbDryLeft, tmpReverbDryRight, tmpReverbWetLeft, tmpReverbWetRight, analog->getDACStreamsLength(thisPassLen)); - analog->process(&stream, tmpNonReverbLeft, tmpNonReverbRight, tmpReverbDryLeft, tmpReverbDryRight, tmpReverbWetLeft, tmpReverbWetRight, thisPassLen); + synth.renderStreams(tmpNonReverbLeft, tmpNonReverbRight, tmpReverbDryLeft, tmpReverbDryRight, tmpReverbWetLeft, tmpReverbWetRight, synth.analog->getDACStreamsLength(thisPassLen)); + synth.analog->process(converter.sampleBuffer, tmpNonReverbLeft, tmpNonReverbRight, tmpReverbDryLeft, tmpReverbDryRight, tmpReverbWetLeft, tmpReverbWetRight, thisPassLen); + converter.convert(thisPassLen << 1); len -= thisPassLen; } } -void Synth::renderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample *reverbDryLeft, Sample *reverbDryRight, Sample *reverbWetLeft, Sample *reverbWetRight, Bit32u len) { +void Synth::render(Bit16s *stream, Bit32u len) { +#if MT32EMU_USE_FLOAT_SAMPLES + BufferedSampleFormatConverter<2> converter(stream); +#else + SampleFormatConverter converter(stream); +#endif + renderer.render(converter, len); +} + +void Synth::render(float *stream, Bit32u len) { +#if MT32EMU_USE_FLOAT_SAMPLES + SampleFormatConverter converter(stream); +#else + BufferedSampleFormatConverter<2> converter(stream); +#endif + renderer.render(converter, len); +} + +void Renderer::renderStreams( + SampleFormatConverter &nonReverbLeft, SampleFormatConverter &nonReverbRight, + SampleFormatConverter &reverbDryLeft, SampleFormatConverter &reverbDryRight, + SampleFormatConverter &reverbWetLeft, SampleFormatConverter &reverbWetRight, + Bit32u len) +{ + if (!synth.opened) { + nonReverbLeft.addSilence(len); + nonReverbRight.addSilence(len); + reverbDryLeft.addSilence(len); + reverbDryRight.addSilence(len); + reverbWetLeft.addSilence(len); + reverbWetRight.addSilence(len); + return; + } + while (len > 0) { // We need to ensure zero-duration notes will play so add minimum 1-sample delay. Bit32u thisLen = 1; - if (!isAbortingPoly()) { - const MidiEvent *nextEvent = midiQueue->peekMidiEvent(); - Bit32s samplesToNextEvent = (nextEvent != NULL) ? Bit32s(nextEvent->timestamp - renderedSampleCount) : MAX_SAMPLES_PER_RUN; + if (!synth.isAbortingPoly()) { + const MidiEvent *nextEvent = synth.midiQueue->peekMidiEvent(); + Bit32s samplesToNextEvent = (nextEvent != NULL) ? Bit32s(nextEvent->timestamp - synth.renderedSampleCount) : MAX_SAMPLES_PER_RUN; if (samplesToNextEvent > 0) { thisLen = len > MAX_SAMPLES_PER_RUN ? MAX_SAMPLES_PER_RUN : len; - if (thisLen > (Bit32u)samplesToNextEvent) { + if (thisLen > Bit32u(samplesToNextEvent)) { thisLen = samplesToNextEvent; } } else { if (nextEvent->sysexData == NULL) { - playMsgNow(nextEvent->shortMessageData); + synth.playMsgNow(nextEvent->shortMessageData); // If a poly is aborting we don't drop the event from the queue. // Instead, we'll return to it again when the abortion is done. - if (!isAbortingPoly()) { - midiQueue->dropMidiEvent(); + if (!synth.isAbortingPoly()) { + synth.midiQueue->dropMidiEvent(); } } else { - playSysexNow(nextEvent->sysexData, nextEvent->sysexLength); - midiQueue->dropMidiEvent(); + synth.playSysexNow(nextEvent->sysexData, nextEvent->sysexLength); + synth.midiQueue->dropMidiEvent(); } } } - doRenderStreams(nonReverbLeft, nonReverbRight, reverbDryLeft, reverbDryRight, reverbWetLeft, reverbWetRight, thisLen); - advanceStreamPosition(nonReverbLeft, thisLen); - advanceStreamPosition(nonReverbRight, thisLen); - advanceStreamPosition(reverbDryLeft, thisLen); - advanceStreamPosition(reverbDryRight, thisLen); - advanceStreamPosition(reverbWetLeft, thisLen); - advanceStreamPosition(reverbWetRight, thisLen); + DACOutputStreams streams = { + nonReverbLeft.sampleBuffer, nonReverbRight.sampleBuffer, + reverbDryLeft.sampleBuffer, reverbDryRight.sampleBuffer, + reverbWetLeft.sampleBuffer, reverbWetRight.sampleBuffer + }; + doRenderStreams(streams, thisLen); + nonReverbLeft.convert(thisLen); + nonReverbRight.convert(thisLen); + reverbDryLeft.convert(thisLen); + reverbDryRight.convert(thisLen); + reverbWetLeft.convert(thisLen); + reverbWetRight.convert(thisLen); len -= thisLen; } } +void Synth::renderStreams( + Bit16s *nonReverbLeft, Bit16s *nonReverbRight, + Bit16s *reverbDryLeft, Bit16s *reverbDryRight, + Bit16s *reverbWetLeft, Bit16s *reverbWetRight, + Bit32u len) +{ +#if MT32EMU_USE_FLOAT_SAMPLES + BufferedSampleFormatConverter<> convNonReverbLeft(nonReverbLeft), convNonReverbRight(nonReverbRight); + BufferedSampleFormatConverter<> convReverbDryLeft(reverbDryLeft), convReverbDryRight(reverbDryRight); + BufferedSampleFormatConverter<> convReverbWetLeft(reverbWetLeft), convReverbWetRight(reverbWetRight); +#else + SampleFormatConverter convNonReverbLeft(nonReverbLeft), convNonReverbRight(nonReverbRight); + SampleFormatConverter convReverbDryLeft(reverbDryLeft), convReverbDryRight(reverbDryRight); + SampleFormatConverter convReverbWetLeft(reverbWetLeft), convReverbWetRight(reverbWetRight); +#endif + renderer.renderStreams( + convNonReverbLeft, convNonReverbRight, + convReverbDryLeft, convReverbDryRight, + convReverbWetLeft, convReverbWetRight, + len); +} + +void Synth::renderStreams( + float *nonReverbLeft, float *nonReverbRight, + float *reverbDryLeft, float *reverbDryRight, + float *reverbWetLeft, float *reverbWetRight, + Bit32u len) +{ +#if MT32EMU_USE_FLOAT_SAMPLES + SampleFormatConverter convNonReverbLeft(nonReverbLeft), convNonReverbRight(nonReverbRight); + SampleFormatConverter convReverbDryLeft(reverbDryLeft), convReverbDryRight(reverbDryRight); + SampleFormatConverter convReverbWetLeft(reverbWetLeft), convReverbWetRight(reverbWetRight); +#else + BufferedSampleFormatConverter<> convNonReverbLeft(nonReverbLeft), convNonReverbRight(nonReverbRight); + BufferedSampleFormatConverter<> convReverbDryLeft(reverbDryLeft), convReverbDryRight(reverbDryRight); + BufferedSampleFormatConverter<> convReverbWetLeft(reverbWetLeft), convReverbWetRight(reverbWetRight); +#endif + renderer.renderStreams( + convNonReverbLeft, convNonReverbRight, + convReverbDryLeft, convReverbDryRight, + convReverbWetLeft, convReverbWetRight, + len); +} + // In GENERATION2 units, the output from LA32 goes to the Boss chip already bit-shifted. // In NICE mode, it's also better to increase volume before the reverb processing to preserve accuracy. -void Synth::produceLA32Output(Sample *buffer, Bit32u len) { +void Renderer::produceLA32Output(Sample *buffer, Bit32u len) { #if MT32EMU_USE_FLOAT_SAMPLES (void)buffer; (void)len; #else - switch (dacInputMode) { + switch (synth.dacInputMode) { case DACInputMode_GENERATION2: while (len--) { *buffer = (*buffer & 0x8000) | ((*buffer << 1) & 0x7FFE) | ((*buffer >> 14) & 0x0001); @@ -1569,7 +1839,7 @@ void Synth::produceLA32Output(Sample *buffer, Bit32u len) { break; case DACInputMode_NICE: while (len--) { - *buffer = clipSampleEx(SampleEx(*buffer) << 1); + *buffer = Synth::clipSampleEx(SampleEx(*buffer) << 1); ++buffer; } break; @@ -1579,12 +1849,12 @@ void Synth::produceLA32Output(Sample *buffer, Bit32u len) { #endif } -void Synth::convertSamplesToOutput(Sample *buffer, Bit32u len) { +void Renderer::convertSamplesToOutput(Sample *buffer, Bit32u len) { #if MT32EMU_USE_FLOAT_SAMPLES (void)buffer; (void)len; #else - if (dacInputMode == DACInputMode_GENERATION1) { + if (synth.dacInputMode == DACInputMode_GENERATION1) { while (len--) { *buffer = Sample((*buffer & 0x8000) | ((*buffer << 1) & 0x7FFE)); ++buffer; @@ -1593,78 +1863,77 @@ void Synth::convertSamplesToOutput(Sample *buffer, Bit32u len) { #endif } -void Synth::doRenderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample *reverbDryLeft, Sample *reverbDryRight, Sample *reverbWetLeft, Sample *reverbWetRight, Bit32u len) { - // Even if LA32 output isn't desired, we proceed anyway with temp buffers - Sample tmpBufNonReverbLeft[MAX_SAMPLES_PER_RUN], tmpBufNonReverbRight[MAX_SAMPLES_PER_RUN]; - if (nonReverbLeft == NULL) nonReverbLeft = tmpBufNonReverbLeft; - if (nonReverbRight == NULL) nonReverbRight = tmpBufNonReverbRight; +void Renderer::doRenderStreams(DACOutputStreams &streams, Bit32u len) { + if (synth.activated) { + // Even if LA32 output isn't desired, we proceed anyway with temp buffers + Sample *nonReverbLeft = streams.nonReverbLeft == NULL ? tmpNonReverbLeft : streams.nonReverbLeft; + Sample *nonReverbRight = streams.nonReverbRight == NULL ? tmpNonReverbRight : streams.nonReverbRight; + Sample *reverbDryLeft = streams.reverbDryLeft == NULL ? tmpReverbDryLeft : streams.reverbDryLeft; + Sample *reverbDryRight = streams.reverbDryRight == NULL ? tmpReverbDryRight : streams.reverbDryRight; - Sample tmpBufReverbDryLeft[MAX_SAMPLES_PER_RUN], tmpBufReverbDryRight[MAX_SAMPLES_PER_RUN]; - if (reverbDryLeft == NULL) reverbDryLeft = tmpBufReverbDryLeft; - if (reverbDryRight == NULL) reverbDryRight = tmpBufReverbDryRight; + Synth::muteSampleBuffer(nonReverbLeft, len); + Synth::muteSampleBuffer(nonReverbRight, len); + Synth::muteSampleBuffer(reverbDryLeft, len); + Synth::muteSampleBuffer(reverbDryRight, len); - if (isEnabled) { - muteSampleBuffer(nonReverbLeft, len); - muteSampleBuffer(nonReverbRight, len); - muteSampleBuffer(reverbDryLeft, len); - muteSampleBuffer(reverbDryRight, len); - - for (unsigned int i = 0; i < getPartialCount(); i++) { - if (partialManager->shouldReverb(i)) { - partialManager->produceOutput(i, reverbDryLeft, reverbDryRight, len); + for (unsigned int i = 0; i < synth.getPartialCount(); i++) { + if (synth.partialManager->shouldReverb(i)) { + synth.partialManager->produceOutput(i, reverbDryLeft, reverbDryRight, len); } else { - partialManager->produceOutput(i, nonReverbLeft, nonReverbRight, len); + synth.partialManager->produceOutput(i, nonReverbLeft, nonReverbRight, len); } } produceLA32Output(reverbDryLeft, len); produceLA32Output(reverbDryRight, len); - if (isReverbEnabled()) { - reverbModel->process(reverbDryLeft, reverbDryRight, reverbWetLeft, reverbWetRight, len); - if (reverbWetLeft != NULL) convertSamplesToOutput(reverbWetLeft, len); - if (reverbWetRight != NULL) convertSamplesToOutput(reverbWetRight, len); + if (synth.isReverbEnabled()) { + synth.reverbModel->process(reverbDryLeft, reverbDryRight, streams.reverbWetLeft, streams.reverbWetRight, len); + if (streams.reverbWetLeft != NULL) convertSamplesToOutput(streams.reverbWetLeft, len); + if (streams.reverbWetRight != NULL) convertSamplesToOutput(streams.reverbWetRight, len); } else { - muteSampleBuffer(reverbWetLeft, len); - muteSampleBuffer(reverbWetRight, len); + Synth::muteSampleBuffer(streams.reverbWetLeft, len); + Synth::muteSampleBuffer(streams.reverbWetRight, len); } // Don't bother with conversion if the output is going to be unused - if (nonReverbLeft != tmpBufNonReverbLeft) { + if (streams.nonReverbLeft != NULL) { produceLA32Output(nonReverbLeft, len); convertSamplesToOutput(nonReverbLeft, len); } - if (nonReverbRight != tmpBufNonReverbRight) { + if (streams.nonReverbRight != NULL) { produceLA32Output(nonReverbRight, len); convertSamplesToOutput(nonReverbRight, len); } - if (reverbDryLeft != tmpBufReverbDryLeft) convertSamplesToOutput(reverbDryLeft, len); - if (reverbDryRight != tmpBufReverbDryRight) convertSamplesToOutput(reverbDryRight, len); + if (streams.reverbDryLeft != NULL) convertSamplesToOutput(reverbDryLeft, len); + if (streams.reverbDryRight != NULL) convertSamplesToOutput(reverbDryRight, len); } else { - // Avoid muting buffers that wasn't requested - if (nonReverbLeft != tmpBufNonReverbLeft) muteSampleBuffer(nonReverbLeft, len); - if (nonReverbRight != tmpBufNonReverbRight) muteSampleBuffer(nonReverbRight, len); - if (reverbDryLeft != tmpBufReverbDryLeft) muteSampleBuffer(reverbDryLeft, len); - if (reverbDryRight != tmpBufReverbDryRight) muteSampleBuffer(reverbDryRight, len); - muteSampleBuffer(reverbWetLeft, len); - muteSampleBuffer(reverbWetRight, len); + Synth::muteSampleBuffer(streams.nonReverbLeft, len); + Synth::muteSampleBuffer(streams.nonReverbRight, len); + Synth::muteSampleBuffer(streams.reverbDryLeft, len); + Synth::muteSampleBuffer(streams.reverbDryRight, len); + Synth::muteSampleBuffer(streams.reverbWetLeft, len); + Synth::muteSampleBuffer(streams.reverbWetRight, len); } - partialManager->clearAlreadyOutputed(); - renderedSampleCount += len; + synth.partialManager->clearAlreadyOutputed(); + synth.renderedSampleCount += len; } -void Synth::printPartialUsage(unsigned long sampleOffset) { +void Synth::printPartialUsage(Bit32u sampleOffset) { unsigned int partialUsage[9]; partialManager->getPerPartPartialUsage(partialUsage); if (sampleOffset > 0) { - printDebug("[+%lu] Partial Usage: 1:%02d 2:%02d 3:%02d 4:%02d 5:%02d 6:%02d 7:%02d 8:%02d R: %02d TOTAL: %02d", sampleOffset, partialUsage[0], partialUsage[1], partialUsage[2], partialUsage[3], partialUsage[4], partialUsage[5], partialUsage[6], partialUsage[7], partialUsage[8], getPartialCount() - partialManager->getFreePartialCount()); + printDebug("[+%u] Partial Usage: 1:%02d 2:%02d 3:%02d 4:%02d 5:%02d 6:%02d 7:%02d 8:%02d R: %02d TOTAL: %02d", sampleOffset, partialUsage[0], partialUsage[1], partialUsage[2], partialUsage[3], partialUsage[4], partialUsage[5], partialUsage[6], partialUsage[7], partialUsage[8], getPartialCount() - partialManager->getFreePartialCount()); } else { printDebug("Partial Usage: 1:%02d 2:%02d 3:%02d 4:%02d 5:%02d 6:%02d 7:%02d 8:%02d R: %02d TOTAL: %02d", partialUsage[0], partialUsage[1], partialUsage[2], partialUsage[3], partialUsage[4], partialUsage[5], partialUsage[6], partialUsage[7], partialUsage[8], getPartialCount() - partialManager->getFreePartialCount()); } } bool Synth::hasActivePartials() const { + if (!opened) { + return false; + } for (unsigned int partialNum = 0; partialNum < getPartialCount(); partialNum++) { if (partialManager->getPartial(partialNum)->isActive()) { return true; @@ -1673,51 +1942,81 @@ bool Synth::hasActivePartials() const { return false; } -bool Synth::isAbortingPoly() const { - return abortingPoly != NULL; -} - -bool Synth::isActive() const { - if (hasActivePartials()) { +bool Synth::isActive() { + if (!opened) { + return false; + } + if (!midiQueue->isEmpty() || hasActivePartials()) { return true; } - if (isReverbEnabled()) { - return reverbModel->isActive(); + if (isReverbEnabled() && reverbModel->isActive()) { + return true; } + activated = false; return false; } -unsigned int Synth::getPartialCount() const { +Bit32u Synth::getPartialCount() const { return partialCount; } void Synth::getPartStates(bool *partStates) const { + if (!opened) { + memset(partStates, 0, 9 * sizeof(bool)); + return; + } for (int partNumber = 0; partNumber < 9; partNumber++) { const Part *part = parts[partNumber]; partStates[partNumber] = part->getActiveNonReleasingPartialCount() > 0; } } -void Synth::getPartialStates(PartialState *partialStates) const { - static const PartialState partialPhaseToState[8] = { - PartialState_ATTACK, PartialState_ATTACK, PartialState_ATTACK, PartialState_ATTACK, - PartialState_SUSTAIN, PartialState_SUSTAIN, PartialState_RELEASE, PartialState_INACTIVE - }; +Bit32u Synth::getPartStates() const { + if (!opened) return 0; + bool partStates[9]; + getPartStates(partStates); + Bit32u bitSet = 0; + for (int partNumber = 8; partNumber >= 0; partNumber--) { + bitSet = (bitSet << 1) | (partStates[partNumber] ? 1 : 0); + } + return bitSet; +} - for (unsigned int partialNum = 0; partialNum < getPartialCount(); partialNum++) { - const Partial *partial = partialManager->getPartial(partialNum); - partialStates[partialNum] = partial->isActive() ? partialPhaseToState[partial->getTVA()->getPhase()] : PartialState_INACTIVE; +void Synth::getPartialStates(PartialState *partialStates) const { + if (!opened) { + memset(partialStates, 0, partialCount * sizeof(PartialState)); + return; + } + for (unsigned int partialNum = 0; partialNum < partialCount; partialNum++) { + partialStates[partialNum] = getPartialState(partialManager, partialNum); } } -unsigned int Synth::getPlayingNotes(unsigned int partNumber, Bit8u *keys, Bit8u *velocities) const { - unsigned int playingNotes = 0; - if (isOpen && (partNumber < 9)) { +void Synth::getPartialStates(Bit8u *partialStates) const { + if (!opened) { + memset(partialStates, 0, ((partialCount + 3) >> 2)); + return; + } + for (unsigned int quartNum = 0; (4 * quartNum) < partialCount; quartNum++) { + Bit8u packedStates = 0; + for (unsigned int i = 0; i < 4; i++) { + unsigned int partialNum = (4 * quartNum) + i; + if (partialCount <= partialNum) break; + PartialState partialState = getPartialState(partialManager, partialNum); + packedStates |= (partialState & 3) << (2 * i); + } + partialStates[quartNum] = packedStates; + } +} + +Bit32u Synth::getPlayingNotes(Bit8u partNumber, Bit8u *keys, Bit8u *velocities) const { + Bit32u playingNotes = 0; + if (opened && (partNumber < 9)) { const Part *part = parts[partNumber]; const Poly *poly = part->getFirstActivePoly(); while (poly != NULL) { - keys[playingNotes] = (Bit8u)poly->getKey(); - velocities[playingNotes] = (Bit8u)poly->getVelocity(); + keys[playingNotes] = Bit8u(poly->getKey()); + velocities[playingNotes] = Bit8u(poly->getVelocity()); playingNotes++; poly = poly->getNext(); } @@ -1725,11 +2024,11 @@ unsigned int Synth::getPlayingNotes(unsigned int partNumber, Bit8u *keys, Bit8u return playingNotes; } -const char *Synth::getPatchName(unsigned int partNumber) const { - return (!isOpen || partNumber > 8) ? NULL : parts[partNumber]->getCurrentInstr(); +const char *Synth::getPatchName(Bit8u partNumber) const { + return (!opened || partNumber > 8) ? NULL : parts[partNumber]->getCurrentInstr(); } -const Part *Synth::getPart(unsigned int partNum) const { +const Part *Synth::getPart(Bit8u partNum) const { if (partNum > 8) { return NULL; } @@ -1783,6 +2082,7 @@ void MemoryRegion::write(unsigned int entry, unsigned int off, const Bit8u *src, #if MT32EMU_MONITOR_SYSEX > 0 synth->printDebug("write[%d]: unwritable region: entry=%d, off=%d, len=%d", type, entry, off, len); #endif + return; } for (unsigned int i = 0; i < len; i++) { @@ -1807,4 +2107,4 @@ void MemoryRegion::write(unsigned int entry, unsigned int off, const Bit8u *src, } } -} +} // namespace MT32Emu diff --git a/audio/softsynth/mt32/Synth.h b/audio/softsynth/mt32/Synth.h index 97d4644ee2b..fe31d99f022 100644 --- a/audio/softsynth/mt32/Synth.h +++ b/audio/softsynth/mt32/Synth.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -18,8 +18,13 @@ #ifndef MT32EMU_SYNTH_H #define MT32EMU_SYNTH_H -//#include -//#include +#include +#include +#include + +#include "globals.h" +#include "Types.h" +#include "Enumerations.h" namespace MT32Emu { @@ -31,6 +36,8 @@ class Part; class Poly; class Partial; class PartialManager; +class Renderer; +class ROMImage; class PatchTempMemoryRegion; class RhythmTempMemoryRegion; @@ -41,82 +48,11 @@ class SystemMemoryRegion; class DisplayMemoryRegion; class ResetMemoryRegion; +struct ControlROMFeatureSet; struct ControlROMMap; struct PCMWaveEntry; struct MemParams; -/** - * Methods for emulating the connection between the LA32 and the DAC, which involves - * some hacks in the real devices for doubling the volume. - * See also http://en.wikipedia.org/wiki/Roland_MT-32#Digital_overflow - */ -enum DACInputMode { - // Produces samples at double the volume, without tricks. - // * Nicer overdrive characteristics than the DAC hacks (it simply clips samples within range) - // * Higher quality than the real devices - DACInputMode_NICE, - - // Produces samples that exactly match the bits output from the emulated LA32. - // * Nicer overdrive characteristics than the DAC hacks (it simply clips samples within range) - // * Much less likely to overdrive than any other mode. - // * Half the volume of any of the other modes. - // * Output gain is ignored for both LA32 and reverb output. - // * Perfect for developers while debugging :) - DACInputMode_PURE, - - // Re-orders the LA32 output bits as in early generation MT-32s (according to Wikipedia). - // Bit order at DAC (where each number represents the original LA32 output bit number, and XX means the bit is always low): - // 15 13 12 11 10 09 08 07 06 05 04 03 02 01 00 XX - DACInputMode_GENERATION1, - - // Re-orders the LA32 output bits as in later generations (personally confirmed on my CM-32L - KG). - // Bit order at DAC (where each number represents the original LA32 output bit number): - // 15 13 12 11 10 09 08 07 06 05 04 03 02 01 00 14 - DACInputMode_GENERATION2 -}; - -// Methods for emulating the effective delay of incoming MIDI messages introduced by a MIDI interface. -enum MIDIDelayMode { - // Process incoming MIDI events immediately. - MIDIDelayMode_IMMEDIATE, - - // Delay incoming short MIDI messages as if they where transferred via a MIDI cable to a real hardware unit and immediate sysex processing. - // This ensures more accurate timing of simultaneous NoteOn messages. - MIDIDelayMode_DELAY_SHORT_MESSAGES_ONLY, - - // Delay all incoming MIDI events as if they where transferred via a MIDI cable to a real hardware unit. - MIDIDelayMode_DELAY_ALL -}; - -// Methods for emulating the effects of analogue circuits of real hardware units on the output signal. -enum AnalogOutputMode { - // Only digital path is emulated. The output samples correspond to the digital signal at the DAC entrance. - AnalogOutputMode_DIGITAL_ONLY, - // Coarse emulation of LPF circuit. High frequencies are boosted, sample rate remains unchanged. - AnalogOutputMode_COARSE, - // Finer emulation of LPF circuit. Output signal is upsampled to 48 kHz to allow emulation of audible mirror spectra above 16 kHz, - // which is passed through the LPF circuit without significant attenuation. - AnalogOutputMode_ACCURATE, - // Same as AnalogOutputMode_ACCURATE mode but the output signal is 2x oversampled, i.e. the output sample rate is 96 kHz. - // This makes subsequent resampling easier. Besides, due to nonlinear passband of the LPF emulated, it takes fewer number of MACs - // compared to a regular LPF FIR implementations. - AnalogOutputMode_OVERSAMPLED -}; - -enum ReverbMode { - REVERB_MODE_ROOM, - REVERB_MODE_HALL, - REVERB_MODE_PLATE, - REVERB_MODE_TAP_DELAY -}; - -enum PartialState { - PartialState_INACTIVE, - PartialState_ATTACK, - PartialState_SUSTAIN, - PartialState_RELEASE -}; - const Bit8u SYSEX_MANUFACTURER_ROLAND = 0x41; const Bit8u SYSEX_MDL_MT32 = 0x16; @@ -132,47 +68,66 @@ const Bit8u SYSEX_CMD_EOD = 0x45; // End of data const Bit8u SYSEX_CMD_ERR = 0x4E; // Communications error const Bit8u SYSEX_CMD_RJC = 0x4F; // Rejection -const int MAX_SYSEX_SIZE = 512; // FIXME: Does this correspond to a real MIDI buffer used in h/w devices? +const Bit32u CONTROL_ROM_SIZE = 64 * 1024; -const unsigned int CONTROL_ROM_SIZE = 64 * 1024; - -class ReportHandler { -friend class Synth; +// Set of multiplexed output streams appeared at the DAC entrance. +template +struct DACOutputStreams { + T *nonReverbLeft; + T *nonReverbRight; + T *reverbDryLeft; + T *reverbDryRight; + T *reverbWetLeft; + T *reverbWetRight; +}; +// Class for the client to supply callbacks for reporting various errors and information +class MT32EMU_EXPORT ReportHandler { public: virtual ~ReportHandler() {} -protected: - // Callback for debug messages, in vprintf() format virtual void printDebug(const char *fmt, va_list list); - - // Callbacks for reporting various errors and information + // Callbacks for reporting errors virtual void onErrorControlROM() {} virtual void onErrorPCMROM() {} + // Callback for reporting about displaying a new custom message on LCD virtual void showLCDMessage(const char *message); + // Callback for reporting actual processing of a MIDI message virtual void onMIDIMessagePlayed() {} + // Callback for reporting an overflow of the input MIDI queue. + // Returns true if a recovery action was taken and yet another attempt to enqueue the MIDI event is desired. + virtual bool onMIDIQueueOverflow() { return false; } + // Callback invoked when a System Realtime MIDI message is detected at the input. + virtual void onMIDISystemRealtime(Bit8u /* systemRealtime */) {} + // Callbacks for reporting system events virtual void onDeviceReset() {} virtual void onDeviceReconfig() {} + // Callbacks for reporting changes of reverb settings virtual void onNewReverbMode(Bit8u /* mode */) {} virtual void onNewReverbTime(Bit8u /* time */) {} virtual void onNewReverbLevel(Bit8u /* level */) {} - virtual void onPolyStateChanged(int /* partNum */) {} - virtual void onProgramChanged(int /* partNum */, int /* bankNum */, const char * /* patchName */) {} + // Callbacks for reporting various information + virtual void onPolyStateChanged(Bit8u /* partNum */) {} + virtual void onProgramChanged(Bit8u /* partNum */, const char * /* soundGroupName */, const char * /* patchName */) {} }; class Synth { +friend class DefaultMidiStreamParser; friend class Part; -friend class RhythmPart; -friend class Poly; friend class Partial; friend class PartialManager; -friend class Tables; -friend class MemoryRegion; +friend class Poly; +friend class Renderer; +friend class RhythmPart; +friend class SamplerateAdapter; +friend class SoxrAdapter; friend class TVA; -friend class TVF; friend class TVP; + private: + // **************************** Implementation fields ************************** + PatchTempMemoryRegion *patchTempMemoryRegion; RhythmTempMemoryRegion *rhythmTempMemoryRegion; TimbreTempMemoryRegion *timbreTempMemoryRegion; @@ -184,8 +139,6 @@ private: Bit8u *paddedTimbreMaxTable; - bool isEnabled; - PCMWaveEntry *pcmWaves; // Array const ControlROMFeatureSet *controlROMFeatures; @@ -194,8 +147,11 @@ private: Bit16s *pcmROMData; size_t pcmROMSize; // This is in 16-bit samples, therefore half the number of bytes in the ROM - unsigned int partialCount; - Bit8s chantable[32]; // FIXME: Need explanation why 32 is set, obviously it should be 16 + Bit8u soundGroupIx[128]; // For each standard timbre + const char (*soundGroupNames)[9]; // Array + + Bit32u partialCount; + Bit8u chantable[16]; // NOTE: value above 8 means that the channel is not assigned MidiEventQueue *midiQueue; volatile Bit32u lastReceivedMIDIEventTimestamp; @@ -215,7 +171,8 @@ private: bool reversedStereoEnabled; - bool isOpen; + bool opened; + bool activated; bool isDefaultReportHandler; ReportHandler *reportHandler; @@ -229,15 +186,17 @@ private: Poly *abortingPoly; Analog *analog; + Renderer &renderer; + + // Binary compatibility helper. + void *reserved; + + // **************************** Implementation methods ************************** Bit32u addMIDIInterfaceDelay(Bit32u len, Bit32u timestamp); + bool isAbortingPoly() const { return abortingPoly != NULL; } - void produceLA32Output(Sample *buffer, Bit32u len); - void convertSamplesToOutput(Sample *buffer, Bit32u len); - bool isAbortingPoly() const; - void doRenderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample *reverbDryLeft, Sample *reverbDryRight, Sample *reverbWetLeft, Sample *reverbWetRight, Bit32u len); - - void readSysex(unsigned char channel, const Bit8u *sysex, Bit32u len) const; + void readSysex(Bit8u channel, const Bit8u *sysex, Bit32u len) const; void initMemoryRegions(); void deleteMemoryRegions(); MemoryRegion *findMemoryRegion(Bit32u addr); @@ -248,79 +207,105 @@ private: bool loadPCMROM(const ROMImage &pcmROMImage); bool initPCMList(Bit16u mapAddress, Bit16u count); - bool initTimbres(Bit16u mapAddress, Bit16u offset, int timbreCount, int startTimbre, bool compressed); - bool initCompressedTimbre(int drumNum, const Bit8u *mem, unsigned int memLen); + bool initTimbres(Bit16u mapAddress, Bit16u offset, Bit16u timbreCount, Bit16u startTimbre, bool compressed); + bool initCompressedTimbre(Bit16u drumNum, const Bit8u *mem, Bit32u memLen); + void initReverbModels(bool mt32CompatibleMode); + void initSoundGroups(char newSoundGroupNames[][9]); void refreshSystemMasterTune(); void refreshSystemReverbParameters(); void refreshSystemReserveSettings(); - void refreshSystemChanAssign(unsigned int firstPart, unsigned int lastPart); + void refreshSystemChanAssign(Bit8u firstPart, Bit8u lastPart); void refreshSystemMasterVol(); void refreshSystem(); void reset(); + void dispose(); - void printPartialUsage(unsigned long sampleOffset = 0); + void printPartialUsage(Bit32u sampleOffset = 0); - void polyStateChanged(int partNum); - void newTimbreSet(int partNum, Bit8u timbreGroup, const char patchName[]); + void newTimbreSet(Bit8u partNum, Bit8u timbreGroup, Bit8u timbreNumber, const char patchName[]); void printDebug(const char *fmt, ...); // partNum should be 0..7 for Part 1..8, or 8 for Rhythm - const Part *getPart(unsigned int partNum) const; + const Part *getPart(Bit8u partNum) const; public: - static inline Sample clipSampleEx(SampleEx sampleEx) { -#if MT32EMU_USE_FLOAT_SAMPLES - return sampleEx; -#else + static inline Bit16s clipSampleEx(Bit32s sampleEx) { // Clamp values above 32767 to 32767, and values below -32768 to -32768 // FIXME: Do we really need this stuff? I think these branches are very well predicted. Instead, this introduces a chain. // The version below is actually a bit faster on my system... - //return ((sampleEx + 0x8000) & ~0xFFFF) ? (sampleEx >> 31) ^ 0x7FFF : (Sample)sampleEx; - return ((-0x8000 <= sampleEx) && (sampleEx <= 0x7FFF)) ? (Sample)sampleEx : (sampleEx >> 31) ^ 0x7FFF; -#endif + //return ((sampleEx + 0x8000) & ~0xFFFF) ? Bit16s((sampleEx >> 31) ^ 0x7FFF) : (Bit16s)sampleEx; + return ((-0x8000 <= sampleEx) && (sampleEx <= 0x7FFF)) ? Bit16s(sampleEx) : Bit16s((sampleEx >> 31) ^ 0x7FFF); } - static inline void muteSampleBuffer(Sample *buffer, Bit32u len) { - if (buffer == NULL) return; + static inline float clipSampleEx(float sampleEx) { + return sampleEx; + } -#if MT32EMU_USE_FLOAT_SAMPLES + template + static inline void muteSampleBuffer(S *buffer, Bit32u len) { + if (buffer == NULL) return; + memset(buffer, 0, len * sizeof(S)); + } + + static inline void muteSampleBuffer(float *buffer, Bit32u len) { + if (buffer == NULL) return; // FIXME: Use memset() where compatibility is guaranteed (if this turns out to be a win) while (len--) { *(buffer++) = 0.0f; } -#else - memset(buffer, 0, len * sizeof(Sample)); -#endif } - static Bit32u getShortMessageLength(Bit32u msg); - static Bit8u calcSysexChecksum(const Bit8u *data, const Bit32u len, const Bit8u initChecksum = 0); + static inline Bit16s convertSample(float sample) { + return Synth::clipSampleEx(Bit32s(sample * 16384.0f)); // This multiplier takes into account the DAC bit shift + } + + static inline float convertSample(Bit16s sample) { + return float(sample) / 16384.0f; // This multiplier takes into account the DAC bit shift + } + + // Returns library version as an integer in format: 0x00MMmmpp, where: + // MM - major version number + // mm - minor version number + // pp - patch number + MT32EMU_EXPORT static Bit32u getLibraryVersionInt(); + // Returns library version as a C-string in format: "MAJOR.MINOR.PATCH" + MT32EMU_EXPORT static const char *getLibraryVersionString(); + + MT32EMU_EXPORT static Bit32u getShortMessageLength(Bit32u msg); + MT32EMU_EXPORT static Bit8u calcSysexChecksum(const Bit8u *data, const Bit32u len, const Bit8u initChecksum = 0); + + // Returns output sample rate used in emulation of stereo analog circuitry of hardware units. + // See comment for AnalogOutputMode. + MT32EMU_EXPORT static Bit32u getStereoOutputSampleRate(AnalogOutputMode analogOutputMode); // Optionally sets callbacks for reporting various errors, information and debug messages - Synth(ReportHandler *useReportHandler = NULL); - ~Synth(); + MT32EMU_EXPORT explicit Synth(ReportHandler *useReportHandler = NULL); + MT32EMU_EXPORT ~Synth(); // Used to initialise the MT-32. Must be called before any other function. // Returns true if initialization was sucessful, otherwise returns false. // controlROMImage and pcmROMImage represent Control and PCM ROM images for use by synth. // usePartialCount sets the maximum number of partials playing simultaneously for this session (optional). // analogOutputMode sets the mode for emulation of analogue circuitry of the hardware units (optional). - bool open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, unsigned int usePartialCount = DEFAULT_MAX_PARTIALS, AnalogOutputMode analogOutputMode = AnalogOutputMode_COARSE); + MT32EMU_EXPORT bool open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, Bit32u usePartialCount = DEFAULT_MAX_PARTIALS, AnalogOutputMode analogOutputMode = AnalogOutputMode_COARSE); // Overloaded method which opens the synth with default partial count. - bool open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, AnalogOutputMode analogOutputMode); + MT32EMU_EXPORT bool open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, AnalogOutputMode analogOutputMode); // Closes the MT-32 and deallocates any memory used by the synthesizer - void close(bool forced = false); + MT32EMU_EXPORT void close(); + + // Returns true if the synth is in completely initialized state, otherwise returns false. + MT32EMU_EXPORT bool isOpen() const; // All the enqueued events are processed by the synth immediately. - void flushMIDIQueue(); + MT32EMU_EXPORT void flushMIDIQueue(); // Sets size of the internal MIDI event queue. The queue size is set to the minimum power of 2 that is greater or equal to the size specified. // The queue is flushed before reallocation. // Returns the actual queue size being used. - Bit32u setMIDIEventQueueSize(Bit32u); + MT32EMU_EXPORT Bit32u setMIDIEventQueueSize(Bit32u); // Enqueues a MIDI event for subsequent playback. // The MIDI event will be processed not before the specified timestamp. @@ -330,14 +315,15 @@ public: // Calls from multiple threads must be synchronised, although, no synchronisation is required with the rendering thread. // The methods return false if the MIDI event queue is full and the message cannot be enqueued. - // Enqueues a single short MIDI message. The message must contain a status byte. - bool playMsg(Bit32u msg, Bit32u timestamp); - // Enqueues a single well formed System Exclusive MIDI message. - bool playSysex(const Bit8u *sysex, Bit32u len, Bit32u timestamp); + // Enqueues a single short MIDI message to play at specified time. The message must contain a status byte. + MT32EMU_EXPORT bool playMsg(Bit32u msg, Bit32u timestamp); + // Enqueues a single well formed System Exclusive MIDI message to play at specified time. + MT32EMU_EXPORT bool playSysex(const Bit8u *sysex, Bit32u len, Bit32u timestamp); - // Overloaded methods for the MIDI events to be processed ASAP. - bool playMsg(Bit32u msg); - bool playSysex(const Bit8u *sysex, Bit32u len); + // Enqueues a single short MIDI message to be processed ASAP. The message must contain a status byte. + MT32EMU_EXPORT bool playMsg(Bit32u msg); + // Enqueues a single well formed System Exclusive MIDI message to be processed ASAP. + MT32EMU_EXPORT bool playSysex(const Bit8u *sysex, Bit32u len); // WARNING: // The methods below don't ensure minimum 1-sample delay between sequential MIDI events, @@ -345,40 +331,60 @@ public: // A thread that invokes these methods must be explicitly synchronised with the thread performing sample rendering. // Sends a short MIDI message to the synth for immediate playback. The message must contain a status byte. - void playMsgNow(Bit32u msg); - void playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity); + // See the WARNING above. + MT32EMU_EXPORT void playMsgNow(Bit32u msg); + // Sends unpacked short MIDI message to the synth for immediate playback. The message must contain a status byte. + // See the WARNING above. + MT32EMU_EXPORT void playMsgOnPart(Bit8u part, Bit8u code, Bit8u note, Bit8u velocity); - // Sends a string of Sysex commands to the MT-32 for immediate interpretation - // The length is in bytes - void playSysexNow(const Bit8u *sysex, Bit32u len); - void playSysexWithoutFraming(const Bit8u *sysex, Bit32u len); - void playSysexWithoutHeader(unsigned char device, unsigned char command, const Bit8u *sysex, Bit32u len); - void writeSysex(unsigned char channel, const Bit8u *sysex, Bit32u len); + // Sends a single well formed System Exclusive MIDI message for immediate processing. The length is in bytes. + // See the WARNING above. + MT32EMU_EXPORT void playSysexNow(const Bit8u *sysex, Bit32u len); + // Sends inner body of a System Exclusive MIDI message for direct processing. The length is in bytes. + // See the WARNING above. + MT32EMU_EXPORT void playSysexWithoutFraming(const Bit8u *sysex, Bit32u len); + // Sends inner body of a System Exclusive MIDI message for direct processing. The length is in bytes. + // See the WARNING above. + MT32EMU_EXPORT void playSysexWithoutHeader(Bit8u device, Bit8u command, const Bit8u *sysex, Bit32u len); + // Sends inner body of a System Exclusive MIDI message for direct processing. The length is in bytes. + // See the WARNING above. + MT32EMU_EXPORT void writeSysex(Bit8u channel, const Bit8u *sysex, Bit32u len); - void setReverbEnabled(bool reverbEnabled); - bool isReverbEnabled() const; + // Allows to disable wet reverb output altogether. + MT32EMU_EXPORT void setReverbEnabled(bool reverbEnabled); + // Returns whether wet reverb output is enabled. + MT32EMU_EXPORT bool isReverbEnabled() const; // Sets override reverb mode. In this mode, emulation ignores sysexes (or the related part of them) which control the reverb parameters. // This mode is in effect until it is turned off. When the synth is re-opened, the override mode is unchanged but the state // of the reverb model is reset to default. - void setReverbOverridden(bool reverbOverridden); - bool isReverbOverridden() const; + MT32EMU_EXPORT void setReverbOverridden(bool reverbOverridden); + // Returns whether reverb settings are overridden. + MT32EMU_EXPORT bool isReverbOverridden() const; // Forces reverb model compatibility mode. By default, the compatibility mode corresponds to the used control ROM version. // Invoking this method with the argument set to true forces emulation of old MT-32 reverb circuit. // When the argument is false, emulation of the reverb circuit used in new generation of MT-32 compatible modules is enforced // (these include CM-32L and LAPC-I). - void setReverbCompatibilityMode(bool mt32CompatibleMode); - bool isMT32ReverbCompatibilityMode() const; - void setDACInputMode(DACInputMode mode); - DACInputMode getDACInputMode() const; - void setMIDIDelayMode(MIDIDelayMode mode); - MIDIDelayMode getMIDIDelayMode() const; + MT32EMU_EXPORT void setReverbCompatibilityMode(bool mt32CompatibleMode); + // Returns whether reverb is in old MT-32 compatibility mode. + MT32EMU_EXPORT bool isMT32ReverbCompatibilityMode() const; + // Returns whether default reverb compatibility mode is the old MT-32 compatibility mode. + MT32EMU_EXPORT bool isDefaultReverbMT32Compatible() const; + // Sets new DAC input mode. See DACInputMode for details. + MT32EMU_EXPORT void setDACInputMode(DACInputMode mode); + // Returns current DAC input mode. See DACInputMode for details. + MT32EMU_EXPORT DACInputMode getDACInputMode() const; + // Sets new MIDI delay mode. See MIDIDelayMode for details. + MT32EMU_EXPORT void setMIDIDelayMode(MIDIDelayMode mode); + // Returns current MIDI delay mode. See MIDIDelayMode for details. + MT32EMU_EXPORT MIDIDelayMode getMIDIDelayMode() const; // Sets output gain factor for synth output channels. Applied to all output samples and unrelated with the synth's Master volume, // it rather corresponds to the gain of the output analog circuitry of the hardware units. However, together with setReverbOutputGain() // it offers to the user a capability to control the gain of reverb and non-reverb output channels independently. // Ignored in DACInputMode_PURE - void setOutputGain(float); - float getOutputGain() const; + MT32EMU_EXPORT void setOutputGain(float gain); + // Returns current output gain factor for synth output channels. + MT32EMU_EXPORT float getOutputGain() const; // Sets output gain factor for the reverb wet output channels. It rather corresponds to the gain of the output // analog circuitry of the hardware units. However, together with setOutputGain() it offers to the user a capability @@ -389,59 +395,85 @@ public: // there is a difference in the reverb analogue circuit, and the resulting output gain is 0.68 // of that for LA32 analogue output. This factor is applied to the reverb output gain. // Ignored in DACInputMode_PURE - void setReverbOutputGain(float); - float getReverbOutputGain() const; + MT32EMU_EXPORT void setReverbOutputGain(float gain); + // Returns current output gain factor for reverb wet output channels. + MT32EMU_EXPORT float getReverbOutputGain() const; - void setReversedStereoEnabled(bool enabled); - bool isReversedStereoEnabled(); + // Swaps left and right output channels. + MT32EMU_EXPORT void setReversedStereoEnabled(bool enabled); + // Returns whether left and right output channels are swapped. + MT32EMU_EXPORT bool isReversedStereoEnabled() const; // Returns actual sample rate used in emulation of stereo analog circuitry of hardware units. // See comment for render() below. - unsigned int getStereoOutputSampleRate() const; + MT32EMU_EXPORT Bit32u getStereoOutputSampleRate() const; // Renders samples to the specified output stream as if they were sampled at the analog stereo output. - // When AnalogOutputMode is set to ACCURATE, the output signal is upsampled to 48 kHz in order + // When AnalogOutputMode is set to ACCURATE (OVERSAMPLED), the output signal is upsampled to 48 (96) kHz in order // to retain emulation accuracy in whole audible frequency spectra. Otherwise, native digital signal sample rate is retained. // getStereoOutputSampleRate() can be used to query actual sample rate of the output signal. - // The length is in frames, not bytes (in 16-bit stereo, one frame is 4 bytes). - void render(Sample *stream, Bit32u len); + // The length is in frames, not bytes (in 16-bit stereo, one frame is 4 bytes). Uses NATIVE byte ordering. + MT32EMU_EXPORT void render(Bit16s *stream, Bit32u len); + // Same as above but outputs to a float stereo stream. + MT32EMU_EXPORT void render(float *stream, Bit32u len); // Renders samples to the specified output streams as if they appeared at the DAC entrance. // No further processing performed in analog circuitry emulation is applied to the signal. - // NULL may be specified in place of any or all of the stream buffers. - // The length is in samples, not bytes. - void renderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample *reverbDryLeft, Sample *reverbDryRight, Sample *reverbWetLeft, Sample *reverbWetRight, Bit32u len); + // NULL may be specified in place of any or all of the stream buffers to skip it. + // The length is in samples, not bytes. Uses NATIVE byte ordering. + MT32EMU_EXPORT void renderStreams(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u len); + void renderStreams(const DACOutputStreams &streams, Bit32u len) { + renderStreams(streams.nonReverbLeft, streams.nonReverbRight, streams.reverbDryLeft, streams.reverbDryRight, streams.reverbWetLeft, streams.reverbWetRight, len); + } + // Same as above but outputs to float streams. + MT32EMU_EXPORT void renderStreams(float *nonReverbLeft, float *nonReverbRight, float *reverbDryLeft, float *reverbDryRight, float *reverbWetLeft, float *reverbWetRight, Bit32u len); + void renderStreams(const DACOutputStreams &streams, Bit32u len) { + renderStreams(streams.nonReverbLeft, streams.nonReverbRight, streams.reverbDryLeft, streams.reverbDryRight, streams.reverbWetLeft, streams.reverbWetRight, len); + } // Returns true when there is at least one active partial, otherwise false. - bool hasActivePartials() const; + MT32EMU_EXPORT bool hasActivePartials() const; - // Returns true if hasActivePartials() returns true, or reverb is (somewhat unreliably) detected as being active. - bool isActive() const; + // Returns true if the synth is active and subsequent calls to render() may result in non-trivial output (i.e. silence). + // The synth is considered active when either there are pending MIDI events in the queue, there is at least one active partial, + // or the reverb is (somewhat unreliably) detected as being active. + MT32EMU_EXPORT bool isActive(); // Returns the maximum number of partials playing simultaneously. - unsigned int getPartialCount() const; + MT32EMU_EXPORT Bit32u getPartialCount() const; // Fills in current states of all the parts into the array provided. The array must have at least 9 entries to fit values for all the parts. // If the value returned for a part is true, there is at least one active non-releasing partial playing on this part. // This info is useful in emulating behaviour of LCD display of the hardware units. - void getPartStates(bool *partStates) const; + MT32EMU_EXPORT void getPartStates(bool *partStates) const; + + // Returns current states of all the parts as a bit set. The least significant bit corresponds to the state of part 1, + // total of 9 bits hold the states of all the parts. If the returned bit for a part is set, there is at least one active + // non-releasing partial playing on this part. This info is useful in emulating behaviour of LCD display of the hardware units. + MT32EMU_EXPORT Bit32u getPartStates() const; // Fills in current states of all the partials into the array provided. The array must be large enough to accommodate states of all the partials. - void getPartialStates(PartialState *partialStates) const; + MT32EMU_EXPORT void getPartialStates(PartialState *partialStates) const; + + // Fills in current states of all the partials into the array provided. Each byte in the array holds states of 4 partials + // starting from the least significant bits. The state of each partial is packed in a pair of bits. + // The array must be large enough to accommodate states of all the partials (see getPartialCount()). + MT32EMU_EXPORT void getPartialStates(Bit8u *partialStates) const; // Fills in information about currently playing notes on the specified part into the arrays provided. The arrays must be large enough // to accommodate data for all the playing notes. The maximum number of simultaneously playing notes cannot exceed the number of partials. // Argument partNumber should be 0..7 for Part 1..8, or 8 for Rhythm. // Returns the number of currently playing notes on the specified part. - unsigned int getPlayingNotes(unsigned int partNumber, Bit8u *keys, Bit8u *velocities) const; + MT32EMU_EXPORT Bit32u getPlayingNotes(Bit8u partNumber, Bit8u *keys, Bit8u *velocities) const; // Returns name of the patch set on the specified part. // Argument partNumber should be 0..7 for Part 1..8, or 8 for Rhythm. - const char *getPatchName(unsigned int partNumber) const; + MT32EMU_EXPORT const char *getPatchName(Bit8u partNumber) const; - void readMemory(Bit32u addr, Bit32u len, Bit8u *data); -}; + // Stores internal state of emulated synth into an array provided (as it would be acquired from hardware). + MT32EMU_EXPORT void readMemory(Bit32u addr, Bit32u len, Bit8u *data); +}; // class Synth -} +} // namespace MT32Emu -#endif +#endif // #ifndef MT32EMU_SYNTH_H diff --git a/audio/softsynth/mt32/TVA.cpp b/audio/softsynth/mt32/TVA.cpp index 894e53f14a4..c20b8b63933 100644 --- a/audio/softsynth/mt32/TVA.cpp +++ b/audio/softsynth/mt32/TVA.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -19,19 +19,23 @@ * This class emulates the calculations performed by the 8095 microcontroller in order to configure the LA-32's amplitude ramp for a single partial at each stage of its TVA envelope. * Unless we introduced bugs, it should be pretty much 100% accurate according to Mok's specifications. */ -//#include -#include "mt32emu.h" -#include "mmath.h" #include "internals.h" +#include "TVA.h" +#include "Part.h" +#include "Partial.h" +#include "Poly.h" +#include "Synth.h" +#include "Tables.h" + namespace MT32Emu { // CONFIRMED: Matches a table in ROM - haven't got around to coming up with a formula for it yet. static Bit8u biasLevelToAmpSubtractionCoeff[13] = {255, 187, 137, 100, 74, 54, 40, 29, 21, 15, 10, 5, 0}; TVA::TVA(const Partial *usePartial, LA32Ramp *useAmpRamp) : - partial(usePartial), ampRamp(useAmpRamp), system_(&usePartial->getSynth()->mt32ram.system), phase(TVA_PHASE_DEAD) { + partial(usePartial), ampRamp(useAmpRamp), system(&usePartial->getSynth()->mt32ram.system), phase(TVA_PHASE_DEAD) { } void TVA::startRamp(Bit8u newTarget, Bit8u newIncrement, int newPhase) { @@ -91,15 +95,15 @@ static int calcVeloAmpSubtraction(Bit8u veloSensitivity, unsigned int velocity) // FIXME:KG: Better variable names int velocityMult = veloSensitivity - 50; int absVelocityMult = velocityMult < 0 ? -velocityMult : velocityMult; - velocityMult = (signed)((unsigned)(velocityMult * ((signed)velocity - 64)) << 2); + velocityMult = signed(unsigned(velocityMult * (signed(velocity) - 64)) << 2); return absVelocityMult - (velocityMult >> 8); // PORTABILITY NOTE: Assumes arithmetic shift } -static int calcBasicAmp(const Tables *tables, const Partial *partial, const MemParams::System *system_, const TimbreParam::PartialParam *partialParam, const MemParams::PatchTemp *patchTemp, const MemParams::RhythmTemp *rhythmTemp, int biasAmpSubtraction, int veloAmpSubtraction, Bit8u expression) { +static int calcBasicAmp(const Tables *tables, const Partial *partial, const MemParams::System *system, const TimbreParam::PartialParam *partialParam, const MemParams::PatchTemp *patchTemp, const MemParams::RhythmTemp *rhythmTemp, int biasAmpSubtraction, int veloAmpSubtraction, Bit8u expression) { int amp = 155; if (!partial->isRingModulatingSlave()) { - amp -= tables->masterVolToAmpSubtraction[system_->masterVol]; + amp -= tables->masterVolToAmpSubtraction[system->masterVol]; if (amp < 0) { return 0; } @@ -140,7 +144,7 @@ static int calcBasicAmp(const Tables *tables, const Partial *partial, const MemP return amp; } -int calcKeyTimeSubtraction(Bit8u envTimeKeyfollow, int key) { +static int calcKeyTimeSubtraction(Bit8u envTimeKeyfollow, int key) { if (envTimeKeyfollow == 0) { return 0; } @@ -165,7 +169,7 @@ void TVA::reset(const Part *newPart, const TimbreParam::PartialParam *newPartial biasAmpSubtraction = calcBiasAmpSubtractions(partialParam, key); veloAmpSubtraction = calcVeloAmpSubtraction(partialParam->tva.veloSensitivity, velocity); - int newTarget = calcBasicAmp(tables, partial, system_, partialParam, patchTemp, newRhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression()); + int newTarget = calcBasicAmp(tables, partial, system, partialParam, patchTemp, newRhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression()); int newPhase; if (partialParam->tva.envTime[0] == 0) { // Initially go to the TVA_PHASE_ATTACK target amp, and spend the next phase going from there to the TVA_PHASE_2 target amp @@ -182,7 +186,7 @@ void TVA::reset(const Part *newPart, const TimbreParam::PartialParam *newPartial // "Go downward as quickly as possible". // Since the current value is 0, the LA32Ramp will notice that we're already at or below the target and trying to go downward, // and therefore jump to the target immediately and raise an interrupt. - startRamp((Bit8u)newTarget, 0x80 | 127, newPhase); + startRamp(Bit8u(newTarget), 0x80 | 127, newPhase); } void TVA::startAbort() { @@ -217,7 +221,7 @@ void TVA::recalcSustain() { } // We're sustaining. Recalculate all the values const Tables *tables = &Tables::getInstance(); - int newTarget = calcBasicAmp(tables, partial, system_, partialParam, patchTemp, rhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression()); + int newTarget = calcBasicAmp(tables, partial, system, partialParam, patchTemp, rhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression()); newTarget += partialParam->tva.envLevel[3]; // Since we're in TVA_PHASE_SUSTAIN at this point, we know that target has been reached and an interrupt fired, so we can rely on it being the current amp. int targetDelta = newTarget - target; @@ -225,9 +229,9 @@ void TVA::recalcSustain() { // Calculate an increment to get to the new amp value in a short, more or less consistent amount of time Bit8u newIncrement; if (targetDelta >= 0) { - newIncrement = tables->envLogarithmicTime[(Bit8u)targetDelta] - 2; + newIncrement = tables->envLogarithmicTime[Bit8u(targetDelta)] - 2; } else { - newIncrement = (tables->envLogarithmicTime[(Bit8u)-targetDelta] - 2) | 0x80; + newIncrement = (tables->envLogarithmicTime[Bit8u(-targetDelta)] - 2) | 0x80; } // Configure so that once the transition's complete and nextPhase() is called, we'll just re-enter sustain phase (or decay phase, depending on parameters at the time). startRamp(newTarget, newIncrement, TVA_PHASE_SUSTAIN - 1); @@ -279,7 +283,7 @@ void TVA::nextPhase() { int envPointIndex = phase; if (!allLevelsZeroFromNowOn) { - newTarget = calcBasicAmp(tables, partial, system_, partialParam, patchTemp, rhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression()); + newTarget = calcBasicAmp(tables, partial, system, partialParam, patchTemp, rhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression()); if (newPhase == TVA_PHASE_SUSTAIN || newPhase == TVA_PHASE_RELEASE) { if (partialParam->tva.envLevel[3] == 0) { @@ -311,7 +315,7 @@ void TVA::nextPhase() { int envTimeSetting = partialParam->tva.envTime[envPointIndex]; if (newPhase == TVA_PHASE_ATTACK) { - envTimeSetting -= ((signed)partial->getPoly()->getVelocity() - 64) >> (6 - partialParam->tva.envTimeVeloSensitivity); // PORTABILITY NOTE: Assumes arithmetic shift + envTimeSetting -= (signed(partial->getPoly()->getVelocity()) - 64) >> (6 - partialParam->tva.envTimeVeloSensitivity); // PORTABILITY NOTE: Assumes arithmetic shift if (envTimeSetting <= 0 && partialParam->tva.envTime[envPointIndex] != 0) { envTimeSetting = 1; @@ -338,14 +342,14 @@ void TVA::nextPhase() { } } targetDelta = -targetDelta; - newIncrement = tables->envLogarithmicTime[(Bit8u)targetDelta] - envTimeSetting; + newIncrement = tables->envLogarithmicTime[Bit8u(targetDelta)] - envTimeSetting; if (newIncrement <= 0) { newIncrement = 1; } newIncrement = newIncrement | 0x80; } else { // FIXME: The last 22 or so entries in this table are 128 - surely that fucks things up, since that ends up being -128 signed? - newIncrement = tables->envLogarithmicTime[(Bit8u)targetDelta] - envTimeSetting; + newIncrement = tables->envLogarithmicTime[Bit8u(targetDelta)] - envTimeSetting; if (newIncrement <= 0) { newIncrement = 1; } @@ -360,7 +364,7 @@ void TVA::nextPhase() { } } - startRamp((Bit8u)newTarget, (Bit8u)newIncrement, newPhase); + startRamp(Bit8u(newTarget), Bit8u(newIncrement), newPhase); } -} +} // namespace MT32Emu diff --git a/audio/softsynth/mt32/TVA.h b/audio/softsynth/mt32/TVA.h index a100107a699..f593b4e7d18 100644 --- a/audio/softsynth/mt32/TVA.h +++ b/audio/softsynth/mt32/TVA.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -18,9 +18,15 @@ #ifndef MT32EMU_TVA_H #define MT32EMU_TVA_H +#include "globals.h" +#include "Types.h" +#include "Structures.h" + namespace MT32Emu { +class LA32Ramp; class Part; +class Partial; // Note that when entering nextPhase(), newPhase is set to phase + 1, and the descriptions/names below refer to // newPhase's value. @@ -57,7 +63,7 @@ class TVA { private: const Partial * const partial; LA32Ramp *ampRamp; - const MemParams::System * const system_; + const MemParams::System * const system; const Part *part; const TimbreParam::PartialParam *partialParam; @@ -87,8 +93,8 @@ public: bool isPlaying() const; int getPhase() const; -}; +}; // class TVA -} +} // namespace MT32Emu -#endif /* TVA_H_ */ +#endif // #ifndef MT32EMU_TVA_H diff --git a/audio/softsynth/mt32/TVF.cpp b/audio/softsynth/mt32/TVF.cpp index 164cf2b4cbb..b296c34132a 100644 --- a/audio/softsynth/mt32/TVF.cpp +++ b/audio/softsynth/mt32/TVF.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -15,12 +15,14 @@ * along with this program. If not, see . */ -//#include - -#include "mt32emu.h" -#include "mmath.h" #include "internals.h" +#include "TVF.h" +#include "LA32Ramp.h" +#include "Partial.h" +#include "Poly.h" +#include "Tables.h" + namespace MT32Emu { // Note that when entering nextPhase(), newPhase is set to phase + 1, and the descriptions/names below refer to @@ -60,7 +62,7 @@ static int calcBaseCutoff(const TimbreParam::PartialParam *partialParam, Bit32u static const Bit8s keyfollowMult21[] = {-21, -10, -5, 0, 2, 5, 8, 10, 13, 16, 18, 21, 26, 32, 42, 21, 21}; int baseCutoff = keyfollowMult21[partialParam->tvf.keyfollow] - keyfollowMult21[partialParam->wg.pitchKeyfollow]; // baseCutoff range now: -63 to 63 - baseCutoff *= (int)key - 60; + baseCutoff *= int(key) - 60; // baseCutoff range now: -3024 to 3024 int biasPoint = partialParam->tvf.biasPoint; if ((biasPoint & 0x40) == 0) { @@ -75,7 +77,7 @@ static int calcBaseCutoff(const TimbreParam::PartialParam *partialParam, Bit32u // biasPoint range here: 64 to 127 int bias = biasPoint - 31 - key; // bias range here: -75 to 84 if (bias < 0) { - baseCutoff += bias * biasLevelToBiasMult[partialParam->tvf.biasLevel]; // Calculation range: −6375 to 6375 + baseCutoff += bias * biasLevelToBiasMult[partialParam->tvf.biasLevel]; // Calculation range: -6375 to 6375 // baseCutoff range now: -9399 to 9399 } } @@ -96,7 +98,7 @@ static int calcBaseCutoff(const TimbreParam::PartialParam *partialParam, Bit32u if (baseCutoff > 255) { baseCutoff = 255; } - return (Bit8u)baseCutoff; + return Bit8u(baseCutoff); } TVF::TVF(const Partial *usePartial, LA32Ramp *useCutoffModifierRamp) : @@ -128,7 +130,7 @@ void TVF::reset(const TimbreParam::PartialParam *newPartialParam, unsigned int b int newLevelMult = velocity * newPartialParam->tvf.envVeloSensitivity; newLevelMult >>= 6; newLevelMult += 109 - newPartialParam->tvf.envVeloSensitivity; - newLevelMult += ((signed)key - 60) >> (4 - newPartialParam->tvf.envDepthKeyfollow); + newLevelMult += (signed(key) - 60) >> (4 - newPartialParam->tvf.envDepthKeyfollow); if (newLevelMult < 0) { newLevelMult = 0; } @@ -140,7 +142,7 @@ void TVF::reset(const TimbreParam::PartialParam *newPartialParam, unsigned int b levelMult = newLevelMult; if (newPartialParam->tvf.envTimeKeyfollow != 0) { - keyTimeSubtraction = ((signed)key - 60) >> (5 - newPartialParam->tvf.envTimeKeyfollow); + keyTimeSubtraction = (signed(key) - 60) >> (5 - newPartialParam->tvf.envTimeKeyfollow); } else { keyTimeSubtraction = 0; } @@ -228,4 +230,4 @@ void TVF::nextPhase() { startRamp(newTarget, newIncrement, newPhase); } -} +} // namespace MT32Emu diff --git a/audio/softsynth/mt32/TVF.h b/audio/softsynth/mt32/TVF.h index 1e2c6d17287..38dcef708cd 100644 --- a/audio/softsynth/mt32/TVF.h +++ b/audio/softsynth/mt32/TVF.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -18,8 +18,15 @@ #ifndef MT32EMU_TVF_H #define MT32EMU_TVF_H +#include "globals.h" +#include "Types.h" +#include "Structures.h" + namespace MT32Emu { +class LA32Ramp; +class Partial; + class TVF { private: const Partial * const partial; @@ -47,8 +54,8 @@ public: Bit8u getBaseCutoff() const; void handleInterrupt(); void startDecay(); -}; +}; // class TVF -} +} // namespace MT32Emu -#endif +#endif // #ifndef MT32EMU_TVF_H diff --git a/audio/softsynth/mt32/TVP.cpp b/audio/softsynth/mt32/TVP.cpp index a8003d96dc5..dca0003843d 100644 --- a/audio/softsynth/mt32/TVP.cpp +++ b/audio/softsynth/mt32/TVP.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -15,12 +15,17 @@ * along with this program. If not, see . */ -//#include -//#include +#include -#include "mt32emu.h" #include "internals.h" +#include "TVP.h" +#include "Part.h" +#include "Partial.h" +#include "Poly.h" +#include "Synth.h" +#include "TVA.h" + namespace MT32Emu { // FIXME: Add Explanation @@ -47,7 +52,7 @@ static Bit16u keyToPitchTable[] = { }; TVP::TVP(const Partial *usePartial) : - partial(usePartial), system_(&usePartial->getSynth()->mt32ram.system) { + partial(usePartial), system(&usePartial->getSynth()->mt32ram.system) { // We want to do processing 4000 times per second. FIXME: This is pretty arbitrary. maxCounter = SAMPLE_RATE / 4000; // The timer runs at 500kHz. We only need to bother updating it every maxCounter samples, before we do processing. @@ -58,7 +63,7 @@ TVP::TVP(const Partial *usePartial) : static Bit16s keyToPitch(unsigned int key) { // We're using a table to do: return round_to_nearest_or_even((key - 60) * (4096.0 / 12.0)) // Banker's rounding is just slightly annoying to do in C++ - int k = (int)key; + int k = int(key); Bit16s pitch = keyToPitchTable[abs(k - 60)]; return key < 60 ? -pitch : pitch; } @@ -82,7 +87,7 @@ static Bit32u calcBasePitch(const Partial *partial, const TimbreParam::PartialPa const ControlROMPCMStruct *controlROMPCMStruct = partial->getControlROMPCMStruct(); if (controlROMPCMStruct != NULL) { - basePitch += (Bit32s)((((Bit32s)controlROMPCMStruct->pitchMSB) << 8) | (Bit32s)controlROMPCMStruct->pitchLSB); + basePitch += (Bit32s(controlROMPCMStruct->pitchMSB) << 8) | Bit32s(controlROMPCMStruct->pitchLSB); } else { if ((partialParam->wg.waveform & 1) == 0) { basePitch += 37133; // This puts Middle C at around 261.64Hz (assuming no other modifications, masterTune of 64, etc.) @@ -98,7 +103,7 @@ static Bit32u calcBasePitch(const Partial *partial, const TimbreParam::PartialPa if (basePitch > 59392) { basePitch = 59392; } - return (Bit32u)basePitch; + return Bit32u(basePitch); } static Bit32u calcVeloMult(Bit8u veloSensitivity, unsigned int velocity) { @@ -119,7 +124,7 @@ static Bit32u calcVeloMult(Bit8u veloSensitivity, unsigned int velocity) { static Bit32s calcTargetPitchOffsetWithoutLFO(const TimbreParam::PartialParam *partialParam, int levelIndex, unsigned int velocity) { int veloMult = calcVeloMult(partialParam->pitchEnv.veloSensitivity, velocity); int targetPitchOffsetWithoutLFO = partialParam->pitchEnv.level[levelIndex] - 50; - targetPitchOffsetWithoutLFO = (Bit32s)(targetPitchOffsetWithoutLFO * veloMult) >> (16 - partialParam->pitchEnv.depth); // PORTABILITY NOTE: Assumes arithmetic shift + targetPitchOffsetWithoutLFO = (targetPitchOffsetWithoutLFO * veloMult) >> (16 - partialParam->pitchEnv.depth); // PORTABILITY NOTE: Assumes arithmetic shift return targetPitchOffsetWithoutLFO; } @@ -140,7 +145,7 @@ void TVP::reset(const Part *usePart, const TimbreParam::PartialParam *usePartial phase = 0; if (partialParam->pitchEnv.timeKeyfollow) { - timeKeyfollowSubtraction = (key - 60) >> (5 - partialParam->pitchEnv.timeKeyfollow); // PORTABILITY NOTE: Assumes arithmetic shift + timeKeyfollowSubtraction = Bit32s(key - 60) >> (5 - partialParam->pitchEnv.timeKeyfollow); // PORTABILITY NOTE: Assumes arithmetic shift } else { timeKeyfollowSubtraction = 0; } @@ -163,7 +168,7 @@ void TVP::updatePitch() { if (!partial->isPCM() || (partial->getControlROMPCMStruct()->len & 0x01) == 0) { // FIXME: Use !partial->pcmWaveEntry->unaffectedByMasterTune instead // FIXME: masterTune recalculation doesn't really happen here, and there are various bugs not yet emulated // 171 is ~half a semitone. - newPitch += ((system_->masterTune - 64) * 171) >> 6; // PORTABILITY NOTE: Assumes arithmetic shift. + newPitch += ((system->masterTune - 64) * 171) >> 6; // PORTABILITY NOTE: Assumes arithmetic shift. } if ((partialParam->wg.pitchBenderEnabled & 1) != 0) { newPitch += part->getPitchBend(); @@ -172,14 +177,13 @@ void TVP::updatePitch() { newPitch = 0; } -// Note: Temporary #ifdef until we have proper "quirk" configuration -// This is about right emulation of MT-32 GEN0 quirk exploited in Colonel's Bequest timbre "Lightning" -#ifndef MT32EMU_QUIRK_PITCH_ENVELOPE_OVERFLOW_MT32 - if (newPitch > 59392) { - newPitch = 59392; + // Skipping this check seems about right emulation of MT-32 GEN0 quirk exploited in Colonel's Bequest timbre "Lightning" + if (partial->getSynth()->controlROMFeatures->quirkPitchEnvelopeOverflow == 0) { + if (newPitch > 59392) { + newPitch = 59392; + } } -#endif - pitch = (Bit16u)newPitch; + pitch = Bit16u(newPitch); // FIXME: We're doing this here because that's what the CM-32L does - we should probably move this somewhere more appropriate in future. partial->getTVA()->recalcSustain(); @@ -317,10 +321,10 @@ void TVP::process() { negativeBigTicksRemaining = negativeBigTicksRemaining >> rightShifts; // PORTABILITY NOTE: Assumes arithmetic shift rightShifts = 13; } - int newResult = ((Bit32s)(negativeBigTicksRemaining * pitchOffsetChangePerBigTick)) >> rightShifts; // PORTABILITY NOTE: Assumes arithmetic shift + int newResult = (negativeBigTicksRemaining * pitchOffsetChangePerBigTick) >> rightShifts; // PORTABILITY NOTE: Assumes arithmetic shift newResult += targetPitchOffsetWithoutLFO + lfoPitchOffset; currentPitchOffset = newResult; updatePitch(); } -} +} // namespace MT32Emu diff --git a/audio/softsynth/mt32/TVP.h b/audio/softsynth/mt32/TVP.h index e9d05ffa7a1..be90f0ff087 100644 --- a/audio/softsynth/mt32/TVP.h +++ b/audio/softsynth/mt32/TVP.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -18,12 +18,19 @@ #ifndef MT32EMU_TVP_H #define MT32EMU_TVP_H +#include "globals.h" +#include "Types.h" +#include "Structures.h" + namespace MT32Emu { +class Part; +class Partial; + class TVP { private: const Partial * const partial; - const MemParams::System * const system_; // FIXME: Only necessary because masterTune calculation is done in the wrong place atm. + const MemParams::System * const system; // FIXME: Only necessary because masterTune calculation is done in the wrong place atm. const Part *part; const TimbreParam::PartialParam *partialParam; @@ -60,8 +67,8 @@ public: Bit32u getBasePitch() const; Bit16u nextPitch(); void startDecay(); -}; +}; // class TVP -} +} // namespace MT32Emu -#endif +#endif // #ifndef MT32EMU_TVP_H diff --git a/audio/softsynth/mt32/Tables.cpp b/audio/softsynth/mt32/Tables.cpp index 7e165b5a7a1..cb3493285a8 100644 --- a/audio/softsynth/mt32/Tables.cpp +++ b/audio/softsynth/mt32/Tables.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -15,11 +15,10 @@ * along with this program. If not, see . */ -//#include +#include "internals.h" -#include "mt32emu.h" -#include "mmath.h" #include "Tables.h" +#include "mmath.h" namespace MT32Emu { @@ -31,24 +30,25 @@ const Tables &Tables::getInstance() { } Tables::Tables() { - int lf; - for (lf = 0; lf <= 100; lf++) { + for (int lf = 0; lf <= 100; lf++) { // CONFIRMED:KG: This matches a ROM table found by Mok - float fVal = (2.0f - LOG10F((float)lf + 1.0f)) * 128.0f; - int val = (int)(fVal + 1.0); + float fVal = (2.0f - LOG10F(float(lf) + 1.0f)) * 128.0f; + int val = int(fVal + 1.0); if (val > 255) { val = 255; } - levelToAmpSubtraction[lf] = (Bit8u)val; + levelToAmpSubtraction[lf] = Bit8u(val); } envLogarithmicTime[0] = 64; - for (lf = 1; lf <= 255; lf++) { + for (int lf = 1; lf <= 255; lf++) { // CONFIRMED:KG: This matches a ROM table found by Mok - envLogarithmicTime[lf] = (Bit8u)ceil(64.0f + LOG2F((float)lf) * 8.0f); + envLogarithmicTime[lf] = Bit8u(ceil(64.0f + LOG2F(float(lf)) * 8.0f)); } -#ifdef EMULATE_LAPC_I // Dummy #ifdef - we'll have runtime emulation mode selection in future. +#if 0 + // The table below is to be used in conjunction with emulation of VCA of newer generation units which is currently missing. + // These relatively small values are rather intended to fine-tune the overall amplification of the VCA. // CONFIRMED: Based on a table found by Mok in the LAPC-I control ROM // Note that this matches the MT-32 table, but with the values clamped to a maximum of 8. memset(masterVolToAmpSubtraction, 8, 71); @@ -64,12 +64,12 @@ Tables::Tables() { // CONFIRMED: Based on a table found by Mok in the MT-32 control ROM masterVolToAmpSubtraction[0] = 255; for (int masterVol = 1; masterVol <= 100; masterVol++) { - masterVolToAmpSubtraction[masterVol] = (int)(106.31 - 16.0f * LOG2F((float)masterVol)); + masterVolToAmpSubtraction[masterVol] = Bit8u(106.31 - 16.0f * LOG2F(float(masterVol))); } #endif for (int i = 0; i <= 100; i++) { - pulseWidth100To255[i] = (int)(i * 255 / 100.0f + 0.5f); + pulseWidth100To255[i] = Bit8u(i * 255 / 100.0f + 0.5f); //synth->printDebug("%d: %d", i, pulseWidth100To255[i]); } @@ -94,4 +94,4 @@ Tables::Tables() { resAmpDecayFactor = resAmpDecayFactorTable; } -} +} // namespace MT32Emu diff --git a/audio/softsynth/mt32/Tables.h b/audio/softsynth/mt32/Tables.h index 8865c7fac82..249e32919a3 100644 --- a/audio/softsynth/mt32/Tables.h +++ b/audio/softsynth/mt32/Tables.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -18,6 +18,9 @@ #ifndef MT32EMU_TABLES_H #define MT32EMU_TABLES_H +#include "globals.h" +#include "Types.h" + namespace MT32Emu { class Tables { @@ -52,8 +55,8 @@ public: Bit16u logsin9[512]; const Bit8u *resAmpDecayFactor; -}; +}; // class Tables -} +} // namespace MT32Emu -#endif +#endif // #ifndef MT32EMU_TABLES_H diff --git a/audio/softsynth/mt32/Types.h b/audio/softsynth/mt32/Types.h index 934b1a1173e..f90dce19a4c 100644 --- a/audio/softsynth/mt32/Types.h +++ b/audio/softsynth/mt32/Types.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -27,14 +27,6 @@ typedef signed short int Bit16s; typedef unsigned char Bit8u; typedef signed char Bit8s; -#if MT32EMU_USE_FLOAT_SAMPLES -typedef float Sample; -typedef float SampleEx; -#else -typedef Bit16s Sample; -typedef Bit32s SampleEx; -#endif - } #endif diff --git a/audio/softsynth/mt32/config.h b/audio/softsynth/mt32/config.h new file mode 100644 index 00000000000..af59f055a0b --- /dev/null +++ b/audio/softsynth/mt32/config.h @@ -0,0 +1,28 @@ +/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#ifndef MT32EMU_CONFIG_H +#define MT32EMU_CONFIG_H + +#define MT32EMU_VERSION "2.0.3" +#define MT32EMU_VERSION_MAJOR 2 +#define MT32EMU_VERSION_MINOR 0 +#define MT32EMU_VERSION_PATCH 3 + +#define MT32EMU_EXPORTS_TYPE 3 + +#endif diff --git a/audio/softsynth/mt32/globals.h b/audio/softsynth/mt32/globals.h new file mode 100644 index 00000000000..49a5ecc250e --- /dev/null +++ b/audio/softsynth/mt32/globals.h @@ -0,0 +1,119 @@ +/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#ifndef MT32EMU_GLOBALS_H +#define MT32EMU_GLOBALS_H + +#include "config.h" + +/* Support for compiling shared library. */ +#ifdef MT32EMU_SHARED +#if defined _WIN32 || defined __CYGWIN__ +#ifdef _MSC_VER +#ifdef mt32emu_EXPORTS +#define MT32EMU_EXPORT_ATTRIBUTE _declspec(dllexport) +#else /* #ifdef mt32emu_EXPORTS */ +#define MT32EMU_EXPORT_ATTRIBUTE _declspec(dllimport) +#endif /* #ifdef mt32emu_EXPORTS */ +#else /* #ifdef _MSC_VER */ +#ifdef mt32emu_EXPORTS +#define MT32EMU_EXPORT_ATTRIBUTE __attribute__ ((dllexport)) +#else /* #ifdef mt32emu_EXPORTS */ +#define MT32EMU_EXPORT_ATTRIBUTE __attribute__ ((dllimport)) +#endif /* #ifdef mt32emu_EXPORTS */ +#endif /* #ifdef _MSC_VER */ +#else /* #if defined _WIN32 || defined __CYGWIN__ */ +#define MT32EMU_EXPORT_ATTRIBUTE __attribute__ ((visibility("default"))) +#endif /* #if defined _WIN32 || defined __CYGWIN__ */ +#else /* #ifdef MT32EMU_SHARED */ +#define MT32EMU_EXPORT_ATTRIBUTE +#endif /* #ifdef MT32EMU_SHARED */ + +#if MT32EMU_EXPORTS_TYPE == 1 || MT32EMU_EXPORTS_TYPE == 2 +#define MT32EMU_EXPORT +#else +#define MT32EMU_EXPORT MT32EMU_EXPORT_ATTRIBUTE +#endif + +/* Useful constants */ + +/* Sample rate to use in mixing. With the progress of development, we've found way too many thing dependent. + * In order to achieve further advance in emulation accuracy, sample rate made fixed throughout the emulator, + * except the emulation of analogue path. + * The output from the synth is supposed to be resampled externally in order to convert to the desired sample rate. + */ +#define MT32EMU_SAMPLE_RATE 32000 + +/* The default value for the maximum number of partials playing simultaneously. */ +#define MT32EMU_DEFAULT_MAX_PARTIALS 32 + +/* The higher this number, the more memory will be used, but the more samples can be processed in one run - + * various parts of sample generation can be processed more efficiently in a single run. + * A run's maximum length is that given to Synth::render(), so giving a value here higher than render() is ever + * called with will give no gain (but simply waste the memory). + * Note that this value does *not* in any way impose limitations on the length given to render(), and has no effect + * on the generated audio. + * This value must be >= 1. + */ +#define MT32EMU_MAX_SAMPLES_PER_RUN 4096 + +/* The default size of the internal MIDI event queue. + * It holds the incoming MIDI events before the rendering engine actually processes them. + * The main goal is to fairly emulate the real hardware behaviour which obviously + * uses an internal MIDI event queue to gather incoming data as well as the delays + * introduced by transferring data via the MIDI interface. + * This also facilitates building of an external rendering loop + * as the queue stores timestamped MIDI events. + */ +#define MT32EMU_DEFAULT_MIDI_EVENT_QUEUE_SIZE 1024 + +/* Maximum allowed size of MIDI parser input stream buffer. + * Should suffice for any reasonable bulk dump SysEx, as the h/w units have only 32K of RAM onboard. + */ +#define MT32EMU_MAX_STREAM_BUFFER_SIZE 32768 + +/* This should correspond to the MIDI buffer size used in real h/w devices. + * CM-32L control ROM seems using 1000 bytes, old MT-32 isn't confirmed by now. + */ +#define MT32EMU_SYSEX_BUFFER_SIZE 1000 + +#if defined(__cplusplus) && MT32EMU_API_TYPE != 1 + +namespace MT32Emu +{ +const unsigned int SAMPLE_RATE = MT32EMU_SAMPLE_RATE; +#undef MT32EMU_SAMPLE_RATE + +const unsigned int DEFAULT_MAX_PARTIALS = MT32EMU_DEFAULT_MAX_PARTIALS; +#undef MT32EMU_DEFAULT_MAX_PARTIALS + +const unsigned int MAX_SAMPLES_PER_RUN = MT32EMU_MAX_SAMPLES_PER_RUN; +#undef MT32EMU_MAX_SAMPLES_PER_RUN + +const unsigned int DEFAULT_MIDI_EVENT_QUEUE_SIZE = MT32EMU_DEFAULT_MIDI_EVENT_QUEUE_SIZE; +#undef MT32EMU_DEFAULT_MIDI_EVENT_QUEUE_SIZE + +const unsigned int MAX_STREAM_BUFFER_SIZE = MT32EMU_MAX_STREAM_BUFFER_SIZE; +#undef MT32EMU_MAX_STREAM_BUFFER_SIZE + +const unsigned int SYSEX_BUFFER_SIZE = MT32EMU_SYSEX_BUFFER_SIZE; +#undef MT32EMU_SYSEX_BUFFER_SIZE +} + +#endif /* #if defined(__cplusplus) && MT32EMU_API_TYPE != 1 */ + +#endif /* #ifndef MT32EMU_GLOBALS_H */ diff --git a/audio/softsynth/mt32/internals.h b/audio/softsynth/mt32/internals.h index ef56819a42d..c64ba392128 100644 --- a/audio/softsynth/mt32/internals.h +++ b/audio/softsynth/mt32/internals.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -18,66 +18,111 @@ #ifndef MT32EMU_INTERNALS_H #define MT32EMU_INTERNALS_H +#include "Types.h" + // Debugging // 0: Standard debug output is not stamped with the rendered sample count // 1: Standard debug output is stamped with the rendered sample count // NOTE: The "samplestamp" corresponds to the end of the last completed rendering run. // This is important to bear in mind for debug output that occurs during a run. +#ifndef MT32EMU_DEBUG_SAMPLESTAMPS #define MT32EMU_DEBUG_SAMPLESTAMPS 0 +#endif // 0: No debug output for initialisation progress // 1: Debug output for initialisation progress +#ifndef MT32EMU_MONITOR_INIT #define MT32EMU_MONITOR_INIT 0 +#endif // 0: No debug output for MIDI events // 1: Debug output for weird MIDI events +#ifndef MT32EMU_MONITOR_MIDI #define MT32EMU_MONITOR_MIDI 0 +#endif // 0: No debug output for note on/off // 1: Basic debug output for note on/off // 2: Comprehensive debug output for note on/off +#ifndef MT32EMU_MONITOR_INSTRUMENTS #define MT32EMU_MONITOR_INSTRUMENTS 0 +#endif // 0: No debug output for partial allocations // 1: Show partial stats when an allocation fails // 2: Show partial stats with every new poly // 3: Show individual partial allocations/deactivations +#ifndef MT32EMU_MONITOR_PARTIALS #define MT32EMU_MONITOR_PARTIALS 0 +#endif // 0: No debug output for sysex // 1: Basic debug output for sysex +#ifndef MT32EMU_MONITOR_SYSEX #define MT32EMU_MONITOR_SYSEX 0 +#endif // 0: No debug output for sysex writes to the timbre areas // 1: Debug output with the name and location of newly-written timbres // 2: Complete dump of timbre parameters for newly-written timbres +#ifndef MT32EMU_MONITOR_TIMBRES #define MT32EMU_MONITOR_TIMBRES 0 +#endif // 0: No TVA/TVF-related debug output. // 1: Shows changes to TVA/TVF target, increment and phase. +#ifndef MT32EMU_MONITOR_TVA #define MT32EMU_MONITOR_TVA 0 +#endif +#ifndef MT32EMU_MONITOR_TVF #define MT32EMU_MONITOR_TVF 0 +#endif // Configuration +// 0: Use 16-bit signed samples and refined wave generator based on logarithmic fixed-point computations and LUTs. Maximum emulation accuracy and speed. +// 1: Use float samples in the wave generator and renderer. Maximum output quality and minimum noise. +#ifndef MT32EMU_USE_FLOAT_SAMPLES +#define MT32EMU_USE_FLOAT_SAMPLES 0 +#endif + // If non-zero, deletes reverb buffers that are not in use to save memory. // If zero, keeps reverb buffers for all modes around all the time to avoid allocating/freeing in the critical path. +#ifndef MT32EMU_REDUCE_REVERB_MEMORY #define MT32EMU_REDUCE_REVERB_MEMORY 1 +#endif // 0: Maximum speed at the cost of a bit lower emulation accuracy. // 1: Maximum achievable emulation accuracy. +#ifndef MT32EMU_BOSS_REVERB_PRECISE_MODE #define MT32EMU_BOSS_REVERB_PRECISE_MODE 0 - -#include "Structures.h" -#include "Tables.h" -#include "Poly.h" -#include "LA32Ramp.h" -#include "LA32WaveGenerator.h" -#include "TVA.h" -#include "TVP.h" -#include "TVF.h" -#include "Partial.h" -#include "Part.h" - #endif + +namespace MT32Emu { + +enum PolyState { + POLY_Playing, + POLY_Held, // This marks keys that have been released on the keyboard, but are being held by the pedal + POLY_Releasing, + POLY_Inactive +}; + +enum ReverbMode { + REVERB_MODE_ROOM, + REVERB_MODE_HALL, + REVERB_MODE_PLATE, + REVERB_MODE_TAP_DELAY +}; + +#if MT32EMU_USE_FLOAT_SAMPLES +typedef float Sample; +typedef float SampleEx; +#else +typedef Bit16s Sample; +typedef Bit32s SampleEx; +#endif + +} + +#endif // #ifndef MT32EMU_INTERNALS_H diff --git a/audio/softsynth/mt32/mmath.h b/audio/softsynth/mt32/mmath.h index 602242e74f8..f233bedcbb0 100644 --- a/audio/softsynth/mt32/mmath.h +++ b/audio/softsynth/mt32/mmath.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -18,12 +18,7 @@ #ifndef MT32EMU_MMATH_H #define MT32EMU_MMATH_H -#define FIXEDPOINT_UDIV(x, y, point) (((x) << (point)) / ((y))) -#define FIXEDPOINT_SDIV(x, y, point) (((x) * (1 << point)) / ((y))) -#define FIXEDPOINT_UMULT(x, y, point) (((x) * (y)) >> point) -#define FIXEDPOINT_SMULT(x, y, point) (((x) * (y)) / (1 << point)) - -#define FIXEDPOINT_MAKE(x, point) ((Bit32u)((1 << point) * x)) +#include namespace MT32Emu { @@ -46,7 +41,7 @@ static inline float EXPF(float x) { static inline float EXP2F(float x) { #ifdef __APPLE__ // on OSX exp2f() is 1.59 times faster than "exp() and the multiplication with FLOAT_LN_2" - return exp2(x); + return exp2f(x); #else return exp(FLOAT_LN_2 * x); #endif @@ -68,6 +63,6 @@ static inline float LOG10F(float x) { return log10(x); } -} +} // namespace MT32Emu -#endif +#endif // #ifndef MT32EMU_MMATH_H diff --git a/audio/softsynth/mt32/module.mk b/audio/softsynth/mt32/module.mk index f966da8d086..7657f5b55f6 100644 --- a/audio/softsynth/mt32/module.mk +++ b/audio/softsynth/mt32/module.mk @@ -3,8 +3,11 @@ MODULE := audio/softsynth/mt32 MODULE_OBJS := \ Analog.o \ BReverbModel.o \ + File.o \ + FileStream.o \ LA32Ramp.o \ LA32WaveGenerator.o \ + MidiStreamParser.o \ Part.o \ Partial.o \ PartialManager.o \ @@ -14,7 +17,24 @@ MODULE_OBJS := \ Tables.o \ TVA.o \ TVF.o \ - TVP.o + TVP.o \ + sha1/sha1.o \ + c_interface/c_interface.o + +# SampleRateConverter.o \ +# srchelper/InternalResampler.o \ +# srchelper/SamplerateAdapter.o \ +# srchelper/SoxrAdapter.o \ +# srchelper/srctools/src/FIRResampler.o \ +# srchelper/srctools/src/IIR2xResampler.o \ +# srchelper/srctools/src/LinearResampler.o \ +# srchelper/srctools/src/ResamplerModel.o \ +# srchelper/srctools/src/SincResampler.o +# TODO: The Munt SampleRateConverter requires these additional -I options. +# This is not a very nice way of doing that, though, as it adds them globally. +# INCLUDES += -I $(srcdir)/$(MODULE)/srchelper/srctools/include +# INCLUDES += -I $(srcdir)/$(MODULE)/ + # Include common rules include $(srcdir)/rules.mk diff --git a/audio/softsynth/mt32/mt32emu.h b/audio/softsynth/mt32/mt32emu.h index 1574c08f0d9..3f3b6af3449 100644 --- a/audio/softsynth/mt32/mt32emu.h +++ b/audio/softsynth/mt32/mt32emu.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011, 2012, 2013, 2014 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -18,44 +18,67 @@ #ifndef MT32EMU_MT32EMU_H #define MT32EMU_MT32EMU_H -// Configuration +#include "config.h" -// 0: Use 16-bit signed samples and refined wave generator based on logarithmic fixed-point computations and LUTs. Maximum emulation accuracy and speed. -// 1: Use float samples in the wave generator and renderer. Maximum output quality and minimum noise. -#define MT32EMU_USE_FLOAT_SAMPLES 0 +/* API Configuration */ -namespace MT32Emu -{ -// Sample rate to use in mixing. With the progress of development, we've found way too many thing dependent. -// In order to achieve further advance in emulation accuracy, sample rate made fixed throughout the emulator, -// except the emulation of analogue path. -// The output from the synth is supposed to be resampled externally in order to convert to the desired sample rate. -const unsigned int SAMPLE_RATE = 32000; +/* 0: Use full-featured C++ API. Well suitable when the library is to be linked statically. + * When the library is shared, ABI compatibility may be an issue. Therefore, it should + * only be used within a project comprising of several modules to share the library code. + * 1: Use C-compatible API. Make the library looks as a regular C library with well-defined ABI. + * This is also crucial when the library is to be linked with modules in a different + * language, either statically or dynamically. + * 2: Use plugin-like API via C-interface wrapped in a C++ class. This is mainly intended + * for a shared library being dynamically loaded in run-time. To get access to all the library + * services, a client application only needs to bind with a single factory function. + * 3: Use optimised C++ API compatible with the plugin API (type 2). The facade class also wraps + * the C functions but they are invoked directly. This enables the compiler to generate better + * code for the library when linked statically yet being consistent with the plugin-like API. + */ -// The default value for the maximum number of partials playing simultaneously. -const unsigned int DEFAULT_MAX_PARTIALS = 32; +#ifdef MT32EMU_API_TYPE +#if MT32EMU_API_TYPE == 0 && (MT32EMU_EXPORTS_TYPE == 1 || MT32EMU_EXPORTS_TYPE == 2) +#error Incompatible setting MT32EMU_API_TYPE=0 +#elif MT32EMU_API_TYPE == 1 && (MT32EMU_EXPORTS_TYPE == 0 || MT32EMU_EXPORTS_TYPE == 2) +#error Incompatible setting MT32EMU_API_TYPE=1 +#elif MT32EMU_API_TYPE == 2 && (MT32EMU_EXPORTS_TYPE == 0) +#error Incompatible setting MT32EMU_API_TYPE=2 +#elif MT32EMU_API_TYPE == 3 && (MT32EMU_EXPORTS_TYPE == 0) +#error Incompatible setting MT32EMU_API_TYPE=3 +#endif +#else /* #ifdef MT32EMU_API_TYPE */ +#if 0 < MT32EMU_EXPORTS_TYPE && MT32EMU_EXPORTS_TYPE < 3 +#define MT32EMU_API_TYPE MT32EMU_EXPORTS_TYPE +#else +#define MT32EMU_API_TYPE 0 +#endif +#endif /* #ifdef MT32EMU_API_TYPE */ -// The higher this number, the more memory will be used, but the more samples can be processed in one run - -// various parts of sample generation can be processed more efficiently in a single run. -// A run's maximum length is that given to Synth::render(), so giving a value here higher than render() is ever -// called with will give no gain (but simply waste the memory). -// Note that this value does *not* in any way impose limitations on the length given to render(), and has no effect -// on the generated audio. -// This value must be >= 1. -const unsigned int MAX_SAMPLES_PER_RUN = 4096; +/* MT32EMU_SHARED should be defined when building shared library, especially for Windows platforms. */ +/* +#define MT32EMU_SHARED +*/ -// The default size of the internal MIDI event queue. -// It holds the incoming MIDI events before the rendering engine actually processes them. -// The main goal is to fairly emulate the real hardware behaviour which obviously -// uses an internal MIDI event queue to gather incoming data as well as the delays -// introduced by transferring data via the MIDI interface. -// This also facilitates building of an external rendering loop -// as the queue stores timestamped MIDI events. -const unsigned int DEFAULT_MIDI_EVENT_QUEUE_SIZE = 1024; -} +#include "globals.h" + +#if !defined(__cplusplus) || MT32EMU_API_TYPE == 1 + +#include "c_interface/c_interface.h" + +#elif MT32EMU_API_TYPE == 2 || MT32EMU_API_TYPE == 3 + +#include "c_interface/cpp_interface.h" + +#else /* #if !defined(__cplusplus) || MT32EMU_API_TYPE == 1 */ #include "Types.h" +#include "File.h" +#include "FileStream.h" #include "ROMInfo.h" #include "Synth.h" +#include "MidiStreamParser.h" +#include "SampleRateConverter.h" -#endif +#endif /* #if !defined(__cplusplus) || MT32EMU_API_TYPE == 1 */ + +#endif /* #ifndef MT32EMU_MT32EMU_H */ diff --git a/audio/softsynth/mt32/sha1/sha1.cpp b/audio/softsynth/mt32/sha1/sha1.cpp new file mode 100755 index 00000000000..9b91cd9f298 --- /dev/null +++ b/audio/softsynth/mt32/sha1/sha1.cpp @@ -0,0 +1,185 @@ +/* + Copyright (c) 2011, Micael Hildenborg + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Micael Hildenborg nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY Micael Hildenborg ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL Micael Hildenborg BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + Contributors: + Gustav + Several members in the gamedev.se forum. + Gregory Petrosyan + */ + +#include "sha1.h" + +namespace sha1 +{ + namespace // local + { + // Rotate an integer value to left. + inline unsigned int rol(const unsigned int value, + const unsigned int steps) + { + return ((value << steps) | (value >> (32 - steps))); + } + + // Sets the first 16 integers in the buffert to zero. + // Used for clearing the W buffert. + inline void clearWBuffert(unsigned int* buffert) + { + for (int pos = 16; --pos >= 0;) + { + buffert[pos] = 0; + } + } + + void innerHash(unsigned int* result, unsigned int* w) + { + unsigned int a = result[0]; + unsigned int b = result[1]; + unsigned int c = result[2]; + unsigned int d = result[3]; + unsigned int e = result[4]; + + int round = 0; + + #define sha1macro(func,val) \ + { \ + const unsigned int t = rol(a, 5) + (func) + e + val + w[round]; \ + e = d; \ + d = c; \ + c = rol(b, 30); \ + b = a; \ + a = t; \ + } + + while (round < 16) + { + sha1macro((b & c) | (~b & d), 0x5a827999) + ++round; + } + while (round < 20) + { + w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); + sha1macro((b & c) | (~b & d), 0x5a827999) + ++round; + } + while (round < 40) + { + w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); + sha1macro(b ^ c ^ d, 0x6ed9eba1) + ++round; + } + while (round < 60) + { + w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); + sha1macro((b & c) | (b & d) | (c & d), 0x8f1bbcdc) + ++round; + } + while (round < 80) + { + w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); + sha1macro(b ^ c ^ d, 0xca62c1d6) + ++round; + } + + #undef sha1macro + + result[0] += a; + result[1] += b; + result[2] += c; + result[3] += d; + result[4] += e; + } + } // namespace + + void calc(const void* src, const int bytelength, unsigned char* hash) + { + // Init the result array. + unsigned int result[5] = { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 }; + + // Cast the void src pointer to be the byte array we can work with. + const unsigned char* sarray = static_cast(src); + + // The reusable round buffer + unsigned int w[80]; + + // Loop through all complete 64byte blocks. + const int endOfFullBlocks = bytelength - 64; + int endCurrentBlock; + int currentBlock = 0; + + while (currentBlock <= endOfFullBlocks) + { + endCurrentBlock = currentBlock + 64; + + // Init the round buffer with the 64 byte block data. + for (int roundPos = 0; currentBlock < endCurrentBlock; currentBlock += 4) + { + // This line will swap endian on big endian and keep endian on little endian. + w[roundPos++] = static_cast(sarray[currentBlock + 3]) + | (static_cast(sarray[currentBlock + 2]) << 8) + | (static_cast(sarray[currentBlock + 1]) << 16) + | (static_cast(sarray[currentBlock]) << 24); + } + innerHash(result, w); + } + + // Handle the last and not full 64 byte block if existing. + endCurrentBlock = bytelength - currentBlock; + clearWBuffert(w); + int lastBlockBytes = 0; + for (;lastBlockBytes < endCurrentBlock; ++lastBlockBytes) + { + w[lastBlockBytes >> 2] |= static_cast(sarray[lastBlockBytes + currentBlock]) << ((3 - (lastBlockBytes & 3)) << 3); + } + w[lastBlockBytes >> 2] |= 0x80 << ((3 - (lastBlockBytes & 3)) << 3); + if (endCurrentBlock >= 56) + { + innerHash(result, w); + clearWBuffert(w); + } + w[15] = bytelength << 3; + innerHash(result, w); + + // Store hash in result pointer, and make sure we get in in the correct order on both endian models. + for (int hashByte = 20; --hashByte >= 0;) + { + hash[hashByte] = (result[hashByte >> 2] >> (((3 - hashByte) & 0x3) << 3)) & 0xff; + } + } + + void toHexString(const unsigned char* hash, char* hexstring) + { + const char hexDigits[] = { "0123456789abcdef" }; + + for (int hashByte = 20; --hashByte >= 0;) + { + hexstring[hashByte << 1] = hexDigits[(hash[hashByte] >> 4) & 0xf]; + hexstring[(hashByte << 1) + 1] = hexDigits[hash[hashByte] & 0xf]; + } + hexstring[40] = 0; + } +} // namespace sha1 diff --git a/audio/softsynth/mt32/sha1/sha1.h b/audio/softsynth/mt32/sha1/sha1.h new file mode 100755 index 00000000000..96d8ce4da6e --- /dev/null +++ b/audio/softsynth/mt32/sha1/sha1.h @@ -0,0 +1,49 @@ +/* + Copyright (c) 2011, Micael Hildenborg + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Micael Hildenborg nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY Micael Hildenborg ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL Micael Hildenborg BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SHA1_DEFINED +#define SHA1_DEFINED + +namespace sha1 +{ + + /** + @param src points to any kind of data to be hashed. + @param bytelength the number of bytes to hash from the src pointer. + @param hash should point to a buffer of at least 20 bytes of size for storing the sha1 result in. + */ + void calc(const void* src, const int bytelength, unsigned char* hash); + + /** + @param hash is 20 bytes of sha1 hash. This is the same data that is the result from the calc function. + @param hexstring should point to a buffer of at least 41 bytes of size for storing the hexadecimal representation of the hash. A zero will be written at position 40, so the buffer will be a valid zero ended string. + */ + void toHexString(const unsigned char* hash, char* hexstring); + +} // namespace sha1 + +#endif // SHA1_DEFINED diff --git a/audio/softsynth/opl/dbopl.cpp b/audio/softsynth/opl/dbopl.cpp index 43eb40e7ba3..19be94cf698 100644 --- a/audio/softsynth/opl/dbopl.cpp +++ b/audio/softsynth/opl/dbopl.cpp @@ -413,6 +413,7 @@ Bits Operator::TemplateVolume( ) { return vol; } //In sustain phase, but not sustaining, do regular release + //fall through case RELEASE: vol += RateForward( releaseAdd ); if ( GCC_UNLIKELY(vol >= ENV_MAX) ) { diff --git a/backends/cloud/box/boxstorage.cpp b/backends/cloud/box/boxstorage.cpp index 598ca55c15c..fb01521016d 100644 --- a/backends/cloud/box/boxstorage.cpp +++ b/backends/cloud/box/boxstorage.cpp @@ -67,8 +67,8 @@ void BoxStorage::loadKeyAndSecret() { #endif } -BoxStorage::BoxStorage(Common::String accessToken, Common::String refreshToken): - _token(accessToken), _refreshToken(refreshToken) {} +BoxStorage::BoxStorage(Common::String token, Common::String refreshToken): + _token(token), _refreshToken(refreshToken) {} BoxStorage::BoxStorage(Common::String code) { getAccessToken( @@ -191,36 +191,36 @@ void BoxStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networking return; } - Common::JSONObject info = json->asObject(); + Common::JSONObject jsonInfo = json->asObject(); - Common::String uid, name, email; + Common::String uid, displayName, email; uint64 quotaUsed = 0, quotaAllocated = 0; // can check that "type": "user" // there is also "max_upload_size", "phone" and "avatar_url" - if (Networking::CurlJsonRequest::jsonContainsString(info, "id", "BoxStorage::infoInnerCallback")) - uid = info.getVal("id")->asString(); + if (Networking::CurlJsonRequest::jsonContainsString(jsonInfo, "id", "BoxStorage::infoInnerCallback")) + uid = jsonInfo.getVal("id")->asString(); - if (Networking::CurlJsonRequest::jsonContainsString(info, "name", "BoxStorage::infoInnerCallback")) - name = info.getVal("name")->asString(); + if (Networking::CurlJsonRequest::jsonContainsString(jsonInfo, "name", "BoxStorage::infoInnerCallback")) + displayName = jsonInfo.getVal("name")->asString(); - if (Networking::CurlJsonRequest::jsonContainsString(info, "login", "BoxStorage::infoInnerCallback")) - email = info.getVal("login")->asString(); + if (Networking::CurlJsonRequest::jsonContainsString(jsonInfo, "login", "BoxStorage::infoInnerCallback")) + email = jsonInfo.getVal("login")->asString(); - if (Networking::CurlJsonRequest::jsonContainsIntegerNumber(info, "space_amount", "BoxStorage::infoInnerCallback")) - quotaAllocated = info.getVal("space_amount")->asIntegerNumber(); + if (Networking::CurlJsonRequest::jsonContainsIntegerNumber(jsonInfo, "space_amount", "BoxStorage::infoInnerCallback")) + quotaAllocated = jsonInfo.getVal("space_amount")->asIntegerNumber(); - if (Networking::CurlJsonRequest::jsonContainsIntegerNumber(info, "space_used", "BoxStorage::infoInnerCallback")) - quotaUsed = info.getVal("space_used")->asIntegerNumber(); + if (Networking::CurlJsonRequest::jsonContainsIntegerNumber(jsonInfo, "space_used", "BoxStorage::infoInnerCallback")) + quotaUsed = jsonInfo.getVal("space_used")->asIntegerNumber(); Common::String username = email; - if (username == "") username = name; + if (username == "") username = displayName; if (username == "") username = uid; CloudMan.setStorageUsername(kStorageBoxId, username); if (outerCallback) { - (*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, name, email, quotaUsed, quotaAllocated))); + (*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, displayName, email, quotaUsed, quotaAllocated))); delete outerCallback; } @@ -245,8 +245,8 @@ void BoxStorage::createDirectoryInnerCallback(BoolCallback outerCallback, Networ if (outerCallback) { if (Networking::CurlJsonRequest::jsonIsObject(json, "BoxStorage::createDirectoryInnerCallback")) { - Common::JSONObject info = json->asObject(); - (*outerCallback)(BoolResponse(nullptr, info.contains("id"))); + Common::JSONObject jsonInfo = json->asObject(); + (*outerCallback)(BoolResponse(nullptr, jsonInfo.contains("id"))); } else { (*outerCallback)(BoolResponse(nullptr, false)); } @@ -256,7 +256,7 @@ void BoxStorage::createDirectoryInnerCallback(BoolCallback outerCallback, Networ delete json; } -Networking::Request *BoxStorage::createDirectoryWithParentId(Common::String parentId, Common::String name, BoolCallback callback, Networking::ErrorCallback errorCallback) { +Networking::Request *BoxStorage::createDirectoryWithParentId(Common::String parentId, Common::String directoryName, BoolCallback callback, Networking::ErrorCallback errorCallback) { if (!errorCallback) errorCallback = getErrorPrintingCallback(); @@ -270,7 +270,7 @@ Networking::Request *BoxStorage::createDirectoryWithParentId(Common::String pare parentObject.setVal("id", new Common::JSONValue(parentId)); Common::JSONObject jsonRequestParameters; - jsonRequestParameters.setVal("name", new Common::JSONValue(name)); + jsonRequestParameters.setVal("name", new Common::JSONValue(directoryName)); jsonRequestParameters.setVal("parent", new Common::JSONValue(parentObject)); Common::JSONValue value(jsonRequestParameters); diff --git a/backends/cloud/box/boxstorage.h b/backends/cloud/box/boxstorage.h index 27bf60e39b4..a641669b2a6 100644 --- a/backends/cloud/box/boxstorage.h +++ b/backends/cloud/box/boxstorage.h @@ -74,7 +74,7 @@ public: /** Public Cloud API comes down there. */ virtual Networking::Request *listDirectoryById(Common::String id, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback); - virtual Networking::Request *createDirectoryWithParentId(Common::String parentId, Common::String name, BoolCallback callback, Networking::ErrorCallback errorCallback); + virtual Networking::Request *createDirectoryWithParentId(Common::String parentId, Common::String directoryName, BoolCallback callback, Networking::ErrorCallback errorCallback); /** Returns UploadStatus struct with info about uploaded file. */ virtual Networking::Request *upload(Common::String remotePath, Common::String localPath, UploadCallback callback, Networking::ErrorCallback errorCallback); diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp index 5f7d6942cad..5519e4baab3 100644 --- a/backends/cloud/cloudmanager.cpp +++ b/backends/cloud/cloudmanager.cpp @@ -253,19 +253,18 @@ void CloudManager::setStorageLastSync(uint32 index, Common::String date) { void CloudManager::connectStorage(uint32 index, Common::String code) { freeStorages(); - Storage *storage = nullptr; switch (index) { case kStorageDropboxId: - storage = new Dropbox::DropboxStorage(code); + new Dropbox::DropboxStorage(code); break; case kStorageOneDriveId: - storage = new OneDrive::OneDriveStorage(code); + new OneDrive::OneDriveStorage(code); break; case kStorageGoogleDriveId: - storage = new GoogleDrive::GoogleDriveStorage(code); + new GoogleDrive::GoogleDriveStorage(code); break; case kStorageBoxId: - storage = new Box::BoxStorage(code); + new Box::BoxStorage(code); break; } // in these constructors Storages request token using the passed code diff --git a/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp b/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp index 411e367a9dd..d6937d9cb2a 100644 --- a/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp +++ b/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp @@ -52,8 +52,8 @@ void DropboxCreateDirectoryRequest::start() { _ignoreCallback = false; Networking::JsonCallback innerCallback = new Common::Callback(this, &DropboxCreateDirectoryRequest::responseCallback); - Networking::ErrorCallback errorCallback = new Common::Callback(this, &DropboxCreateDirectoryRequest::errorCallback); - Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, DROPBOX_API_CREATE_FOLDER); + Networking::ErrorCallback errorResponseCallback = new Common::Callback(this, &DropboxCreateDirectoryRequest::errorCallback); + Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorResponseCallback, DROPBOX_API_CREATE_FOLDER); request->addHeader("Authorization: Bearer " + _token); request->addHeader("Content-Type: application/json"); diff --git a/backends/cloud/dropbox/dropboxinforequest.cpp b/backends/cloud/dropbox/dropboxinforequest.cpp index 3d82c8d34be..ebf2d12170e 100644 --- a/backends/cloud/dropbox/dropboxinforequest.cpp +++ b/backends/cloud/dropbox/dropboxinforequest.cpp @@ -54,8 +54,8 @@ void DropboxInfoRequest::start() { _ignoreCallback = false; Networking::JsonCallback innerCallback = new Common::Callback(this, &DropboxInfoRequest::userResponseCallback); - Networking::ErrorCallback errorCallback = new Common::Callback(this, &DropboxInfoRequest::errorCallback); - Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, DROPBOX_API_GET_CURRENT_ACCOUNT); + Networking::ErrorCallback errorResponseCallback = new Common::Callback(this, &DropboxInfoRequest::errorCallback); + Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorResponseCallback, DROPBOX_API_GET_CURRENT_ACCOUNT); request->addHeader("Authorization: Bearer " + _token); request->addHeader("Content-Type: application/json"); request->addPostField("null"); //use POST @@ -108,8 +108,8 @@ void DropboxInfoRequest::userResponseCallback(Networking::JsonResponse response) delete json; Networking::JsonCallback innerCallback = new Common::Callback(this, &DropboxInfoRequest::quotaResponseCallback); - Networking::ErrorCallback errorCallback = new Common::Callback(this, &DropboxInfoRequest::errorCallback); - Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, DROPBOX_API_GET_SPACE_USAGE); + Networking::ErrorCallback errorResponseCallback = new Common::Callback(this, &DropboxInfoRequest::errorCallback); + Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorResponseCallback, DROPBOX_API_GET_SPACE_USAGE); request->addHeader("Authorization: Bearer " + _token); request->addHeader("Content-Type: application/json"); request->addPostField("null"); //use POST diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp index ec4c84400ca..c00d9549669 100644 --- a/backends/cloud/googledrive/googledrivestorage.cpp +++ b/backends/cloud/googledrive/googledrivestorage.cpp @@ -68,8 +68,8 @@ void GoogleDriveStorage::loadKeyAndSecret() { #endif } -GoogleDriveStorage::GoogleDriveStorage(Common::String accessToken, Common::String refreshToken): - _token(accessToken), _refreshToken(refreshToken) {} +GoogleDriveStorage::GoogleDriveStorage(Common::String token, Common::String refreshToken): + _token(token), _refreshToken(refreshToken) {} GoogleDriveStorage::GoogleDriveStorage(Common::String code) { getAccessToken( @@ -192,28 +192,28 @@ void GoogleDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Ne return; } - Common::JSONObject info = json->asObject(); + Common::JSONObject jsonInfo = json->asObject(); - Common::String uid, name, email; + Common::String uid, displayName, email; uint64 quotaUsed = 0, quotaAllocated = 0; - if (Networking::CurlJsonRequest::jsonContainsAttribute(info, "user", "GoogleDriveStorage::infoInnerCallback") && - Networking::CurlJsonRequest::jsonIsObject(info.getVal("user"), "GoogleDriveStorage::infoInnerCallback")) { + if (Networking::CurlJsonRequest::jsonContainsAttribute(jsonInfo, "user", "GoogleDriveStorage::infoInnerCallback") && + Networking::CurlJsonRequest::jsonIsObject(jsonInfo.getVal("user"), "GoogleDriveStorage::infoInnerCallback")) { //"me":true, "kind":"drive#user","photoLink": "", //"displayName":"Alexander Tkachev","emailAddress":"alexander@tkachov.ru","permissionId":"" - Common::JSONObject user = info.getVal("user")->asObject(); + Common::JSONObject user = jsonInfo.getVal("user")->asObject(); if (Networking::CurlJsonRequest::jsonContainsString(user, "permissionId", "GoogleDriveStorage::infoInnerCallback")) uid = user.getVal("permissionId")->asString(); //not sure it's user's id, but who cares anyway? if (Networking::CurlJsonRequest::jsonContainsString(user, "displayName", "GoogleDriveStorage::infoInnerCallback")) - name = user.getVal("displayName")->asString(); + displayName = user.getVal("displayName")->asString(); if (Networking::CurlJsonRequest::jsonContainsString(user, "emailAddress", "GoogleDriveStorage::infoInnerCallback")) email = user.getVal("emailAddress")->asString(); } - if (Networking::CurlJsonRequest::jsonContainsAttribute(info, "storageQuota", "GoogleDriveStorage::infoInnerCallback") && - Networking::CurlJsonRequest::jsonIsObject(info.getVal("storageQuota"), "GoogleDriveStorage::infoInnerCallback")) { + if (Networking::CurlJsonRequest::jsonContainsAttribute(jsonInfo, "storageQuota", "GoogleDriveStorage::infoInnerCallback") && + Networking::CurlJsonRequest::jsonIsObject(jsonInfo.getVal("storageQuota"), "GoogleDriveStorage::infoInnerCallback")) { //"usageInDrive":"6332462","limit":"18253611008","usage":"6332462","usageInDriveTrash":"0" - Common::JSONObject storageQuota = info.getVal("storageQuota")->asObject(); + Common::JSONObject storageQuota = jsonInfo.getVal("storageQuota")->asObject(); if (Networking::CurlJsonRequest::jsonContainsString(storageQuota, "usage", "GoogleDriveStorage::infoInnerCallback")) { Common::String usage = storageQuota.getVal("usage")->asString(); @@ -229,7 +229,7 @@ void GoogleDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Ne CloudMan.setStorageUsername(kStorageGoogleDriveId, email); if (outerCallback) { - (*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, name, email, quotaUsed, quotaAllocated))); + (*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, displayName, email, quotaUsed, quotaAllocated))); delete outerCallback; } @@ -246,8 +246,8 @@ void GoogleDriveStorage::createDirectoryInnerCallback(BoolCallback outerCallback if (outerCallback) { if (Networking::CurlJsonRequest::jsonIsObject(json, "GoogleDriveStorage::createDirectoryInnerCallback")) { - Common::JSONObject info = json->asObject(); - (*outerCallback)(BoolResponse(nullptr, info.contains("id"))); + Common::JSONObject jsonInfo = json->asObject(); + (*outerCallback)(BoolResponse(nullptr, jsonInfo.contains("id"))); } else { (*outerCallback)(BoolResponse(nullptr, false)); } @@ -289,7 +289,7 @@ void GoogleDriveStorage::printInfo(StorageInfoResponse response) { debug(9, "\tdisk usage: %lu/%lu", response.value.used(), response.value.available()); } -Networking::Request *GoogleDriveStorage::createDirectoryWithParentId(Common::String parentId, Common::String name, BoolCallback callback, Networking::ErrorCallback errorCallback) { +Networking::Request *GoogleDriveStorage::createDirectoryWithParentId(Common::String parentId, Common::String directoryName, BoolCallback callback, Networking::ErrorCallback errorCallback) { if (!errorCallback) errorCallback = getErrorPrintingCallback(); @@ -304,7 +304,7 @@ Networking::Request *GoogleDriveStorage::createDirectoryWithParentId(Common::Str Common::JSONObject jsonRequestParameters; jsonRequestParameters.setVal("mimeType", new Common::JSONValue("application/vnd.google-apps.folder")); - jsonRequestParameters.setVal("name", new Common::JSONValue(name)); + jsonRequestParameters.setVal("name", new Common::JSONValue(directoryName)); jsonRequestParameters.setVal("parents", new Common::JSONValue(parentsArray)); Common::JSONValue value(jsonRequestParameters); diff --git a/backends/cloud/googledrive/googledrivestorage.h b/backends/cloud/googledrive/googledrivestorage.h index 457369dc903..d0585bc403b 100644 --- a/backends/cloud/googledrive/googledrivestorage.h +++ b/backends/cloud/googledrive/googledrivestorage.h @@ -86,7 +86,7 @@ public: virtual Networking::Request *streamFileById(Common::String id, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback); /** Calls the callback when finished. */ - virtual Networking::Request *createDirectoryWithParentId(Common::String parentId, Common::String name, BoolCallback callback, Networking::ErrorCallback errorCallback); + virtual Networking::Request *createDirectoryWithParentId(Common::String parentId, Common::String directoryName, BoolCallback callback, Networking::ErrorCallback errorCallback); /** Returns the StorageInfo struct. */ virtual Networking::Request *info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback); diff --git a/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp index da67266d12a..74cf3208e38 100644 --- a/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp +++ b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp @@ -72,8 +72,8 @@ void OneDriveCreateDirectoryRequest::start() { url += ":/" + ConnMan.urlEncode(parent) + ":"; url += "/children"; Networking::JsonCallback innerCallback = new Common::Callback(this, &OneDriveCreateDirectoryRequest::responseCallback); - Networking::ErrorCallback errorCallback = new Common::Callback(this, &OneDriveCreateDirectoryRequest::errorCallback); - Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(_storage, innerCallback, errorCallback, url.c_str()); + Networking::ErrorCallback errorResponseCallback = new Common::Callback(this, &OneDriveCreateDirectoryRequest::errorCallback); + Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(_storage, innerCallback, errorResponseCallback, url.c_str()); request->addHeader("Authorization: Bearer " + _storage->accessToken()); request->addHeader("Content-Type: application/json"); diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp index 8799f3d69f6..14e803d980e 100644 --- a/backends/cloud/onedrive/onedrivestorage.cpp +++ b/backends/cloud/onedrive/onedrivestorage.cpp @@ -67,8 +67,8 @@ void OneDriveStorage::loadKeyAndSecret() { #endif } -OneDriveStorage::OneDriveStorage(Common::String accessToken, Common::String userId, Common::String refreshToken): - _token(accessToken), _uid(userId), _refreshToken(refreshToken) {} +OneDriveStorage::OneDriveStorage(Common::String token, Common::String uid, Common::String refreshToken): + _token(token), _uid(uid), _refreshToken(refreshToken) {} OneDriveStorage::OneDriveStorage(Common::String code) { getAccessToken( @@ -193,35 +193,35 @@ void OneDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Netwo return; } - Common::JSONObject info = json->asObject(); + Common::JSONObject jsonInfo = json->asObject(); - Common::String uid, name, email; + Common::String uid, displayName, email; uint64 quotaUsed = 0, quotaAllocated = 26843545600LL; // 25 GB, because I actually don't know any way to find out the real one - if (Networking::CurlJsonRequest::jsonContainsObject(info, "createdBy", "OneDriveStorage::infoInnerCallback")) { - Common::JSONObject createdBy = info.getVal("createdBy")->asObject(); + if (Networking::CurlJsonRequest::jsonContainsObject(jsonInfo, "createdBy", "OneDriveStorage::infoInnerCallback")) { + Common::JSONObject createdBy = jsonInfo.getVal("createdBy")->asObject(); if (Networking::CurlJsonRequest::jsonContainsObject(createdBy, "user", "OneDriveStorage::infoInnerCallback")) { Common::JSONObject user = createdBy.getVal("user")->asObject(); if (Networking::CurlJsonRequest::jsonContainsString(user, "id", "OneDriveStorage::infoInnerCallback")) uid = user.getVal("id")->asString(); if (Networking::CurlJsonRequest::jsonContainsString(user, "displayName", "OneDriveStorage::infoInnerCallback")) - name = user.getVal("displayName")->asString(); + displayName = user.getVal("displayName")->asString(); } } - if (Networking::CurlJsonRequest::jsonContainsIntegerNumber(info, "size", "OneDriveStorage::infoInnerCallback")) { - quotaUsed = info.getVal("size")->asIntegerNumber(); + if (Networking::CurlJsonRequest::jsonContainsIntegerNumber(jsonInfo, "size", "OneDriveStorage::infoInnerCallback")) { + quotaUsed = jsonInfo.getVal("size")->asIntegerNumber(); } Common::String username = email; if (username == "") - username = name; + username = displayName; if (username == "") username = uid; CloudMan.setStorageUsername(kStorageOneDriveId, username); if (outerCallback) { - (*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, name, email, quotaUsed, quotaAllocated))); + (*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, displayName, email, quotaUsed, quotaAllocated))); delete outerCallback; } diff --git a/backends/cloud/storagefile.cpp b/backends/cloud/storagefile.cpp index 2428d8ff802..90ec7da1205 100644 --- a/backends/cloud/storagefile.cpp +++ b/backends/cloud/storagefile.cpp @@ -56,10 +56,10 @@ StorageFile::StorageFile(Common::String pth, uint32 sz, uint32 ts, bool dir) { _isDirectory = dir; } -StorageFile::StorageFile(Common::String id, Common::String path, Common::String name, uint32 sz, uint32 ts, bool dir) { - _id = id; - _path = path; - _name = name; +StorageFile::StorageFile(Common::String fileId, Common::String filePath, Common::String fileName, uint32 sz, uint32 ts, bool dir) { + _id = fileId; + _path = filePath; + _name = fileName; _size = sz; _timestamp = ts; _isDirectory = dir; diff --git a/backends/cloud/storagefile.h b/backends/cloud/storagefile.h index 44f16c36893..c29ac77dd11 100644 --- a/backends/cloud/storagefile.h +++ b/backends/cloud/storagefile.h @@ -48,7 +48,7 @@ class StorageFile { public: StorageFile(); //invalid empty file StorageFile(Common::String pth, uint32 sz, uint32 ts, bool dir); - StorageFile(Common::String id, Common::String path, Common::String name, uint32 sz, uint32 ts, bool dir); + StorageFile(Common::String fileId, Common::String filePath, Common::String fileName, uint32 sz, uint32 ts, bool dir); Common::String id() const { return _id; } Common::String path() const { return _path; } diff --git a/backends/events/ps3sdl/ps3sdl-events.cpp b/backends/events/ps3sdl/ps3sdl-events.cpp index 1fc10559c25..01cdc2f0f63 100644 --- a/backends/events/ps3sdl/ps3sdl-events.cpp +++ b/backends/events/ps3sdl/ps3sdl-events.cpp @@ -60,11 +60,11 @@ bool PS3SdlEventSource::handleJoyButtonDown(SDL_Event &ev, Common::Event &event) switch (ev.jbutton.button) { case BTN_CROSS: // Left mouse button event.type = Common::EVENT_LBUTTONDOWN; - processMouseEvent(event, _km.x, _km.y); + processMouseEvent(event, _km.x / MULTIPLIER, _km.y / MULTIPLIER); break; case BTN_CIRCLE: // Right mouse button event.type = Common::EVENT_RBUTTONDOWN; - processMouseEvent(event, _km.x, _km.y); + processMouseEvent(event, _km.x / MULTIPLIER, _km.y / MULTIPLIER); break; case BTN_TRIANGLE: // Game menu event.type = Common::EVENT_KEYDOWN; @@ -98,11 +98,11 @@ bool PS3SdlEventSource::handleJoyButtonUp(SDL_Event &ev, Common::Event &event) { switch (ev.jbutton.button) { case BTN_CROSS: // Left mouse button event.type = Common::EVENT_LBUTTONUP; - processMouseEvent(event, _km.x, _km.y); + processMouseEvent(event, _km.x / MULTIPLIER, _km.y / MULTIPLIER); break; case BTN_CIRCLE: // Right mouse button event.type = Common::EVENT_RBUTTONUP; - processMouseEvent(event, _km.x, _km.y); + processMouseEvent(event, _km.x / MULTIPLIER, _km.y / MULTIPLIER); break; case BTN_TRIANGLE: // Game menu event.type = Common::EVENT_KEYUP; diff --git a/backends/events/sdl/sdl-events.cpp b/backends/events/sdl/sdl-events.cpp index 439689b8ccb..e710fdf9e13 100644 --- a/backends/events/sdl/sdl-events.cpp +++ b/backends/events/sdl/sdl-events.cpp @@ -40,6 +40,10 @@ #endif #endif +#ifdef JOY_ANALOG +#include "math.h" +#endif + // FIXME move joystick defines out and replace with confile file options // we should really allow users to map any key to a joystick button #define JOY_DEADZONE 3200 @@ -186,17 +190,18 @@ void SdlEventSource::processMouseEvent(Common::Event &event, int x, int y, int r _graphicsManager->notifyMousePos(Common::Point(x, y)); _graphicsManager->transformMouseCoordinates(event.mouse); } - - // Update the "keyboard mouse" coords - _km.x = x; - _km.y = y; } -void SdlEventSource::handleKbdMouse() { +bool SdlEventSource::handleKbdMouse(Common::Event &event) { + // returns true if an event is generated // Skip recording of these events uint32 curTime = g_system->getMillis(true); if (curTime >= _km.last_time + _km.delay_time) { + + int16 oldKmX = _km.x; + int16 oldKmY = _km.y; + _km.last_time = curTime; if (_km.x_down_count == 1) { _km.x_down_time = curTime; @@ -209,61 +214,119 @@ void SdlEventSource::handleKbdMouse() { if (_km.x_vel || _km.y_vel) { if (_km.x_down_count) { - if (curTime > _km.x_down_time + _km.delay_time * 12) { + if (curTime > _km.x_down_time + 300) { if (_km.x_vel > 0) - _km.x_vel++; + _km.x_vel += MULTIPLIER; else - _km.x_vel--; - } else if (curTime > _km.x_down_time + _km.delay_time * 8) { + _km.x_vel -= MULTIPLIER; + } else if (curTime > _km.x_down_time + 200) { if (_km.x_vel > 0) - _km.x_vel = 5; + _km.x_vel = 5 * MULTIPLIER; else - _km.x_vel = -5; + _km.x_vel = -5 * MULTIPLIER; } } if (_km.y_down_count) { - if (curTime > _km.y_down_time + _km.delay_time * 12) { + if (curTime > _km.y_down_time + 300) { if (_km.y_vel > 0) - _km.y_vel++; + _km.y_vel += MULTIPLIER; else - _km.y_vel--; - } else if (curTime > _km.y_down_time + _km.delay_time * 8) { + _km.y_vel -= MULTIPLIER; + } else if (curTime > _km.y_down_time + 200) { if (_km.y_vel > 0) - _km.y_vel = 5; + _km.y_vel = 5 * MULTIPLIER; else - _km.y_vel = -5; + _km.y_vel = -5 * MULTIPLIER; } } - _km.x += _km.x_vel; - _km.y += _km.y_vel; + int16 speedFactor = 25; + + if (g_system->hasFeature(OSystem::kFeatureKbdMouseSpeed)) { + switch (ConfMan.getInt("kbdmouse_speed")) { + // 0.25 keyboard pointer speed + case 0: + speedFactor = 100; + break; + // 0.5 speed + case 1: + speedFactor = 50; + break; + // 0.75 speed + case 2: + speedFactor = 33; + break; + // 1.0 speed + case 3: + speedFactor = 25; + break; + // 1.25 speed + case 4: + speedFactor = 20; + break; + // 1.5 speed + case 5: + speedFactor = 17; + break; + // 1.75 speed + case 6: + speedFactor = 14; + break; + // 2.0 speed + case 7: + speedFactor = 12; + break; + default: + speedFactor = 25; + } + } + + // - The modifier key makes the mouse movement slower + // - The extra factor "delay/speedFactor" ensures velocities + // are independent of the kbdMouse update rate + // - all velocities were originally chosen + // at a delay of 25, so that is the reference used here + // - note: operator order is important to avoid overflow + if (_km.modifier) { + _km.x += ((_km.x_vel / 10) * ((int16)_km.delay_time)) / speedFactor; + _km.y += ((_km.y_vel / 10) * ((int16)_km.delay_time)) / speedFactor; + } else { + _km.x += (_km.x_vel * ((int16)_km.delay_time)) / speedFactor; + _km.y += (_km.y_vel * ((int16)_km.delay_time)) / speedFactor; + } if (_km.x < 0) { _km.x = 0; - _km.x_vel = -1; + _km.x_vel = -1 * MULTIPLIER; _km.x_down_count = 1; - } else if (_km.x > _km.x_max) { - _km.x = _km.x_max; - _km.x_vel = 1; + } else if (_km.x > _km.x_max * MULTIPLIER) { + _km.x = _km.x_max * MULTIPLIER; + _km.x_vel = 1 * MULTIPLIER; _km.x_down_count = 1; } if (_km.y < 0) { _km.y = 0; - _km.y_vel = -1; + _km.y_vel = -1 * MULTIPLIER; _km.y_down_count = 1; - } else if (_km.y > _km.y_max) { - _km.y = _km.y_max; - _km.y_vel = 1; + } else if (_km.y > _km.y_max * MULTIPLIER) { + _km.y = _km.y_max * MULTIPLIER; + _km.y_vel = 1 * MULTIPLIER; _km.y_down_count = 1; } - // ResidualVM: disable wrap mouse for now, it's really annoying if (_graphicsManager) { - //_graphicsManager->getWindow()->warpMouseInWindow((Uint16)_km.x, (Uint16)_km.y); + _graphicsManager->getWindow()->warpMouseInWindow((Uint16)(_km.x / MULTIPLIER), (Uint16)(_km.y / MULTIPLIER)); + } + + if (_km.x != oldKmX || _km.y != oldKmY) { + event.type = Common::EVENT_MOUSEMOVE; + processMouseEvent(event, _km.x / MULTIPLIER, _km.y / MULTIPLIER); + return true; } } } + return false; } void SdlEventSource::SDLModToOSystemKeyFlags(SDLMod mod, Common::Event &event) { @@ -439,7 +502,6 @@ Common::KeyCode SdlEventSource::SDLToOSystemKeycode(const SDLKey key) { } bool SdlEventSource::pollEvent(Common::Event &event) { - handleKbdMouse(); #if SDL_VERSION_ATLEAST(2, 0, 0) // In case we still need to send a key up event for a key down from a @@ -465,6 +527,12 @@ bool SdlEventSource::pollEvent(Common::Event &event) { if (dispatchSDLEvent(ev, event)) return true; } + + // Handle mouse control via analog joystick and keyboard + if (handleKbdMouse(event)) { + return true; + } + return false; } @@ -490,16 +558,11 @@ bool SdlEventSource::dispatchSDLEvent(SDL_Event &ev, Common::Event &event) { #if SDL_VERSION_ATLEAST(2, 0, 0) case SDL_MOUSEWHEEL: { Sint32 yDir = ev.wheel.y; -#if SDL_VERSION_ATLEAST(2, 0, 4) - if (ev.wheel.direction == SDL_MOUSEWHEEL_FLIPPED) { - yDir *= -1; - } -#endif // HACK: It seems we want the mouse coordinates supplied // with a mouse wheel event. However, SDL2 does not supply // these, thus we use whatever we got last time. It seems // these are always stored in _km.x, _km.y. - processMouseEvent(event, _km.x, _km.y); + processMouseEvent(event, _km.x / MULTIPLIER, _km.y / MULTIPLIER); if (yDir < 0) { event.type = Common::EVENT_WHEELDOWN; return true; @@ -539,7 +602,18 @@ bool SdlEventSource::dispatchSDLEvent(SDL_Event &ev, Common::Event &event) { _graphicsManager->notifyVideoExpose(); return false; - case SDL_WINDOWEVENT_RESIZED: + // SDL2 documentation indicate that SDL_WINDOWEVENT_SIZE_CHANGED is sent either as a result + // of the size being changed by an external event (for example the user resizing the window + // or going fullscreen) or a call to the SDL API (for example SDL_SetWindowSize). On the + // other hand SDL_WINDOWEVENT_RESIZED is only sent for resize resulting from an external event, + // and is always preceded by a SDL_WINDOWEVENT_SIZE_CHANGED event. + // We need to handle the programmatic resize as well so that the graphics manager always know + // the current size. See comments in SdlWindow::createOrUpdateWindow for details of one case + // where we need to call SDL_SetWindowSize and we need the resulting event to be processed. + // However if the documentation is correct we can ignore SDL_WINDOWEVENT_RESIZED since when we + // get one we should always get a SDL_WINDOWEVENT_SIZE_CHANGED as well. + case SDL_WINDOWEVENT_SIZE_CHANGED: + //case SDL_WINDOWEVENT_RESIZED: return handleResizeEvent(event, ev.window.data1, ev.window.data2); default: @@ -680,6 +754,9 @@ bool SdlEventSource::handleKeyUp(SDL_Event &ev, Common::Event &event) { bool SdlEventSource::handleMouseMotion(SDL_Event &ev, Common::Event &event) { event.type = Common::EVENT_MOUSEMOVE; processMouseEvent(event, ev.motion.x, ev.motion.y, ev.motion.xrel, ev.motion.yrel); // ResidualVM xrel,yrel + // update KbdMouse + _km.x = ev.motion.x * MULTIPLIER; + _km.y = ev.motion.y * MULTIPLIER; return true; } @@ -703,6 +780,9 @@ bool SdlEventSource::handleMouseButtonDown(SDL_Event &ev, Common::Event &event) return false; processMouseEvent(event, ev.button.x, ev.button.y); + // update KbdMouse + _km.x = ev.button.x * MULTIPLIER; + _km.y = ev.button.y * MULTIPLIER; return true; } @@ -719,6 +799,9 @@ bool SdlEventSource::handleMouseButtonUp(SDL_Event &ev, Common::Event &event) { else return false; processMouseEvent(event, ev.button.x, ev.button.y); + // update KbdMouse + _km.x = ev.button.x * MULTIPLIER; + _km.y = ev.button.y * MULTIPLIER; return true; } @@ -726,10 +809,10 @@ bool SdlEventSource::handleMouseButtonUp(SDL_Event &ev, Common::Event &event) { bool SdlEventSource::handleJoyButtonDown(SDL_Event &ev, Common::Event &event) { if (ev.jbutton.button == JOY_BUT_LMOUSE) { event.type = Common::EVENT_LBUTTONDOWN; - processMouseEvent(event, _km.x, _km.y); + processMouseEvent(event, _km.x / MULTIPLIER, _km.y / MULTIPLIER); } else if (ev.jbutton.button == JOY_BUT_RMOUSE) { event.type = Common::EVENT_RBUTTONDOWN; - processMouseEvent(event, _km.x, _km.y); + processMouseEvent(event, _km.x / MULTIPLIER, _km.y / MULTIPLIER); } else { event.type = Common::EVENT_KEYDOWN; switch (ev.jbutton.button) { @@ -757,10 +840,10 @@ bool SdlEventSource::handleJoyButtonDown(SDL_Event &ev, Common::Event &event) { bool SdlEventSource::handleJoyButtonUp(SDL_Event &ev, Common::Event &event) { if (ev.jbutton.button == JOY_BUT_LMOUSE) { event.type = Common::EVENT_LBUTTONUP; - processMouseEvent(event, _km.x, _km.y); + processMouseEvent(event, _km.x / MULTIPLIER, _km.y / MULTIPLIER); } else if (ev.jbutton.button == JOY_BUT_RMOUSE) { event.type = Common::EVENT_RBUTTONUP; - processMouseEvent(event, _km.x, _km.y); + processMouseEvent(event, _km.x / MULTIPLIER, _km.y / MULTIPLIER); } else { event.type = Common::EVENT_KEYUP; switch (ev.jbutton.button) { @@ -786,23 +869,26 @@ bool SdlEventSource::handleJoyButtonUp(SDL_Event &ev, Common::Event &event) { } bool SdlEventSource::handleJoyAxisMotion(SDL_Event &ev, Common::Event &event) { + int axis = ev.jaxis.value; +#ifdef JOY_ANALOG + // conversion factor between keyboard mouse and joy axis value + int vel_to_axis = (1500 / MULTIPLIER); +#else if (axis > JOY_DEADZONE) { axis -= JOY_DEADZONE; - event.type = Common::EVENT_MOUSEMOVE; } else if (axis < -JOY_DEADZONE) { axis += JOY_DEADZONE; - event.type = Common::EVENT_MOUSEMOVE; } else axis = 0; +#endif if (ev.jaxis.axis == JOY_XAXIS) { #ifdef JOY_ANALOG - _km.x_vel = axis / 2000; - _km.x_down_count = 0; + _km.joy_x = axis; #else if (axis != 0) { - _km.x_vel = (axis > 0) ? 1:-1; + _km.x_vel = (axis > 0) ? 1 * MULTIPLIER:-1 * MULTIPLIER; _km.x_down_count = 1; } else { _km.x_vel = 0; @@ -814,11 +900,10 @@ bool SdlEventSource::handleJoyAxisMotion(SDL_Event &ev, Common::Event &event) { axis = -axis; #endif #ifdef JOY_ANALOG - _km.y_vel = -axis / 2000; - _km.y_down_count = 0; + _km.joy_y = -axis; #else if (axis != 0) { - _km.y_vel = (-axis > 0) ? 1: -1; + _km.y_vel = (-axis > 0) ? 1 * MULTIPLIER: -1 * MULTIPLIER; _km.y_down_count = 1; } else { _km.y_vel = 0; @@ -826,10 +911,31 @@ bool SdlEventSource::handleJoyAxisMotion(SDL_Event &ev, Common::Event &event) { } #endif } +#ifdef JOY_ANALOG + // radial and scaled analog joystick deadzone + float analogX = (float)_km.joy_x; + float analogY = (float)_km.joy_y; + float deadZone = (float)JOY_DEADZONE; + if (g_system->hasFeature(OSystem::kFeatureJoystickDeadzone)) + deadZone = (float)ConfMan.getInt("joystick_deadzone") * 1000.0f; + float scalingFactor = 1.0f; + float magnitude = 0.0f; - processMouseEvent(event, _km.x, _km.y); + magnitude = sqrt(analogX * analogX + analogY * analogY); - return true; + if (magnitude >= deadZone) { + _km.x_down_count = 0; + _km.y_down_count = 0; + scalingFactor = 1.0f / magnitude * (magnitude - deadZone) / (32769.0f - deadZone); + _km.x_vel = (int16)(analogX * scalingFactor * 32768.0f / vel_to_axis); + _km.y_vel = (int16)(analogY * scalingFactor * 32768.0f / vel_to_axis); + } else { + _km.x_vel = 0; + _km.y_vel = 0; + } +#endif + + return false; } bool SdlEventSource::remapKey(SDL_Event &ev, Common::Event &event) { @@ -903,8 +1009,11 @@ bool SdlEventSource::remapKey(SDL_Event &ev, Common::Event &event) { void SdlEventSource::resetKeyboardEmulation(int16 x_max, int16 y_max) { _km.x_max = x_max; _km.y_max = y_max; - _km.delay_time = 25; + _km.delay_time = 12; _km.last_time = 0; + _km.modifier = false; + _km.joy_x = 0; + _km.joy_y = 0; } bool SdlEventSource::handleResizeEvent(Common::Event &event, int w, int h) { diff --git a/backends/events/sdl/sdl-events.h b/backends/events/sdl/sdl-events.h index ab5aadaf5ed..af5138bb680 100644 --- a/backends/events/sdl/sdl-events.h +++ b/backends/events/sdl/sdl-events.h @@ -28,6 +28,8 @@ #include "common/events.h" +// multiplier used to increase resolution for keyboard/joystick mouse +#define MULTIPLIER 16 /** * The SDL event source. @@ -58,8 +60,9 @@ protected: //@{ struct KbdMouse { - int16 x, y, x_vel, y_vel, x_max, y_max, x_down_count, y_down_count; + int16 x, y, x_vel, y_vel, x_max, y_max, x_down_count, y_down_count, joy_x, joy_y; uint32 last_time, delay_time, x_down_time, y_down_time; + bool modifier; }; KbdMouse _km; @@ -106,7 +109,7 @@ protected: virtual bool handleJoyButtonDown(SDL_Event &ev, Common::Event &event); virtual bool handleJoyButtonUp(SDL_Event &ev, Common::Event &event); virtual bool handleJoyAxisMotion(SDL_Event &ev, Common::Event &event); - virtual void handleKbdMouse(); + virtual bool handleKbdMouse(Common::Event &event); //@} diff --git a/backends/fs/abstract-fs.h b/backends/fs/abstract-fs.h index 24286b649db..28ea3bbecd7 100644 --- a/backends/fs/abstract-fs.h +++ b/backends/fs/abstract-fs.h @@ -195,11 +195,11 @@ public: /** * Creates a file referred by this node. * - * @param isDirectory true if created file must be a directory + * @param isDirectoryFlag true if created file must be a directory * * @return true if file is created successfully */ - virtual bool create(bool isDirectory) = 0; + virtual bool create(bool isDirectoryFlag) = 0; }; diff --git a/backends/fs/amigaos4/amigaos4-fs.cpp b/backends/fs/amigaos4/amigaos4-fs.cpp index 24a8fb98ad0..09ba3a1c836 100644 --- a/backends/fs/amigaos4/amigaos4-fs.cpp +++ b/backends/fs/amigaos4/amigaos4-fs.cpp @@ -443,7 +443,7 @@ Common::WriteStream *AmigaOSFilesystemNode::createWriteStream() { return StdioStream::makeFromPath(getPath(), true); } -bool AmigaOSFilesystemNode::create(bool isDirectory) { +bool AmigaOSFilesystemNode::create(bool isDirectoryFlag) { error("Not supported"); return false; } diff --git a/backends/fs/amigaos4/amigaos4-fs.h b/backends/fs/amigaos4/amigaos4-fs.h index 3ed45d3f77f..c86bb4c57a5 100644 --- a/backends/fs/amigaos4/amigaos4-fs.h +++ b/backends/fs/amigaos4/amigaos4-fs.h @@ -116,7 +116,7 @@ public: virtual Common::SeekableReadStream *createReadStream(); virtual Common::WriteStream *createWriteStream(); - virtual bool create(bool isDirectory); + virtual bool create(bool isDirectoryFlag); }; diff --git a/backends/fs/chroot/chroot-fs.cpp b/backends/fs/chroot/chroot-fs.cpp index be6805bbd86..ac7cde33987 100644 --- a/backends/fs/chroot/chroot-fs.cpp +++ b/backends/fs/chroot/chroot-fs.cpp @@ -108,7 +108,7 @@ Common::WriteStream *ChRootFilesystemNode::createWriteStream() { return _realNode->createWriteStream(); } -bool ChRootFilesystemNode::create(bool /*isDir*/) { +bool ChRootFilesystemNode::create(bool isDirectoryFlag) { error("Not supported"); return false; } diff --git a/backends/fs/chroot/chroot-fs.h b/backends/fs/chroot/chroot-fs.h index e0ecc1c47ee..76902bc92a8 100644 --- a/backends/fs/chroot/chroot-fs.h +++ b/backends/fs/chroot/chroot-fs.h @@ -49,7 +49,7 @@ public: virtual Common::SeekableReadStream *createReadStream(); virtual Common::WriteStream *createWriteStream(); - virtual bool create(bool isDirectory); + virtual bool create(bool isDirectoryFlag); private: static Common::String addPathComponent(const Common::String &path, const Common::String &component); diff --git a/backends/fs/posix/posix-fs.cpp b/backends/fs/posix/posix-fs.cpp index ce5715210a7..3f90fc1a191 100644 --- a/backends/fs/posix/posix-fs.cpp +++ b/backends/fs/posix/posix-fs.cpp @@ -20,7 +20,7 @@ * */ -#if defined(POSIX) || defined(PLAYSTATION3) +#if defined(POSIX) || defined(PLAYSTATION3) || defined(PSP2) // Re-enable some forbidden symbols to avoid clashes with stat.h and unistd.h. // Also with clock() in sys/time.h in some Mac OS X SDKs. @@ -36,7 +36,12 @@ #include #include +#ifdef PSP2 +#include "backends/fs/psp2/psp2-dirent.h" +#define mkdir sceIoMkdir +#else #include +#endif #include #include #include @@ -253,10 +258,10 @@ Common::WriteStream *POSIXFilesystemNode::createWriteStream() { return StdioStream::makeFromPath(getPath(), true); } -bool POSIXFilesystemNode::create(bool isDir) { +bool POSIXFilesystemNode::create(bool isDirectoryFlag) { bool success; - if (isDir) { + if (isDirectoryFlag) { success = mkdir(_path.c_str(), 0755) == 0; } else { int fd = open(_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0755); @@ -270,12 +275,12 @@ bool POSIXFilesystemNode::create(bool isDir) { if (success) { setFlags(); if (_isValid) { - if (_isDirectory != isDir) warning("failed to create %s: got %s", isDir ? "directory" : "file", _isDirectory ? "directory" : "file"); - return _isDirectory == isDir; + if (_isDirectory != isDirectoryFlag) warning("failed to create %s: got %s", isDirectoryFlag ? "directory" : "file", _isDirectory ? "directory" : "file"); + return _isDirectory == isDirectoryFlag; } warning("POSIXFilesystemNode: %s() was a success, but stat indicates there is no such %s", - isDir ? "mkdir" : "creat", isDir ? "directory" : "file"); + isDirectoryFlag ? "mkdir" : "creat", isDirectoryFlag ? "directory" : "file"); return false; } diff --git a/backends/fs/posix/posix-fs.h b/backends/fs/posix/posix-fs.h index 4ebce7e9d92..5a6b6bd2bd2 100644 --- a/backends/fs/posix/posix-fs.h +++ b/backends/fs/posix/posix-fs.h @@ -73,7 +73,7 @@ public: virtual Common::SeekableReadStream *createReadStream(); virtual Common::WriteStream *createWriteStream(); - virtual bool create(bool isDirectory); + virtual bool create(bool isDirectoryFlag); private: /** diff --git a/backends/fs/windows/windows-fs.cpp b/backends/fs/windows/windows-fs.cpp index b43686f9112..caf0e31360b 100644 --- a/backends/fs/windows/windows-fs.cpp +++ b/backends/fs/windows/windows-fs.cpp @@ -244,10 +244,10 @@ Common::WriteStream *WindowsFilesystemNode::createWriteStream() { return StdioStream::makeFromPath(getPath(), true); } -bool WindowsFilesystemNode::create(bool isDirectory) { +bool WindowsFilesystemNode::create(bool isDirectoryFlag) { bool success; - if (isDirectory) { + if (isDirectoryFlag) { success = CreateDirectory(toUnicode(_path.c_str()), NULL) != 0; } else { success = CreateFile(toUnicode(_path.c_str()), GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL) != INVALID_HANDLE_VALUE; @@ -264,12 +264,12 @@ bool WindowsFilesystemNode::create(bool isDirectory) { _path += '\\'; } - if (_isDirectory != isDirectory) warning("failed to create %s: got %s", isDirectory ? "directory" : "file", _isDirectory ? "directory" : "file"); - return _isDirectory == isDirectory; + if (_isDirectory != isDirectoryFlag) warning("failed to create %s: got %s", isDirectoryFlag ? "directory" : "file", _isDirectory ? "directory" : "file"); + return _isDirectory == isDirectoryFlag; } warning("WindowsFilesystemNode: Create%s() was a success, but GetFileAttributes() indicates there is no such %s", - isDirectory ? "Directory" : "File", isDirectory ? "directory" : "file"); + isDirectoryFlag ? "Directory" : "File", isDirectoryFlag ? "directory" : "file"); return false; } diff --git a/backends/fs/windows/windows-fs.h b/backends/fs/windows/windows-fs.h index 7c9f2c1e1ad..3758877f05e 100644 --- a/backends/fs/windows/windows-fs.h +++ b/backends/fs/windows/windows-fs.h @@ -88,7 +88,7 @@ public: virtual Common::SeekableReadStream *createReadStream(); virtual Common::WriteStream *createWriteStream(); - virtual bool create(bool isDirectory); + virtual bool create(bool isDirectoryFlag); private: /** diff --git a/backends/graphics/graphics.h b/backends/graphics/graphics.h index cdc91c54046..78f0aa84a63 100644 --- a/backends/graphics/graphics.h +++ b/backends/graphics/graphics.h @@ -46,6 +46,13 @@ public: virtual bool setGraphicsMode(int mode) = 0; virtual void resetGraphicsScale() = 0; virtual int getGraphicsMode() const = 0; + virtual const OSystem::GraphicsMode *getSupportedShaders() const { + static const OSystem::GraphicsMode no_shader[2] = {{"NONE", "Normal (no shader)", 0}, {0, 0, 0}}; + return no_shader; + }; + virtual bool setShader(int id) { return false; } + virtual int getShader() const { return 0; } + #ifdef USE_RGB_COLOR virtual Graphics::PixelFormat getScreenFormat() const = 0; virtual Common::List getSupportedFormats() const = 0; diff --git a/backends/midi/timidity.cpp b/backends/midi/timidity.cpp index de68e5bd2a2..5b2bd1e64c1 100644 --- a/backends/midi/timidity.cpp +++ b/backends/midi/timidity.cpp @@ -52,24 +52,18 @@ #include #include #include -#include /* for gethostbyname */ +#include /* for getaddrinfo */ #include #include #include #include #include -// WORKAROUND bug #1870304: Solaris does not provide INADDR_NONE. -#ifndef INADDR_NONE -#define INADDR_NONE 0xffffffff -#endif - // BeOS BONE uses snooze (x/1000) in place of usleep(x) #ifdef __BEOS__ #define usleep(v) snooze(v/1000) #endif - #define SEQ_MIDIPUTC 5 #define TIMIDITY_LOW_DELAY @@ -84,7 +78,7 @@ /* default host & port */ #define DEFAULT_TIMIDITY_HOST "127.0.0.1" -#define DEFAULT_TIMIDITY_PORT 7777 +#define DEFAULT_TIMIDITY_PORT "7777" class MidiDriver_TIMIDITY : public MidiDriver_MPU401 { public: @@ -97,11 +91,8 @@ public: void sysEx(const byte *msg, uint16 length); private: - /* standart routine to extract ip address from a string */ - in_addr_t host_to_addr(const char* address); - /* creates a tcp connection to TiMidity server, returns filedesc (like open()) */ - int connect_to_server(const char* hostname, unsigned short tcp_port); + int connect_to_server(const char* hostname, const char* tcp_port); /* send command to the server; printf-like; returns reply string */ char *timidity_ctl_command(const char *fmt, ...) GCC_PRINTF(2, 3); @@ -150,7 +141,8 @@ MidiDriver_TIMIDITY::MidiDriver_TIMIDITY() { int MidiDriver_TIMIDITY::open() { char *res; char timidity_host[NI_MAXHOST]; - int timidity_port, data_port, i; + char timidity_port[6], data_port[6]; + int num; /* count ourselves open */ if (_isOpen) @@ -166,16 +158,16 @@ int MidiDriver_TIMIDITY::open() { /* extract control port */ if ((res = strrchr(timidity_host, ':')) != NULL) { *res++ = '\0'; - timidity_port = atoi(res); + Common::strlcpy(timidity_port, res, sizeof(timidity_port)); } else { - timidity_port = DEFAULT_TIMIDITY_PORT; + Common::strlcpy(timidity_port, DEFAULT_TIMIDITY_PORT, sizeof(timidity_port)); } /* * create control connection to the server */ if ((_control_fd = connect_to_server(timidity_host, timidity_port)) < 0) { - warning("TiMidity: can't open control connection (host=%s, port=%d)", timidity_host, timidity_port); + warning("TiMidity: can't open control connection (host=%s, port=%s)", timidity_host, timidity_port); return -1; } @@ -183,7 +175,7 @@ int MidiDriver_TIMIDITY::open() { * "220 TiMidity++ v2.13.2 ready)" */ res = timidity_ctl_command(NULL); if (atoi(res) != 220) { - warning("TiMidity: bad response from server (host=%s, port=%d): %s", timidity_host, timidity_port, res); + warning("TiMidity: bad response from server (host=%s, port=%s): %s", timidity_host, timidity_port, res); close_all(); return -1; } @@ -198,13 +190,11 @@ int MidiDriver_TIMIDITY::open() { /* should read something like "200 63017 is ready acceptable", * where 63017 is port for data connection */ - // FIXME: The following looks like a cheap endian test. If this is true, then - // it should be replaced by suitable #ifdef SCUMM_LITTLE_ENDIAN. - i = 1; - if (*(char *)&i == 1) - res = timidity_ctl_command("OPEN lsb"); - else - res = timidity_ctl_command("OPEN msb"); +#ifdef SCUMM_LITTLE_ENDIAN + res = timidity_ctl_command("OPEN lsb"); +#else + res = timidity_ctl_command("OPEN msb"); +#endif if (atoi(res) != 200) { warning("TiMidity: bad reply for OPEN command: %s", res); @@ -215,9 +205,15 @@ int MidiDriver_TIMIDITY::open() { /* * open data connection */ - data_port = atoi(res + 4); + num = atoi(res + 4); + if (num > 65535) { + warning("TiMidity: Invalid port %d given.\n", num); + close_all(); + return -1; + } + snprintf(data_port, sizeof(data_port), "%d", num); if ((_data_fd = connect_to_server(timidity_host, data_port)) < 0) { - warning("TiMidity: can't open data connection (host=%s, port=%d)", timidity_host, data_port); + warning("TiMidity: can't open data connection (host=%s, port=%s)", timidity_host, data_port); close_all(); return -1; } @@ -226,7 +222,7 @@ int MidiDriver_TIMIDITY::open() { * "200 Ready data connection" */ res = timidity_ctl_command(NULL); if (atoi(res) != 200) { - warning("Can't connect timidity: %s\t(host=%s, port=%d)", res, timidity_host, data_port); + warning("Can't connect timidity: %s\t(host=%s, port=%s)", res, timidity_host, data_port); close_all(); return -1; } @@ -277,46 +273,33 @@ void MidiDriver_TIMIDITY::teardown() { close_all(); } -in_addr_t MidiDriver_TIMIDITY::host_to_addr(const char* address) { - in_addr_t addr; - struct hostent *hp; - - /* first check if IP address is given (like 127.0.0.1)*/ - if ((addr = inet_addr(address)) != INADDR_NONE) - return addr; - - /* if not, try to resolve a hostname */ - if ((hp = gethostbyname(address)) == NULL) { - warning("TiMidity: unknown hostname: %s", address); - return INADDR_NONE; - } - - memcpy(&addr, hp->h_addr, (int)sizeof(in_addr_t) <= hp->h_length ? sizeof(in_addr_t) : hp->h_length); - - return addr; -} - -int MidiDriver_TIMIDITY::connect_to_server(const char* hostname, unsigned short tcp_port) { +int MidiDriver_TIMIDITY::connect_to_server(const char* hostname, const char* tcp_port) { int fd; - struct sockaddr_in in; - unsigned int addr; + struct addrinfo hints; + struct addrinfo *result, *rp; - /* create socket */ - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - warning("TiMidity: socket(): %s", strerror(errno)); + /* get all address(es) matching host and port */ + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_socktype = SOCK_STREAM; + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + if (getaddrinfo(hostname, tcp_port, &hints, &result) != 0) { + warning("TiMidity: getaddrinfo: %s\n", strerror(errno)); return -1; } - /* connect */ - memset(&in, 0, sizeof(in)); - in.sin_family = AF_INET; - in.sin_port = htons(tcp_port); - addr = host_to_addr(hostname); - memcpy(&in.sin_addr, &addr, 4); - - if (connect(fd, (struct sockaddr *)&in, sizeof(in)) < 0) { - warning("TiMidity: connect(): %s", strerror(errno)); + /* Try all address structures we have got previously */ + for (rp = result; rp != NULL; rp = rp->ai_next) { + if ((fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) == -1) + continue; + if (connect(fd, rp->ai_addr, rp->ai_addrlen) != -1) + break; ::close(fd); + } + + freeaddrinfo(result); + + if (rp == NULL) { + warning("TiMidity: Could not connect\n"); return -1; } @@ -561,4 +544,4 @@ Common::Error TimidityMusicPlugin::createInstance(MidiDriver **mididriver, MidiD REGISTER_PLUGIN_STATIC(TIMIDITY, PLUGIN_TYPE_MUSIC, TimidityMusicPlugin); //#endif -#endif // defined (USE_TIMIDITY) +#endif // defined(USE_TIMIDITY) diff --git a/backends/mixer/sdl/sdl-mixer.cpp b/backends/mixer/sdl/sdl-mixer.cpp index 0ca3231892b..3e65fbae974 100644 --- a/backends/mixer/sdl/sdl-mixer.cpp +++ b/backends/mixer/sdl/sdl-mixer.cpp @@ -32,7 +32,7 @@ #if defined(GP2X) #define SAMPLES_PER_SEC 11025 -#elif defined(PLAYSTATION3) +#elif defined(PLAYSTATION3) || defined(PSP2) #define SAMPLES_PER_SEC 48000 #else #define SAMPLES_PER_SEC 44100 diff --git a/backends/modular-backend.cpp b/backends/modular-backend.cpp index acab95e31c9..8215934f8e2 100644 --- a/backends/modular-backend.cpp +++ b/backends/modular-backend.cpp @@ -82,6 +82,18 @@ int ModularBackend::getGraphicsMode() const { return _graphicsManager->getGraphicsMode(); } +const OSystem::GraphicsMode *ModularBackend::getSupportedShaders() const { + return _graphicsManager->getSupportedShaders(); +} + +bool ModularBackend::setShader(int id) { + return _graphicsManager->setShader(id); +} + +int ModularBackend::getShader() const { + return _graphicsManager->getShader(); +} + void ModularBackend::resetGraphicsScale() { _graphicsManager->resetGraphicsScale(); } diff --git a/backends/modular-backend.h b/backends/modular-backend.h index e40ddbfb066..009a1799d56 100644 --- a/backends/modular-backend.h +++ b/backends/modular-backend.h @@ -66,6 +66,9 @@ public: virtual int getDefaultGraphicsMode() const; virtual bool setGraphicsMode(int mode); virtual int getGraphicsMode() const; + virtual const GraphicsMode *getSupportedShaders() const; + virtual int getShader() const; + virtual bool setShader(int id); virtual void resetGraphicsScale(); #ifdef USE_RGB_COLOR virtual Graphics::PixelFormat getScreenFormat() const; diff --git a/backends/module.mk b/backends/module.mk index 122df256de8..58c385b86a7 100644 --- a/backends/module.mk +++ b/backends/module.mk @@ -271,6 +271,15 @@ MODULE_OBJS += \ timer/psp/timer.o endif +ifeq ($(BACKEND),psp2) +MODULE_OBJS += \ + fs/posix/posix-fs.o \ + fs/psp2/psp2-fs-factory.o \ + fs/psp2/psp2-dirent.o \ + events/psp2sdl/psp2sdl-events.o \ + graphics/psp2sdl/psp2sdl-graphics.o +endif + ifeq ($(BACKEND),samsungtv) MODULE_OBJS += \ events/samsungtvsdl/samsungtvsdl-events.o \ diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp index e1761bddc69..d8662ab78d4 100644 --- a/backends/networking/curl/connectionmanager.cpp +++ b/backends/networking/curl/connectionmanager.cpp @@ -81,7 +81,11 @@ Request *ConnectionManager::addRequest(Request *request, RequestCallback callbac Common::String ConnectionManager::urlEncode(Common::String s) const { if (!_multi) return ""; +#if LIBCURL_VERSION_NUM >= 0x070F04 char *output = curl_easy_escape(_multi, s.c_str(), s.size()); +#else + char *output = curl_escape(s.c_str(), s.size()); +#endif if (output) { Common::String result = output; curl_free(output); diff --git a/backends/networking/curl/networkreadstream.cpp b/backends/networking/curl/networkreadstream.cpp index b4516701a75..e4fc5492b5d 100644 --- a/backends/networking/curl/networkreadstream.cpp +++ b/backends/networking/curl/networkreadstream.cpp @@ -67,6 +67,7 @@ void NetworkReadStream::init(const char *url, curl_slist *headersList, const byt _sendingContentsBuffer = nullptr; _sendingContentsSize = _sendingContentsPos = 0; _progressDownloaded = _progressTotal = 0; + _bufferCopy = nullptr; _easy = curl_easy_init(); curl_easy_setopt(_easy, CURLOPT_WRITEFUNCTION, curlDataCallback); @@ -100,7 +101,14 @@ void NetworkReadStream::init(const char *url, curl_slist *headersList, const byt } else { if (post || bufferSize != 0) { curl_easy_setopt(_easy, CURLOPT_POSTFIELDSIZE, bufferSize); +#if LIBCURL_VERSION_NUM >= 0x071101 + // CURLOPT_COPYPOSTFIELDS available since curl 7.17.1 curl_easy_setopt(_easy, CURLOPT_COPYPOSTFIELDS, buffer); +#else + _bufferCopy = (byte*)malloc(bufferSize); + memcpy(_bufferCopy, buffer, bufferSize); + curl_easy_setopt(_easy, CURLOPT_POSTFIELDS, _bufferCopy); +#endif } } ConnMan.registerEasyHandle(_easy); @@ -111,6 +119,7 @@ void NetworkReadStream::init(const char *url, curl_slist *headersList, Common::H _sendingContentsBuffer = nullptr; _sendingContentsSize = _sendingContentsPos = 0; _progressDownloaded = _progressTotal = 0; + _bufferCopy = nullptr; _easy = curl_easy_init(); curl_easy_setopt(_easy, CURLOPT_WRITEFUNCTION, curlDataCallback); @@ -184,6 +193,7 @@ NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, c NetworkReadStream::~NetworkReadStream() { if (_easy) curl_easy_cleanup(_easy); + free(_bufferCopy); } bool NetworkReadStream::eos() const { @@ -228,19 +238,19 @@ Common::String NetworkReadStream::responseHeaders() const { } uint32 NetworkReadStream::fillWithSendingContents(char *bufferToFill, uint32 maxSize) { - uint32 size = _sendingContentsSize - _sendingContentsPos; - if (size > maxSize) - size = maxSize; - for (uint32 i = 0; i < size; ++i) { + uint32 sendSize = _sendingContentsSize - _sendingContentsPos; + if (sendSize > maxSize) + sendSize = maxSize; + for (uint32 i = 0; i < sendSize; ++i) { bufferToFill[i] = _sendingContentsBuffer[_sendingContentsPos + i]; } - _sendingContentsPos += size; - return size; + _sendingContentsPos += sendSize; + return sendSize; } -uint32 NetworkReadStream::addResponseHeaders(char *buffer, uint32 size) { - _responseHeaders += Common::String(buffer, size); - return size; +uint32 NetworkReadStream::addResponseHeaders(char *buffer, uint32 bufferSize) { + _responseHeaders += Common::String(buffer, bufferSize); + return bufferSize; } double NetworkReadStream::getProgress() const { diff --git a/backends/networking/curl/networkreadstream.h b/backends/networking/curl/networkreadstream.h index 62a3e41d9ba..8e59429a0ab 100644 --- a/backends/networking/curl/networkreadstream.h +++ b/backends/networking/curl/networkreadstream.h @@ -40,6 +40,7 @@ class NetworkReadStream: public Common::MemoryReadWriteStream { const byte *_sendingContentsBuffer; uint32 _sendingContentsSize; uint32 _sendingContentsPos; + byte* _bufferCopy; // To use with old curl version where CURLOPT_COPYPOSTFIELDS is not available Common::String _responseHeaders; uint64 _progressDownloaded, _progressTotal; void init(const char *url, curl_slist *headersList, const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post); @@ -128,7 +129,7 @@ public: * * @returns how many bytes were actually read */ - uint32 addResponseHeaders(char *buffer, uint32 size); + uint32 addResponseHeaders(char *buffer, uint32 bufferSize); /** Returns a number in range [0, 1], where 1 is "complete". */ double getProgress() const; diff --git a/backends/networking/sdl_net/client.cpp b/backends/networking/sdl_net/client.cpp index fbb5fadf358..0ad366d8a5c 100644 --- a/backends/networking/sdl_net/client.cpp +++ b/backends/networking/sdl_net/client.cpp @@ -84,7 +84,7 @@ bool Client::readMoreIfNeeded() { return false; } - if (_stream->write(_buffer, bytes) != bytes) { + if (_stream->write(_buffer, bytes) != (uint32)bytes) { warning("Client::readMoreIfNeeded: failed to write() into MemoryReadWriteStream"); close(); return false; diff --git a/backends/networking/sdl_net/getclienthandler.cpp b/backends/networking/sdl_net/getclienthandler.cpp index b22c80cebff..ef085c60820 100644 --- a/backends/networking/sdl_net/getclienthandler.cpp +++ b/backends/networking/sdl_net/getclienthandler.cpp @@ -145,7 +145,7 @@ void GetClientHandler::handle(Client *client) { } if (readBytes != 0) - if (client->send(_buffer, readBytes) != readBytes) { + if (client->send(_buffer, readBytes) != (int)readBytes) { warning("GetClientHandler: unable to send all bytes to the client"); client->close(); return; diff --git a/backends/networking/sdl_net/handlers/filespagehandler.cpp b/backends/networking/sdl_net/handlers/filespagehandler.cpp index 01b40c3bcb9..22afa83af8d 100644 --- a/backends/networking/sdl_net/handlers/filespagehandler.cpp +++ b/backends/networking/sdl_net/handlers/filespagehandler.cpp @@ -56,7 +56,7 @@ Common::String encodeHtmlEntities(Common::String s) { result += ">"; else if (s[i] == '&') result += "&"; - else if (s[i] > 0x7F) + else if (s[i] > (byte)0x7F) result += Common::String::format("&#%d;", (int)s[i]); else result += s[i]; return result; diff --git a/backends/networking/sdl_net/handlers/indexpagehandler.cpp b/backends/networking/sdl_net/handlers/indexpagehandler.cpp index 04a3f45eff6..17e5159768f 100644 --- a/backends/networking/sdl_net/handlers/indexpagehandler.cpp +++ b/backends/networking/sdl_net/handlers/indexpagehandler.cpp @@ -37,9 +37,9 @@ IndexPageHandler::~IndexPageHandler() {} Common::String IndexPageHandler::code() const { return _code; } void IndexPageHandler::handle(Client &client) { - Common::String code = client.queryParameter("code"); + Common::String queryCode = client.queryParameter("code"); - if (code == "") { + if (queryCode == "") { // redirect to "/filesAJAX" HandlerUtils::setMessageHandler( client, @@ -53,7 +53,7 @@ void IndexPageHandler::handle(Client &client) { return; } - _code = code; + _code = queryCode; sendCommand(GUI::kStorageCodePassedCmd, 0); HandlerUtils::setMessageHandler(client, _("ScummVM got the code and already connects to your cloud storage!")); } diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp index fdc89b51e4c..7ef1565ad40 100644 --- a/backends/networking/sdl_net/localwebserver.cpp +++ b/backends/networking/sdl_net/localwebserver.cpp @@ -33,10 +33,24 @@ #include #ifdef POSIX -#include -#include -#include +#include +#include #include +#include +#include +#include +#include +#include + +#ifndef SIOCGIFCONF +#include +#endif + +#ifndef _SIZEOF_ADDR_IFREQ +#define _SIZEOF_ADDR_IFREQ sizeof +#endif + +#define LSSDP_BUFFER_LEN 2048 #endif namespace Common { @@ -231,8 +245,8 @@ void LocalWebserver::handleClient(uint32 i) { break; // if no handler, answer with default BAD REQUEST - // fallthrough } + // fall through case BAD_REQUEST: setClientGetHandler(_client[i], "ScummVM - Bad RequestBAD REQUEST", 400); @@ -295,56 +309,70 @@ void LocalWebserver::resolveAddress(void *ipAddress) { // if not - try platform-specific #ifdef POSIX - struct ifaddrs *ifAddrStruct = NULL; void *tmpAddrPtr = NULL; - getifaddrs(&ifAddrStruct); + int fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + warning("LocalWebserver: failed to create socket: %s (%d)", strerror(errno), errno); + } else { + // get ifconfig + char buffer[LSSDP_BUFFER_LEN] = {}; + struct ifconf ifc; + ifc.ifc_len = sizeof(buffer); + ifc.ifc_buf = (caddr_t) buffer; - for (struct ifaddrs *i = ifAddrStruct; i != NULL; i = i->ifa_next) { - if (!i->ifa_addr) { - continue; + if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) { + warning("LocalWebserver: ioctl SIOCGIFCONF failed: %s (%d)", strerror(errno), errno); + } else { + struct ifreq *i; + for (size_t index = 0; index < (size_t)ifc.ifc_len; index += _SIZEOF_ADDR_IFREQ(*i)) { + i = (struct ifreq *)(buffer + index); + + Common::String addr; + + // IPv4 + if (i->ifr_addr.sa_family == AF_INET) { + tmpAddrPtr = &((struct sockaddr_in *)&i->ifr_addr)->sin_addr; + char addressBuffer[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN); + debug(9, "%s IP Address %s", i->ifr_name, addressBuffer); + addr = addressBuffer; + } + + // IPv6 + /* + if (i->ifr_addr.sa_family == AF_INET6) { + tmpAddrPtr = &((struct sockaddr_in6 *)&i->ifr_addr)->sin6_addr; + char addressBuffer[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN); + debug(9, "%s IP Address %s", i->ifr_name, addressBuffer); + addr = addressBuffer; + } + */ + + if (addr.empty()) + continue; + + // ignored IPv4 addresses + if (addr.equals("127.0.0.1") || addr.equals("0.0.0.0") || addr.equals("localhost")) + continue; + + // ignored IPv6 addresses + /* + if (addr.equals("::1")) + continue; + */ + + // use the address found + _address = "http://" + addr + Common::String::format(":%u/", _serverPort); + } } - Common::String addr; - - // IPv4 - if (i->ifa_addr->sa_family == AF_INET) { - tmpAddrPtr = &((struct sockaddr_in *)i->ifa_addr)->sin_addr; - char addressBuffer[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN); - debug(9, "%s IP Address %s", i->ifa_name, addressBuffer); - addr = addressBuffer; + // close socket + if (close(fd) != 0) { + warning("LocalWebserver: failed to close socket [fd %d]: %s (%d)", fd, strerror(errno), errno); } - - // IPv6 - /* - if (i->ifa_addr->sa_family == AF_INET6) { - tmpAddrPtr = &((struct sockaddr_in6 *)i->ifa_addr)->sin6_addr; - char addressBuffer[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN); - debug(9, "%s IP Address %s", i->ifa_name, addressBuffer); - addr = addressBuffer; - } - */ - - if (addr.empty()) - continue; - - // ignored IPv4 addresses - if (addr.equals("127.0.0.1") || addr.equals("0.0.0.0") || addr.equals("localhost")) - continue; - - // ignored IPv6 addresses - /* - if (addr.equals("::1")) - continue; - */ - - // use the address found - _address = "http://" + addr + Common::String::format(":%u/", _serverPort); } - - if (ifAddrStruct != NULL) freeifaddrs(ifAddrStruct); #endif } diff --git a/backends/networking/sdl_net/localwebserver.h b/backends/networking/sdl_net/localwebserver.h index 5c94d6d091a..243db11fb46 100644 --- a/backends/networking/sdl_net/localwebserver.h +++ b/backends/networking/sdl_net/localwebserver.h @@ -59,7 +59,7 @@ class LocalWebserver : public Common::Singleton { SDLNet_SocketSet _set; TCPsocket _serverSocket; Client _client[MAX_CONNECTIONS]; - int _clients; + uint32 _clients; bool _timerStarted, _stopOnIdle, _minimalMode; Common::HashMap _pathHandlers; BaseHandler *_defaultHandler; diff --git a/backends/networking/sdl_net/reader.cpp b/backends/networking/sdl_net/reader.cpp index aa9f4746ddd..46fb21a47a7 100644 --- a/backends/networking/sdl_net/reader.cpp +++ b/backends/networking/sdl_net/reader.cpp @@ -169,27 +169,27 @@ void Reader::handleFirstHeaders(Common::MemoryReadWriteStream *headersStream) { _availableBytes = _contentLength; } -void Reader::parseFirstLine(const Common::String &headers) { - uint32 headersSize = headers.size(); +void Reader::parseFirstLine(const Common::String &headersToParse) { + uint32 headersSize = headersToParse.size(); bool bad = false; if (headersSize > 0) { - const char *cstr = headers.c_str(); + const char *cstr = headersToParse.c_str(); const char *position = strstr(cstr, "\r\n"); if (position) { //we have at least one line - and we want the first one //" HTTP/\r\n" - Common::String method, path, http, buf; + Common::String methodParsed, pathParsed, http, buf; uint32 length = position - cstr; if (headersSize > length) headersSize = length; for (uint32 i = 0; i < headersSize; ++i) { - if (headers[i] != ' ') - buf += headers[i]; - if (headers[i] == ' ' || i == headersSize - 1) { - if (method == "") { - method = buf; - } else if (path == "") { - path = buf; + if (headersToParse[i] != ' ') + buf += headersToParse[i]; + if (headersToParse[i] == ' ' || i == headersSize - 1) { + if (methodParsed == "") { + methodParsed = buf; + } else if (pathParsed == "") { + pathParsed = buf; } else if (http == "") { http = buf; } else { @@ -201,44 +201,44 @@ void Reader::parseFirstLine(const Common::String &headers) { } //check that method is supported - if (method != "GET" && method != "PUT" && method != "POST") + if (methodParsed != "GET" && methodParsed != "PUT" && methodParsed != "POST") bad = true; //check that HTTP/ is OK if (!http.hasPrefix("HTTP/")) bad = true; - _method = method; - parsePathQueryAndAnchor(path); + _method = methodParsed; + parsePathQueryAndAnchor(pathParsed); } } if (bad) _isBadRequest = true; } -void Reader::parsePathQueryAndAnchor(Common::String path) { +void Reader::parsePathQueryAndAnchor(Common::String pathToParse) { //[?query][#anchor] bool readingPath = true; bool readingQuery = false; _path = ""; _query = ""; _anchor = ""; - for (uint32 i = 0; i < path.size(); ++i) { + for (uint32 i = 0; i < pathToParse.size(); ++i) { if (readingPath) { - if (path[i] == '?') { + if (pathToParse[i] == '?') { readingPath = false; readingQuery = true; } else { - _path += path[i]; + _path += pathToParse[i]; } } else if (readingQuery) { - if (path[i] == '#') { + if (pathToParse[i] == '#') { readingQuery = false; } else { - _query += path[i]; + _query += pathToParse[i]; } } else { - _anchor += path[i]; + _anchor += pathToParse[i]; } } @@ -350,7 +350,7 @@ bool Reader::readOneByteInStream(Common::WriteStream *stream, const Common::Stri } byte Reader::readOne() { - byte b; + byte b = 0; _content->read(&b, 1); --_availableBytes; --_bytesLeft; diff --git a/backends/networking/sdl_net/reader.h b/backends/networking/sdl_net/reader.h index 1a60e7a58df..4955edfc39a 100644 --- a/backends/networking/sdl_net/reader.h +++ b/backends/networking/sdl_net/reader.h @@ -100,7 +100,7 @@ class Reader { void handleFirstHeaders(Common::MemoryReadWriteStream *headers); void parseFirstLine(const Common::String &headers); - void parsePathQueryAndAnchor(Common::String path); + void parsePathQueryAndAnchor(Common::String pathToParse); void parseQueryParameters(); void makeWindow(uint32 size); diff --git a/backends/platform/sdl/macosx/macosx.cpp b/backends/platform/sdl/macosx/macosx.cpp index 7d80f0ed353..9ce05238187 100644 --- a/backends/platform/sdl/macosx/macosx.cpp +++ b/backends/platform/sdl/macosx/macosx.cpp @@ -137,7 +137,7 @@ bool OSystem_MacOSX::openUrl(const Common::String &url) { CFURLRef urlRef = CFURLCreateWithBytes (NULL, (UInt8*)url.c_str(), url.size(), kCFStringEncodingASCII, NULL); OSStatus err = LSOpenCFURLRef(urlRef, NULL); CFRelease(urlRef); - return err != noErr; + return err == noErr; } Common::String OSystem_MacOSX::getSystemLanguage() const { @@ -187,6 +187,15 @@ Common::String OSystem_MacOSX::getSystemLanguage() const { #endif // USE_DETECTLANG } +Common::String OSystem_MacOSX::getScreenshotsPath() { + Common::String path = ConfMan.get("screenshotpath"); + if (path.empty()) + path = getDesktopPathMacOSX(); + if (!path.empty() && !path.hasSuffix("/")) + path += "/"; + return path; +} + AudioCDManager *OSystem_MacOSX::createAudioCDManager() { return createMacOSXAudioCDManager(); } diff --git a/backends/platform/sdl/macosx/macosx.h b/backends/platform/sdl/macosx/macosx.h index 72bb4a4787d..ba07364681a 100644 --- a/backends/platform/sdl/macosx/macosx.h +++ b/backends/platform/sdl/macosx/macosx.h @@ -44,6 +44,9 @@ public: virtual void initBackend(); virtual void addSysArchivesToSearchSet(Common::SearchSet &s, int priority = 0); + //Screenshots + virtual Common::String getScreenshotsPath(); + protected: // Override createAudioCDManager() to get our Mac-specific // version. diff --git a/backends/platform/sdl/macosx/macosx_wrapper.h b/backends/platform/sdl/macosx/macosx_wrapper.h index 3b346fc4865..84f0c1b2bab 100644 --- a/backends/platform/sdl/macosx/macosx_wrapper.h +++ b/backends/platform/sdl/macosx/macosx_wrapper.h @@ -27,5 +27,6 @@ bool hasTextInClipboardMacOSX(); Common::String getTextFromClipboardMacOSX(); +Common::String getDesktopPathMacOSX(); #endif diff --git a/backends/platform/sdl/macosx/macosx_wrapper.mm b/backends/platform/sdl/macosx/macosx_wrapper.mm index 8ec9eac5acb..02516e5ffee 100644 --- a/backends/platform/sdl/macosx/macosx_wrapper.mm +++ b/backends/platform/sdl/macosx/macosx_wrapper.mm @@ -27,6 +27,8 @@ #include #include +#include +#include bool hasTextInClipboardMacOSX() { return [[NSPasteboard generalPasteboard] availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]] != nil; @@ -46,3 +48,17 @@ Common::String getTextFromClipboardMacOSX() { // we should use NSISOLatin1StringEncoding?). return Common::String([str cStringUsingEncoding:NSASCIIStringEncoding]); } + +Common::String getDesktopPathMacOSX() { + // The recommanded method is to use NSFileManager. + // NSUrl *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDesktopDirectory inDomains:NSUserDomainMask] firstObject]; + // However it is only available in OS X 10.6+. So use NSSearchPathForDirectoriesInDomains instead (available since OS X 10.0) + // [NSArray firstObject] is also only available in OS X 10.6+. So we need to use [NSArray count] and [NSArray objectAtIndex:] + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDesktopDirectory, NSUserDomainMask, YES); + if ([paths count] == 0) + return Common::String(); + NSString *path = [paths objectAtIndex:0]; + if (path == nil) + return Common::String(); + return Common::String([path cStringUsingEncoding:NSASCIIStringEncoding]); +} diff --git a/backends/platform/sdl/module.mk b/backends/platform/sdl/module.mk index 84ce272d3cf..7fde04037f1 100644 --- a/backends/platform/sdl/module.mk +++ b/backends/platform/sdl/module.mk @@ -37,6 +37,13 @@ MODULE_OBJS += \ ps3/ps3.o endif +ifdef PSP2 +CC=arm-vita-eabi-gcc +MODULE_OBJS += \ + psp2/psp2-main.o \ + psp2/psp2.o +endif + # We don't use rules.mk but rather manually update OBJS and MODULE_DIRS. MODULE_OBJS := $(addprefix $(MODULE)/, $(MODULE_OBJS)) OBJS := $(MODULE_OBJS) $(OBJS) diff --git a/backends/platform/sdl/posix/posix-main.cpp b/backends/platform/sdl/posix/posix-main.cpp index 5deebb0ae33..92354b273ef 100644 --- a/backends/platform/sdl/posix/posix-main.cpp +++ b/backends/platform/sdl/posix/posix-main.cpp @@ -22,7 +22,7 @@ #include "common/scummsys.h" -#if defined(POSIX) && !defined(MACOSX) && !defined(SAMSUNGTV) && !defined(MAEMO) && !defined(WEBOS) && !defined(LINUXMOTO) && !defined(GPH_DEVICE) && !defined(GP2X) && !defined(DINGUX) && !defined(OPENPANDORA) && !defined(PLAYSTATION3) && !defined(ANDROIDSDL) +#if defined(POSIX) && !defined(MACOSX) && !defined(SAMSUNGTV) && !defined(MAEMO) && !defined(WEBOS) && !defined(LINUXMOTO) && !defined(GPH_DEVICE) && !defined(GP2X) && !defined(DINGUX) && !defined(OPENPANDORA) && !defined(PLAYSTATION3) && !defined(PSP2) && !defined(ANDROIDSDL) #include "backends/platform/sdl/posix/posix.h" #include "backends/plugins/sdl/sdl-provider.h" diff --git a/backends/platform/sdl/sdl.cpp b/backends/platform/sdl/sdl.cpp index d39ba5b774e..415eb36ac82 100644 --- a/backends/platform/sdl/sdl.cpp +++ b/backends/platform/sdl/sdl.cpp @@ -174,6 +174,11 @@ bool OSystem_SDL::hasFeature(Feature f) { #if SDL_VERSION_ATLEAST(2, 0, 0) if (f == kFeatureClipboardSupport) return true; #endif +#ifdef JOY_ANALOG + if (f == kFeatureJoystickDeadzone) return true; +#endif + if (f == kFeatureKbdMouseSpeed) return true; + // ResidualVM specific code: if (f == kFeatureSideTextures) return true; @@ -240,6 +245,16 @@ void OSystem_SDL::initBackend() { _inited = true; + if (!ConfMan.hasKey("kbdmouse_speed")) { + ConfMan.registerDefault("kbdmouse_speed", 3); + ConfMan.setInt("kbdmouse_speed", 3); + } +#ifdef JOY_ANALOG + if (!ConfMan.hasKey("joystick_deadzone")) { + ConfMan.registerDefault("joystick_deadzone", 3); + ConfMan.setInt("joystick_deadzone", 3); + } +#endif ModularBackend::initBackend(); // We have to initialize the graphics manager before the event manager @@ -474,7 +489,7 @@ Common::String OSystem_SDL::getSystemLanguage() const { char langName[9]; char ctryName[9]; - const LCID languageIdentifier = GetThreadLocale(); + const LCID languageIdentifier = GetUserDefaultUILanguage(); if (GetLocaleInfo(languageIdentifier, LOCALE_SISO639LANGNAME, langName, sizeof(langName)) != 0 && GetLocaleInfo(languageIdentifier, LOCALE_SISO3166CTRYNAME, ctryName, sizeof(ctryName)) != 0) { @@ -612,3 +627,11 @@ Common::SaveFileManager *OSystem_SDL::getSavefileManager() { return _savefileManager; #endif } + +//Not specified in base class +Common::String OSystem_SDL::getScreenshotsPath() { + Common::String path = ConfMan.get("screenshotpath"); + if (!path.empty() && !path.hasSuffix("/")) + path += "/"; + return path; +} diff --git a/backends/platform/sdl/sdl.h b/backends/platform/sdl/sdl.h index 243ec261de3..e9c3f756eee 100644 --- a/backends/platform/sdl/sdl.h +++ b/backends/platform/sdl/sdl.h @@ -85,6 +85,9 @@ public: virtual Common::TimerManager *getTimerManager(); virtual Common::SaveFileManager *getSavefileManager(); + //Screenshots + virtual Common::String getScreenshotsPath(); + // ResidualVM specific code virtual void setupScreen(uint screenW, uint screenH, bool fullscreen, bool accel3d) override; // ResidualVM specific code diff --git a/backends/platform/sdl/win32/win32.cpp b/backends/platform/sdl/win32/win32.cpp index a339417560f..3e52085d8a4 100644 --- a/backends/platform/sdl/win32/win32.cpp +++ b/backends/platform/sdl/win32/win32.cpp @@ -29,6 +29,11 @@ #include #undef ARRAYSIZE // winnt.h defines ARRAYSIZE, but we want our own one... #include +#if defined(__GNUC__) && defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) +// required for SHGFP_TYPE_CURRENT in shlobj.h +#define _WIN32_IE 0x500 +#endif +#include #include "common/scummsys.h" #include "common/config-manager.h" @@ -145,6 +150,34 @@ bool OSystem_Win32::openUrl(const Common::String &url) { return true; } +Common::String OSystem_Win32::getScreenshotsPath() { + Common::String screenshotsPath = ConfMan.get("screenshotpath"); + if (!screenshotsPath.empty()) { + if (!screenshotsPath.hasSuffix("\\") && !screenshotsPath.hasSuffix("/")) + screenshotsPath += "\\"; + return screenshotsPath; + } + + char picturesPath[MAXPATHLEN]; + + // Use the My Pictures folder. + if (SHGetFolderPath(NULL, CSIDL_MYPICTURES, NULL, SHGFP_TYPE_CURRENT, picturesPath) != S_OK) { + warning("Unable to access My Pictures directory"); + return Common::String(); + } + + screenshotsPath = Common::String(picturesPath) + "\\ResidualVM Screenshots\\"; + + // If the directory already exists (as it should in most cases), + // we don't want to fail, but we need to stop on other errors (such as ERROR_PATH_NOT_FOUND) + if (!CreateDirectory(screenshotsPath.c_str(), NULL)) { + if (GetLastError() != ERROR_ALREADY_EXISTS) + error("Cannot create ResidualVM Screenshots folder"); + } + + return screenshotsPath; +} + Common::String OSystem_Win32::getDefaultConfigFileName() { char configFile[MAXPATHLEN]; diff --git a/backends/platform/sdl/win32/win32.h b/backends/platform/sdl/win32/win32.h index 636ebae88f1..6764a7fe49f 100644 --- a/backends/platform/sdl/win32/win32.h +++ b/backends/platform/sdl/win32/win32.h @@ -38,6 +38,8 @@ public: virtual bool openUrl(const Common::String &url); + virtual Common::String getScreenshotsPath(); + protected: /** * The path of the currently open log file, if any. diff --git a/common/EventDispatcher.cpp b/common/EventDispatcher.cpp index 2650d795b7e..5c8a9a31198 100644 --- a/common/EventDispatcher.cpp +++ b/common/EventDispatcher.cpp @@ -70,6 +70,15 @@ void EventDispatcher::dispatch() { } } +void EventDispatcher::clearEvents() { + Event event; + + for (List::iterator i = _sources.begin(); i != _sources.end(); ++i) { + while (i->source->pollEvent(event)) {} + } +} + + void EventDispatcher::registerMapper(EventMapper *mapper, bool autoFree) { if (_autoFreeMapper) { delete _mapper; diff --git a/common/EventMapper.cpp b/common/EventMapper.cpp index cf65946d507..f84d24b4d05 100644 --- a/common/EventMapper.cpp +++ b/common/EventMapper.cpp @@ -35,7 +35,6 @@ List DefaultEventMapper::mapEvent(const Event &ev, EventSource *source) { // of middle mouse button. const uint32 vkeybdTime = 1000; - static bool vkeybd = false; static uint32 vkeybdThen = 0; if (ev.type == EVENT_MBUTTONDOWN) { diff --git a/common/bitstream.h b/common/bitstream.h index 325118bbee9..1fbc0065ab5 100644 --- a/common/bitstream.h +++ b/common/bitstream.h @@ -29,53 +29,10 @@ #include "common/textconsole.h" #include "common/stream.h" #include "common/types.h" +#include "common/util.h" namespace Common { -/** A bit stream. */ -class BitStream { -public: - virtual ~BitStream() { - } - - /** Return the stream position in bits. */ - virtual uint32 pos() const = 0; - - /** Return the stream size in bits. */ - virtual uint32 size() const = 0; - - /** Has the end of the stream been reached? */ - virtual bool eos() const = 0; - - /** Rewind the bit stream back to the start. */ - virtual void rewind() = 0; - - /** Skip the specified amount of bits. */ - virtual void skip(uint32 n) = 0; - - /** Skip the bits to closest data value border. */ - virtual void align() = 0; - - /** Read a bit from the bit stream. */ - virtual uint32 getBit() = 0; - - /** Read a multi-bit value from the bit stream. */ - virtual uint32 getBits(uint8 n) = 0; - - /** Read a bit from the bit stream, without changing the stream's position. */ - virtual uint32 peekBit() = 0; - - /** Read a multi-bit value from the bit stream, without changing the stream's position. */ - virtual uint32 peekBits(uint8 n) = 0; - - /** Add a bit to the value x, making it an n+1-bit value. */ - virtual void addBit(uint32 &x, uint32 n) = 0; - -protected: - BitStream() { - } -}; - /** * A template implementing a bit stream for different data memory layouts. * @@ -86,14 +43,16 @@ protected: * for valueBits, isLE and isMSB2LSB, reads 32bit little-endian values * from the data stream and hands out the bits in the order of LSB to MSB. */ -template -class BitStreamImpl : public BitStream { +template +class BitStreamImpl { private: - SeekableReadStream *_stream; ///< The input stream. + STREAM *_stream; ///< The input stream. DisposeAfterUse::Flag _disposeAfterUse; ///< Should we delete the stream on destruction? uint32 _value; ///< Current value. uint8 _inValue; ///< Position within the current value. + uint32 _size; ///< Total bitstream size (in bits) + uint32 _pos; ///< Current bitstream position (in bits) /** Read a data value. */ inline uint32 readData() { @@ -119,7 +78,7 @@ private: /** Read the next data value. */ inline void readValue() { - if ((size() - pos()) < valueBits) + if (_size - _pos < valueBits) error("BitStreamImpl::readValue(): End of bit stream reached"); _value = readData(); @@ -133,19 +92,23 @@ private: public: /** Create a bit stream using this input data stream and optionally delete it on destruction. */ - BitStreamImpl(SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::NO) : - _stream(stream), _disposeAfterUse(disposeAfterUse), _value(0), _inValue(0) { + BitStreamImpl(STREAM *stream, DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::NO) : + _stream(stream), _disposeAfterUse(disposeAfterUse), _value(0), _inValue(0), _pos(0) { if ((valueBits != 8) && (valueBits != 16) && (valueBits != 32)) error("BitStreamImpl: Invalid memory layout %d, %d, %d", valueBits, isLE, isMSB2LSB); + + _size = (_stream->size() & ~((uint32) ((valueBits >> 3) - 1))) * 8; } /** Create a bit stream using this input data stream. */ - BitStreamImpl(SeekableReadStream &stream) : - _stream(&stream), _disposeAfterUse(DisposeAfterUse::NO), _value(0), _inValue(0) { + BitStreamImpl(STREAM &stream) : + _stream(&stream), _disposeAfterUse(DisposeAfterUse::NO), _value(0), _inValue(0), _pos(0) { if ((valueBits != 8) && (valueBits != 16) && (valueBits != 32)) error("BitStreamImpl: Invalid memory layout %d, %d, %d", valueBits, isLE, isMSB2LSB); + + _size = (_stream->size() & ~((uint32) ((valueBits >> 3) - 1))) * 8; } ~BitStreamImpl() { @@ -153,14 +116,10 @@ public: delete _stream; } - /** Read a bit from the bit stream. */ - uint32 getBit() { - // Check if we need the next value - if (_inValue == 0) - readValue(); - +private: + uint32 getBit_internal() { // Get the current bit - int b = 0; + uint32 b = 0; if (isMSB2LSB) b = ((_value & 0x80000000) == 0) ? 0 : 1; else @@ -172,8 +131,21 @@ public: else _value >>= 1; + return b; + } + +public: + /** Read a bit from the bit stream. */ + uint32 getBit() { + // Check if we need the next value + if (_inValue == 0) + readValue(); + + uint32 b = getBit_internal(); + // Increase the position within the current value _inValue = (_inValue + 1) % valueBits; + _pos++; return b; } @@ -198,16 +170,42 @@ public: // Read the number of bits uint32 v = 0; - if (isMSB2LSB) { - while (n-- > 0) - v = (v << 1) | getBit(); - } else { - for (uint32 i = 0; i < n; i++) - v = (v >> 1) | (((uint32) getBit()) << 31); + uint8 nOrig = n; + if (_inValue) { + int count = MIN((int)n, valueBits - _inValue); + for (int i = 0; i < count; ++i) { + if (isMSB2LSB) { + v = (v << 1) | getBit_internal(); + } else { + v = (v >> 1) | (getBit_internal() << 31); + } + } - v >>= (32 - n); + n -= count; } + while (n > 0) { + // NB: readValue doesn't care that _inValue is incorrect here + readValue(); + + int count = MIN((int)n, valueBits); + for (int i = 0; i < count; ++i) { + if (isMSB2LSB) { + v = (v << 1) | getBit_internal(); + } else { + v = (v >> 1) | (getBit_internal() << 31); + } + } + + n -= count; + } + + _inValue = (_inValue + nOrig) % valueBits; + _pos += nOrig; + + if (!isMSB2LSB) + v >>= (32 - nOrig); + return v; } @@ -215,11 +213,13 @@ public: uint32 peekBit() { uint32 value = _value; uint8 inValue = _inValue; - uint32 curPos = _stream->pos(); + uint32 curStreamPos = _stream->pos(); + uint32 curPos = _pos; uint32 v = getBit(); - _stream->seek(curPos); + _pos = curPos; + _stream->seek(curStreamPos); _inValue = inValue; _value = value; @@ -234,11 +234,13 @@ public: uint32 peekBits(uint8 n) { uint32 value = _value; uint8 inValue = _inValue; - uint32 curPos = _stream->pos(); + uint32 curStreamPos = _stream->pos(); + uint32 curPos = _pos; uint32 v = getBits(n); - _stream->seek(curPos); + _pos = curPos; + _stream->seek(curStreamPos); _inValue = inValue; _value = value; @@ -272,6 +274,7 @@ public: _value = 0; _inValue = 0; + _pos = 0; } /** Skip the specified amount of bits. */ @@ -288,47 +291,215 @@ public: /** Return the stream position in bits. */ uint32 pos() const { - if (_stream->pos() == 0) - return 0; - - uint32 p = (_inValue == 0) ? _stream->pos() : ((_stream->pos() - 1) & ~((uint32) ((valueBits >> 3) - 1))); - return p * 8 + _inValue; + return _pos; } /** Return the stream size in bits. */ uint32 size() const { - return (_stream->size() & ~((uint32) ((valueBits >> 3) - 1))) * 8; + return _size; } bool eos() const { - return _stream->eos() || (pos() >= size()); + return _stream->eos() || (_pos >= _size); } }; + + +/** + * A cut-down version of MemoryReadStream specifically for use with BitStream. + * It removes the virtual call overhead for reading bytes from a memory buffer, + * and allows directly inlining this access. + * + * The code duplication with MemoryReadStream is not ideal. + * It might be possible to avoid this by making this a final subclass of + * MemoryReadStream, but that is a C++11 feature. + */ +class BitStreamMemoryStream { +private: + const byte * const _ptrOrig; + const byte *_ptr; + const uint32 _size; + uint32 _pos; + DisposeAfterUse::Flag _disposeMemory; + bool _eos; + +public: + BitStreamMemoryStream(const byte *dataPtr, uint32 dataSize, DisposeAfterUse::Flag disposeMemory = DisposeAfterUse::NO) : + _ptrOrig(dataPtr), + _ptr(dataPtr), + _size(dataSize), + _pos(0), + _disposeMemory(disposeMemory), + _eos(false) {} + + ~BitStreamMemoryStream() { + if (_disposeMemory) + free(const_cast(_ptrOrig)); + } + + bool eos() const { + return _eos; + } + + bool err() const { + return false; + } + + int32 pos() const { + return _pos; + } + + int32 size() const { + return _size; + } + + bool seek(uint32 offset) { + assert(offset <= _size); + + _eos = false; + _pos = offset; + _ptr = _ptrOrig + _pos; + return true; + } + + byte readByte() { + if (_pos >= _size) { + _eos = true; + return 0; + } + + _pos++; + return *_ptr++; + } + + uint16 readUint16LE() { + if (_pos + 2 > _size) { + _eos = true; + if (_pos < _size) { + _pos++; + return *_ptr++; + } else { + return 0; + } + } + + uint16 val = READ_LE_UINT16(_ptr); + + _pos += 2; + _ptr += 2; + + return val; + } + + uint16 readUint16BE() { + if (_pos + 2 > _size) { + _eos = true; + if (_pos < _size) { + _pos++; + return (*_ptr++) << 8; + } else { + return 0; + } + } + + uint16 val = READ_LE_UINT16(_ptr); + + _pos += 2; + _ptr += 2; + + return val; + } + + uint32 readUint32LE() { + if (_pos + 4 > _size) { + uint32 val = readByte(); + val |= (uint32)readByte() << 8; + val |= (uint32)readByte() << 16; + val |= (uint32)readByte() << 24; + + return val; + } + + uint32 val = READ_LE_UINT32(_ptr); + + _pos += 4; + _ptr += 4; + + return val; + } + + uint32 readUint32BE() { + if (_pos + 4 > _size) { + uint32 val = (uint32)readByte() << 24; + val |= (uint32)readByte() << 16; + val |= (uint32)readByte() << 8; + val |= (uint32)readByte(); + + return val; + } + + uint32 val = READ_BE_UINT32(_ptr); + + _pos += 4; + _ptr += 4; + + return val; + } + +}; + + // typedefs for various memory layouts. /** 8-bit data, MSB to LSB. */ -typedef BitStreamImpl<8, false, true > BitStream8MSB; +typedef BitStreamImpl BitStream8MSB; /** 8-bit data, LSB to MSB. */ -typedef BitStreamImpl<8, false, false> BitStream8LSB; +typedef BitStreamImpl BitStream8LSB; /** 16-bit little-endian data, MSB to LSB. */ -typedef BitStreamImpl<16, true , true > BitStream16LEMSB; +typedef BitStreamImpl BitStream16LEMSB; /** 16-bit little-endian data, LSB to MSB. */ -typedef BitStreamImpl<16, true , false> BitStream16LELSB; +typedef BitStreamImpl BitStream16LELSB; /** 16-bit big-endian data, MSB to LSB. */ -typedef BitStreamImpl<16, false, true > BitStream16BEMSB; +typedef BitStreamImpl BitStream16BEMSB; /** 16-bit big-endian data, LSB to MSB. */ -typedef BitStreamImpl<16, false, false> BitStream16BELSB; +typedef BitStreamImpl BitStream16BELSB; /** 32-bit little-endian data, MSB to LSB. */ -typedef BitStreamImpl<32, true , true > BitStream32LEMSB; +typedef BitStreamImpl BitStream32LEMSB; /** 32-bit little-endian data, LSB to MSB. */ -typedef BitStreamImpl<32, true , false> BitStream32LELSB; +typedef BitStreamImpl BitStream32LELSB; /** 32-bit big-endian data, MSB to LSB. */ -typedef BitStreamImpl<32, false, true > BitStream32BEMSB; +typedef BitStreamImpl BitStream32BEMSB; /** 32-bit big-endian data, LSB to MSB. */ -typedef BitStreamImpl<32, false, false> BitStream32BELSB; +typedef BitStreamImpl BitStream32BELSB; + + + +/** 8-bit data, MSB to LSB. */ +typedef BitStreamImpl BitStreamMemory8MSB; +/** 8-bit data, LSB to MSB. */ +typedef BitStreamImpl BitStreamMemory8LSB; + +/** 16-bit little-endian data, MSB to LSB. */ +typedef BitStreamImpl BitStreamMemory16LEMSB; +/** 16-bit little-endian data, LSB to MSB. */ +typedef BitStreamImpl BitStreamMemory16LELSB; +/** 16-bit big-endian data, MSB to LSB. */ +typedef BitStreamImpl BitStreamMemory16BEMSB; +/** 16-bit big-endian data, LSB to MSB. */ +typedef BitStreamImpl BitStreamMemory16BELSB; + +/** 32-bit little-endian data, MSB to LSB. */ +typedef BitStreamImpl BitStreamMemory32LEMSB; +/** 32-bit little-endian data, LSB to MSB. */ +typedef BitStreamImpl BitStreamMemory32LELSB; +/** 32-bit big-endian data, MSB to LSB. */ +typedef BitStreamImpl BitStreamMemory32BEMSB; +/** 32-bit big-endian data, LSB to MSB. */ +typedef BitStreamImpl BitStreamMemory32BELSB; + } // End of namespace Common diff --git a/common/debug-channels.h b/common/debug-channels.h index 1414a1053a5..0fb80068038 100644 --- a/common/debug-channels.h +++ b/common/debug-channels.h @@ -117,7 +117,7 @@ public: /** * Test whether the given debug channel is enabled. */ - bool isDebugChannelEnabled(uint32 channel); + bool isDebugChannelEnabled(uint32 channel, bool enforce = false); private: typedef HashMap DebugChannelMap; diff --git a/common/debug.cpp b/common/debug.cpp index c61fc63deac..5db8990db80 100644 --- a/common/debug.cpp +++ b/common/debug.cpp @@ -110,9 +110,9 @@ void DebugManager::disableAllDebugChannels() { disableDebugChannel(i->_value.name); } -bool DebugManager::isDebugChannelEnabled(uint32 channel) { +bool DebugManager::isDebugChannelEnabled(uint32 channel, bool enforce) { // Debug level 11 turns on all special debug level messages - if (gDebugLevel == 11) + if (gDebugLevel == 11 && enforce == false) return true; else return (gDebugChannelsEnabled & channel) != 0; @@ -125,8 +125,8 @@ bool debugLevelSet(int level) { } bool debugChannelSet(int level, uint32 debugChannels) { - if (gDebugLevel != 11) - if (level > gDebugLevel || !(DebugMan.isDebugChannelEnabled(debugChannels))) + if (gDebugLevel != 11 || level == -1) + if ((level != -1 && level > gDebugLevel) || !(DebugMan.isDebugChannelEnabled(debugChannels, level == -1))) return false; return true; diff --git a/common/debug.h b/common/debug.h index 883a0bf29dc..5ec37f2f1ed 100644 --- a/common/debug.h +++ b/common/debug.h @@ -117,6 +117,7 @@ bool debugLevelSet(int level); /** * Returns true if the debug level and channel are active * + * @param level debug level to check against. If set to -1, only channel check is active * @see enableDebugChannel */ bool debugChannelSet(int level, uint32 debugChannels); diff --git a/common/endian.h b/common/endian.h index 295cff38393..be382c6048e 100644 --- a/common/endian.h +++ b/common/endian.h @@ -608,6 +608,38 @@ inline uint32 READ_BE_UINT24(const void *ptr) { #define READ_UINT24(a) READ_BE_UINT24(a) #endif +inline int16 READ_LE_INT16(const void *ptr) { + return static_cast(READ_LE_UINT16(ptr)); +} + +inline void WRITE_LE_INT16(void *ptr, int16 value) { + WRITE_LE_UINT16(ptr, static_cast(value)); +} + +inline int16 READ_BE_INT16(const void *ptr) { + return static_cast(READ_BE_UINT16(ptr)); +} + +inline void WRITE_BE_INT16(void *ptr, int16 value) { + WRITE_BE_UINT16(ptr, static_cast(value)); +} + +inline int32 READ_LE_INT32(const void *ptr) { + return static_cast(READ_LE_UINT32(ptr)); +} + +inline void WRITE_LE_INT32(void *ptr, int32 value) { + WRITE_LE_UINT32(ptr, static_cast(value)); +} + +inline int32 READ_BE_INT32(const void *ptr) { + return static_cast(READ_BE_UINT32(ptr)); +} + +inline void WRITE_BE_INT32(void *ptr, int32 value) { + WRITE_BE_UINT32(ptr, static_cast(value)); +} + // ResidualVM specific: #if defined(SCUMM_NEED_ALIGNMENT) inline float READ_FLOAT(const char *data) { diff --git a/common/error.cpp b/common/error.cpp index 53cd8cec55a..bbbcb7c95e3 100644 --- a/common/error.cpp +++ b/common/error.cpp @@ -65,7 +65,7 @@ static String errorToString(ErrorCode errorCode) { case kEnginePluginNotFound: return _s("Could not find suitable engine plugin"); case kEnginePluginNotSupportSaves: - return _s("Engine plugin does not support save states"); + return _s("Engine plugin does not support saved games"); case kUserCanceled: return _s("User canceled"); diff --git a/common/events.h b/common/events.h index ccb6cd7ed20..8e9490618b1 100644 --- a/common/events.h +++ b/common/events.h @@ -317,6 +317,12 @@ public: */ void dispatch(); + /** + * Clear all events currently in the event queue. + * The cleared events are not dispatched and are simply discarded. + */ + void clearEvents(); + /** * Registers an event mapper with the dispatcher. * diff --git a/common/forbidden.h b/common/forbidden.h index d6fd9c42fed..8f0220bb04c 100644 --- a/common/forbidden.h +++ b/common/forbidden.h @@ -183,7 +183,7 @@ #endif // mingw-w64 uses [set|long]jmp in system headers -#ifndef __MINGW64__ +#if !defined __MINGW64__ && ! defined __MINGW32__ #ifndef FORBIDDEN_SYMBOL_EXCEPTION_setjmp #undef setjmp #define setjmp(a) FORBIDDEN_SYMBOL_REPLACEMENT @@ -193,7 +193,7 @@ #undef longjmp #define longjmp(a,b) FORBIDDEN_SYMBOL_REPLACEMENT #endif -#endif // __MINGW64__ +#endif // __MINGW64__ __MINGW32__ #ifndef FORBIDDEN_SYMBOL_EXCEPTION_system #undef system diff --git a/common/gui_options.cpp b/common/gui_options.cpp index df880f4fee8..500830a303c 100644 --- a/common/gui_options.cpp +++ b/common/gui_options.cpp @@ -29,6 +29,8 @@ namespace Common { const struct GameOpt { const char *option; + // Each description must be a unique identifier not containing a substring + // of any other description const char *desc; } g_gameOptions[] = { { GUIO_NOSUBTITLES, "sndNoSubs" }, @@ -36,6 +38,9 @@ const struct GameOpt { { GUIO_NOSPEECH, "sndNoSpeech" }, { GUIO_NOSFX, "sndNoSFX" }, { GUIO_NOMIDI, "sndNoMIDI" }, + { GUIO_LINKSPEECHTOSFX, "sndLinkSpeechToSfx" }, + { GUIO_LINKMUSICTOSFX, "sndLinkMusicToSfx" }, + { GUIO_NOSPEECHVOLUME, "sndNoSpchVolume" }, { GUIO_NOLAUNCHLOAD, "launchNoLoad" }, @@ -75,6 +80,9 @@ const struct GameOpt { { GUIO_GAMEOPTIONS7, "gameOption7" }, { GUIO_GAMEOPTIONS8, "gameOption8" }, { GUIO_GAMEOPTIONS9, "gameOption9" }, + // Option strings must not contain substrings of any other options, so + // "gameOption10" would be invalid here because it contains "gameOption1" + { GUIO_GAMEOPTIONS10, "gameOptionA" }, { GUIO_NONE, 0 } }; diff --git a/common/gui_options.h b/common/gui_options.h index d17f45cac19..6c1aa2fdcd6 100644 --- a/common/gui_options.h +++ b/common/gui_options.h @@ -26,6 +26,7 @@ #define GUIO_NONE "\000" #define GUIO_NOSUBTITLES "\001" #define GUIO_NOMUSIC "\002" +// GUIO_NOSPEECH is a combination of GUIO_NOSPEECHVOLUME and GUIO_NOSUBTITLES #define GUIO_NOSPEECH "\003" #define GUIO_NOSFX "\004" #define GUIO_NOMIDI "\005" @@ -58,6 +59,10 @@ #define GUIO_RENDERATARIST "\042" #define GUIO_RENDERMACINTOSH "\043" +#define GUIO_LINKSPEECHTOSFX "\044" +#define GUIO_LINKMUSICTOSFX "\045" +#define GUIO_NOSPEECHVOLUME "\046" + // Special GUIO flags for the AdvancedDetector's caching of game specific // options. #define GUIO_GAMEOPTIONS1 "\050" @@ -69,6 +74,7 @@ #define GUIO_GAMEOPTIONS7 "\056" #define GUIO_GAMEOPTIONS8 "\057" #define GUIO_GAMEOPTIONS9 "\060" +#define GUIO_GAMEOPTIONS10 "\061" #define GUIO0() (GUIO_NONE) #define GUIO1(a) (a) diff --git a/common/huffman.cpp b/common/huffman.cpp index afb4fa00b6a..3268ddf2513 100644 --- a/common/huffman.cpp +++ b/common/huffman.cpp @@ -68,19 +68,4 @@ void Huffman::setSymbols(const uint32 *symbols) { _symbols[i]->symbol = symbols ? *symbols++ : i; } -uint32 Huffman::getSymbol(BitStream &bits) const { - uint32 code = 0; - - for (uint32 i = 0; i < _codes.size(); i++) { - bits.addBit(code, i); - - for (CodeList::const_iterator cCode = _codes[i].begin(); cCode != _codes[i].end(); ++cCode) - if (code == cCode->code) - return cCode->symbol; - } - - error("Unknown Huffman code"); - return 0; -} - } // End of namespace Common diff --git a/common/huffman.h b/common/huffman.h index 5ba7deca87e..f703d078dce 100644 --- a/common/huffman.h +++ b/common/huffman.h @@ -31,8 +31,6 @@ namespace Common { -class BitStream; - /** * Huffman bitstream decoding * @@ -56,7 +54,21 @@ public: void setSymbols(const uint32 *symbols = 0); /** Return the next symbol in the bitstream. */ - uint32 getSymbol(BitStream &bits) const; + template + uint32 getSymbol(BITSTREAM &bits) const { + uint32 code = 0; + + for (uint32 i = 0; i < _codes.size(); i++) { + bits.addBit(code, i); + + for (CodeList::const_iterator cCode = _codes[i].begin(); cCode != _codes[i].end(); ++cCode) + if (code == cCode->code) + return cCode->symbol; + } + + error("Unknown Huffman code"); + return 0; + } private: struct Symbol { diff --git a/common/json.cpp b/common/json.cpp index 792d1967e9c..c8caf015199 100644 --- a/common/json.cpp +++ b/common/json.cpp @@ -979,17 +979,13 @@ String JSONValue::stringifyImpl(size_t const indentDepth) const { if (isinf(_numberValue) || isnan(_numberValue)) ret_string = "null"; else { - char str[80]; - sprintf(str, "%g", _numberValue); - ret_string = str; + ret_string = String::format("%g", _numberValue); } break; } case JSONType_IntegerNumber: { - char str[80]; - sprintf(str, "%lld", _integerValue); - ret_string = str; + ret_string = String::format("%lld", _integerValue); break; } diff --git a/common/language.cpp b/common/language.cpp index 97c515b45a3..b7397bec6d7 100644 --- a/common/language.cpp +++ b/common/language.cpp @@ -35,6 +35,7 @@ const LanguageDescription g_languages[] = { { "en", "en", "English", EN_ANY }, // Generic English (when only one game version exist) { "gb", "en_GB", "English (GB)", EN_GRB }, { "us", "en_US", "English (US)", EN_USA }, + { "et", "et_EE", "Estonian", ET_EST }, { "fr", "fr_FR", "French", FR_FRA }, { "de", "de_DE", "German", DE_DEU }, { "gr", "el_GR", "Greek", GR_GRE }, diff --git a/common/language.h b/common/language.h index ac3c7267ab0..af2c252d61d 100644 --- a/common/language.h +++ b/common/language.h @@ -41,6 +41,7 @@ enum Language { EN_ANY, // Generic English (when only one game version exist) EN_GRB, EN_USA, + ET_EST, FR_FRA, DE_DEU, GR_GRE, diff --git a/common/macresman.h b/common/macresman.h index 05b2a875f42..1be825b7b46 100644 --- a/common/macresman.h +++ b/common/macresman.h @@ -176,6 +176,11 @@ public: */ MacResTagArray getResTagArray(); + /** + * Load from stream in MacBinary format + */ + bool loadFromMacBinary(SeekableReadStream &stream); + private: SeekableReadStream *_stream; String _baseFileName; @@ -183,7 +188,6 @@ private: bool load(SeekableReadStream &stream); bool loadFromRawFork(SeekableReadStream &stream); - bool loadFromMacBinary(SeekableReadStream &stream); bool loadFromAppleDouble(SeekableReadStream &stream); static String constructAppleDoubleName(String name); diff --git a/common/memstream.h b/common/memstream.h index 25fdde91c70..0338d35378a 100644 --- a/common/memstream.h +++ b/common/memstream.h @@ -212,13 +212,14 @@ public: /** * MemoryStream based on RingBuffer. Grows if has insufficient buffer size. */ -class MemoryReadWriteStream : public WriteStream { +class MemoryReadWriteStream : public SeekableReadStream, public WriteStream { private: uint32 _capacity; uint32 _size; byte *_data; uint32 _writePos, _readPos, _pos, _length; DisposeAfterUse::Flag _disposeMemory; + bool _eos; void ensureCapacity(uint32 new_len) { if (new_len <= _capacity) @@ -246,7 +247,7 @@ private: } } public: - MemoryReadWriteStream(DisposeAfterUse::Flag disposeMemory = DisposeAfterUse::NO) : _capacity(0), _size(0), _data(0), _writePos(0), _readPos(0), _pos(0), _length(0), _disposeMemory(disposeMemory) {} + MemoryReadWriteStream(DisposeAfterUse::Flag disposeMemory = DisposeAfterUse::NO) : _capacity(0), _size(0), _data(0), _writePos(0), _readPos(0), _pos(0), _length(0), _disposeMemory(disposeMemory), _eos(false) {} ~MemoryReadWriteStream() { if (_disposeMemory) @@ -271,8 +272,10 @@ public: } virtual uint32 read(void *dataPtr, uint32 dataSize) { - uint32 length = _length; - if (length < dataSize) dataSize = length; + if (_length < dataSize) { + dataSize = _length; + _eos = true; + } if (dataSize == 0 || _capacity == 0) return 0; if (_readPos + dataSize < _capacity) { memcpy(dataPtr, _data + _readPos, dataSize); @@ -287,7 +290,10 @@ public: } int32 pos() const { return _pos - _length; } //'read' position in the stream - uint32 size() const { return _size; } //that's also 'write' position in the stream, as it's append-only + int32 size() const { return _size; } //that's also 'write' position in the stream, as it's append-only + bool seek(int32, int) { return false; } + bool eos() const { return _eos; } + void clearErr() { _eos = false; } byte *getData() { return _data; } }; diff --git a/common/scummsys.h b/common/scummsys.h index 153db3c623e..785978764dd 100644 --- a/common/scummsys.h +++ b/common/scummsys.h @@ -132,6 +132,7 @@ #include #include #include + #include #include #include // MSVC does not define M_PI, M_SQRT2 and other math defines by default. @@ -152,6 +153,19 @@ #endif #endif +#ifndef STATIC_ASSERT + /** + * Generates a compile-time assertion. + * + * @param expression An expression that can be evaluated at compile time. + * @param message An underscore-delimited message to be presented at compile + * time if the expression evaluates to false. + */ + #define STATIC_ASSERT(expression, message) \ + extern int STATIC_ASSERT_##message[(expression) ? 1 : -1]; \ + (void)(STATIC_ASSERT_##message); +#endif + // The following math constants are usually defined by the system math.h header, but // they are not part of the ANSI C++ standards and so can NOT be relied upon to be // present i.e. when -std=c++11 is passed to GCC, enabling strict ANSI compliance. @@ -295,6 +309,25 @@ #endif #endif +// +// Determine 64 bitness +// Reference: http://nadeausoftware.com/articles/2012/02/c_c_tip_how_detect_processor_type_using_compiler_predefined_macros +// +#if !defined(HAVE_CONFIG_H) + + #if defined(__x86_64__) || \ + defined(_M_X64) || \ + defined(__ppc64__) || \ + defined(__powerpc64__) || \ + defined(__LP64__) + + #if !defined(SCUMM_64BITS) + #define SCUMM_64BITS + #endif + + #endif + +#endif // // Some more system specific settings. diff --git a/common/serializer.h b/common/serializer.h index ef8fca9906b..2def17cfbd2 100644 --- a/common/serializer.h +++ b/common/serializer.h @@ -144,6 +144,10 @@ public: */ Version getVersion() const { return _version; } + /** + * Manually set the version + */ + void setVersion(Version version) { _version = version; } /** * Return the total number of bytes synced so far. diff --git a/common/str.cpp b/common/str.cpp index 90bd5397908..3a0fd6a08e8 100644 --- a/common/str.cpp +++ b/common/str.cpp @@ -942,6 +942,13 @@ size_t strlcat(char *dst, const char *src, size_t size) { return dstLength + (src - srcStart); } +size_t strnlen(const char *src, size_t maxSize) { + size_t counter = 0; + while (counter != maxSize && *src++) + ++counter; + return counter; +} + } // End of namespace Common // Portable implementation of stricmp / strcasecmp / strcmpi. diff --git a/common/str.h b/common/str.h index d55ba072a9f..ba1e0b83419 100644 --- a/common/str.h +++ b/common/str.h @@ -444,6 +444,17 @@ size_t strlcpy(char *dst, const char *src, size_t size); */ size_t strlcat(char *dst, const char *src, size_t size); +/** + * Determine the length of a string up to a maximum of `maxSize` characters. + * This should be used instead of `strlen` when reading the length of a C string + * from potentially unsafe or corrupt sources, like game assets. + * + * @param src The source string. + * @param maxSize The maximum size of the string. + * @return The length of the string. + */ +size_t strnlen(const char *src, size_t maxSize); + /** * Convenience wrapper for tag2string which "returns" a C string. * Note: It is *NOT* safe to do anything with the return value other than directly diff --git a/common/stream.cpp b/common/stream.cpp index 08774312fdf..8d938880208 100644 --- a/common/stream.cpp +++ b/common/stream.cpp @@ -39,6 +39,27 @@ SeekableReadStream *ReadStream::readStream(uint32 dataSize) { return new MemoryReadStream((byte *)buf, dataSize, DisposeAfterUse::YES); } +Common::String ReadStream::readPascalString(bool transformCR) { + Common::String s; + char *buf; + int len; + int i; + + len = readByte(); + buf = (char *)malloc(len + 1); + for (i = 0; i < len; i++) { + buf[i] = readByte(); + if (transformCR && buf[i] == 0x0d) + buf[i] = '\n'; + } + + buf[i] = 0; + + s = buf; + free(buf); + + return s; +} uint32 MemoryReadStream::read(void *dataPtr, uint32 dataSize) { // Read at most as many bytes as are still available... diff --git a/common/stream.h b/common/stream.h index 30107720dc7..6932c7d2d0b 100644 --- a/common/stream.h +++ b/common/stream.h @@ -185,6 +185,32 @@ public: } #endif + + /** + * Write the given 32-bit floating point value stored + * in little endian(LSB first) order into the stream. + */ + FORCEINLINE void writeFloatLE(float value) { + uint32 n; + + memcpy(&n, &value, 4); + + writeUint32LE(n); + } + + + /** + * Write the given 32-bit floating point value stored + * in big endian order into the stream. + */ + FORCEINLINE void writeFloatBE(float value) { + uint32 n; + + memcpy(&n, &value, 4); + + writeUint32BE(n); + } + /** * Write the given string to the stream. * This writes str.size() characters, but no terminating zero byte. @@ -417,6 +443,22 @@ public: return f; } + /** + * Read a 32-bit floating point value stored in big endian + * order from the stream and return it. + * Performs no error checking. The return value is undefined + * if a read error occurred (for which client code can check by + * calling err() and eos() ). + */ + FORCEINLINE float readFloatBE() { + uint32 n = readUint32BE(); + float f; + + memcpy(&f, &n, 4); + + return f; + } + /** * Read the specified amount of data into a malloc'ed buffer * which then is wrapped into a MemoryReadStream. @@ -427,6 +469,14 @@ public: */ SeekableReadStream *readStream(uint32 dataSize); + /** + * Read stream in Pascal format, that is, one byte is + * string length, followed by string data + * + * @param transformCR if set (default), then transform \r into \n + */ + Common::String readPascalString(bool transformCR = true); + }; diff --git a/common/system.h b/common/system.h index e25feee3018..fb086a6ece8 100644 --- a/common/system.h +++ b/common/system.h @@ -256,7 +256,7 @@ public: * particular, interpolation, and works in-place. */ kFeatureAspectRatioCorrection, - + /** * If supported this flag can be used to switch between unfiltered and * filtered graphics modes. @@ -366,7 +366,38 @@ public: * * This feature has no associated state. */ - kFeatureOpenUrl + kFeatureOpenUrl , + + /** + * show on-screen control + */ + kFeatureOnScreenControl, + + /** + * mouse emulation mode + */ + kFeatureTouchpadMode, + + /** + * swap menu and back buttons + */ + kFeatureSwapMenuAndBackButtons, + + /** + * keyboard mouse and joystick mouse speed + */ + kFeatureKbdMouseSpeed, + + /** + * change analog joystick deadzone + */ + kFeatureJoystickDeadzone, + + /** + * shaders + */ + kFeatureShader + }; /** @@ -598,6 +629,38 @@ public: /** * !!! Not used in ResidualVM !!! * + * Retrieve a list of all hardware shaders supported by this backend. + * This can be only hardware shaders. + * it is completely up to the backend maintainer to decide what is + * appropriate here and what not. + * The list is terminated by an all-zero entry. + * @return a list of supported shaders + */ + virtual const GraphicsMode *getSupportedShaders() const { + static const OSystem::GraphicsMode no_shader[2] = {{"NONE", "Normal (no shader)", 0}, {0, 0, 0}}; + return no_shader; + } + + /** + * !!! Not used in ResidualVM !!! + * + * Switch to the specified shader mode. If switching to the new mode + * failed, this method returns false. + * + * @param mode the ID of the new shader mode + * @return true if the switch was successful, false otherwise + */ + virtual bool setShader(int id) { return false; } + + /** + * !!! Not used in ResidualVM !!! + * + * Determine which shader is currently active. + * @return the ID of the active shader + */ + virtual int getShader() const { return 0; } + + /** * Set the size and color format of the virtual screen. Typical sizes include: * - 320x200 (e.g. for most SCUMM games, and Simon) * - 320x240 (e.g. for FM-TOWN SCUMM games) diff --git a/common/zlib.h b/common/zlib.h index 5adba640761..c8877e98a12 100644 --- a/common/zlib.h +++ b/common/zlib.h @@ -111,6 +111,7 @@ bool inflateZlibInstallShield(byte *dst, uint dstLen, const byte *src, uint srcL * still need the length carried along with the stream, and you know * the decompressed length at wrap-time, then it can be supplied as knownSize * here. knownSize will be ignored if the GZip-stream DOES include a length. + * The created stream also becomes responsible for freeing the passed stream. * * It is safe to call this with a NULL parameter (in this case, NULL is * returned). @@ -125,6 +126,7 @@ SeekableReadStream *wrapCompressedReadStream(SeekableReadStream *toBeWrapped, ui * transparent on-the-fly compression. The compressed data is written in the * gzip format, unless ZLIB support has been disabled, in which case the given * stream is returned unmodified (and in particular, not wrapped). + * The created stream also becomes responsible for freeing the passed stream. * * It is safe to call this with a NULL parameter (in this case, NULL is * returned). diff --git a/configure b/configure index 164ad7a0794..bb4e08a470a 100755 --- a/configure +++ b/configure @@ -1557,6 +1557,22 @@ ps3) datadir='${datarootdir}' docdir='${prefix}/doc' ;; +psp2) + _host_os=psp2 + _host_cpu=arm + _host_alias=arm-vita-eabi + + # The prefix is always the same on PSP2 so we hardcode the default + # here. It is still possible to define a custom prefix which is + # needed when packaging the app with a user-specific app ID. + test "x$prefix" = xNONE && prefix=app0: + # PSP2 apps are installed into app-specific directories. The + # default directory structure of ScummVM makes no sense here so we + # hardcode PSP2 specific directories here. + datarootdir='${prefix}/data' + datadir='${datarootdir}' + docdir='${prefix}/doc' + ;; psp) _host_os=psp _host_cpu=mipsallegrexel @@ -1623,7 +1639,7 @@ fi # case $_host in -caanoo | gp2x | gp2xwiz | openpandora | ps2) +caanoo | gp2x | gp2xwiz | openpandora | ps2 | psp2) if test "$_debug_build" = auto; then # If you want to debug one of these platforms, use '--disable-optimizations --enable-debug' _debug_build=no @@ -1717,6 +1733,12 @@ ps3) exit 1 fi ;; +psp2) + if test -z "$VITASDK"; then + echo "Please set VITASDK in your environment. export VITASDK=" + exit 1 + fi + ;; psp) if test -z "$PSPDEV"; then PSPDEV=`psp-config --pspdev-path` @@ -1945,7 +1967,7 @@ if test "$have_gcc" = yes ; then case $_host_os in # newlib-based system include files suppress non-C89 function # declarations under __STRICT_ANSI__ - 3ds | amigaos* | android | androidsdl | dreamcast | ds | gamecube | mingw* | n64 | psp | ps2 | ps3 | tizen | wii | wince ) + 3ds | amigaos* | android | androidsdl | dreamcast | ds | gamecube | mingw* | n64 | psp | ps2 | ps3 | psp2 | tizen | wii | wince ) ;; *) append_var CXXFLAGS "-ansi" @@ -1998,13 +2020,30 @@ echocheck "whether -Wglobal-constructors work" cat > $TMPC << EOF int main() { return 0; } EOF -cc_check -Wglobal-constructors && _global_constructors=yes +cc_check -Wglobal-constructors -Werror && _global_constructors=yes if test "$_global_constructors" = yes; then append_var CXXFLAGS "-Wglobal-constructors" fi echo $_global_constructors +# If the compiler supports the -Wundefined-var-template flag, silence that warning. +# We get this warning a lot with regard to the Singleton class as we explicitly +# instantiate each specialisation. An alternate way to deal with it would be to +# change the way we instantiate the singleton classes as done in PR #967. +# Note: we check the -Wundefined-var-template as gcc does not error out on unknown +# -Wno-xxx flags. +echocheck "whether -Wno-undefined-var-template work" +cat > $TMPC << EOF +int main() { return 0; } +EOF +cc_check -Wundefined-var-template -Werror && _no_undefined_var_template=yes + +if test "$_no_undefined_var_template" = yes; then + append_var CXXFLAGS "-Wno-undefined-var-template" +fi +echo $_no_undefined_var_template + echo_n "Checking for $_host_alias-strings... " >> "$TMPLOG" if `which $_host_alias-strings >/dev/null 2>&1`; then _strings=$_host_alias-strings @@ -2165,7 +2204,7 @@ cc_check_clean tmp_find_type_with_size.cpp # for the smaller sizes. echo_n "Alignment required... " case $_host_cpu in - i[3-6]86 | amd64 | x86_64 | powerpc*) + i[3-6]86 | amd64 | x86_64 | powerpc* | ppc*) # Unaligned access should work _need_memalign=no ;; @@ -2198,6 +2237,9 @@ case $_host_cpu in ;; arm-apple-darwin11) ;; + # psvita does not like the asm code... + arm-vita-eabi) + ;; *) define_in_config_if_yes yes 'USE_ARM_SCALER_ASM' @@ -2226,7 +2268,7 @@ case $_host_cpu in echo "MIPS" append_var DEFINES "-DMIPS_TARGET" ;; - powerpc*) + powerpc* | ppc*) echo "PowerPC" append_var DEFINES "-DPPC_TARGET" ;; @@ -2294,7 +2336,6 @@ case $_host_os in append_var CXXFLAGS "-mfpu=vfp" append_var LDFLAGS "-Wl,--fix-cortex-a8" ABI="armeabi-v7a" - # Platform version 9 is used by ResidualVM ANDROID_PLATFORM=9 ANDROID_PLATFORM_ARCH="arm" ;; @@ -2601,6 +2642,7 @@ case $_host_os in append_var DEFINES "-DDISABLE_NES_APU" append_var DEFINES "-DDISABLE_SID" append_var DEFINES "-DREDUCE_MEMORY_USAGE" + add_line_to_config_mk 'N64 = 1' ;; ps2) append_var CXXFLAGS "-G2" @@ -2624,6 +2666,35 @@ case $_host_os in add_line_to_config_mk 'PLAYSTATION3 = 1' add_line_to_config_h "#define PREFIX \"${prefix}\"" ;; + psp2) + _freetypepath="$VITASDK/arm-vita-eabi/bin" + _freetype2=yes + _libcurlpath="$VITASDK/arm-vita-eabi/bin" + append_var CXXFLAGS "--sysroot=$VITASDK/arm-vita-eabi" + append_var LDFLAGS "--sysroot=$VITASDK/arm-vita-eabi" + append_var DEFINES "-DPSP2 -DSYSTEM_NOT_SUPPORTING_D_TYPE" + append_var CXXFLAGS "-Wl,-q -I$VITASDK/arm-vita-eabi/include" + append_var CXXFLAGS "-march=armv7-a -mtune=cortex-a9 -mfpu=neon -mfloat-abi=hard" + append_var LDFLAGS "-Wl,-q -L$VITASDK/arm-vita-eabi/lib" + #the next line fixes a crash when starting scumm-7-8 games (The Dig etc.) + #when either -O2, -O3 or -Os optimizations are enabled + append_var CXXFLAGS "-fno-optimize-sibling-calls" + #the next line fixes "branch out of range" error in gob engine when -Os is used + append_var CXXFLAGS "-mlong-calls" + if test "$_debug_build" = no; then + #optimize for smallest file size. This is necessary to prevent a crash on startup + #due to the large executable file size when many engines are enabled + #for example when --enable-all-engines is used to enable all the unstable engines + _optimization_level=-Os + fi + if test "$_debug_build" = yes; then + _optimization_level=-O0 + append_var DEFINES "-D__PSP2_DEBUG__" + append_var LIBS "-lpsp2shell" + fi + add_line_to_config_mk 'PSP2 = 1' + add_line_to_config_h "#define PREFIX \"${prefix}\"" + ;; psp) if test -d "$PSPDEV/psp/lib"; then append_var LDFLAGS "-L$PSPDEV/psp/lib" @@ -2634,6 +2705,7 @@ case $_host_os in append_var CXXFLAGS "-I$PSPSDK/include" # FIXME: Why is the following in CXXFLAGS and not in DEFINES? Change or document this. append_var CXXFLAGS "-D_PSP_FW_VERSION=150" + add_line_to_config_mk 'PSP = 1' ;; solaris*) append_var DEFINES "-DSOLARIS" @@ -2732,7 +2804,7 @@ if test -n "$_host"; then _mt32emu=no _timidity=no ;; - androidsdl | androidsdl-armeabi | androidsdl-armeabi-v7a | androidsdl-mips | androidsdl-x86 | androidsdl-arm64-v8a) + androidsdl | androidsdl-armeabi | androidsdl-armeabi-v7a | androidsdl-mips | androidsdl-x86 | androidsdl-arm64-v8a | androidsdl-x86_64) DEFINES="$DEFINES -DANDROIDSDL" _unix=yes _seq_midi=no @@ -3117,6 +3189,16 @@ if test -n "$_host"; then _eventrec=no _port_mk="backends/platform/sdl/ps3/ps3.mk" ;; + psp2) + _backend="psp2" + _vkeybd=yes + _build_scalers=yes + _build_hq_scalers=no + _mt32emu=no + _timidity=no + _eventrec=no + _port_mk="backends/platform/sdl/psp2/psp2.mk" + ;; psp) _backend="psp" _build_scalers=no @@ -3195,7 +3277,7 @@ case $_backend in append_var DEFINES "-DREDUCE_MEMORY_USAGE" append_var CXXFLAGS "-Wa,--noexecstack" append_var LDFLAGS "-Wl,-z,noexecstack" - append_var INCLUDES "-I$ANDROID_NDK/sources/cxx-stl/system/include" + append_var INCLUDES "-isystem $ANDROID_NDK/sources/cxx-stl/system/include" ;; androidsdl) ;; @@ -3290,6 +3372,17 @@ case $_backend in append_var LIBS "-lpng" append_var LIBS "-Wl,-Map,mapfile.txt" ;; + psp2) + append_var LIBS "-lvitashaders -lSDL2 -lvita2d_fbo -lSceCommonDialog_stub" + append_var LIBS "-lSceSysmodule_stub -lSceDisplay_stub -lSceGxm_stub" + append_var LIBS "-lSceAudio_stub -lSceCtrl_stub -lScePower_stub" + append_var LIBS "-lSceNet_stub -lSceNetCtl_stub -lSceAppMgr_stub -lScePgf_stub" + append_var DEFINES "-DSDL_BACKEND" + add_line_to_config_mk "SDL_BACKEND = 1" + add_line_to_config_mk "USE_SDL2 = 1" + append_var MODULES "backends/platform/sdl" + append_var INCLUDES "-I$VITASDK/arm-vita-eabi/include/SDL2" + ;; samsungtv) append_var DEFINES "-DSAMSUNGTV" append_var LDFLAGS "-shared" @@ -3400,7 +3493,7 @@ esac # Enable 16bit support only for backends which support it # case $_backend in - 3ds | android | androidsdl | dingux | dc | gph | iphone | ios7 | maemo | openpandora | psp | samsungtv | sdl | tizen | webos | wii) + 3ds | android | androidsdl | dingux | dc | gph | iphone | ios7 | maemo | openpandora | psp | psp2 | samsungtv | sdl | tizen | webos | wii) if test "$_16bit" = auto ; then _16bit=yes else @@ -3476,7 +3569,7 @@ esac # echo_n "Checking if host is POSIX compliant... " case $_host_os in - amigaos* | cygwin* | dreamcast | ds | gamecube | mingw* | n64 | ps2 | ps3 | psp | wii | wince) + amigaos* | cygwin* | dreamcast | ds | gamecube | mingw* | n64 | ps2 | ps3 | psp2 | psp | wii | wince) _posix=no ;; 3ds | android | androidsdl | beos* | bsd* | darwin* | freebsd* | gnu* | gph-linux | haiku* | hpux* | iphone | ios7 | irix*| k*bsd*-gnu* | linux* | maemo | mint* | netbsd* | openbsd* | solaris* | sunos* | uclinux* | webos) @@ -3647,7 +3740,7 @@ PRE_OBJS_FLAGS := -Wl,-export-dynamic -Wl,-whole-archive POST_OBJS_FLAGS := -Wl,-no-whole-archive ' ;; - *mingw32*) + *mingw32* | mingw64) _plugin_prefix="" _plugin_suffix=".dll" _mak_plugins=' @@ -4052,7 +4145,8 @@ EOF cc_check $ZLIB_CFLAGS $ZLIB_LIBS -lz && _zlib=yes fi if test "$_zlib" = yes ; then - append_var LIBS "$ZLIB_LIBS -lz" + append_var ZLIB_LIBS "-lz" + append_var LIBS "$ZLIB_LIBS" append_var INCLUDES "$ZLIB_CFLAGS" fi define_in_config_if_yes "$_zlib" 'USE_ZLIB' @@ -4185,6 +4279,9 @@ if test "$_libcurl" != "no"; then amigaos*) append_var LIBCURL_LIBS "-lpthread" ;; + psp2*) + append_var LIBCURL_LIBS "-lssl -lcrypto" + ;; esac if test "$_libcurl" = "auto"; then @@ -5310,6 +5407,8 @@ EXEPRE := $HOSTEXEPRE EXEEXT := $HOSTEXEEXT NASM := $NASM NASMFLAGS := $NASMFLAGS +ZLIB_LIBS := $ZLIB_LIBS +ZLIB_CFLAGS := $ZLIB_CFLAGS prefix = $prefix exec_prefix = $exec_prefix diff --git a/devtools/create_project/cmake.cpp b/devtools/create_project/cmake.cpp index 44fc95dd77c..d52e907a9ab 100644 --- a/devtools/create_project/cmake.cpp +++ b/devtools/create_project/cmake.cpp @@ -83,8 +83,8 @@ void CMakeProvider::createWorkspace(const BuildSetup &setup) { workspace << "# Generate options for the engines\n"; writeEngineOptions(workspace); - workspace << "include_directories(${" << setup.projectDescription << "_SOURCE_DIR} ${" << setup.projectDescription << "_SOURCE_DIR}/engines\n" - "$ENV{"<enable = false; } + + // Disable engines for which we are missing dependencies + for (EngineDescList::const_iterator i = setup.engines.begin(); i != setup.engines.end(); ++i) { + if (i->enable) { + for (StringList::const_iterator ef = i->requiredFeatures.begin(); ef != i->requiredFeatures.end(); ++ef) { + FeatureList::iterator feature = std::find(setup.features.begin(), setup.features.end(), *ef); + if (feature != setup.features.end() && !feature->enable) { + setEngineBuildState(i->name, setup.engines, false); + break; + } + } + } + } // Print status cout << "Enabled engines:\n\n"; @@ -566,7 +579,7 @@ int main(int argc, char *argv[]) { globalWarnings.push_back("6385"); globalWarnings.push_back("6386"); - if (msvcVersion == 14) { + if (msvcVersion == 14 || msvcVersion == 15) { globalWarnings.push_back("4267"); globalWarnings.push_back("4577"); } @@ -688,7 +701,8 @@ void displayHelp(const char *exe) { " 11 stands for \"Visual Studio 2012\"\n" " 12 stands for \"Visual Studio 2013\"\n" " 14 stands for \"Visual Studio 2015\"\n" - " The default is \"9\", thus \"Visual Studio 2008\"\n" + " 15 stands for \"Visual Studio 2017\"\n" + " The default is \"12\", thus \"Visual Studio 2013\"\n" " --build-events Run custom build events as part of the build\n" " (default: false)\n" " --installer Create NSIS installer after the build (implies --build-events)\n" @@ -906,7 +920,7 @@ namespace { */ bool parseEngine(const std::string &line, EngineDesc &engine) { // Format: - // add_engine engine_name "Readable Description" enable_default ["SubEngineList"] + // add_engine engine_name "Readable Description" enable_default ["SubEngineList"] ["base games"] ["dependencies"] TokenList tokens = tokenize(line); if (tokens.size() < 4) @@ -921,8 +935,14 @@ bool parseEngine(const std::string &line, EngineDesc &engine) { engine.name = *token; ++token; engine.desc = *token; ++token; engine.enable = (*token == "yes"); ++token; - if (token != tokens.end()) + if (token != tokens.end()) { engine.subEngines = tokenize(*token); + ++token; + if (token != tokens.end()) + ++token; + if (token != tokens.end()) + engine.requiredFeatures = tokenize(*token); + } return true; } @@ -1427,7 +1447,7 @@ void ProjectProvider::createProject(BuildSetup &setup) { in.push_back(setup.srcDir + "/COPYING.FREEFONT"); in.push_back(setup.srcDir + "/COPYRIGHT"); in.push_back(setup.srcDir + "/NEWS"); - in.push_back(setup.srcDir + "/README.md"); // ResidualVM change + in.push_back(setup.srcDir + "/README.md"); in.push_back(setup.srcDir + "/TODO"); // Create the main project file. @@ -1522,7 +1542,8 @@ void ProjectProvider::addFilesToProject(const std::string &dir, std::ofstream &p StringList duplicate; for (StringList::const_iterator i = includeList.begin(); i != includeList.end(); ++i) { - const std::string fileName = getLastPathComponent(*i); + std::string fileName = getLastPathComponent(*i); + std::transform(fileName.begin(), fileName.end(), fileName.begin(), tolower); // Leave out non object file names. if (fileName.size() < 2 || fileName.compare(fileName.size() - 2, 2, ".o")) @@ -1535,7 +1556,9 @@ void ProjectProvider::addFilesToProject(const std::string &dir, std::ofstream &p // Search for duplicates StringList::const_iterator j = i; ++j; for (; j != includeList.end(); ++j) { - if (fileName == getLastPathComponent(*j)) { + std::string candidateFileName = getLastPathComponent(*j); + std::transform(candidateFileName.begin(), candidateFileName.end(), candidateFileName.begin(), tolower); + if (fileName == candidateFileName) { duplicate.push_back(fileName); break; } @@ -1741,18 +1764,20 @@ void ProjectProvider::createModuleList(const std::string &moduleDir, const Strin if (std::find(defines.begin(), defines.end(), *i) == defines.end()) shouldInclude.push(false); else - shouldInclude.push(true); + shouldInclude.push(true && shouldInclude.top()); } else if (*i == "ifndef") { if (tokens.size() < 2) error("Malformed ifndef in " + moduleMkFile); ++i; if (std::find(defines.begin(), defines.end(), *i) == defines.end()) - shouldInclude.push(true); + shouldInclude.push(true && shouldInclude.top()); else shouldInclude.push(false); } else if (*i == "else") { - shouldInclude.top() = !shouldInclude.top(); + bool last = shouldInclude.top(); + shouldInclude.pop(); + shouldInclude.push(!last && shouldInclude.top()); } else if (*i == "endif") { if (shouldInclude.size() <= 1) error("endif without ifdef found in " + moduleMkFile); diff --git a/devtools/create_project/create_project.h b/devtools/create_project/create_project.h index bd0dcf6be9e..9f662ae8060 100644 --- a/devtools/create_project/create_project.h +++ b/devtools/create_project/create_project.h @@ -86,6 +86,11 @@ struct EngineDesc { * Whether the engine should be included in the build or not. */ bool enable; + + /** + * Features required for this engine. + */ + StringList requiredFeatures; /** * A list of all available sub engine names. Sub engines are engines diff --git a/devtools/create_project/msbuild.cpp b/devtools/create_project/msbuild.cpp index dfbc6ca45ce..052ab4f19f8 100644 --- a/devtools/create_project/msbuild.cpp +++ b/devtools/create_project/msbuild.cpp @@ -58,11 +58,17 @@ int MSBuildProvider::getVisualStudioVersion() { if (_version == 14) return 14; + if (_version == 15) + return 15; + error("Unsupported version passed to getVisualStudioVersion"); } int MSBuildProvider::getSolutionVersion() { - return (_version < 14) ? _version + 1 : _version; + if (_version == 14 || _version == 15) + return 14; + + return _version + 1; } namespace { @@ -123,7 +129,7 @@ void MSBuildProvider::createProjectFile(const std::string &name, const std::stri // Shared configuration project << "\t\n"; - std::string version = "v" + toString(_version) + "0"; + std::string version = _version == 15 ? "v141" : "v" + toString(_version) + "0"; std::string llvm = "LLVM-vs" + toString(getVisualStudioVersion()); outputConfigurationType(setup, project, name, "Release|Win32", version); @@ -514,7 +520,9 @@ void MSBuildProvider::writeFileListToProject(const FileNode &dir, std::ofstream if (!_compileFiles.empty()) { projectFile << "\t\n"; for (std::list::const_iterator entry = _compileFiles.begin(); entry != _compileFiles.end(); ++entry) { - const bool isDuplicate = (std::find(duplicate.begin(), duplicate.end(), (*entry).name + ".o") != duplicate.end()); + std::string fileName = (*entry).name + ".o"; + std::transform(fileName.begin(), fileName.end(), fileName.begin(), tolower); + const bool isDuplicate = (std::find(duplicate.begin(), duplicate.end(), fileName) != duplicate.end()); // Deal with duplicated file names if (isDuplicate) { diff --git a/devtools/create_project/msvc10/create_project.vcxproj b/devtools/create_project/msvc10/create_project.vcxproj index 700c4bb2833..0386c3e5ad8 100644 --- a/devtools/create_project/msvc10/create_project.vcxproj +++ b/devtools/create_project/msvc10/create_project.vcxproj @@ -50,7 +50,7 @@ Level4 EditAndContinue false - 4003;4512;4127 + 4003;4512;4127;4100 Rpcrt4.lib;%(AdditionalDependencies) diff --git a/devtools/create_project/msvc11/create_project.vcxproj b/devtools/create_project/msvc11/create_project.vcxproj index 09392a43e3a..9168163e3ea 100644 --- a/devtools/create_project/msvc11/create_project.vcxproj +++ b/devtools/create_project/msvc11/create_project.vcxproj @@ -53,7 +53,7 @@ Level4 EditAndContinue false - 4003;4512;4127 + 4003;4512;4127;4100 Rpcrt4.lib;%(AdditionalDependencies) diff --git a/devtools/create_project/msvc12/create_project.vcxproj b/devtools/create_project/msvc12/create_project.vcxproj index 3b38972e51e..0623b24a40d 100644 --- a/devtools/create_project/msvc12/create_project.vcxproj +++ b/devtools/create_project/msvc12/create_project.vcxproj @@ -52,7 +52,7 @@ Level4 EditAndContinue false - 4003;4512;4127 + 4003;4512;4127;4100 Rpcrt4.lib;%(AdditionalDependencies) diff --git a/devtools/create_project/msvc9/create_project.vcproj b/devtools/create_project/msvc9/create_project.vcproj index eaa72099cc4..f348226337a 100644 --- a/devtools/create_project/msvc9/create_project.vcproj +++ b/devtools/create_project/msvc9/create_project.vcproj @@ -45,7 +45,7 @@ RuntimeLibrary="3" WarningLevel="4" DebugInformationFormat="4" - DisableSpecificWarnings="4003;4512;4127" + DisableSpecificWarnings="4003;4512;4127;4100" /> name)) { std::string name, ext; splitFilename(node->name, name, ext); - const bool isDuplicate = (std::find(duplicate.begin(), duplicate.end(), name + ".o") != duplicate.end()); + name += ".o"; + std::transform(name.begin(), name.end(), name.begin(), tolower); + const bool isDuplicate = (std::find(duplicate.begin(), duplicate.end(), name) != duplicate.end()); if (ext == "asm") { std::string objFileName = "$(IntDir)\\"; diff --git a/devtools/create_project/xcode.cpp b/devtools/create_project/xcode.cpp index 21fc25643f0..d60934bcb8a 100644 --- a/devtools/create_project/xcode.cpp +++ b/devtools/create_project/xcode.cpp @@ -665,6 +665,8 @@ XcodeProvider::ValueList& XcodeProvider::getResourceFiles() const { files.push_back("gui/themes/scummclassic.zip"); files.push_back("gui/themes/scummmodern.zip"); files.push_back("gui/themes/translations.dat"); + files.push_back("dists/engine-data/access.dat"); + files.push_back("dists/engine-data/cryo.dat"); files.push_back("dists/engine-data/drascula.dat"); files.push_back("dists/engine-data/hugo.dat"); files.push_back("dists/engine-data/kyra.dat"); @@ -674,9 +676,11 @@ XcodeProvider::ValueList& XcodeProvider::getResourceFiles() const { files.push_back("dists/engine-data/queen.tbl"); files.push_back("dists/engine-data/sky.cpt"); files.push_back("dists/engine-data/teenagent.dat"); + files.push_back("dists/engine-data/titanic.dat"); files.push_back("dists/engine-data/tony.dat"); files.push_back("dists/engine-data/toon.dat"); files.push_back("dists/engine-data/wintermute.zip"); + files.push_back("dists/engine-data/macventure.dat"); files.push_back("dists/pred.dic"); files.push_back("icons/scummvm.icns"); } @@ -950,6 +954,7 @@ void XcodeProvider::setupBuildConfiguration(const BuildSetup &setup) { scummvmOSX_LdFlags.push_back("-lvorbis"); scummvmOSX_LdFlags.push_back("-lmad"); scummvmOSX_LdFlags.push_back("-lFLAC"); + scummvmOSX_LdFlags.push_back("-lcurl"); if (setup.useSDL2) { scummvmOSX_LdFlags.push_back("-lSDL2main"); scummvmOSX_LdFlags.push_back("-lSDL2"); diff --git a/devtools/create_translations/cp_parser.cpp b/devtools/create_translations/cp_parser.cpp index f5e4c9d6bb1..c7c7597837e 100644 --- a/devtools/create_translations/cp_parser.cpp +++ b/devtools/create_translations/cp_parser.cpp @@ -50,7 +50,7 @@ Codepage *parseCodepageMapping(const std::string &filename) { std::ifstream in(filename.c_str()); if (!in) { - fprintf(stderr, "ERROR: Could not open file \"%s\"!", filename.c_str()); + fprintf(stderr, "ERROR: Could not open file \"%s\"!\n", filename.c_str()); return 0; } @@ -63,7 +63,7 @@ Codepage *parseCodepageMapping(const std::string &filename) { if (in.eof()) break; if (in.fail()) { - fprintf(stderr, "ERROR: Reading from file \"%s\" failed!", filename.c_str()); + fprintf(stderr, "ERROR: Reading from file \"%s\" failed!\n", filename.c_str()); return 0; } @@ -76,7 +76,7 @@ Codepage *parseCodepageMapping(const std::string &filename) { int unicode, required; const int parsed = sscanf(line.c_str(), "%d %d", &unicode, &required); if (parsed < 1 || parsed > 2) { - fprintf(stderr, "ERROR: Line \"%s\" is malformed!", filename.c_str()); + fprintf(stderr, "ERROR: Line \"%s\" is malformed!\n", filename.c_str()); return 0; } // In case no required integer was given, we assume the character is @@ -97,7 +97,7 @@ Codepage *parseCodepageMapping(const std::string &filename) { // Check whether all character encodings are mapped, if not error out if (processedMappings != 256) { - fprintf(stderr, "ERROR: File \"%s\" does not contain mappings for exactly 256 characters!", filename.c_str()); + fprintf(stderr, "ERROR: File \"%s\" does not contain mappings for exactly 256 characters!\n", filename.c_str()); return 0; } diff --git a/devtools/encode-macbinary.sh b/devtools/encode-macbinary.sh index 6635f7fcda8..235dd860a44 100644 --- a/devtools/encode-macbinary.sh +++ b/devtools/encode-macbinary.sh @@ -5,7 +5,19 @@ for i in * do - macbinary encode "$i" - touch -r "$i" "$i.bin" - mv "$i.bin" "$i" + if test -d "$i" ; then + cd "$i" + bash $0 "$1/$i" + cd .. + else + echo -n $1/$i ; echo -ne "... \r" + macbinary encode "$i" + touch -r "$i" "$i.bin" + mv "$i.bin" "$i" + fi done + +# on the top level we want to print a new line +if test -z $1 ; then + echo +fi diff --git a/engines/advancedDetector.cpp b/engines/advancedDetector.cpp index 0c8a20d1d60..02eab48f29c 100644 --- a/engines/advancedDetector.cpp +++ b/engines/advancedDetector.cpp @@ -331,10 +331,10 @@ void AdvancedMetaEngine::reportUnknown(const Common::FSNode &path, const ADFileP // // Might also be helpful to display the full path (for when this is used // from the mass detector). - Common::String report = Common::String::format(_("The game in '%s' seems to be unknown."), path.getPath().c_str()) + "\n"; - report += _("Please, report the following data to the ResidualVM team along with name"); - report += "\n"; - report += _("of the game you tried to add and its version/language/etc.:"); + Common::String report = Common::String::format( + _("The game in '%s' seems to be unknown.\n" + "Please, report the following data to the ResidualVM team along with name\n" + "of the game you tried to add and its version, language, etc.:"), path.getPath().c_str()) + "\n"; report += "\n"; for (ADFilePropertiesMap::const_iterator file = filesProps.begin(); file != filesProps.end(); ++file) @@ -345,7 +345,7 @@ void AdvancedMetaEngine::reportUnknown(const Common::FSNode &path, const ADFileP g_system->logMessage(LogMessageType::kInfo, report.c_str()); } -void AdvancedMetaEngine::composeFileHashMap(FileMap &allFiles, const Common::FSList &fslist, int depth) const { +void AdvancedMetaEngine::composeFileHashMap(FileMap &allFiles, const Common::FSList &fslist, int depth, const Common::String &parentName) const { if (depth <= 0) return; @@ -353,6 +353,8 @@ void AdvancedMetaEngine::composeFileHashMap(FileMap &allFiles, const Common::FSL return; for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) { + Common::String tstr = (_matchFullPaths && !parentName.empty() ? parentName + "/" : "") + file->getName(); + if (file->isDirectory()) { Common::FSList files; @@ -372,11 +374,9 @@ void AdvancedMetaEngine::composeFileHashMap(FileMap &allFiles, const Common::FSL if (!file->getChildren(files, Common::FSNode::kListAll)) continue; - composeFileHashMap(allFiles, files, depth - 1); + composeFileHashMap(allFiles, files, depth - 1, tstr); } - Common::String tstr = file->getName(); - // Strip any trailing dot if (tstr.lastChar() == '.') tstr.deleteLastChar(); @@ -397,7 +397,9 @@ bool AdvancedMetaEngine::getFileProperties(const Common::FSNode &parent, const F fileProps.md5 = macResMan.computeResForkMD5AsString(_md5Bytes); fileProps.size = macResMan.getResForkDataSize(); - return true; + + if (fileProps.size != 0) + return true; } if (!allFiles.contains(fname)) @@ -623,6 +625,7 @@ AdvancedMetaEngine::AdvancedMetaEngine(const void *descs, uint descItemSize, con _guiOptions = GUIO_NONE; _maxScanDepth = 1; _directoryGlobs = NULL; + _matchFullPaths = false; } void AdvancedMetaEngine::initSubSystems(const ADGameDescription *gameDesc) const { diff --git a/engines/advancedDetector.h b/engines/advancedDetector.h index ab3ec22bdcb..e218c41b52d 100644 --- a/engines/advancedDetector.h +++ b/engines/advancedDetector.h @@ -251,6 +251,17 @@ protected: */ const char * const *_directoryGlobs; + /** + * If true, filenames will be matched against the entire path, relative to + * the root detection directory (e.g. "foo/bar.000" for a file at + * "/foo/bar.000"). Otherwise, filenames only match the basename + * (e.g. "bar.000" for the same file). + * + * @note _maxScanDepth and _directoryGlobs must still be configured to allow + * the detector to find files inside subdirectories. + */ + bool _matchFullPaths; + public: AdvancedMetaEngine(const void *descs, uint descItemSize, const PlainGameDescriptor *gameIds, const ADExtraGuiOptionsMap *extraGuiOptions = 0); @@ -326,7 +337,7 @@ protected: * Compose a hashmap of all files in fslist. * Includes nifty stuff like removing trailing dots and ignoring case. */ - void composeFileHashMap(FileMap &allFiles, const Common::FSList &fslist, int depth) const; + void composeFileHashMap(FileMap &allFiles, const Common::FSList &fslist, int depth, const Common::String &parentName = Common::String()) const; /** Get the properties (size and MD5) of this file. */ bool getFileProperties(const Common::FSNode &parent, const FileMap &allFiles, const ADGameDescription &game, const Common::String fname, ADFileProperties &fileProps) const; diff --git a/engines/dialogs.cpp b/engines/dialogs.cpp index bc0dd80931a..fab790fb374 100644 --- a/engines/dialogs.cpp +++ b/engines/dialogs.cpp @@ -85,11 +85,11 @@ MainMenuDialog::MainMenuDialog(Engine *engine) new GUI::ButtonWidget(this, "GlobalMenu.Resume", _("~R~esume"), 0, kPlayCmd, 'P'); _loadButton = new GUI::ButtonWidget(this, "GlobalMenu.Load", _("~L~oad"), 0, kLoadCmd); - // TODO: setEnabled -> setVisible + _loadButton->setVisible(_engine->hasFeature(Engine::kSupportsLoadingDuringRuntime)); _loadButton->setEnabled(_engine->hasFeature(Engine::kSupportsLoadingDuringRuntime)); _saveButton = new GUI::ButtonWidget(this, "GlobalMenu.Save", _("~S~ave"), 0, kSaveCmd); - // TODO: setEnabled -> setVisible + _saveButton->setVisible(_engine->hasFeature(Engine::kSupportsSavingDuringRuntime)); _saveButton->setEnabled(_engine->hasFeature(Engine::kSupportsSavingDuringRuntime)); new GUI::ButtonWidget(this, "GlobalMenu.Options", _("~O~ptions"), 0, kOptionsCmd); @@ -231,7 +231,7 @@ void MainMenuDialog::save() { Common::Error status = _engine->saveGameState(slot, result); if (status.getCode() != Common::kNoError) { - Common::String failMessage = Common::String::format(_("Gamestate save failed (%s)! " + Common::String failMessage = Common::String::format(_("Failed to save game (%s)! " "Please consult the README for basic information, and for " "instructions on how to obtain further assistance."), status.getDesc().c_str()); GUI::MessageDialog dialog(failMessage); diff --git a/engines/engine.cpp b/engines/engine.cpp index b88d724d8e4..834d1742155 100644 --- a/engines/engine.cpp +++ b/engines/engine.cpp @@ -540,7 +540,7 @@ void Engine::openMainMenuDialog() { if (_saveSlotToLoad >= 0) { Common::Error status = loadGameState(_saveSlotToLoad); if (status.getCode() != Common::kNoError) { - Common::String failMessage = Common::String::format(_("Gamestate load failed (%s)! " + Common::String failMessage = Common::String::format(_("Failed to load saved game (%s)! " "Please consult the README for basic information, and for " "instructions on how to obtain further assistance."), status.getDesc().c_str()); GUI::MessageDialog dialog(failMessage); diff --git a/engines/grim/movie/codecs/smush_decoder.cpp b/engines/grim/movie/codecs/smush_decoder.cpp index 49908807a5f..62769a4a1c5 100644 --- a/engines/grim/movie/codecs/smush_decoder.cpp +++ b/engines/grim/movie/codecs/smush_decoder.cpp @@ -184,7 +184,7 @@ bool SmushDecoder::readHeader() { _file->readUint32BE(); _file->readUint32BE(); - _audioTrack = new SmushAudioTrack(false, audioRate, 2); + _audioTrack = new SmushAudioTrack(getSoundType(), false, audioRate, 2); addTrack(_audioTrack); return true; @@ -245,7 +245,7 @@ bool SmushDecoder::handleFramesHeader() { } while (pos < size); delete[] f_header; - _audioTrack = new SmushAudioTrack(true, freq, channels); + _audioTrack = new SmushAudioTrack(getSoundType(), true, freq, channels); addTrack(_audioTrack); return true; } @@ -592,7 +592,8 @@ Graphics::Surface *SmushDecoder::SmushVideoTrack::decodeNextFrame() { void SmushDecoder::SmushVideoTrack::setMsPerFrame(int ms) { _frameRate = Common::Rational(1000000, ms); } -SmushDecoder::SmushAudioTrack::SmushAudioTrack(bool isVima, int freq, int channels) { +SmushDecoder::SmushAudioTrack::SmushAudioTrack(Audio::Mixer::SoundType soundType, bool isVima, int freq, int channels) : + AudioTrack(soundType) { _isVima = isVima; _channels = channels; _freq = freq; diff --git a/engines/grim/movie/codecs/smush_decoder.h b/engines/grim/movie/codecs/smush_decoder.h index 8f1619c571d..a69658a51c3 100644 --- a/engines/grim/movie/codecs/smush_decoder.h +++ b/engines/grim/movie/codecs/smush_decoder.h @@ -108,7 +108,7 @@ protected: class SmushAudioTrack : public AudioTrack { public: - SmushAudioTrack(bool isVima, int freq = 22050, int channels = -1); + SmushAudioTrack(Audio::Mixer::SoundType soundType, bool isVima, int freq = 22050, int channels = -1); ~SmushAudioTrack(); Audio::AudioStream *getAudioStream() const override { return _queueStream; } diff --git a/engines/grim/stuffit.cpp b/engines/grim/stuffit.cpp index c26dabfe2c4..cb99c92fcb0 100644 --- a/engines/grim/stuffit.cpp +++ b/engines/grim/stuffit.cpp @@ -25,7 +25,6 @@ #include "engines/grim/stuffit.h" -#include "common/bitstream.h" #include "common/debug.h" #include "common/memstream.h" #include "common/substream.h" @@ -258,7 +257,7 @@ struct SIT14Data { if (b->pos() & 7) \ b->skip(8 - (b->pos() & 7)) -void StuffItArchive::readTree14(Common::BitStream *bits, SIT14Data *dat, uint16 codesize, uint16 *result) const { +void StuffItArchive::readTree14(Common::BitStream8LSB *bits, SIT14Data *dat, uint16 codesize, uint16 *result) const { uint32 i, l, n; uint32 k = bits->getBit(); uint32 j = bits->getBits(2) + 2; @@ -386,7 +385,7 @@ Common::SeekableReadStream *StuffItArchive::decompress14(Common::SeekableReadStr byte *dst = new byte[uncompressedSize]; Common::MemoryWriteStream out(dst, uncompressedSize); - Common::BitStream *bits = new Common::BitStream8LSB(src); + Common::BitStream8LSB *bits = new Common::BitStream8LSB(src); uint32 i, j, k, l, m, n; diff --git a/engines/grim/stuffit.h b/engines/grim/stuffit.h index dc9a7d81f4b..d46cf3a6e6d 100644 --- a/engines/grim/stuffit.h +++ b/engines/grim/stuffit.h @@ -24,6 +24,7 @@ #define GRIM_STUFFIT_H #include "common/archive.h" +#include "common/bitstream.h" #include "common/scummsys.h" #include "common/endian.h" #include "common/file.h" @@ -31,10 +32,6 @@ #include "common/hashmap.h" #include "common/str.h" -namespace Common { -class BitStream; -} - namespace Grim { struct SIT14Data; @@ -72,7 +69,7 @@ private: // Decompression Helpers void update14(uint16 first, uint16 last, byte *code, uint16 *freq) const; - void readTree14(Common::BitStream *bits, SIT14Data *dat, uint16 codesize, uint16 *result) const; + void readTree14(Common::BitStream8LSB *bits, SIT14Data *dat, uint16 codesize, uint16 *result) const; }; } // End of namespace Grim diff --git a/engines/logo_data.h b/engines/logo_data.h index f8a5cdbbb77..384c2130d08 100644 --- a/engines/logo_data.h +++ b/engines/logo_data.h @@ -1,4 +1,4 @@ -/* ResidualVM - Graphic Adventure Engine +/* ResidualVM - A 3D game interpreter * * ResidualVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/engines/savestate.cpp b/engines/savestate.cpp index 7366aa6a619..20a37581dc8 100644 --- a/engines/savestate.cpp +++ b/engines/savestate.cpp @@ -43,7 +43,7 @@ void SaveStateDescriptor::setThumbnail(Graphics::Surface *t) { } void SaveStateDescriptor::setSaveDate(int year, int month, int day) { - _saveDate = Common::String::format("%.2d.%.2d.%.4d", day, month, year); + _saveDate = Common::String::format("%.4d-%.2d-%.2d", year, month, day); } void SaveStateDescriptor::setSaveTime(int hour, int min) { diff --git a/graphics/VectorRendererSpec.cpp b/graphics/VectorRendererSpec.cpp index 3dd9c86f455..ef7d672fd08 100644 --- a/graphics/VectorRendererSpec.cpp +++ b/graphics/VectorRendererSpec.cpp @@ -449,13 +449,13 @@ void colorFill(PixelType *first, PixelType *last, PixelType color) { register int n = (count + 7) >> 3; switch (count % 8) { case 0: do { - *first++ = color; - case 7: *first++ = color; - case 6: *first++ = color; - case 5: *first++ = color; - case 4: *first++ = color; - case 3: *first++ = color; - case 2: *first++ = color; + *first++ = color; // fall through + case 7: *first++ = color; // fall through + case 6: *first++ = color; // fall through + case 5: *first++ = color; // fall through + case 4: *first++ = color; // fall through + case 3: *first++ = color; // fall through + case 2: *first++ = color; // fall through case 1: *first++ = color; } while (--n > 0); } @@ -488,13 +488,13 @@ void colorFillClip(PixelType *first, PixelType *last, PixelType color, int realX register int n = (count + 7) >> 3; switch (count % 8) { case 0: do { - *first++ = color; - case 7: *first++ = color; - case 6: *first++ = color; - case 5: *first++ = color; - case 4: *first++ = color; - case 3: *first++ = color; - case 2: *first++ = color; + *first++ = color; // fall through + case 7: *first++ = color; // fall through + case 6: *first++ = color; // fall through + case 5: *first++ = color; // fall through + case 4: *first++ = color; // fall through + case 3: *first++ = color; // fall through + case 2: *first++ = color; // fall through case 1: *first++ = color; } while (--n > 0); } diff --git a/graphics/font.cpp b/graphics/font.cpp index 7768b7362d1..3446619299f 100644 --- a/graphics/font.cpp +++ b/graphics/font.cpp @@ -142,11 +142,11 @@ struct WordWrapper { }; template -int wordWrapTextImpl(const Font &font, const StringType &str, int maxWidth, Common::Array &lines) { +int wordWrapTextImpl(const Font &font, const StringType &str, int maxWidth, Common::Array &lines, int initWidth) { WordWrapper wrapper(lines); StringType line; StringType tmpStr; - int lineWidth = 0; + int lineWidth = initWidth; int tmpWidth = 0; // The rough idea behind this algorithm is as follows: @@ -305,12 +305,12 @@ void Font::drawString(ManagedSurface *dst, const Common::U32String &str, int x, } } -int Font::wordWrapText(const Common::String &str, int maxWidth, Common::Array &lines) const { - return wordWrapTextImpl(*this, str, maxWidth, lines); +int Font::wordWrapText(const Common::String &str, int maxWidth, Common::Array &lines, int initWidth) const { + return wordWrapTextImpl(*this, str, maxWidth, lines, initWidth); } -int Font::wordWrapText(const Common::U32String &str, int maxWidth, Common::Array &lines) const { - return wordWrapTextImpl(*this, str, maxWidth, lines); +int Font::wordWrapText(const Common::U32String &str, int maxWidth, Common::Array &lines, int initWidth) const { + return wordWrapTextImpl(*this, str, maxWidth, lines, initWidth); } Common::String Font::handleEllipsis(const Common::String &input, int w) const { diff --git a/graphics/font.h b/graphics/font.h index 0478608708e..6682ce015b5 100644 --- a/graphics/font.h +++ b/graphics/font.h @@ -169,13 +169,14 @@ public: * It returns the maximal width of any of the new lines (i.e. a value which is less * or equal to maxWidth). * - * @param str the string to word wrap - * @param maxWidth the maximum width a line may have - * @param lines the string list to which the text lines from str are appended + * @param str the string to word wrap + * @param maxWidth the maximum width a line may have + * @param lines the string list to which the text lines from str are appended + * @param initWidth the starting width of the first line, for partially filled lines (optional) * @return the maximal width of any of the lines added to lines */ - int wordWrapText(const Common::String &str, int maxWidth, Common::Array &lines) const; - int wordWrapText(const Common::U32String &str, int maxWidth, Common::Array &lines) const; + int wordWrapText(const Common::String &str, int maxWidth, Common::Array &lines, int initWidth = 0) const; + int wordWrapText(const Common::U32String &str, int maxWidth, Common::Array &lines, int initWidth = 0) const; private: Common::String handleEllipsis(const Common::String &str, int w) const; diff --git a/graphics/managed_surface.cpp b/graphics/managed_surface.cpp index 0b9fa111819..60b2941efa5 100644 --- a/graphics/managed_surface.cpp +++ b/graphics/managed_surface.cpp @@ -166,15 +166,60 @@ void ManagedSurface::blitFrom(const Surface &src, const Common::Rect &srcRect, Common::Rect srcBounds = srcRect; Common::Rect destBounds(destPos.x, destPos.y, destPos.x + srcRect.width(), destPos.y + srcRect.height()); - assert(src.format.bytesPerPixel == format.bytesPerPixel); + uint destPixel; + byte rSrc, gSrc, bSrc, aSrc; + byte rDest, gDest, bDest; + double alpha; if (!srcRect.isValidRect() || !clip(srcBounds, destBounds)) return; + if (format != src.format) { + // When the pixel format differs, both source an dest must be + // 2 or 4 bytes per pixel + assert(format.bytesPerPixel == 2 || format.bytesPerPixel == 4); + assert(src.format.bytesPerPixel == 2 || src.format.bytesPerPixel == 4); + } + for (int y = 0; y < srcBounds.height(); ++y) { const byte *srcP = (const byte *)src.getBasePtr(srcBounds.left, srcBounds.top + y); byte *destP = (byte *)getBasePtr(destBounds.left, destBounds.top + y); - Common::copy(srcP, srcP + srcBounds.width() * format.bytesPerPixel, destP); + + if (src.format == format) { + // Matching surface formats, so we can do a straight copy + Common::copy(srcP, srcP + srcBounds.width() * format.bytesPerPixel, destP); + } else { + for (int x = 0; x < srcBounds.width(); ++x, + srcP += src.format.bytesPerPixel, + destP += format.bytesPerPixel) { + src.format.colorToARGB(src.format.bytesPerPixel == 2 ? *(const uint16 *)srcP : *(const uint32 *)srcP, + aSrc, rSrc, gSrc, bSrc); + format.colorToRGB(format.bytesPerPixel == 2 ? *(const uint16 *)destP : *(const uint32 *)destP, + rDest, gDest, bDest); + + if (aSrc == 0) { + // Completely transparent, so skip + continue; + } else if (aSrc == 0xff) { + // Completely opaque, so copy RGB values over + rDest = rSrc; + gDest = gSrc; + bDest = bSrc; + } else { + // Partially transparent, so calculate new pixel colors + alpha = (double)aSrc / 255.0; + rDest = static_cast((rSrc * alpha) + (rDest * (1.0 - alpha))); + gDest = static_cast((gSrc * alpha) + (gDest * (1.0 - alpha))); + bDest = static_cast((bSrc * alpha) + (bDest * (1.0 - alpha))); + } + + destPixel = format.ARGBToColor(0xff, rDest, gDest, bDest); + if (format.bytesPerPixel == 2) + *(uint16 *)destP = destPixel; + else + *(uint32 *)destP = destPixel; + } + } } addDirtyRect(Common::Rect(0, 0, this->w, this->h)); @@ -197,49 +242,85 @@ void ManagedSurface::transBlitFrom(const Surface &src, const Common::Rect &srcRe destPos.x + src.w, destPos.y + src.h), transColor, false, overrideColor); } -template -void transBlit(const Surface &src, const Common::Rect &srcRect, Surface *dest, const Common::Rect &destRect, uint transColor, bool flipped, uint overrideColor) { +template +void transBlit(const Surface &src, const Common::Rect &srcRect, Surface &dest, const Common::Rect &destRect, TSRC transColor, bool flipped, uint overrideColor) { int scaleX = SCALE_THRESHOLD * srcRect.width() / destRect.width(); int scaleY = SCALE_THRESHOLD * srcRect.height() / destRect.height(); + const Graphics::PixelFormat &srcFormat = src.format; + const Graphics::PixelFormat &destFormat = dest.format; + byte aSrc, rSrc, gSrc, bSrc; + byte rDest, gDest, bDest; + double alpha; // Loop through drawing output lines for (int destY = destRect.top, scaleYCtr = 0; destY < destRect.bottom; ++destY, scaleYCtr += scaleY) { - if (destY < 0 || destY >= dest->h) + if (destY < 0 || destY >= dest.h) continue; - const T *srcLine = (const T *)src.getBasePtr(0, scaleYCtr / SCALE_THRESHOLD); - T *destLine = (T *)dest->getBasePtr(destRect.left, destY); + const TSRC *srcLine = (const TSRC *)src.getBasePtr(srcRect.left, scaleYCtr / SCALE_THRESHOLD + srcRect.top); + TDEST *destLine = (TDEST *)dest.getBasePtr(destRect.left, destY); // Loop through drawing the pixels of the row for (int destX = destRect.left, xCtr = 0, scaleXCtr = 0; destX < destRect.right; ++destX, ++xCtr, scaleXCtr += scaleX) { - if (destX < 0 || destX >= dest->w) + if (destX < 0 || destX >= dest.w) continue; - T srcVal = srcLine[flipped ? src.w - scaleXCtr / SCALE_THRESHOLD - 1 : scaleXCtr / SCALE_THRESHOLD]; - if (srcVal != transColor) { + TSRC srcVal = srcLine[flipped ? src.w - scaleXCtr / SCALE_THRESHOLD - 1 : scaleXCtr / SCALE_THRESHOLD]; + if (srcVal == transColor) + continue; + + if (srcFormat == destFormat) { + // Matching formats, so we can do a straight copy destLine[xCtr] = overrideColor ? overrideColor : srcVal; + } else { + // Otherwise we have to manually decode and re-encode each pixel + srcFormat.colorToARGB(*srcLine, aSrc, rSrc, gSrc, bSrc); + destFormat.colorToRGB(destLine[xCtr], rDest, gDest, bDest); + + if (aSrc == 0) { + // Completely transparent, so skip + continue; + } else if (aSrc == 0xff) { + // Completely opaque, so copy RGB values over + rDest = rSrc; + gDest = gSrc; + bDest = bSrc; + } else { + // Partially transparent, so calculate new pixel colors + alpha = (double)aSrc / 255.0; + rDest = static_cast((rSrc * alpha) + (rDest * (1.0 - alpha))); + gDest = static_cast((gSrc * alpha) + (gDest * (1.0 - alpha))); + bDest = static_cast((bSrc * alpha) + (bDest * (1.0 - alpha))); + } + + destLine[xCtr] = destFormat.ARGBToColor(0xff, rDest, gDest, bDest); } } } } +#define HANDLE_BLIT(SRC_BYTES, DEST_BYTES, SRC_TYPE, DEST_TYPE) \ + if (src.format.bytesPerPixel == SRC_BYTES && format.bytesPerPixel == DEST_BYTES) \ + transBlit(src, srcRect, _innerSurface, destRect, transColor, flipped, overrideColor); \ + else + void ManagedSurface::transBlitFrom(const Surface &src, const Common::Rect &srcRect, const Common::Rect &destRect, uint transColor, bool flipped, uint overrideColor) { if (src.w == 0 || src.h == 0 || destRect.width() == 0 || destRect.height() == 0) return; - if (format.bytesPerPixel == 1) - transBlit(src, srcRect, &_innerSurface, destRect, transColor, flipped, overrideColor); - else if (format.bytesPerPixel == 2) - transBlit(src, srcRect, &_innerSurface, destRect, transColor, flipped, overrideColor); - else if (format.bytesPerPixel == 4) - transBlit(src, srcRect, &_innerSurface, destRect, transColor, flipped, overrideColor); - else + HANDLE_BLIT(1, 1, byte, byte) + HANDLE_BLIT(2, 2, uint16, uint16) + HANDLE_BLIT(4, 4, uint32, uint32) + HANDLE_BLIT(2, 4, uint16, uint32) + HANDLE_BLIT(4, 2, uint32, uint16) error("Surface::transBlitFrom: bytesPerPixel must be 1, 2, or 4"); // Mark the affected area addDirtyRect(destRect); } +#undef HANDLE_BLIT + void ManagedSurface::markAllDirty() { addDirtyRect(Common::Rect(0, 0, this->w, this->h)); } diff --git a/graphics/pixelformat.cpp b/graphics/pixelformat.cpp index 0a464112549..3134600c526 100644 --- a/graphics/pixelformat.cpp +++ b/graphics/pixelformat.cpp @@ -58,7 +58,7 @@ Common::String PixelFormat::toString() const { digits += '0' + 8 - componentLoss; } - return letters + digits; + return letters + digits + '@' + ('0' + bytesPerPixel); } } // End of namespace Graphics diff --git a/graphics/transform_struct.h b/graphics/transform_struct.h index 1d37492de40..c935f314201 100644 --- a/graphics/transform_struct.h +++ b/graphics/transform_struct.h @@ -32,6 +32,7 @@ enum TSpriteBlendMode { BLEND_NORMAL = 0, BLEND_ADDITIVE = 1, BLEND_SUBTRACTIVE = 2, + BLEND_MULTIPLY = 3, NUM_BLEND_MODES }; diff --git a/graphics/transparent_surface.cpp b/graphics/transparent_surface.cpp index 7ef7d002863..10f69b52816 100644 --- a/graphics/transparent_surface.cpp +++ b/graphics/transparent_surface.cpp @@ -37,8 +37,6 @@ #include "graphics/transparent_surface.h" #include "graphics/transform_tools.h" -//#define ENABLE_BILINEAR - namespace Graphics { static const int kBModShift = 0;//img->format.bShift; @@ -64,6 +62,7 @@ void doBlitBinaryFast(byte *ino, byte *outo, uint32 width, uint32 height, uint32 void doBlitAlphaBlend(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep, uint32 color); void doBlitAdditiveBlend(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep, uint32 color); void doBlitSubtractiveBlend(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep, uint32 color); +void doBlitMultiplyBlend(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep, uint32 color); TransparentSurface::TransparentSurface() : Surface(), _alphaMode(ALPHA_FULL) {} @@ -328,6 +327,72 @@ void doBlitSubtractiveBlend(byte *ino, byte *outo, uint32 width, uint32 height, } } +/** + * Optimized version of doBlit to be used with multiply blended blitting + */ +void doBlitMultiplyBlend(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep, uint32 color) { + byte *in; + byte *out; + + if (color == 0xffffffff) { + for (uint32 i = 0; i < height; i++) { + out = outo; + in = ino; + for (uint32 j = 0; j < width; j++) { + + if (in[kAIndex] != 0) { + out[kRIndex] = MIN((in[kRIndex] * in[kAIndex] >> 8) * out[kRIndex] >> 8, 255); + out[kGIndex] = MIN((in[kGIndex] * in[kAIndex] >> 8) * out[kGIndex] >> 8, 255); + out[kBIndex] = MIN((in[kBIndex] * in[kAIndex] >> 8) * out[kBIndex] >> 8, 255); + } + + in += inStep; + out += 4; + } + outo += pitch; + ino += inoStep; + } + } else { + byte ca = (color >> kAModShift) & 0xFF; + byte cr = (color >> kRModShift) & 0xFF; + byte cg = (color >> kGModShift) & 0xFF; + byte cb = (color >> kBModShift) & 0xFF; + + for (uint32 i = 0; i < height; i++) { + out = outo; + in = ino; + for (uint32 j = 0; j < width; j++) { + + uint32 ina = in[kAIndex] * ca >> 8; + + if (cb != 255) { + out[kBIndex] = MIN(out[kBIndex] * ((in[kBIndex] * cb * ina) >> 16) >> 8, 255u); + } else { + out[kBIndex] = MIN(out[kBIndex] * (in[kBIndex] * ina >> 8) >> 8, 255u); + } + + if (cg != 255) { + out[kGIndex] = MIN(out[kGIndex] * ((in[kGIndex] * cg * ina) >> 16) >> 8, 255u); + } else { + out[kGIndex] = MIN(out[kGIndex] * (in[kGIndex] * ina >> 8) >> 8, 255u); + } + + if (cr != 255) { + out[kRIndex] = MIN(out[kRIndex] * ((in[kRIndex] * cr * ina) >> 16) >> 8, 255u); + } else { + out[kRIndex] = MIN(out[kRIndex] * (in[kRIndex] * ina >> 8) >> 8, 255u); + } + + in += inStep; + out += 4; + } + outo += pitch; + ino += inoStep; + } + } + +} + Common::Rect TransparentSurface::blit(Graphics::Surface &target, int posX, int posY, int flipping, Common::Rect *pPartRect, uint color, int width, int height, TSpriteBlendMode blendMode) { Common::Rect retSize; @@ -402,19 +467,31 @@ Common::Rect TransparentSurface::blit(Graphics::Surface &target, int posX, int p // Handle off-screen clipping if (posY < 0) { img->h = MAX(0, (int)img->h - -posY); - img->setPixels((byte *)img->getBasePtr(0, -posY)); + if (!(flipping & FLIP_V)) + img->setPixels((byte *)img->getBasePtr(0, -posY)); posY = 0; } if (posX < 0) { img->w = MAX(0, (int)img->w - -posX); - img->setPixels((byte *)img->getBasePtr(-posX, 0)); + if (!(flipping & FLIP_H)) + img->setPixels((byte *)img->getBasePtr(-posX, 0)); posX = 0; } - img->w = CLIP((int)img->w, 0, (int)MAX((int)target.w - posX, 0)); - img->h = CLIP((int)img->h, 0, (int)MAX((int)target.h - posY, 0)); + if (img->w > target.w - posX) { + if (flipping & FLIP_H) + img->setPixels((byte *)img->getBasePtr(img->w - target.w + posX, 0)); + img->w = CLIP((int)img->w, 0, (int)MAX((int)target.w - posX, 0)); + } + if (img->h > target.h - posY) { + if (flipping & FLIP_V) + img->setPixels((byte *)img->getBasePtr(0, img->h - target.h + posY)); + img->h = CLIP((int)img->h, 0, (int)MAX((int)target.h - posY, 0)); + } + + // Flip surface if ((img->w > 0) && (img->h > 0)) { int xp = 0, yp = 0; @@ -442,6 +519,8 @@ Common::Rect TransparentSurface::blit(Graphics::Surface &target, int posX, int p doBlitAdditiveBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color); } else if (blendMode == BLEND_SUBTRACTIVE) { doBlitSubtractiveBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color); + } else if (blendMode == BLEND_MULTIPLY) { + doBlitMultiplyBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color); } else { assert(blendMode == BLEND_NORMAL); doBlitAlphaBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color); @@ -535,19 +614,31 @@ Common::Rect TransparentSurface::blitClip(Graphics::Surface &target, Common::Rec // Handle off-screen clipping if (posY < clippingArea.top) { img->h = MAX(0, (int)img->h - (clippingArea.top - posY)); - img->setPixels((byte *)img->getBasePtr(0, clippingArea.top - posY)); + if (!(flipping & FLIP_V)) + img->setPixels((byte *)img->getBasePtr(0, clippingArea.top - posY)); posY = clippingArea.top; } if (posX < clippingArea.left) { img->w = MAX(0, (int)img->w - (clippingArea.left - posX)); - img->setPixels((byte *)img->getBasePtr(clippingArea.left - posX, 0)); + if (!(flipping & FLIP_H)) + img->setPixels((byte *)img->getBasePtr(clippingArea.left - posX, 0)); posX = clippingArea.left; } - img->w = CLIP((int)img->w, 0, (int)MAX((int)clippingArea.right - posX, 0)); - img->h = CLIP((int)img->h, 0, (int)MAX((int)clippingArea.bottom - posY, 0)); + if (img->w > clippingArea.right - posX) { + if (flipping & FLIP_H) + img->setPixels((byte *)img->getBasePtr(img->w - clippingArea.right + posX, 0)); + img->w = CLIP((int)img->w, 0, (int)MAX((int)clippingArea.right - posX, 0)); + } + if (img->h > clippingArea.bottom - posY) { + if (flipping & FLIP_V) + img->setPixels((byte *)img->getBasePtr(0, img->h - clippingArea.bottom + posY)); + img->h = CLIP((int)img->h, 0, (int)MAX((int)clippingArea.bottom - posY, 0)); + } + + // Flip surface if ((img->w > 0) && (img->h > 0)) { int xp = 0, yp = 0; @@ -575,6 +666,8 @@ Common::Rect TransparentSurface::blitClip(Graphics::Surface &target, Common::Rec doBlitAdditiveBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color); } else if (blendMode == BLEND_SUBTRACTIVE) { doBlitSubtractiveBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color); + } else if (blendMode == BLEND_MULTIPLY) { + doBlitMultiplyBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color); } else { assert(blendMode == BLEND_NORMAL); doBlitAlphaBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color); @@ -676,8 +769,10 @@ systems. +struct tColorRGBA { byte r; byte g; byte b; byte a; }; -TransparentSurface *TransparentSurface::rotoscale(const TransformStruct &transform) const { +template +TransparentSurface *TransparentSurface::rotoscaleT(const TransformStruct &transform) const { assert(transform._angle != 0); // This would not be ideal; rotoscale() should never be called in conditional branches where angle = 0 anyway. @@ -704,7 +799,6 @@ TransparentSurface *TransparentSurface::rotoscale(const TransformStruct &transfo float invCos = cos(invAngle * M_PI / 180.0); float invSin = sin(invAngle * M_PI / 180.0); - struct tColorRGBA { byte r; byte g; byte b; byte a; }; int icosx = (int)(invCos * (65536.0f * kDefaultZoomX / transform._zoom.x)); int isinx = (int)(invSin * (65536.0f * kDefaultZoomX / transform._zoom.x)); int icosy = (int)(invCos * (65536.0f * kDefaultZoomY / transform._zoom.y)); @@ -739,50 +833,50 @@ TransparentSurface *TransparentSurface::rotoscale(const TransformStruct &transfo dy = sh - dy; } -#ifdef ENABLE_BILINEAR - if ((dx > -1) && (dy > -1) && (dx < sw) && (dy < sh)) { - const tColorRGBA *sp = (const tColorRGBA *)getBasePtr(dx, dy); - tColorRGBA c00, c01, c10, c11, cswap; - c00 = *sp; - sp += 1; - c01 = *sp; - sp += (this->pitch / 4); - c11 = *sp; - sp -= 1; - c10 = *sp; - if (flipx) { - cswap = c00; c00=c01; c01=cswap; - cswap = c10; c10=c11; c11=cswap; + if (filteringMode == FILTER_BILINEAR) { + if ((dx > -1) && (dy > -1) && (dx < sw) && (dy < sh)) { + const tColorRGBA *sp = (const tColorRGBA *)getBasePtr(dx, dy); + tColorRGBA c00, c01, c10, c11, cswap; + c00 = *sp; + sp += 1; + c01 = *sp; + sp += (this->pitch / 4); + c11 = *sp; + sp -= 1; + c10 = *sp; + if (flipx) { + cswap = c00; c00=c01; c01=cswap; + cswap = c10; c10=c11; c11=cswap; + } + if (flipy) { + cswap = c00; c00=c10; c10=cswap; + cswap = c01; c01=c11; c11=cswap; + } + /* + * Interpolate colors + */ + int ex = (sdx & 0xffff); + int ey = (sdy & 0xffff); + int t1, t2; + t1 = ((((c01.r - c00.r) * ex) >> 16) + c00.r) & 0xff; + t2 = ((((c11.r - c10.r) * ex) >> 16) + c10.r) & 0xff; + pc->r = (((t2 - t1) * ey) >> 16) + t1; + t1 = ((((c01.g - c00.g) * ex) >> 16) + c00.g) & 0xff; + t2 = ((((c11.g - c10.g) * ex) >> 16) + c10.g) & 0xff; + pc->g = (((t2 - t1) * ey) >> 16) + t1; + t1 = ((((c01.b - c00.b) * ex) >> 16) + c00.b) & 0xff; + t2 = ((((c11.b - c10.b) * ex) >> 16) + c10.b) & 0xff; + pc->b = (((t2 - t1) * ey) >> 16) + t1; + t1 = ((((c01.a - c00.a) * ex) >> 16) + c00.a) & 0xff; + t2 = ((((c11.a - c10.a) * ex) >> 16) + c10.a) & 0xff; + pc->a = (((t2 - t1) * ey) >> 16) + t1; } - if (flipy) { - cswap = c00; c00=c10; c10=cswap; - cswap = c01; c01=c11; c11=cswap; + } else { + if ((dx >= 0) && (dy >= 0) && (dx < srcW) && (dy < srcH)) { + const tColorRGBA *sp = (const tColorRGBA *)getBasePtr(dx, dy); + *pc = *sp; } - /* - * Interpolate colors - */ - int ex = (sdx & 0xffff); - int ey = (sdy & 0xffff); - int t1, t2; - t1 = ((((c01.r - c00.r) * ex) >> 16) + c00.r) & 0xff; - t2 = ((((c11.r - c10.r) * ex) >> 16) + c10.r) & 0xff; - pc->r = (((t2 - t1) * ey) >> 16) + t1; - t1 = ((((c01.g - c00.g) * ex) >> 16) + c00.g) & 0xff; - t2 = ((((c11.g - c10.g) * ex) >> 16) + c10.g) & 0xff; - pc->g = (((t2 - t1) * ey) >> 16) + t1; - t1 = ((((c01.b - c00.b) * ex) >> 16) + c00.b) & 0xff; - t2 = ((((c11.b - c10.b) * ex) >> 16) + c10.b) & 0xff; - pc->b = (((t2 - t1) * ey) >> 16) + t1; - t1 = ((((c01.a - c00.a) * ex) >> 16) + c00.a) & 0xff; - t2 = ((((c11.a - c10.a) * ex) >> 16) + c10.a) & 0xff; - pc->a = (((t2 - t1) * ey) >> 16) + t1; } -#else - if ((dx >= 0) && (dy >= 0) && (dx < srcW) && (dy < srcH)) { - const tColorRGBA *sp = (const tColorRGBA *)getBasePtr(dx, dy); - *pc = *sp; - } -#endif sdx += icosx; sdy += isiny; pc++; @@ -791,192 +885,189 @@ TransparentSurface *TransparentSurface::rotoscale(const TransformStruct &transfo return target; } -TransparentSurface *TransparentSurface::scale(uint16 newWidth, uint16 newHeight) const { - - Common::Rect srcRect(0, 0, (int16)w, (int16)h); - Common::Rect dstRect(0, 0, (int16)newWidth, (int16)newHeight); +template +TransparentSurface *TransparentSurface::scaleT(uint16 newWidth, uint16 newHeight) const { TransparentSurface *target = new TransparentSurface(); - assert(format.bytesPerPixel == 4); + int srcW = w; + int srcH = h; + int dstW = newWidth; + int dstH = newHeight; - int srcW = srcRect.width(); - int srcH = srcRect.height(); - int dstW = dstRect.width(); - int dstH = dstRect.height(); + target->create((uint16)dstW, (uint16)dstH, format); - target->create((uint16)dstW, (uint16)dstH, this->format); + if (filteringMode == FILTER_BILINEAR) { + assert(format.bytesPerPixel == 4); -#ifdef ENABLE_BILINEAR - - // NB: The actual order of these bytes may not be correct, but - // since all values are treated equal, that does not matter. - struct tColorRGBA { byte r; byte g; byte b; byte a; }; - - bool flipx = false, flipy = false; // TODO: See mirroring comment in RenderTicket ctor + bool flipx = false, flipy = false; // TODO: See mirroring comment in RenderTicket ctor - int *sax = new int[dstW + 1]; - int *say = new int[dstH + 1]; - assert(sax && say); + int *sax = new int[dstW + 1]; + int *say = new int[dstH + 1]; + assert(sax && say); - /* - * Precalculate row increments - */ - int spixelw = (srcW - 1); - int spixelh = (srcH - 1); - int sx = (int) (65536.0f * (float) spixelw / (float) (dstW - 1)); - int sy = (int) (65536.0f * (float) spixelh / (float) (dstH - 1)); - - /* Maximum scaled source size */ - int ssx = (srcW << 16) - 1; - int ssy = (srcH << 16) - 1; - - /* Precalculate horizontal row increments */ - int csx = 0; - int *csax = sax; - for (int x = 0; x <= dstW; x++) { - *csax = csx; - csax++; - csx += sx; - - /* Guard from overflows */ - if (csx > ssx) { - csx = ssx; - } - } - - /* Precalculate vertical row increments */ - int csy = 0; - int *csay = say; - for (int y = 0; y <= dstH; y++) { - *csay = csy; - csay++; - csy += sy; - - /* Guard from overflows */ - if (csy > ssy) { - csy = ssy; - } - } - - const tColorRGBA *sp = (const tColorRGBA *) getBasePtr(0, 0); - tColorRGBA *dp = (tColorRGBA *) target->getBasePtr(0, 0); - int spixelgap = srcW; - - if (flipx) { - sp += spixelw; - } - if (flipy) { - sp += spixelgap * spixelh; - } - - csay = say; - for (int y = 0; y < dstH; y++) { - const tColorRGBA *csp = sp; - csax = sax; - for (int x = 0; x < dstW; x++) { - /* - * Setup color source pointers - */ - int ex = (*csax & 0xffff); - int ey = (*csay & 0xffff); - int cx = (*csax >> 16); - int cy = (*csay >> 16); - - const tColorRGBA *c00, *c01, *c10, *c11; - c00 = sp; - c01 = sp; - c10 = sp; - if (cy < spixelh) { - if (flipy) { - c10 -= spixelgap; - } else { - c10 += spixelgap; - } - } - c11 = c10; - if (cx < spixelw) { - if (flipx) { - c01--; - c11--; - } else { - c01++; - c11++; - } - } - - /* - * Draw and interpolate colors - */ - int t1, t2; - t1 = ((((c01->r - c00->r) * ex) >> 16) + c00->r) & 0xff; - t2 = ((((c11->r - c10->r) * ex) >> 16) + c10->r) & 0xff; - dp->r = (((t2 - t1) * ey) >> 16) + t1; - t1 = ((((c01->g - c00->g) * ex) >> 16) + c00->g) & 0xff; - t2 = ((((c11->g - c10->g) * ex) >> 16) + c10->g) & 0xff; - dp->g = (((t2 - t1) * ey) >> 16) + t1; - t1 = ((((c01->b - c00->b) * ex) >> 16) + c00->b) & 0xff; - t2 = ((((c11->b - c10->b) * ex) >> 16) + c10->b) & 0xff; - dp->b = (((t2 - t1) * ey) >> 16) + t1; - t1 = ((((c01->a - c00->a) * ex) >> 16) + c00->a) & 0xff; - t2 = ((((c11->a - c10->a) * ex) >> 16) + c10->a) & 0xff; - dp->a = (((t2 - t1) * ey) >> 16) + t1; - - /* - * Advance source pointer x - */ - int *salastx = csax; - csax++; - int sstepx = (*csax >> 16) - (*salastx >> 16); - if (flipx) { - sp -= sstepx; - } else { - sp += sstepx; - } - - /* - * Advance destination pointer x - */ - dp++; - } /* - * Advance source pointer y + * Precalculate row increments */ - int *salasty = csay; - csay++; - int sstepy = (*csay >> 16) - (*salasty >> 16); - sstepy *= spixelgap; + int spixelw = (srcW - 1); + int spixelh = (srcH - 1); + int sx = (int) (65536.0f * (float) spixelw / (float) (dstW - 1)); + int sy = (int) (65536.0f * (float) spixelh / (float) (dstH - 1)); + + /* Maximum scaled source size */ + int ssx = (srcW << 16) - 1; + int ssy = (srcH << 16) - 1; + + /* Precalculate horizontal row increments */ + int csx = 0; + int *csax = sax; + for (int x = 0; x <= dstW; x++) { + *csax = csx; + csax++; + csx += sx; + + /* Guard from overflows */ + if (csx > ssx) { + csx = ssx; + } + } + + /* Precalculate vertical row increments */ + int csy = 0; + int *csay = say; + for (int y = 0; y <= dstH; y++) { + *csay = csy; + csay++; + csy += sy; + + /* Guard from overflows */ + if (csy > ssy) { + csy = ssy; + } + } + + const tColorRGBA *sp = (const tColorRGBA *) getBasePtr(0, 0); + tColorRGBA *dp = (tColorRGBA *) target->getBasePtr(0, 0); + int spixelgap = srcW; + + if (flipx) { + sp += spixelw; + } if (flipy) { - sp = csp - sstepy; - } else { - sp = csp + sstepy; + sp += spixelgap * spixelh; } - } - delete[] sax; - delete[] say; + csay = say; + for (int y = 0; y < dstH; y++) { + const tColorRGBA *csp = sp; + csax = sax; + for (int x = 0; x < dstW; x++) { + /* + * Setup color source pointers + */ + int ex = (*csax & 0xffff); + int ey = (*csay & 0xffff); + int cx = (*csax >> 16); + int cy = (*csay >> 16); -#else + const tColorRGBA *c00, *c01, *c10, *c11; + c00 = sp; + c01 = sp; + c10 = sp; + if (cy < spixelh) { + if (flipy) { + c10 -= spixelgap; + } else { + c10 += spixelgap; + } + } + c11 = c10; + if (cx < spixelw) { + if (flipx) { + c01--; + c11--; + } else { + c01++; + c11++; + } + } - int *scaleCacheX = new int[dstW]; - for (int x = 0; x < dstW; x++) { - scaleCacheX[x] = (x * srcW) / dstW; - } + /* + * Draw and interpolate colors + */ + int t1, t2; + t1 = ((((c01->r - c00->r) * ex) >> 16) + c00->r) & 0xff; + t2 = ((((c11->r - c10->r) * ex) >> 16) + c10->r) & 0xff; + dp->r = (((t2 - t1) * ey) >> 16) + t1; + t1 = ((((c01->g - c00->g) * ex) >> 16) + c00->g) & 0xff; + t2 = ((((c11->g - c10->g) * ex) >> 16) + c10->g) & 0xff; + dp->g = (((t2 - t1) * ey) >> 16) + t1; + t1 = ((((c01->b - c00->b) * ex) >> 16) + c00->b) & 0xff; + t2 = ((((c11->b - c10->b) * ex) >> 16) + c10->b) & 0xff; + dp->b = (((t2 - t1) * ey) >> 16) + t1; + t1 = ((((c01->a - c00->a) * ex) >> 16) + c00->a) & 0xff; + t2 = ((((c11->a - c10->a) * ex) >> 16) + c10->a) & 0xff; + dp->a = (((t2 - t1) * ey) >> 16) + t1; - for (int y = 0; y < dstH; y++) { - uint32 *destP = (uint32 *)target->getBasePtr(0, y); - const uint32 *srcP = (const uint32 *)getBasePtr(0, (y * srcH) / dstH); + /* + * Advance source pointer x + */ + int *salastx = csax; + csax++; + int sstepx = (*csax >> 16) - (*salastx >> 16); + if (flipx) { + sp -= sstepx; + } else { + sp += sstepx; + } + + /* + * Advance destination pointer x + */ + dp++; + } + /* + * Advance source pointer y + */ + int *salasty = csay; + csay++; + int sstepy = (*csay >> 16) - (*salasty >> 16); + sstepy *= spixelgap; + if (flipy) { + sp = csp - sstepy; + } else { + sp = csp + sstepy; + } + } + + delete[] sax; + delete[] say; + + } else { + int *scaleCacheX = new int[dstW]; for (int x = 0; x < dstW; x++) { - *destP++ = srcP[scaleCacheX[x]]; + scaleCacheX[x] = (x * srcW) / dstW; } - } - delete[] scaleCacheX; -#endif + switch (format.bytesPerPixel) { + case 1: + scaleNN(scaleCacheX, target); + break; + case 2: + scaleNN(scaleCacheX, target); + break; + case 4: + scaleNN(scaleCacheX, target); + break; + default: + error("Can only scale 8bpp, 16bpp, and 32bpp"); + } + + delete[] scaleCacheX; + } return target; - } TransparentSurface *TransparentSurface::convertTo(const PixelFormat &dstFormat, const byte *palette) const { @@ -1057,4 +1148,32 @@ TransparentSurface *TransparentSurface::convertTo(const PixelFormat &dstFormat, return surface; } +template +void TransparentSurface::scaleNN(int *scaleCacheX, TransparentSurface *target) const { + for (int y = 0; y < target->h; y++) { + Size *destP = (Size *)target->getBasePtr(0, y); + const Size *srcP = (const Size *)getBasePtr(0, (y * h) / target->h); + for (int x = 0; x < target->w; x++) { + *destP++ = srcP[scaleCacheX[x]]; + } + } +} + +template TransparentSurface *TransparentSurface::rotoscaleT(const TransformStruct &transform) const; +template TransparentSurface *TransparentSurface::rotoscaleT(const TransformStruct &transform) const; +template TransparentSurface *TransparentSurface::scaleT(uint16 newWidth, uint16 newHeight) const; +template TransparentSurface *TransparentSurface::scaleT(uint16 newWidth, uint16 newHeight) const; + +template void TransparentSurface::scaleNN(int *scaleCacheX, TransparentSurface *target) const; +template void TransparentSurface::scaleNN(int *scaleCacheX, TransparentSurface *target) const; +template void TransparentSurface::scaleNN(int *scaleCacheX, TransparentSurface *target) const; + +TransparentSurface *TransparentSurface::rotoscale(const TransformStruct &transform) const { + return rotoscaleT(transform); +} + +TransparentSurface *TransparentSurface::scale(uint16 newWidth, uint16 newHeight) const { + return scaleT(newWidth, newHeight); +} + } // End of namespace Graphics diff --git a/graphics/transparent_surface.h b/graphics/transparent_surface.h index 86541835483..22fe04551ea 100644 --- a/graphics/transparent_surface.h +++ b/graphics/transparent_surface.h @@ -68,6 +68,11 @@ enum AlphaType { ALPHA_FULL = 2 }; +enum TFilteringMode { + FILTER_NEAREST = 0, + FILTER_BILINEAR = 1 +}; + /** * A transparent graphics surface, which implements alpha blitting. */ @@ -141,6 +146,9 @@ struct TransparentSurface : public Graphics::Surface { * @param newHeight the resulting height. * @see TransformStruct */ + template + TransparentSurface *scaleT(uint16 newWidth, uint16 newHeight) const; + TransparentSurface *scale(uint16 newWidth, uint16 newHeight) const; /** @@ -150,6 +158,9 @@ struct TransparentSurface : public Graphics::Surface { * @param transform a TransformStruct wrapping the required info. @see TransformStruct * */ + template + TransparentSurface *rotoscaleT(const TransformStruct &transform) const; + TransparentSurface *rotoscale(const TransformStruct &transform) const; TransparentSurface *convertTo(const PixelFormat &dstFormat, const byte *palette = 0) const; @@ -166,6 +177,8 @@ struct TransparentSurface : public Graphics::Surface { private: AlphaType _alphaMode; + template + void scaleNN(int *scaleCacheX, TransparentSurface *target) const; }; /** diff --git a/gui/KeysDialog.cpp b/gui/KeysDialog.cpp index 7adb20a3796..fcf9201877e 100644 --- a/gui/KeysDialog.cpp +++ b/gui/KeysDialog.cpp @@ -68,7 +68,7 @@ void KeysDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) { case kListSelectionChangedCmd: if (_actionsList->getSelected() >= 0) { - char selection[100]; + Common::String selection; uint16 key = Actions::Instance()->getMapping(_actionsList->getSelected()); #ifdef __SYMBIAN32__ @@ -77,9 +77,9 @@ void KeysDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) { key = key - Common::ASCII_F1 + SDLK_F1; #endif if (key != 0) - sprintf(selection, _("Associated key : %s"), SDL_GetKeyName((SDLKey)key)); + selection = Common::String::format(_("Associated key : %s"), SDL_GetKeyName((SDLKey)key)); else - sprintf(selection, _("Associated key : none")); + selection = Common::String::format(_("Associated key : none")); _keyMapping->setLabel(selection); _keyMapping->draw(); @@ -89,7 +89,7 @@ void KeysDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) { if (_actionsList->getSelected() < 0) { _actionTitle->setLabel(_("Please select an action")); } else { - char selection[100]; + Common::String selection; _actionSelected = _actionsList->getSelected(); uint16 key = Actions::Instance()->getMapping(_actionSelected); @@ -99,9 +99,9 @@ void KeysDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) { key = key - Common::ASCII_F1 + SDLK_F1; #endif if (key != 0) - sprintf(selection, _("Associated key : %s"), SDL_GetKeyName((SDLKey)key)); + selection = Common::String::format(_("Associated key : %s"), SDL_GetKeyName((SDLKey)key)); else - sprintf(selection, _("Associated key : none")); + selection = Common::String::format(_("Associated key : none")); _actionTitle->setLabel(_("Press the key to associate")); _keyMapping->setLabel(selection); @@ -133,14 +133,14 @@ void KeysDialog::handleKeyUp(Common::KeyState state) { #else if (state.flags == 0xff && Actions::Instance()->mappingActive()) { // GAPI key was selected #endif - char selection[100]; + Common::String selection; Actions::Instance()->setMapping((ActionType)_actionSelected, state.ascii); if (state.ascii != 0) - sprintf(selection, _("Associated key : %s"), SDL_GetKeyName((SDLKey) state.keycode)); + selection = Common::String::format(_("Associated key : %s"), SDL_GetKeyName((SDLKey) state.keycode)); else - sprintf(selection, _("Associated key : none")); + selection = Common::String::format(_("Associated key : none")); _actionTitle->setLabel(_("Choose an action to map")); _keyMapping->setLabel(selection); diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp index 8763b3c4529..1a6d8e71a56 100644 --- a/gui/ThemeEngine.cpp +++ b/gui/ThemeEngine.cpp @@ -87,6 +87,7 @@ struct WidgetDrawData { E.g. when taking into account rounded corners, drop shadows, etc Used when restoring the widget background */ uint16 _backgroundOffset; + uint16 _shadowOffset; bool _buffer; @@ -271,6 +272,10 @@ void ThemeItemDrawData::drawSelf(bool draw, bool restore) { Common::Rect extendedRect = _area; extendedRect.grow(_engine->kDirtyRectangleThreshold + _data->_backgroundOffset); + if (_data->_shadowOffset > _data->_backgroundOffset) { + extendedRect.right += _data->_shadowOffset - _data->_backgroundOffset; + extendedRect.bottom += _data->_shadowOffset - _data->_backgroundOffset; + } if (restore) _engine->restoreBackground(extendedRect); @@ -288,6 +293,10 @@ void ThemeItemDrawDataClip::drawSelf(bool draw, bool restore) { Common::Rect extendedRect = _area; extendedRect.grow(_engine->kDirtyRectangleThreshold + _data->_backgroundOffset); + if (_data->_shadowOffset > _data->_backgroundOffset) { + extendedRect.right += _data->_shadowOffset - _data->_backgroundOffset; + extendedRect.bottom += _data->_shadowOffset - _data->_backgroundOffset; + } if (restore) _engine->restoreBackground(extendedRect); @@ -621,6 +630,7 @@ void ThemeEngine::setGraphicsMode(GraphicsMode mode) { _bytesPerPixel = sizeof(uint16); break; } + // fall through default: error("Invalid graphics mode"); } @@ -646,17 +656,18 @@ void ThemeEngine::setGraphicsMode(GraphicsMode mode) { } void WidgetDrawData::calcBackgroundOffset() { - uint maxShadow = 0; + uint maxShadow = 0, maxBevel = 0; for (Common::List::const_iterator step = _steps.begin(); step != _steps.end(); ++step) { if ((step->autoWidth || step->autoHeight) && step->shadow > maxShadow) maxShadow = step->shadow; - if (step->drawingCall == &Graphics::VectorRenderer::drawCallback_BEVELSQ && step->bevel > maxShadow) - maxShadow = step->bevel; + if (step->drawingCall == &Graphics::VectorRenderer::drawCallback_BEVELSQ && step->bevel > maxBevel) + maxBevel = step->bevel; } - _backgroundOffset = maxShadow; + _backgroundOffset = maxBevel; + _shadowOffset = maxShadow; } void ThemeEngine::restoreBackground(Common::Rect r) { @@ -722,7 +733,9 @@ bool ThemeEngine::addFont(TextData textId, const Common::String &file, const Com #ifdef USE_TRANSLATION TransMan.setLanguage("C"); #endif - warning("Failed to load localized font '%s'. Using non-localized font and default GUI language instead", localized.c_str()); + warning("Failed to load localized font '%s'.", localized.c_str()); + + return false; } } @@ -810,7 +823,9 @@ bool ThemeEngine::addAlphaBitmap(const Common::String &filename) { if (surf) return true; +#ifdef USE_PNG const Graphics::TransparentSurface *srcSurface = 0; +#endif if (filename.hasSuffix(".png")) { // Maybe it is PNG? @@ -1306,7 +1321,7 @@ void ThemeEngine::drawRadiobuttonClip(const Common::Rect &r, const Common::Rect queueDDClip(dd, r2, clippingRect); r2.left = r2.right + checkBoxSize; - r2.right = r.right; + r2.right = MAX(r2.left, r.right); queueDDTextClip(getTextData(dd), getTextColor(dd), r2, clippingRect, str, true, false, _widgets[kDDRadiobuttonDefault]->_textAlignH, _widgets[dd]->_textAlignV); } @@ -1517,7 +1532,7 @@ void ThemeEngine::drawPopUpWidgetClip(const Common::Rect &r, const Common::Rect queueDDClip(dd, r, clip); - if (!sel.empty()) { + if (!sel.empty() && r.width() >= 13 && r.height() >= 1) { Common::Rect text(r.left + 3, r.top + 1, r.right - 10, r.bottom); queueDDTextClip(getTextData(dd), getTextColor(dd), text, clip, sel, true, false, _widgets[dd]->_textAlignH, _widgets[dd]->_textAlignV, deltax); } @@ -1618,28 +1633,34 @@ void ThemeEngine::drawTab(const Common::Rect &r, int tabHeight, int tabWidth, co } } -void ThemeEngine::drawTabClip(const Common::Rect &r, const Common::Rect &clip, int tabHeight, int tabWidth, const Common::Array &tabs, int active, uint16 hints, int titleVPad, WidgetStateInfo state) { +void ThemeEngine::drawTabClip(const Common::Rect &r, const Common::Rect &clip, int tabHeight, const Common::Array &tabWidths, const Common::Array &tabs, int active, uint16 hints, int titleVPad, WidgetStateInfo state) { if (!ready()) return; + assert(tabs.size() == tabWidths.size()); + queueDDClip(kDDTabBackground, Common::Rect(r.left, r.top, r.right, r.top + tabHeight), clip); - for (int i = 0; i < (int)tabs.size(); ++i) { - if (i == active) + int width = 0; + int activePos = -1; + for (int i = 0; i < (int)tabs.size(); width += tabWidths[i++]) { + if (r.left + width > r.right || r.left + width + tabWidths[i] > r.right) continue; - if (r.left + i * tabWidth > r.right || r.left + (i + 1) * tabWidth > r.right) + if (i == active) { + activePos = width; continue; + } - Common::Rect tabRect(r.left + i * tabWidth, r.top, r.left + (i + 1) * tabWidth, r.top + tabHeight); + + Common::Rect tabRect(r.left + width, r.top, r.left + width + tabWidths[i], r.top + tabHeight); queueDDClip(kDDTabInactive, tabRect, clip); queueDDTextClip(getTextData(kDDTabInactive), getTextColor(kDDTabInactive), tabRect, clip, tabs[i], false, false, _widgets[kDDTabInactive]->_textAlignH, _widgets[kDDTabInactive]->_textAlignV); } - if (active >= 0 && - (r.left + active * tabWidth < r.right) && (r.left + (active + 1) * tabWidth < r.right)) { - Common::Rect tabRect(r.left + active * tabWidth, r.top, r.left + (active + 1) * tabWidth, r.top + tabHeight); - const uint16 tabLeft = active * tabWidth; + if (activePos >= 0) { + Common::Rect tabRect(r.left + activePos, r.top, r.left + activePos + tabWidths[active], r.top + tabHeight); + const uint16 tabLeft = activePos; const uint16 tabRight = MAX(r.right - tabRect.right, 0); queueDDClip(kDDTabActive, tabRect, clip, (tabLeft << 16) | (tabRight & 0xFFFF)); queueDDTextClip(getTextData(kDDTabActive), getTextColor(kDDTabActive), tabRect, clip, tabs[active], false, false, _widgets[kDDTabActive]->_textAlignH, _widgets[kDDTabActive]->_textAlignV); @@ -2412,19 +2433,32 @@ Common::String ThemeEngine::getThemeId(const Common::String &filename) { return "builtin"; Common::FSNode node(filename); - if (!node.exists()) - return "builtin"; + if (node.exists()) { + if (node.getName().matchString("*.zip", true)) { + Common::String id = node.getName(); - if (node.getName().matchString("*.zip", true)) { - Common::String id = node.getName(); + for (int i = 0; i < 4; ++i) + id.deleteLastChar(); - for (int i = 0; i < 4; ++i) - id.deleteLastChar(); - - return id; - } else { - return node.getName(); + return id; + } else { + return node.getName(); + } } + + // FIXME: + // A very ugly hack to map a id to a filename, this will generate + // a complete theme list, thus it is slower than it could be. + // But it is the easiest solution for now. + Common::List list; + listUsableThemes(list); + + for (Common::List::const_iterator i = list.begin(); i != list.end(); ++i) { + if (filename.equalsIgnoreCase(i->filename)) + return i->id; + } + + return "builtin"; } void ThemeEngine::showCursor() { diff --git a/gui/ThemeEngine.h b/gui/ThemeEngine.h index 8a6db17dfd2..9d8ed417380 100644 --- a/gui/ThemeEngine.h +++ b/gui/ThemeEngine.h @@ -388,7 +388,7 @@ public: void drawTab(const Common::Rect &r, int tabHeight, int tabWidth, const Common::Array &tabs, int active, uint16 hints, int titleVPad, WidgetStateInfo state = kStateEnabled); - void drawTabClip(const Common::Rect &r, const Common::Rect &clippingRect, int tabHeight, int tabWidth, + void drawTabClip(const Common::Rect &r, const Common::Rect &clippingRect, int tabHeight, const Common::Array &tabWidths, const Common::Array &tabs, int active, uint16 hints, int titleVPad, WidgetStateInfo state = kStateEnabled); diff --git a/gui/browser.h b/gui/browser.h index b77907bad28..10663f547e9 100644 --- a/gui/browser.h +++ b/gui/browser.h @@ -52,7 +52,6 @@ protected: #ifdef MACOSX const void *_titleRef; const void *_chooseRef; - const void *_hiddenFilesRef; #else ListWidget *_fileList; StaticTextWidget *_currentPath; diff --git a/gui/browser_osx.mm b/gui/browser_osx.mm index cfb70a3f650..ffb64ee96a5 100644 --- a/gui/browser_osx.mm +++ b/gui/browser_osx.mm @@ -27,45 +27,93 @@ #include "common/config-manager.h" #include "common/system.h" +#include "common/events.h" #include "common/algorithm.h" #include "common/translation.h" #include #include +#include #include #include #include +#include -@interface ShowHiddenFilesController : NSObject { - NSOpenPanel* _panel; + +@interface BrowserDialogPresenter : NSObject { +@public + NSURL *_url; +@private + NSOpenPanel *_panel; } - - (id) init; - (void) dealloc; -- (void) setOpenPanel : (NSOpenPanel*) panel; +- (void) showOpenPanel: (NSOpenPanel*) panel; - (IBAction) showHiddenFiles : (id) sender; - @end -@implementation ShowHiddenFilesController +@implementation BrowserDialogPresenter - (id) init { self = [super init]; + _url = 0; _panel = 0; - return self; } - (void) dealloc { - [_panel release]; + [_url release]; [super dealloc]; } -- (void) setOpenPanel : (NSOpenPanel*) panel { +- (void) showOpenPanel: (NSOpenPanel*) panel { _panel = panel; - [_panel retain]; -} + NSButton *showHiddenFilesButton = 0; + if ([panel respondsToSelector:@selector(setShowsHiddenFiles:)]) { + showHiddenFilesButton = [[NSButton alloc] init]; + [showHiddenFilesButton setButtonType:NSSwitchButton]; + +#ifdef USE_TRANSLATION + CFStringRef encStr = CFStringCreateWithCString(NULL, TransMan.getCurrentCharset().c_str(), kCFStringEncodingASCII); + CFStringEncoding stringEncoding = CFStringConvertIANACharSetNameToEncoding(encStr); + CFRelease(encStr); +#else + CFStringEncoding stringEncoding = kCFStringEncodingASCII; +#endif + CFStringRef hiddenFilesString = CFStringCreateWithCString(0, _("Show hidden files"), stringEncoding); + [showHiddenFilesButton setTitle:(NSString*)hiddenFilesString]; + CFRelease(hiddenFilesString); + + [showHiddenFilesButton sizeToFit]; + if (ConfMan.getBool("gui_browser_show_hidden", Common::ConfigManager::kApplicationDomain)) { + [showHiddenFilesButton setState:NSOnState]; + [panel setShowsHiddenFiles: YES]; + } else { + [showHiddenFilesButton setState:NSOffState]; + [panel setShowsHiddenFiles: NO]; + } + [panel setAccessoryView:showHiddenFilesButton]; + + [showHiddenFilesButton setTarget:self]; + [showHiddenFilesButton setAction:@selector(showHiddenFiles:)]; + } + +#if MAC_OS_X_VERSION_MAX_ALLOWED <= 1090 + if ([panel runModal] == NSOKButton) { +#else + if ([panel runModal] == NSModalResponseOK) { +#endif + NSURL *url = [panel URL]; + if ([url isFileURL]) { + _url = url; + [_url retain]; + } + } + + [showHiddenFilesButton release]; + _panel = 0; +} - (IBAction) showHiddenFiles : (id) sender { if ([sender state] == NSOnState) { @@ -101,13 +149,11 @@ BrowserDialog::BrowserDialog(const char *title, bool dirBrowser) // Convert button text to NSString _chooseRef = CFStringCreateWithCString(0, _("Choose"), stringEncoding); - _hiddenFilesRef = CFStringCreateWithCString(0, _("Show hidden files"), stringEncoding); } BrowserDialog::~BrowserDialog() { CFRelease(_titleRef); CFRelease(_chooseRef); - CFRelease(_hiddenFilesRef); } int BrowserDialog::runModal() { @@ -124,6 +170,10 @@ int BrowserDialog::runModal() { // Temporarily show the real mouse CGDisplayShowCursor(kCGDirectMainDisplay); + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NSWindow *keyWindow = [[NSApplication sharedApplication] keyWindow]; + + NSOpenPanel *panel = [NSOpenPanel openPanel]; [panel setCanChooseFiles:!_isDirBrowser]; [panel setCanChooseDirectories:_isDirBrowser]; @@ -132,43 +182,25 @@ int BrowserDialog::runModal() { [panel setTitle:(NSString *)_titleRef]; [panel setPrompt:(NSString *)_chooseRef]; - NSButton *showHiddenFilesButton = 0; - ShowHiddenFilesController *showHiddenFilesController = 0; - if ([panel respondsToSelector:@selector(setShowsHiddenFiles:)]) { - showHiddenFilesButton = [[NSButton alloc] init]; - [showHiddenFilesButton setButtonType:NSSwitchButton]; - [showHiddenFilesButton setTitle:(NSString *)_hiddenFilesRef]; - [showHiddenFilesButton sizeToFit]; - if (ConfMan.getBool("gui_browser_show_hidden", Common::ConfigManager::kApplicationDomain)) { - [showHiddenFilesButton setState:NSOnState]; - [panel setShowsHiddenFiles: YES]; - } else { - [showHiddenFilesButton setState:NSOffState]; - [panel setShowsHiddenFiles: NO]; - } - [panel setAccessoryView:showHiddenFilesButton]; - - showHiddenFilesController = [[ShowHiddenFilesController alloc] init]; - [showHiddenFilesController setOpenPanel:panel]; - [showHiddenFilesButton setTarget:showHiddenFilesController]; - [showHiddenFilesButton setAction:@selector(showHiddenFiles:)]; + BrowserDialogPresenter* presenter = [[BrowserDialogPresenter alloc] init]; + [presenter performSelectorOnMainThread:@selector(showOpenPanel:) withObject:panel waitUntilDone:YES]; + if (presenter->_url) { + Common::String filename = [[presenter->_url path] UTF8String]; + _choice = Common::FSNode(filename); + choiceMade = true; } + [presenter release]; -#if MAC_OS_X_VERSION_MAX_ALLOWED <= 1090 - if ([panel runModal] == NSOKButton) { -#else - if ([panel runModal] == NSModalResponseOK) { -#endif - NSURL *url = [panel URL]; - if ([url isFileURL]) { - const char *filename = [[url path] UTF8String]; - _choice = Common::FSNode(filename); - choiceMade = true; - } - } + [pool release]; + [keyWindow makeKeyAndOrderFront:nil]; - [showHiddenFilesButton release]; - [showHiddenFilesController release]; + // While the native macOS file browser is open, any input events (e.g. keypresses) are + // still received by the NSApplication. With SDL backend for example this results in the + // events beeing queued and processed after we return, thus dispatching events that were + // intended for the native file browser. For example: pressing Esc to cancel the native + // macOS file browser would cause the application to quit in addition to closing the + // file browser. To avoid this happening clear all pending events. + g_system->getEventManager()->getEventDispatcher()->clearEvents(); // If we were in fullscreen mode, switch back if (wasFullscreen) { diff --git a/gui/console.cpp b/gui/console.cpp index 4cdad41f5f1..27b38ac78b5 100644 --- a/gui/console.cpp +++ b/gui/console.cpp @@ -353,6 +353,7 @@ void ConsoleDialog::handleKeyDown(Common::KeyState state) { defaultKeyDownHandler(state); break; } + // fall through case Common::KEYCODE_DELETE: if (_currentPos < _promptEndPos) { killChar(); @@ -365,6 +366,7 @@ void ConsoleDialog::handleKeyDown(Common::KeyState state) { defaultKeyDownHandler(state); break; } + // fall through case Common::KEYCODE_END: if (state.hasFlags(Common::KBD_SHIFT)) { _scrollLine = _promptEndPos / kCharsPerLine; @@ -382,6 +384,7 @@ void ConsoleDialog::handleKeyDown(Common::KeyState state) { defaultKeyDownHandler(state); break; } + // fall through case Common::KEYCODE_DOWN: historyScroll(-1); break; @@ -391,6 +394,7 @@ void ConsoleDialog::handleKeyDown(Common::KeyState state) { defaultKeyDownHandler(state); break; } + // fall through case Common::KEYCODE_PAGEDOWN: if (state.hasFlags(Common::KBD_SHIFT)) { _scrollLine += _linesPerPage - 1; @@ -409,6 +413,7 @@ void ConsoleDialog::handleKeyDown(Common::KeyState state) { defaultKeyDownHandler(state); break; } + // fall through case Common::KEYCODE_LEFT: if (_currentPos > _promptStartPos) _currentPos--; @@ -420,6 +425,7 @@ void ConsoleDialog::handleKeyDown(Common::KeyState state) { defaultKeyDownHandler(state); break; } + // fall through case Common::KEYCODE_RIGHT: if (_currentPos < _promptEndPos) _currentPos++; @@ -431,6 +437,7 @@ void ConsoleDialog::handleKeyDown(Common::KeyState state) { defaultKeyDownHandler(state); break; } + // fall through case Common::KEYCODE_HOME: if (state.hasFlags(Common::KBD_SHIFT)) { _scrollLine = _firstLineInBuffer + _linesPerPage - 1; @@ -446,6 +453,7 @@ void ConsoleDialog::handleKeyDown(Common::KeyState state) { defaultKeyDownHandler(state); break; } + // fall through case Common::KEYCODE_UP: historyScroll(+1); break; @@ -455,6 +463,7 @@ void ConsoleDialog::handleKeyDown(Common::KeyState state) { defaultKeyDownHandler(state); break; } + // fall through case Common::KEYCODE_PAGEUP: if (state.hasFlags(Common::KBD_SHIFT)) { _scrollLine -= _linesPerPage - 1; diff --git a/gui/dialog.cpp b/gui/dialog.cpp index 523227a2374..24b3db4d6d6 100644 --- a/gui/dialog.cpp +++ b/gui/dialog.cpp @@ -88,13 +88,7 @@ void Dialog::open() { _visible = true; g_gui.openDialog(this); - Widget *w = _firstWidget; - // Search for the first objects that wantsFocus() (if any) and give it the focus - while (w && !w->wantsFocus()) { - w = w->_next; - } - - setFocusWidget(w); + setDefaultFocusedWidget(); } void Dialog::close() { @@ -142,6 +136,16 @@ void Dialog::setFocusWidget(Widget *widget) { _focusedWidget = widget; } +void Dialog::setDefaultFocusedWidget() { + Widget *w = _firstWidget; + // Search for the first objects that wantsFocus() (if any) and give it the focus + while (w && !w->wantsFocus()) { + w = w->_next; + } + + setFocusWidget(w); +} + void Dialog::releaseFocus() { if (_focusedWidget) { _focusedWidget->lostFocus(); @@ -361,11 +365,11 @@ Widget *Dialog::findWidget(const char *name) { } void Dialog::removeWidget(Widget *del) { - if (del == _mouseWidget) + if (del == _mouseWidget || del->containsWidget(_mouseWidget)) _mouseWidget = NULL; - if (del == _focusedWidget) + if (del == _focusedWidget || del->containsWidget(_focusedWidget)) _focusedWidget = NULL; - if (del == _dragWidget) + if (del == _dragWidget || del->containsWidget(_dragWidget)) _dragWidget = NULL; GuiObject::removeWidget(del); diff --git a/gui/dialog.h b/gui/dialog.h index 0e06effabd2..cf734a83f8b 100644 --- a/gui/dialog.h +++ b/gui/dialog.h @@ -107,6 +107,8 @@ protected: Widget *findWidget(const char *name); void removeWidget(Widget *widget); + void setDefaultFocusedWidget(); + void setResult(int result) { _result = result; } int getResult() const { return _result; } }; diff --git a/gui/editgamedialog.cpp b/gui/editgamedialog.cpp index 85b37929dc1..94c74b40c09 100644 --- a/gui/editgamedialog.cpp +++ b/gui/editgamedialog.cpp @@ -539,7 +539,7 @@ void EditGameDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat _domain = newDomain; } } - // FALL THROUGH to default case + // fall through default: OptionsDialog::handleCommand(sender, cmd, data); } diff --git a/gui/gui-manager.cpp b/gui/gui-manager.cpp index 421a8f4cac6..e11afa96d90 100644 --- a/gui/gui-manager.cpp +++ b/gui/gui-manager.cpp @@ -254,6 +254,23 @@ Dialog *GuiManager::getTopDialog() const { return _dialogStack.top(); } +void GuiManager::addToTrash(GuiObject* object, Dialog* parent) { + debug(7, "Adding Gui Object %p to trash", (void *)object); + GuiObjectTrashItem t; + t.object = object; + t.parent = 0; + // If a dialog was provided, check it is in the dialog stack + if (parent != 0) { + for (uint i = 0 ; i < _dialogStack.size() ; ++i) { + if (_dialogStack[i] == parent) { + t.parent = parent; + break; + } + } + } + _guiObjectTrash.push_back(t); +} + void GuiManager::runLoop() { Dialog * const activeDialog = getTopDialog(); bool didSaveState = false; @@ -326,11 +343,14 @@ void GuiManager::runLoop() { // // This hopefully fixes strange behavior/crashes with pop-up widgets. (Most easily // triggered in 3x mode or when running ScummVM under Valgrind.) - if (activeDialog != getTopDialog() && event.type != Common::EVENT_SCREEN_CHANGED) + if (activeDialog != getTopDialog() && event.type != Common::EVENT_SCREEN_CHANGED) { + processEvent(event, getTopDialog()); continue; + } processEvent(event, activeDialog); + if (lastRedraw + waitTime < _system->getMillis(true)) { lastRedraw = _system->getMillis(true); _theme->updateScreen(); @@ -338,6 +358,17 @@ void GuiManager::runLoop() { } } + // Delete GuiObject that have been added to the trash for a delayed deletion + Common::List::iterator it = _guiObjectTrash.begin(); + while (it != _guiObjectTrash.end()) { + if ((*it).parent == 0 || (*it).parent == activeDialog) { + debug(7, "Delayed deletion of Gui Object %p", (void *)(*it).object); + delete (*it).object; + it = _guiObjectTrash.erase(it); + } else + ++it; + } + if (_lastMousePosition.time + kTooltipDelay < _system->getMillis(true)) { Widget *wdg = activeDialog->findWidget(_lastMousePosition.x, _lastMousePosition.y); if (wdg && wdg->hasTooltip() && !(wdg->getFlags() & WIDGET_PRESSED)) { @@ -504,6 +535,8 @@ void GuiManager::screenChange() { } void GuiManager::processEvent(const Common::Event &event, Dialog *const activeDialog) { + if (activeDialog == 0) + return; int button; uint32 time; Common::Point mouse(event.mouse.x - activeDialog->_x, event.mouse.y - activeDialog->_y); diff --git a/gui/gui-manager.h b/gui/gui-manager.h index 4dc9af95fba..82a8aa9cfd0 100644 --- a/gui/gui-manager.h +++ b/gui/gui-manager.h @@ -27,6 +27,7 @@ #include "common/singleton.h" #include "common/stack.h" #include "common/str.h" +#include "common/list.h" #include "gui/ThemeEngine.h" @@ -44,6 +45,7 @@ namespace GUI { class Dialog; class ThemeEval; +class GuiObject; #define g_gui (GUI::GuiManager::instance()) @@ -99,6 +101,13 @@ public: */ bool checkScreenChange(); + /** + * Tell the GuiManager to delete the given GuiObject later. If a parent + * dialog is provided and is present in the DialogStack, the object will + * only be deleted when that dialog is the top level dialog. + */ + void addToTrash(GuiObject*, Dialog* parent = 0); + bool _launched; protected: @@ -137,6 +146,13 @@ protected: int _cursorAnimateTimer; byte _cursor[2048]; + // delayed deletion of GuiObject + struct GuiObjectTrashItem { + GuiObject* object; + Dialog* parent; + }; + Common::List _guiObjectTrash; + void initKeymap(); void pushKeymap(); void popKeymap(); diff --git a/gui/launcher.cpp b/gui/launcher.cpp index 750e4069773..303403abd0d 100644 --- a/gui/launcher.cpp +++ b/gui/launcher.cpp @@ -91,7 +91,7 @@ enum { #pragma mark - LauncherDialog::LauncherDialog() - : Dialog(0, 0, 640, 400) { // ResidualVM specific + : Dialog(0, 0, 640, 480) { // ResidualVM specific _backgroundType = GUI::ThemeEngine::kDialogBackgroundMain; const int screenW = g_system->getOverlayWidth(); const int screenH = g_system->getOverlayHeight(); @@ -204,7 +204,10 @@ void LauncherDialog::clean() { while (_firstWidget) { Widget* w = _firstWidget; removeWidget(w); - delete w; + // This is called from rebuild() which may result from handleCommand being called by + // a child widget sendCommand call. In such a case sendCommand is still being executed + // so we should not delete yet the child widget. Thus delay the deletion. + g_gui.addToTrash(w, this); } delete _browser; delete _loadDialog; @@ -214,7 +217,7 @@ void LauncherDialog::rebuild() { clean(); build(); reflowLayout(); - setFocusWidget(_firstWidget); + setDefaultFocusedWidget(); } void LauncherDialog::open() { diff --git a/gui/options.cpp b/gui/options.cpp index 9e2e29ef5fe..d8fd862f35c 100644 --- a/gui/options.cpp +++ b/gui/options.cpp @@ -81,6 +81,8 @@ enum { kChoosePluginsDirCmd = 'chpl', kChooseThemeCmd = 'chtf', kUpdatesCheckCmd = 'updc', + kKbdMouseSpeedChanged = 'kmsc', + kJoystickDeadzoneChanged= 'jodc', kFullscreenToggled = 'oful' // ResidualVM specific }; @@ -114,7 +116,7 @@ enum { kRootPathClearCmd = 'clrp' }; #endif - + enum { kApplyCmd = 'appl' }; @@ -123,6 +125,9 @@ static const char *savePeriodLabels[] = { _s("Never"), _s("every 5 mins"), _s("e static const int savePeriodValues[] = { 0, 5 * 60, 10 * 60, 15 * 60, 30 * 60, -1 }; static const char *outputRateLabels[] = { _s(""), _s("8 kHz"), _s("11 kHz"), _s("22 kHz"), _s("44 kHz"), _s("48 kHz"), 0 }; static const int outputRateValues[] = { 0, 8000, 11025, 22050, 44100, 48000, -1 }; +// The keyboard mouse speed values range from 0 to 7 and correspond to speeds shown in the label +// "10" (value 3) is the default speed corresponding to the speed before introduction of this control +static const char *kbdMouseSpeedLabels[] = { "3", "5", "8", "10", "13", "15", "18", "20", 0 }; OptionsDialog::OptionsDialog(const Common::String &domain, int x, int y, int w, int h) : Dialog(x, y, w, h), _domain(domain), _graphicsTabId(-1), _midiTabId(-1), _pathsTabId(-1), _tabWidget(0) { @@ -139,6 +144,16 @@ OptionsDialog::~OptionsDialog() { } void OptionsDialog::init() { + _enableControlSettings = false; + _onscreenCheckbox = 0; + _touchpadCheckbox = 0; + _swapMenuAndBackBtnsCheckbox = 0; + _kbdMouseSpeedDesc = 0; + _kbdMouseSpeedSlider = 0; + _kbdMouseSpeedLabel = 0; + _joystickDeadzoneDesc = 0; + _joystickDeadzoneSlider = 0; + _joystickDeadzoneLabel = 0; _enableGraphicSettings = false; _gfxPopUp = 0; _gfxPopUpDesc = 0; @@ -147,6 +162,9 @@ void OptionsDialog::init() { _fullscreenCheckbox = 0; _filteringCheckbox = 0; _aspectCheckbox = 0; + _enableShaderSettings = false; + _shaderPopUpDesc = 0; + _shaderPopUp = 0; _vsyncCheckbox = 0; // ResidualVM specific _rendererTypePopUpDesc = 0; // ResidualVM specific _rendererTypePopUp = 0; // ResidualVM specific @@ -200,7 +218,7 @@ void OptionsDialog::init() { _guioptions = parseGameGUIOptions(_guioptionsString); } } - + void OptionsDialog::build() { // Retrieve game GUI options _guioptions.clear(); @@ -209,6 +227,47 @@ void OptionsDialog::build() { _guioptions = parseGameGUIOptions(_guioptionsString); } + // Control options + if (g_system->hasFeature(OSystem::kFeatureOnScreenControl)) { + if (ConfMan.hasKey("onscreen_control", _domain)) { + bool onscreenState = g_system->getFeatureState(OSystem::kFeatureOnScreenControl); + if (_onscreenCheckbox != 0) + _onscreenCheckbox->setState(onscreenState); + } + } + if (g_system->hasFeature(OSystem::kFeatureTouchpadMode)) { + if (ConfMan.hasKey("touchpad_mouse_mode", _domain)) { + bool touchpadState = g_system->getFeatureState(OSystem::kFeatureTouchpadMode); + if (_touchpadCheckbox != 0) + _touchpadCheckbox->setState(touchpadState); + } + } + if (g_system->hasFeature(OSystem::kFeatureSwapMenuAndBackButtons)) { + if (ConfMan.hasKey("swap_menu_and_back_buttons", _domain)) { + bool state = g_system->getFeatureState(OSystem::kFeatureSwapMenuAndBackButtons); + if (_swapMenuAndBackBtnsCheckbox != 0) + _swapMenuAndBackBtnsCheckbox->setState(state); + } + } + if (g_system->hasFeature(OSystem::kFeatureKbdMouseSpeed)) { + if (ConfMan.hasKey("kbdmouse_speed", _domain)) { + int value = ConfMan.getInt("kbdmouse_speed", _domain); + if (_kbdMouseSpeedSlider && value < ARRAYSIZE(kbdMouseSpeedLabels) - 1 && value >= 0) { + _kbdMouseSpeedSlider->setValue(value); + _kbdMouseSpeedLabel->setLabel(_(kbdMouseSpeedLabels[value])); + } + } + } + if (g_system->hasFeature(OSystem::kFeatureJoystickDeadzone)) { + if (ConfMan.hasKey("joystick_deadzone", _domain)) { + int value = ConfMan.getInt("joystick_deadzone", _domain); + if (_joystickDeadzoneSlider != 0) { + _joystickDeadzoneSlider->setValue(value); + _joystickDeadzoneLabel->setValue(value); + } + } + } + // Graphic options if (_fullscreenCheckbox) { #if 0 // ResidualVM specific @@ -270,6 +329,14 @@ void OptionsDialog::build() { _rendererTypePopUp->setSelectedTag(Graphics::parseRendererTypeCode(ConfMan.get("renderer", _domain))); // ResidualVM specific } + // Shader options + if (g_system->hasFeature(OSystem::kFeatureShader)) { + if (_shaderPopUp) { + int value = ConfMan.getInt("shader", _domain); + _shaderPopUp->setSelected(value); + } + } + // Audio options if (!loadMusicDeviceSetting(_midiPopUp, "music_driver")) _midiPopUp->setSelected(0); @@ -361,24 +428,27 @@ void OptionsDialog::build() { _subSpeedLabel->setValue(speed); } } - + void OptionsDialog::clean() { delete _subToggleGroup; while (_firstWidget) { Widget* w = _firstWidget; removeWidget(w); - delete w; + // This is called from rebuild() which may result from handleCommand being called by + // a child widget sendCommand call. In such a case sendCommand is still being executed + // so we should not delete yet the child widget. Thus delay the deletion. + g_gui.addToTrash(w, this); } init(); } - + void OptionsDialog::rebuild() { int currentTab = _tabWidget->getActiveTab(); clean(); build(); reflowLayout(); _tabWidget->setActiveTab(currentTab); - setFocusWidget(_firstWidget); + setDefaultFocusedWidget(); } void OptionsDialog::open() { @@ -391,8 +461,9 @@ void OptionsDialog::open() { } void OptionsDialog::apply() { - // Graphic options bool graphicsModeChanged = false; + + // Graphic options if (_fullscreenCheckbox) { if (_enableGraphicSettings) { if (ConfMan.getBool("filtering", _domain) != _filteringCheckbox->getState()) @@ -480,11 +551,11 @@ void OptionsDialog::apply() { // Dialog::close) is called, to prevent crashes caused by invalid // widgets being referenced or similar errors. g_gui.checkScreenChange(); - + if (gfxError != OSystem::kTransactionSuccess) { // Revert ConfMan to what OSystem is using. Common::String message = _("Failed to apply some of the graphic options changes:"); - + if (gfxError & OSystem::kTransactionModeSwitchFailed) { const OSystem::GraphicsMode *gm = g_system->getSupportedGraphicsModes(); while (gm->name) { @@ -497,31 +568,72 @@ void OptionsDialog::apply() { message += "\n"; message += _("the video mode could not be changed."); } - + if (gfxError & OSystem::kTransactionAspectRatioFailed) { ConfMan.setBool("aspect_ratio", g_system->getFeatureState(OSystem::kFeatureAspectRatioCorrection), _domain); message += "\n"; message += _("the aspect ratio setting could not be changed"); } - + if (gfxError & OSystem::kTransactionFullscreenFailed) { ConfMan.setBool("fullscreen", g_system->getFeatureState(OSystem::kFeatureFullscreenMode), _domain); message += "\n"; message += _("the fullscreen setting could not be changed"); } - + if (gfxError & OSystem::kTransactionFilteringFailed) { ConfMan.setBool("filtering", g_system->getFeatureState(OSystem::kFeatureFilteringMode), _domain); message += "\n"; message += _("the filtering setting could not be changed"); } - + // And display the error GUI::MessageDialog dialog(message); dialog.runModal(); } } - + + // Shader options + if (_enableShaderSettings) { + if (g_system->hasFeature(OSystem::kFeatureShader)) { + if (_shaderPopUp) { + if (ConfMan.getInt("shader", _domain) != (int32)_shaderPopUp->getSelectedTag()) { + ConfMan.setInt("shader", _shaderPopUp->getSelectedTag(), _domain); + g_system->setShader(_shaderPopUp->getSelectedTag()); + } + } + } + } + + // Control options + if (_enableControlSettings) { + if (g_system->hasFeature(OSystem::kFeatureOnScreenControl)) { + if (ConfMan.getBool("onscreen_control", _domain) != _onscreenCheckbox->getState()) { + g_system->setFeatureState(OSystem::kFeatureOnScreenControl, _onscreenCheckbox->getState()); + } + } + if (g_system->hasFeature(OSystem::kFeatureTouchpadMode)) { + if (ConfMan.getBool("touchpad_mouse_mode", _domain) != _touchpadCheckbox->getState()) { + g_system->setFeatureState(OSystem::kFeatureTouchpadMode, _touchpadCheckbox->getState()); + } + } + if (g_system->hasFeature(OSystem::kFeatureSwapMenuAndBackButtons)) { + if (ConfMan.getBool("swap_menu_and_back_buttons", _domain) != _swapMenuAndBackBtnsCheckbox->getState()) { + g_system->setFeatureState(OSystem::kFeatureSwapMenuAndBackButtons, _swapMenuAndBackBtnsCheckbox->getState()); + } + } + if (g_system->hasFeature(OSystem::kFeatureKbdMouseSpeed)) { + if (ConfMan.getInt("kbdmouse_speed", _domain) != _kbdMouseSpeedSlider->getValue()) { + ConfMan.setInt("kbdmouse_speed", _kbdMouseSpeedSlider->getValue(), _domain); + } + } + if (g_system->hasFeature(OSystem::kFeatureJoystickDeadzone)) { + if (ConfMan.getInt("joystick_deadzone", _domain) != _joystickDeadzoneSlider->getValue()) { + ConfMan.setInt("joystick_deadzone", _joystickDeadzoneSlider->getValue(), _domain); + } + } + } + // Volume options if (_musicVolumeSlider) { if (_enableVolumeSettings) { @@ -536,7 +648,7 @@ void OptionsDialog::apply() { ConfMan.removeKey("mute", _domain); } } - + // Audio options if (_midiPopUp) { if (_enableAudioSettings) { @@ -545,11 +657,11 @@ void OptionsDialog::apply() { ConfMan.removeKey("music_driver", _domain); } } - + if (_oplPopUp) { if (_enableAudioSettings) { const OPL::Config::EmulatorDescription *ed = OPL::Config::findDriver(_oplPopUp->getSelectedTag()); - + if (ed) ConfMan.set("opl_driver", ed->name, _domain); else @@ -558,7 +670,7 @@ void OptionsDialog::apply() { ConfMan.removeKey("opl_driver", _domain); } } - + if (_outputRatePopUp) { if (_enableAudioSettings) { if (_outputRatePopUp->getSelectedTag() != 0) @@ -569,15 +681,15 @@ void OptionsDialog::apply() { ConfMan.removeKey("output_rate", _domain); } } - + // MIDI options if (_multiMidiCheckbox) { if (_enableMIDISettings) { saveMusicDeviceSetting(_gmDevicePopUp, "gm_device"); - + ConfMan.setBool("multi_midi", _multiMidiCheckbox->getState(), _domain); ConfMan.setInt("midi_gain", _midiGainSlider->getValue(), _domain); - + Common::String soundFont(_soundFont->getLabel()); if (!soundFont.empty() && (soundFont != _c("None", "soundfont"))) ConfMan.set("soundfont", soundFont, _domain); @@ -590,7 +702,7 @@ void OptionsDialog::apply() { ConfMan.removeKey("soundfont", _domain); } } - + // MT-32 options if (_mt32DevicePopUp) { if (_enableMT32Settings) { @@ -603,14 +715,14 @@ void OptionsDialog::apply() { ConfMan.removeKey("enable_gs", _domain); } } - + // Subtitle options if (_subToggleGroup) { if (_enableSubtitleSettings) { bool subtitles, speech_mute; int talkspeed; int sliderMaxValue = _subSpeedSlider->getMaxValue(); - + switch (_subToggleGroup->getValue()) { case kSubtitlesSpeech: subtitles = speech_mute = false; @@ -624,22 +736,22 @@ void OptionsDialog::apply() { subtitles = speech_mute = true; break; } - + ConfMan.setBool("subtitles", subtitles, _domain); ConfMan.setBool("speech_mute", speech_mute, _domain); - + // Engines that reuse the subtitle speed widget set their own max value. // Scale the config value accordingly (see addSubtitleControls) talkspeed = (_subSpeedSlider->getValue() * 255 + sliderMaxValue / 2) / sliderMaxValue; ConfMan.setInt("talkspeed", talkspeed, _domain); - + } else { ConfMan.removeKey("subtitles", _domain); ConfMan.removeKey("talkspeed", _domain); ConfMan.removeKey("speech_mute", _domain); } } - + // Save config file ConfMan.flushToDisk(); } @@ -657,18 +769,51 @@ void OptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data _midiGainLabel->setLabel(Common::String::format("%.2f", (double)_midiGainSlider->getValue() / 100.0)); _midiGainLabel->draw(); break; - case kMusicVolumeChanged: - _musicVolumeLabel->setValue(_musicVolumeSlider->getValue()); + case kMusicVolumeChanged: { + const int newValue = _musicVolumeSlider->getValue(); + _musicVolumeLabel->setValue(newValue); _musicVolumeLabel->draw(); + + if (_guioptions.contains(GUIO_LINKMUSICTOSFX)) { + updateSfxVolume(newValue); + + if (_guioptions.contains(GUIO_LINKSPEECHTOSFX)) { + updateSpeechVolume(newValue); + } + } + break; - case kSfxVolumeChanged: + } + case kSfxVolumeChanged: { + const int newValue = _sfxVolumeSlider->getValue(); _sfxVolumeLabel->setValue(_sfxVolumeSlider->getValue()); _sfxVolumeLabel->draw(); + + if (_guioptions.contains(GUIO_LINKMUSICTOSFX)) { + updateMusicVolume(newValue); + } + + if (_guioptions.contains(GUIO_LINKSPEECHTOSFX)) { + updateSpeechVolume(newValue); + } + break; - case kSpeechVolumeChanged: - _speechVolumeLabel->setValue(_speechVolumeSlider->getValue()); + } + case kSpeechVolumeChanged: { + const int newValue = _speechVolumeSlider->getValue(); + _speechVolumeLabel->setValue(newValue); _speechVolumeLabel->draw(); + + if (_guioptions.contains(GUIO_LINKSPEECHTOSFX)) { + updateSfxVolume(newValue); + + if (_guioptions.contains(GUIO_LINKMUSICTOSFX)) { + updateMusicVolume(newValue); + } + } + break; + } case kMuteAllChanged: // 'true' because if control is disabled then event do not pass setVolumeSettingsState(true); @@ -688,6 +833,14 @@ void OptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data _soundFontClearButton->setEnabled(false); draw(); break; + case kKbdMouseSpeedChanged: + _kbdMouseSpeedLabel->setLabel(_(kbdMouseSpeedLabels[_kbdMouseSpeedSlider->getValue()])); + _kbdMouseSpeedLabel->draw(); + break; + case kJoystickDeadzoneChanged: + _joystickDeadzoneLabel->setValue(_joystickDeadzoneSlider->getValue()); + _joystickDeadzoneLabel->draw(); + break; // ResidualVM specific case kFullscreenToggled: _aspectCheckbox->setEnabled(_fullscreenCheckbox->getState()); @@ -716,6 +869,7 @@ void OptionsDialog::setGraphicSettingsState(bool enabled) { _gfxPopUp->setEnabled(enabled); _renderModePopUpDesc->setEnabled(enabled); _renderModePopUp->setEnabled(enabled); + _filteringCheckbox->setEnabled(enabled); #endif #ifndef GUI_ENABLE_KEYSDIALOG _fullscreenCheckbox->setEnabled(enabled); @@ -809,7 +963,7 @@ void OptionsDialog::setVolumeSettingsState(bool enabled) { // Disable speech volume slider, when we are in subtitle only mode. if (_subToggleGroup) ena = ena && _subToggleGroup->getValue() != kSubtitlesSubs; - if (_guioptions.contains(GUIO_NOSPEECH)) + if (_guioptions.contains(GUIO_NOSPEECH) || _guioptions.contains(GUIO_NOSPEECHVOLUME)) ena = false; _speechVolumeDesc->setEnabled(ena); @@ -839,6 +993,64 @@ void OptionsDialog::setSubtitleSettingsState(bool enabled) { _subSpeedLabel->setEnabled(ena); } +void OptionsDialog::addControlControls(GuiObject *boss, const Common::String &prefix) { + // Show On-Screen control + if (g_system->hasFeature(OSystem::kFeatureOnScreenControl)) + _onscreenCheckbox = new CheckboxWidget(boss, prefix + "grOnScreenCheckbox", _("Show On-screen control")); + + // Touchpad Mouse mode + if (g_system->hasFeature(OSystem::kFeatureTouchpadMode)) + _touchpadCheckbox = new CheckboxWidget(boss, prefix + "grTouchpadCheckbox", _("Touchpad mouse mode")); + + // Swap menu and back buttons + if (g_system->hasFeature(OSystem::kFeatureSwapMenuAndBackButtons)) + _swapMenuAndBackBtnsCheckbox = new CheckboxWidget(boss, prefix + "grSwapMenuAndBackBtnsCheckbox", _("Swap Menu and Back buttons")); + + // Keyboard and joystick mouse speed + if (g_system->hasFeature(OSystem::kFeatureKbdMouseSpeed)) { + if (g_system->getOverlayWidth() > 320) + _kbdMouseSpeedDesc = new StaticTextWidget(boss, prefix + "grKbdMouseSpeedDesc", _("Pointer Speed:"), _("Speed for keyboard/joystick mouse pointer control")); + else + _kbdMouseSpeedDesc = new StaticTextWidget(boss, prefix + "grKbdMouseSpeedDesc", _c("Pointer Speed:", "lowres"), _("Speed for keyboard/joystick mouse pointer control")); + _kbdMouseSpeedSlider = new SliderWidget(boss, prefix + "grKbdMouseSpeedSlider", _("Speed for keyboard/joystick mouse pointer control"), kKbdMouseSpeedChanged); + _kbdMouseSpeedLabel = new StaticTextWidget(boss, prefix + "grKbdMouseSpeedLabel", " "); + _kbdMouseSpeedSlider->setMinValue(0); + _kbdMouseSpeedSlider->setMaxValue(7); + _kbdMouseSpeedLabel->setFlags(WIDGET_CLEARBG); + } + + // Joystick deadzone + if (g_system->hasFeature(OSystem::kFeatureJoystickDeadzone)) { + if (g_system->getOverlayWidth() > 320) + _joystickDeadzoneDesc = new StaticTextWidget(boss, prefix + "grJoystickDeadzoneDesc", _("Joy Deadzone:"), _("Analog joystick Deadzone")); + else + _joystickDeadzoneDesc = new StaticTextWidget(boss, prefix + "grJoystickDeadzoneDesc", _c("Joy Deadzone:", "lowres"), _("Analog joystick Deadzone")); + _joystickDeadzoneSlider = new SliderWidget(boss, prefix + "grJoystickDeadzoneSlider", _("Analog joystick Deadzone"), kJoystickDeadzoneChanged); + _joystickDeadzoneLabel = new StaticTextWidget(boss, prefix + "grJoystickDeadzoneLabel", " "); + _joystickDeadzoneSlider->setMinValue(1); + _joystickDeadzoneSlider->setMaxValue(10); + _joystickDeadzoneLabel->setFlags(WIDGET_CLEARBG); + } + _enableControlSettings = true; +} + +void OptionsDialog::addShaderControls(GuiObject *boss, const Common::String &prefix) { + // Shader selector + if (g_system->hasFeature(OSystem::kFeatureShader)) { + if (g_system->getOverlayWidth() > 320) + _shaderPopUpDesc = new StaticTextWidget(boss, prefix + "grShaderPopUpDesc", _("HW Shader:"), _("Different hardware shaders give different visual effects")); + else + _shaderPopUpDesc = new StaticTextWidget(boss, prefix + "grShaderPopUpDesc", _c("HW Shader:", "lowres"), _("Different hardware shaders give different visual effects")); + _shaderPopUp = new PopUpWidget(boss, prefix + "grShaderPopUp", _("Different shaders give different visual effects")); + const OSystem::GraphicsMode *p = g_system->getSupportedShaders(); + while (p->name) { + _shaderPopUp->appendEntry(p->name, p->id); + p++; + } + } + _enableShaderSettings = true; +} + void OptionsDialog::addGraphicControls(GuiObject *boss, const Common::String &prefix) { #if 0 // ResidualVM specific const OSystem::GraphicsMode *gm = g_system->getSupportedGraphicsModes(); @@ -1199,6 +1411,27 @@ int OptionsDialog::getSubtitleMode(bool subtitles, bool speech_mute) { return kSubtitlesSubs; } +void OptionsDialog::updateMusicVolume(const int newValue) const { + _musicVolumeLabel->setValue(newValue); + _musicVolumeSlider->setValue(newValue); + _musicVolumeLabel->draw(); + _musicVolumeSlider->draw(); +} + +void OptionsDialog::updateSfxVolume(const int newValue) const { + _sfxVolumeLabel->setValue(newValue); + _sfxVolumeSlider->setValue(newValue); + _sfxVolumeLabel->draw(); + _sfxVolumeSlider->draw(); +} + +void OptionsDialog::updateSpeechVolume(const int newValue) const { + _speechVolumeLabel->setValue(newValue); + _speechVolumeSlider->setValue(newValue); + _speechVolumeLabel->draw(); + _speechVolumeSlider->draw(); +} + void OptionsDialog::reflowLayout() { if (_graphicsTabId != -1 && _tabWidget) _tabWidget->setTabTitle(_graphicsTabId, g_system->getOverlayWidth() > 320 ? _("Graphics") : _("GFX")); @@ -1278,7 +1511,7 @@ GlobalOptionsDialog::~GlobalOptionsDialog() { delete _fluidSynthSettingsDialog; #endif } - + void GlobalOptionsDialog::build() { // The tab widget TabWidget *tab = new TabWidget(this, "GlobalOptions.TabWidget"); @@ -1289,6 +1522,27 @@ void GlobalOptionsDialog::build() { _graphicsTabId = tab->addTab(g_system->getOverlayWidth() > 320 ? _("Graphics") : _("GFX")); addGraphicControls(tab, "GlobalOptions_Graphics."); + // + // The shader tab (currently visible only for Vita platform), visibility checking by features + // + + if (g_system->hasFeature(OSystem::kFeatureShader)) { + tab->addTab(_("Shader")); + addShaderControls(tab, "GlobalOptions_Shader."); + } + + // + // The control tab (currently visible only for AndroidSDL, SDL, and Vita platform, visibility checking by features + // + if (g_system->hasFeature(OSystem::kFeatureTouchpadMode) || + g_system->hasFeature(OSystem::kFeatureOnScreenControl) || + g_system->hasFeature(OSystem::kFeatureSwapMenuAndBackButtons) || + g_system->hasFeature(OSystem::kFeatureKbdMouseSpeed) || + g_system->hasFeature(OSystem::kFeatureJoystickDeadzone)) { + tab->addTab(_("Control")); + addControlControls(tab, "GlobalOptions_Control."); + } + // // 2) The audio tab // @@ -1425,7 +1679,7 @@ void GlobalOptionsDialog::build() { // Select the currently configured language or default/English if // nothing is specified. - if (ConfMan.hasKey("gui_language")) + if (ConfMan.hasKey("gui_language") && !ConfMan.get("gui_language").empty()) _guiLanguagePopUp->setSelectedTag(TransMan.parseLanguage(ConfMan.get("gui_language"))); else #ifdef USE_DETECTLANG @@ -1601,8 +1855,12 @@ void GlobalOptionsDialog::clean() { OptionsDialog::clean(); } - + void GlobalOptionsDialog::apply() { + OptionsDialog::apply(); + + bool isRebuildNeeded = false; + Common::String savePath(_savePath->getLabel()); if (!savePath.empty() && (savePath != _("Default"))) ConfMan.set("savepath", savePath, _domain); @@ -1639,33 +1897,6 @@ void GlobalOptionsDialog::apply() { ConfMan.setInt("autosave_period", _autosavePeriodPopUp->getSelectedTag(), _domain); - GUI::ThemeEngine::GraphicsMode selected = (GUI::ThemeEngine::GraphicsMode)_rendererPopUp->getSelectedTag(); - const char *cfg = GUI::ThemeEngine::findModeConfigName(selected); - if (!ConfMan.get("gui_renderer").equalsIgnoreCase(cfg)) { - // FIXME: Actually, any changes (including the theme change) should - // only become active *after* the options dialog has closed. - g_gui.loadNewTheme(g_gui.theme()->getThemeId(), selected); - ConfMan.set("gui_renderer", cfg, _domain); - } -#ifdef USE_TRANSLATION - Common::String oldLang = ConfMan.get("gui_language"); - int selLang = _guiLanguagePopUp->getSelectedTag(); - - ConfMan.set("gui_language", TransMan.getLangById(selLang)); - - Common::String newLang = ConfMan.get("gui_language").c_str(); - if (newLang != oldLang) { - // Activate the selected language - TransMan.setLanguage(selLang); - - // Rebuild the Launcher and Options dialogs - g_gui.loadNewTheme(g_gui.theme()->getThemeId(), ThemeEngine::kGfxDisabled, true); - rebuild(); - if (_launcher != 0) - _launcher->rebuild(); - } -#endif // USE_TRANSLATION - #ifdef USE_UPDATES ConfMan.setInt("updates_check", _updatesPopUp->getSelectedTag()); @@ -1708,35 +1939,74 @@ void GlobalOptionsDialog::apply() { #endif // NETWORKING_LOCALWEBSERVER_ENABLE_PORT_OVERRIDE #endif // USE_SDL_NET #endif // USE_CLOUD - + + Common::String oldThemeId = g_gui.theme()->getThemeId(); + Common::String oldThemeName = g_gui.theme()->getThemeName(); if (!_newTheme.empty()) { -#ifdef USE_TRANSLATION - Common::String lang = TransMan.getCurrentLanguage(); -#endif - Common::String oldTheme = g_gui.theme()->getThemeId(); - if (g_gui.loadNewTheme(_newTheme)) { -#ifdef USE_TRANSLATION - // If the charset has changed, it means the font were not found for the - // new theme. Since for the moment we do not support change of translation - // language without restarting, we let the user know about this. - if (lang != TransMan.getCurrentLanguage()) { - TransMan.setLanguage(lang.c_str()); - g_gui.loadNewTheme(oldTheme); - _curTheme->setLabel(g_gui.theme()->getThemeName()); - MessageDialog error(_("The theme you selected does not support your current language. If you want to use this theme you need to switch to another language first.")); - error.runModal(); - } else { -#endif - ConfMan.set("gui_theme", _newTheme); -#ifdef USE_TRANSLATION - } -#endif - } - draw(); - _newTheme.clear(); + ConfMan.set("gui_theme", _newTheme); } - OptionsDialog::apply(); +#ifdef USE_TRANSLATION + int selectedLang = _guiLanguagePopUp->getSelectedTag(); + Common::String oldLang = ConfMan.get("gui_language"); + Common::String newLang = TransMan.getLangById(selectedLang); + Common::String newCharset; + if (newLang != oldLang) { + TransMan.setLanguage(newLang); + ConfMan.set("gui_language", newLang); + newCharset = TransMan.getCurrentCharset(); + isRebuildNeeded = true; + } +#endif + + GUI::ThemeEngine::GraphicsMode gfxMode = (GUI::ThemeEngine::GraphicsMode)_rendererPopUp->getSelectedTag(); + Common::String oldGfxConfig = ConfMan.get("gui_renderer"); + Common::String newGfxConfig = GUI::ThemeEngine::findModeConfigName(gfxMode); + if (newGfxConfig != oldGfxConfig) { + ConfMan.set("gui_renderer", newGfxConfig, _domain); + } + + if (_newTheme.empty()) + _newTheme = oldThemeId; + + if (!g_gui.loadNewTheme(_newTheme, gfxMode, true)) { + Common::String errorMessage; + + _curTheme->setLabel(oldThemeName); + _newTheme = oldThemeId; + ConfMan.set("gui_theme", _newTheme); + gfxMode = GUI::ThemeEngine::findMode(oldGfxConfig); + _rendererPopUp->setSelectedTag(gfxMode); + newGfxConfig = oldGfxConfig; + ConfMan.set("gui_renderer", newGfxConfig, _domain); +#ifdef USE_TRANSLATION + bool isCharsetEqual = (newCharset == TransMan.getCurrentCharset()); + TransMan.setLanguage(oldLang); + _guiLanguagePopUp->setSelectedTag(selectedLang); + ConfMan.set("gui_language", oldLang); + + if (!isCharsetEqual) + errorMessage = _("Theme does not support selected language!"); + else +#endif + errorMessage = _("Theme cannot be loaded!"); + + g_gui.loadNewTheme(_newTheme, gfxMode, true); + errorMessage += _("\nMisc settings will be restored."); + MessageDialog error(errorMessage); + error.runModal(); + } + + if (isRebuildNeeded) { + rebuild(); + if (_launcher != 0) + _launcher->rebuild(); + } + + _newTheme.clear(); + + // Save config file + ConfMan.flushToDisk(); } void GlobalOptionsDialog::close() { @@ -1860,7 +2130,7 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3 #ifdef USE_LIBCURL case kPopUpItemSelectedCmd: { - //update container's scrollbar + // update container's scrollbar reflowLayout(); break; } @@ -1977,6 +2247,7 @@ void GlobalOptionsDialog::handleTickle() { } void GlobalOptionsDialog::reflowLayout() { + int firstVisible = _tabWidget->getFirstVisible(); int activeTab = _tabWidget->getActiveTab(); #if 0 // ResidualVM does not use it @@ -1987,7 +2258,6 @@ void GlobalOptionsDialog::reflowLayout() { _soundFontClearButton->setNext(0); delete _soundFontClearButton; _soundFontClearButton = addClearButton(_tabWidget, "GlobalOptions_MIDI.mcFontClearButton", kClearSoundFontCmd); - } #endif @@ -2011,6 +2281,8 @@ void GlobalOptionsDialog::reflowLayout() { } _tabWidget->setActiveTab(activeTab); + _tabWidget->setFirstVisible(firstVisible); + OptionsDialog::reflowLayout(); #ifdef USE_CLOUD setupCloudTab(); @@ -2021,7 +2293,7 @@ void GlobalOptionsDialog::reflowLayout() { void GlobalOptionsDialog::setupCloudTab() { int serverLabelPosition = -1; //no override #ifdef USE_LIBCURL - _selectedStorageIndex = _storagePopUp->getSelectedTag(); + _selectedStorageIndex = (_storagePopUp ? _storagePopUp->getSelectedTag() : (uint32) Cloud::kStorageNoneId); if (_storagePopUpDesc) _storagePopUpDesc->setVisible(true); if (_storagePopUp) _storagePopUp->setVisible(true); @@ -2182,8 +2454,10 @@ void GlobalOptionsDialog::setupCloudTab() { #else // USE_SDL_NET if (_runServerButton) _runServerButton->setVisible(false); - if (_serverInfoLabel) + if (_serverInfoLabel) { + _serverInfoLabel->setPos(_serverInfoLabel->getRelX(), serverLabelPosition); // Prevent compiler warning from serverLabelPosition being unused. _serverInfoLabel->setVisible(false); + } if (_rootPathButton) _rootPathButton->setVisible(false); if (_rootPath) diff --git a/gui/options.h b/gui/options.h index 9a478ecdf0c..b89f2bd861d 100644 --- a/gui/options.h +++ b/gui/options.h @@ -86,7 +86,10 @@ protected: virtual void clean(); void rebuild(); + + void addControlControls(GuiObject *boss, const Common::String &prefix); void addGraphicControls(GuiObject *boss, const Common::String &prefix); + void addShaderControls(GuiObject *boss, const Common::String &prefix); void addAudioControls(GuiObject *boss, const Common::String &prefix); void addMIDIControls(GuiObject *boss, const Common::String &prefix); void addMT32Controls(GuiObject *boss, const Common::String &prefix); @@ -112,6 +115,23 @@ protected: int _pathsTabId; private: + + // + // Control controls + // + bool _enableControlSettings; + + CheckboxWidget *_touchpadCheckbox; + CheckboxWidget *_onscreenCheckbox; + CheckboxWidget *_swapMenuAndBackBtnsCheckbox; + + StaticTextWidget *_kbdMouseSpeedDesc; + SliderWidget *_kbdMouseSpeedSlider; + StaticTextWidget *_kbdMouseSpeedLabel; + StaticTextWidget *_joystickDeadzoneDesc; + SliderWidget *_joystickDeadzoneSlider; + StaticTextWidget *_joystickDeadzoneLabel; + // // Graphics controls // @@ -126,6 +146,13 @@ private: PopUpWidget *_rendererTypePopUp; // ResidualVM specific StaticTextWidget *_renderModePopUpDesc; PopUpWidget *_renderModePopUp; + + // + // Shader controls + // + bool _enableShaderSettings; + StaticTextWidget *_shaderPopUpDesc; + PopUpWidget *_shaderPopUp; // // Audio controls @@ -178,6 +205,9 @@ private: // // Volume controls // + void updateMusicVolume(const int newValue) const; + void updateSfxVolume(const int newValue) const; + void updateSpeechVolume(const int newValue) const; bool _enableVolumeSettings; StaticTextWidget *_musicVolumeDesc; diff --git a/gui/saveload-dialog.cpp b/gui/saveload-dialog.cpp index af02e36c131..4bf23b89743 100644 --- a/gui/saveload-dialog.cpp +++ b/gui/saveload-dialog.cpp @@ -474,12 +474,14 @@ void SaveLoadChooserSimple::reflowLayout() { int thumbY = y + kLineHeight; int textLines = 0; - if (!_saveDateSupport) - textLines++; - if (!_playTimeSupport) + if (_saveDateSupport) + textLines += 2; + if (_playTimeSupport) textLines++; + if (textLines > 0) + textLines++; // add a line of padding at the bottom - _container->resize(x, y, w, h - (kLineHeight * textLines)); + _container->resize(x, y, w, h + (kLineHeight * textLines)); _gfxWidget->resize(thumbX, thumbY, thumbW, thumbH); int height = thumbY + thumbH + kLineHeight; @@ -574,7 +576,7 @@ void SaveLoadChooserSimple::updateSelection(bool redraw) { if (startEditMode) { _list->startEditMode(); - if (_chooseButton->isEnabled() && _list->getSelectedString() == _("Untitled savestate") && + if (_chooseButton->isEnabled() && _list->getSelectedString() == _("Untitled saved game") && _list->getSelectionColor() == ThemeEngine::kFontColorAlternate) { _list->setEditString(""); _list->setEditColor(ThemeEngine::kFontColorNormal); @@ -657,12 +659,12 @@ void SaveLoadChooserSimple::updateSaveList() { } } - // Show "Untitled savestate" for empty/whitespace saved game descriptions + // Show "Untitled saved game" for empty/whitespace saved game descriptions Common::String description = x->getDescription(); Common::String trimmedDescription = description; trimmedDescription.trim(); if (trimmedDescription.empty()) { - description = _("Untitled savestate"); + description = _("Untitled saved game"); colors.push_back(ThemeEngine::kFontColorAlternate); } else { colors.push_back((x->getLocked() ? ThemeEngine::kFontColorAlternate : ThemeEngine::kFontColorNormal)); diff --git a/gui/saveload.cpp b/gui/saveload.cpp index 2ac68dd8f91..ba17dacc48c 100644 --- a/gui/saveload.cpp +++ b/gui/saveload.cpp @@ -67,7 +67,7 @@ Common::String SaveLoadChooser::createDefaultSaveDescription(const int slot) con g_system->getTimeAndDate(curTime); curTime.tm_year += 1900; // fixup year curTime.tm_mon++; // fixup month - return Common::String::format("%04d.%02d.%02d / %02d:%02d:%02d", curTime.tm_year, curTime.tm_mon, curTime.tm_mday, curTime.tm_hour, curTime.tm_min, curTime.tm_sec); + return Common::String::format("%04d-%02d-%02d / %02d:%02d:%02d", curTime.tm_year, curTime.tm_mon, curTime.tm_mday, curTime.tm_hour, curTime.tm_min, curTime.tm_sec); #else return Common::String::format("Save %d", slot + 1); #endif diff --git a/gui/themes/default.inc b/gui/themes/default.inc index 4a52fab3d88..25f90be03ec 100644 --- a/gui/themes/default.inc +++ b/gui/themes/default.inc @@ -675,7 +675,7 @@ const char *defaultXML1 = "" "size='15,0' " "/>" "" "" "" "" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" "" "" /** ResidualVM - Unused @@ -859,6 +894,18 @@ const char *defaultXML1 = "" // ResidualVM specific -- End "" "" +"" +"" +"" +"" +"" +"" +"" +"" "" "" "" @@ -1799,7 +1846,7 @@ const char *defaultXML1 = "" "" "" "" "" @@ -2235,14 +2282,14 @@ const char *defaultXML1 = "" "size='9,0' " "/>" "" "" "" "" "" "" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" "" "" +/** ResidualVM - Unused "" "" "" +*/ "" "" -"" +// ResidualVM specific -- End "" +// ResidualVM specific -- Start +"" +"" +"" +"" +// ResidualVM specific -- End +"" +"" +"" +"" +"" +"" +"" +"" "" "" "" diff --git a/gui/themes/modern.zip b/gui/themes/modern.zip index 95df7ffe7f21a0c86ca72bb2deb10366797fb6a8..7f514a9e8e1a2826c3d8f2e4785fbea745614659 100644 GIT binary patch delta 2018 zcmd^AZERCj7{2Yj`|R3vy&vyAt=q~-CJ7&Ib1P-Q=su!j6W3T)V8W7))(+U((rw_T zaYHndeFWyhUOy7E1hYgC1BvuPA~+L884^kSL&N|Xk&r+{K|vC6o_p^$8R|d(+2m=S zbNaj==e+52en|u;zYdzcwJawv>L;|VWyrjqHM+0vYJR=haID$r8&g-kr^bvFEColA zM3GF9LXk?5Mv+coQcjI!OkL9=#s|&4bMp@kJrXnrYSQq=c!$ zYGN2G4)qE|3B4@jnzSZ*dxc+3T8-{uA$5Qgs^2VE`g6tgdbii|#HChE=zLOKrn8Ne z;?Yc9nB}8F?S#Fpeo<-lOI0cQfsbvH$|||1+wy9ad!y1tza@>*GgZ$V7&y)_1ILvs z_oNjW#^M4chXc38kcL0|fyW*XSdycW_IR`fvs~cED_u~O6pq(Ld{`$!0qzYzW_mcj zHrlbdITi?FPZN}i;ka*SAhsqH=_ro`D*~IhRkTOiu*VMz(Ru-jMsmS|tqzdV!tvUs z;F@Sh8yd2q@}cFk*|6%b<;ZzhiQdcL!F@|%K~^|k6YXqk-?DjIRVWbbibn8=8_H~d z+YaH$Z(!L!$i4eP!dqLxiji#DhA}G?W1|mD*kgk>^wa~F;NgG~?+75G^()B4K0nOE zZV~MG5kR`KSb}jwvadN148>>fWj^NIr_#|vSZAg;p#A8L#_D5u)D0^$9^S&h53oe5 ztK0tdBKGv5E26Khbh3IcK4DNH?Or8)fPlni1i3`H5*Gnz$jCOjGuj+&AqRL$~D_Q~Bu+ znVO1bHT6caX~t?SB&1mp=GaJ)Y6$4Fk>wg0vXP}4IZJo6+L+FhLaj8e;)zRVojh5Q zs(MkWI5~_4fk^l*Px7_M-+5B5c^;Mvq*!O20x8v*LibdQ!XE|VN>y76O)Q>=uh?md z3XwRpZZROz>Z%#ys7Q0DGQ1!Xx6TBKlxd~vB~qc0cT}Qgy)4l}s_$VoOrPyJAmy59 zWQKf4i7v{ONs)e{`dQX5(>kiz;g^&>;&o^yq|iC{@5L~(_AcpP@9)}u1RDv-Z&06* zyLCTu6Yp~GJ?)ZQ8dG7vOyg?1PNg-)n$^vUQeCEFb`5sS+QP!H zY+A}Dne960iaY~BDs*KkV=da=%3N*1jSX~8#u%(3(t(JwA9mAj?1y{reJ`Qi{@lSg zeC~Ol_j$f~@41`3*z)Umi@UKwloY|d3hOid?w#V-PmOf99&WXbwc580n|ImCVLO#b zB~dM*T1r(wRY>KaT1MqGCWl@3Z?GLxeeUd3pNFc5%1h-lruvo-yeiQqNn?jYzTYd+ z(wmIbQTbt!W6!&CTr{^E=a0#sco=n^qQsYLCghZh6$9Qg)WsR&`nT>+?MFhL`%H~toYa`xEqksz$$v^NNA3(e81_RkLBPeD*|ItXQK=dB>r@~l-M zc+AQVK{coMXv~~Qyc)_CQ+24|i(7Qs)GUi{>$$C;=}^UE_Asnx9TH*Kz~D_2%wm_q zP|f^=H_op{UxfOJmV(BTYD%G&!5caFoB}WDjzFAKB2S$W+R%L4ag5TxMjQW#KzVra zyakXOHMR @@ -251,6 +251,42 @@ + + + + + + + + + + + + + + + + + + @@ -299,6 +335,19 @@ + + + + + + + + + @@ -1267,7 +1316,7 @@ diff --git a/gui/themes/modern/modern_layout_lowres.stx b/gui/themes/modern/modern_layout_lowres.stx index d79c43d3b8f..7a08daaec9e 100644 --- a/gui/themes/modern/modern_layout_lowres.stx +++ b/gui/themes/modern/modern_layout_lowres.stx @@ -91,14 +91,14 @@ size = '9, 0' /> + + + + + + + + + + + + + + + + + +